From 3bac8676c32447f1ef11fc93ad9891bab45fec5d Mon Sep 17 00:00:00 2001 From: bo Date: Wed, 29 Jul 2026 11:22:21 +0800 Subject: [PATCH 1/4] chore(release): prepare v0.0.6 --- CHANGELOG.md | 32 ++++++++++++++++++++++++++++++++ apps/server/package.json | 2 +- apps/web/package.json | 2 +- bun.lock | 10 +++++----- package.json | 2 +- packages/agent-core/package.json | 2 +- packages/protocol/package.json | 2 +- packages/utils/package.json | 2 +- scripts/release.test.ts | 8 ++++---- 9 files changed, 47 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c50b41b9..aa0aa915 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,38 @@ and this project uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html) ## [Unreleased] +## [0.0.6] - 2026-07-29 + +### Added + +- Add durable Session attachments with guarded upload, model projection, and + composer support. +- Stream live Bash output into the workbench while preserving bounded, + redacted tool results. +- Add execution navigation and sortable Project Todo workflows for moving + between parallel work and entering multiple Sessions. +- Add safe Session and Automation deletion with active-run shutdown, + Automation reference protection, and cross-tab cleanup. + +### Changed + +- Unify suspend, resume, steer, approval, and terminal handling under one + logical Execution lifecycle. +- Preserve persisted message phases when projecting model workstreams so + commentary, reasoning, tools, and final output retain their real order. +- Refine the Session composer, navigation, dashboard, and work activity + presentation for denser engineering workflows. + +### Fixed + +- Restore live Execution state from authoritative Session snapshots after + reconnecting or refreshing. +- Stabilize streaming work presentation and temporal text updates during + long-running executions. +- Require the one-time setup token before exposing the first-run setup form. +- Preserve structured tool payloads while keeping secret redaction at the + finalized output boundary. + ## [0.0.5] - 2026-07-27 ### Changed diff --git a/apps/server/package.json b/apps/server/package.json index 2497a782..4bb2e1d4 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -1,6 +1,6 @@ { "name": "@archcode/server", - "version": "0.0.5", + "version": "0.0.6", "private": true, "type": "module", "main": "./src/main.ts", diff --git a/apps/web/package.json b/apps/web/package.json index db36d361..f7a21240 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,6 +1,6 @@ { "name": "@archcode/web", - "version": "0.0.5", + "version": "0.0.6", "private": true, "type": "module", "scripts": { diff --git a/bun.lock b/bun.lock index 9fc5eec4..f53f8ca4 100644 --- a/bun.lock +++ b/bun.lock @@ -24,7 +24,7 @@ }, "apps/server": { "name": "@archcode/server", - "version": "0.0.5", + "version": "0.0.6", "bin": { "archcode": "./src/main.ts", }, @@ -49,7 +49,7 @@ }, "apps/web": { "name": "@archcode/web", - "version": "0.0.5", + "version": "0.0.6", "dependencies": { "@archcode/protocol": "workspace:*", "@dnd-kit/core": "^6.3.1", @@ -83,7 +83,7 @@ }, "packages/agent-core": { "name": "@archcode/agent-core", - "version": "0.0.5", + "version": "0.0.6", "dependencies": { "@ai-sdk/alibaba": "^1.0.0", "@ai-sdk/amazon-bedrock": "^4.0.0", @@ -137,14 +137,14 @@ }, "packages/protocol": { "name": "@archcode/protocol", - "version": "0.0.5", + "version": "0.0.6", "devDependencies": { "typescript": "^6.0.3", }, }, "packages/utils": { "name": "@archcode/utils", - "version": "0.0.5", + "version": "0.0.6", "devDependencies": { "typescript": "^6.0.3", }, diff --git a/package.json b/package.json index da89ef3b..70851bba 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "archcode", - "version": "0.0.5", + "version": "0.0.6", "private": true, "description": "ArchCode — Not just a coding agent. An always-on workbench for AI engineering.", "type": "module", diff --git a/packages/agent-core/package.json b/packages/agent-core/package.json index 8eb32967..ba509f2a 100644 --- a/packages/agent-core/package.json +++ b/packages/agent-core/package.json @@ -1,6 +1,6 @@ { "name": "@archcode/agent-core", - "version": "0.0.5", + "version": "0.0.6", "private": true, "type": "module", "main": "./src/index.ts", diff --git a/packages/protocol/package.json b/packages/protocol/package.json index 90d6b65f..32662dda 100644 --- a/packages/protocol/package.json +++ b/packages/protocol/package.json @@ -1,6 +1,6 @@ { "name": "@archcode/protocol", - "version": "0.0.5", + "version": "0.0.6", "private": true, "type": "module", "main": "./src/index.ts", diff --git a/packages/utils/package.json b/packages/utils/package.json index a70af23c..336cc8d9 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -1,6 +1,6 @@ { "name": "@archcode/utils", - "version": "0.0.5", + "version": "0.0.6", "private": true, "type": "module", "main": "./src/index.ts", diff --git a/scripts/release.test.ts b/scripts/release.test.ts index c0e47c9f..a9a14308 100644 --- a/scripts/release.test.ts +++ b/scripts/release.test.ts @@ -232,7 +232,7 @@ describe("release metadata", () => { test("compares complete release asset directories by content", async () => { const expectedDir = await mkdtemp(join(tmpdir(), "archcode-release-expected-")); const actualDir = await mkdtemp(join(tmpdir(), "archcode-release-actual-")); - const releaseAssetNames = releaseAssetNamesForVersion("0.0.5"); + const releaseAssetNames = releaseAssetNamesForVersion("0.0.6"); try { for (const name of releaseAssetNames) { await Promise.all([ @@ -258,14 +258,14 @@ describe("release metadata", () => { try { for (const target of releaseTargets) { await writeTestArchive( - join(assetDir, releaseArchiveAssetName(target, "0.0.5")), - "0.0.5", + join(assetDir, releaseArchiveAssetName(target, "0.0.6")), + "0.0.6", ); } const template = await Bun.file(join(import.meta.dir, "install.sh")).text(); await Bun.write( join(assetDir, "install.sh"), - renderReleaseInstaller(template, "0.0.5"), + renderReleaseInstaller(template, "0.0.6"), ); await writeBundleMetadata(assetDir); From eaf2593fa661707b9b792c2573f23bfb8f508196 Mon Sep 17 00:00:00 2001 From: bo Date: Wed, 29 Jul 2026 11:53:40 +0800 Subject: [PATCH 2/4] test(execution): stabilize child recovery fixture --- .../session-execution-manager.test.ts | 75 +++++++++++++++---- 1 file changed, 61 insertions(+), 14 deletions(-) diff --git a/packages/agent-core/src/execution/session-execution-manager.test.ts b/packages/agent-core/src/execution/session-execution-manager.test.ts index 1df31b09..67ca5ac7 100644 --- a/packages/agent-core/src/execution/session-execution-manager.test.ts +++ b/packages/agent-core/src/execution/session-execution-manager.test.ts @@ -2421,13 +2421,41 @@ describe("SessionExecutionManager", () => { } as unknown as MockAgent; parentStore.getState().append(testExecutionStart(parentExecutionId)); const parentStartedAt = parentStore.getState().executions[0]!.startedAt; + const parentStepId = `step-${parentExecutionId}`; + const parentRequest = delegationRequest({ + objective: `recover ${childState}`, + background: false, + }); + parentStore.getState().append({ + type: "step-start", + stepId: parentStepId, + step: 0, + }); + parentStore.getState().append({ + type: "tool-call", + toolCallId, + toolName: "delegate", + input: parentRequest, + }); + parentStore.getState().append({ + type: "step-end", + stepId: parentStepId, + step: 0, + finishReason: "tool-calls", + }); + const parentAssistantMessage = parentStore.getState().messages.find( + (message) => message.role === "assistant" && message.stepId === parentStepId, + ); + if (parentAssistantMessage === undefined) { + throw new Error(`Expected Assistant message for ${parentStepId}`); + } const parentBatch: SessionToolBatch = { batchId, executionId: parentExecutionId, runOrdinal: 0, step: 0, - stepId: `step-${parentExecutionId}`, - assistantMessageId: `assistant-${parentExecutionId}`, + stepId: parentStepId, + assistantMessageId: parentAssistantMessage.id, agentName: "lead", allowedTools: ["delegate"], agentSkills: [], @@ -2437,7 +2465,7 @@ describe("SessionExecutionManager", () => { partitionIndex: 0, toolCallId, toolName: "delegate", - input: delegationRequest({ objective: `recover ${childState}`, background: false }), + input: parentRequest, traits: { readOnly: false, destructive: false, concurrencySafe: false }, state: "child_launch", attempt: 1, @@ -2455,7 +2483,7 @@ describe("SessionExecutionManager", () => { createdAt: new Date(parentStartedAt).toISOString(), updatedAt: new Date(parentStartedAt).toISOString(), }; - parentStore.setState({ toolBatches: [parentBatch] }); + await storeManager.updateToolBatches(parentId, workspaceRoot, () => [parentBatch]); parentStore.getState().append(testExecutionSuspended( parentExecutionId, { @@ -2509,17 +2537,26 @@ describe("SessionExecutionManager", () => { } const childRun = deferred(); + const applyOutcomeSettled = deferred(); + let applyOutcomeFailure: { error: unknown } | undefined; const applyOutcome = mock(async (outcome: Parameters>[0]) => { - await applySessionToolBatchChildOutcome({ - storeManager, - sessionId: outcome.parentSessionId, - workspaceRoot: outcome.workspaceRoot, - batchId: outcome.parentToolBatchId, - toolCallId: outcome.parentToolCallId, - childSessionId: outcome.childSessionId, - childExecutionId: outcome.childExecutionId, - outcome: outcome.outcome, - }); + try { + await applySessionToolBatchChildOutcome({ + storeManager, + sessionId: outcome.parentSessionId, + workspaceRoot: outcome.workspaceRoot, + batchId: outcome.parentToolBatchId, + toolCallId: outcome.parentToolCallId, + childSessionId: outcome.childSessionId, + childExecutionId: outcome.childExecutionId, + outcome: outcome.outcome, + }); + } catch (error) { + applyOutcomeFailure = { error }; + throw error; + } finally { + applyOutcomeSettled.resolve(undefined); + } }); const { manager } = createManager({ [parentId]: parentAgent }, { factory: makeFactory(), @@ -2551,6 +2588,16 @@ describe("SessionExecutionManager", () => { .toBe("waiting_for_human"); childRun.resolve({ text: "child recovered", steps: 1 }); await manager.getExecution(workspaceRoot, childId)!.promise; + await applyOutcomeSettled.promise; + if (applyOutcomeFailure !== undefined) throw applyOutcomeFailure.error; + expect(applyOutcome, childState).toHaveBeenCalledTimes(1); + expect( + parentStore.getState().toolBatches[0]!.calls[0]!.childDependency?.outcome, + childState, + ).toMatchObject({ + executionStatus: "completed", + output: "child recovered", + }); } else if (childState === "suspended") { expect(canonicalChild!.getState().executions[0]!.status).toBe("suspended"); expect(parentStore.getState().executions[0]!.status).toBe("suspended"); From 7d90f907e5f377de7c14a4182f163978f5556f21 Mon Sep 17 00:00:00 2001 From: bo Date: Wed, 29 Jul 2026 11:55:22 +0800 Subject: [PATCH 3/4] test(execution): assert recovered child dependency --- .../src/execution/session-execution-manager.test.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/agent-core/src/execution/session-execution-manager.test.ts b/packages/agent-core/src/execution/session-execution-manager.test.ts index 67ca5ac7..f94b67d9 100644 --- a/packages/agent-core/src/execution/session-execution-manager.test.ts +++ b/packages/agent-core/src/execution/session-execution-manager.test.ts @@ -2592,11 +2592,14 @@ describe("SessionExecutionManager", () => { if (applyOutcomeFailure !== undefined) throw applyOutcomeFailure.error; expect(applyOutcome, childState).toHaveBeenCalledTimes(1); expect( - parentStore.getState().toolBatches[0]!.calls[0]!.childDependency?.outcome, + parentStore.getState().toolBatches[0]!.calls[0]!.childDependency, childState, ).toMatchObject({ - executionStatus: "completed", - output: "child recovered", + kind: "child_dependency", + outcome: { + executionStatus: "completed", + output: "child recovered", + }, }); } else if (childState === "suspended") { expect(canonicalChild!.getState().executions[0]!.status).toBe("suspended"); From 97196558129c85a5c55e42543d95f9c582c2560e Mon Sep 17 00:00:00 2001 From: bo Date: Wed, 29 Jul 2026 11:59:09 +0800 Subject: [PATCH 4/4] test(runtime): await parallel HITL execution completion --- packages/agent-core/src/main.test.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/agent-core/src/main.test.ts b/packages/agent-core/src/main.test.ts index 7429977d..65f860f0 100644 --- a/packages/agent-core/src/main.test.ts +++ b/packages/agent-core/src/main.test.ts @@ -1687,11 +1687,14 @@ describe("createRuntime", () => { }); expect((await (await runtime2.contextResolver.resolve(workspaceRoot)).hitl.list()).find((record) => record.hitlId === first.hitlId)?.status).toBe("resolved"); - const secondCallCompleted = nextSessionEvent( + const executionCompleted = nextSessionEvent( runtime2, project.slug, session.sessionId, - (event) => event.payload.type === "tool-result" && event.payload.toolCallId === "question-2", + (event) => ( + event.payload.type === "execution-end" + && event.payload.executionId === batch.executionId + ), ); await runtime2.respondToHitl({ slug: project.slug, @@ -1699,7 +1702,7 @@ describe("createRuntime", () => { hitlId: second.hitlId, response: { type: "question_answer", answers: ["Yes"] }, }); - await secondCallCompleted; + await executionCompleted; const recovered = await runtime2.getSessionFile(workspaceRoot, session.sessionId); expect(recovered.executions).toHaveLength(1);