@platless/axom-cli 0.5.9 → 0.6.52
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -6
- package/dist/axom-patch/cli/README.md +129 -12
- package/dist/axom-patch/cli/account-limits/generate.py +15 -12
- package/dist/axom-patch/cli/account-limits/kimi-proxy.mjs +20 -3
- package/dist/axom-patch/cli/claude-account-limits.json +9 -9
- package/dist/axom-patch/cli/claude-replay-lifecycle.json +16 -0
- package/dist/axom-patch/cli/codex-account-limits.json +11 -11
- package/dist/axom-patch/cli/codex-terminal-output.json +16 -0
- package/dist/axom-patch/cli/commandcode-account-limits.json +9 -9
- package/dist/axom-patch/cli/kimi-quota.mjs +12 -0
- package/dist/axom-patch/cli/kimi-subagents.mjs +173 -0
- package/dist/axom-patch/cli/nightly-task.md +55 -13
- package/dist/axom-patch/cli/probes/claude-replay-lifecycle.mjs +31 -0
- package/dist/axom-patch/cli/probes/codex-terminal-output.mjs +33 -0
- package/dist/axom-patch/cli/probes/kimi-subagent-subwork.mjs +82 -0
- package/dist/axom-patch/cli/probes/zcode-env-account.mjs +81 -0
- package/dist/axom-patch/cli/probes/zcode-subagent-subwork.mjs +7 -0
- package/dist/axom-patch/cli/qoder/dist/axom-acp-compat.mjs +1687 -0
- package/dist/axom-patch/cli/qoder/releases/1.1.75.json +277 -0
- package/dist/axom-patch/cli/qoder-background-terminal.md +13 -6
- package/dist/axom-patch/cli/qoder-foreground-output.md +14 -8
- package/dist/axom-patch/cli/qoder-native-acp.json +227 -0
- package/dist/axom-patch/cli/qoder-steering.md +20 -25
- package/dist/axom-patch/cli/qoder-subagent-subwork.md +17 -68
- package/dist/axom-patch/cli/runtime.json +19 -19
- package/dist/axom-patch/cli/zcode/env-account.mjs +13 -0
- package/dist/axom-patch/cli/zcode/generate-env-account.py +52 -0
- package/dist/axom-patch/cli/zcode-account-limits.json +10 -11
- package/dist/axom-patch/cli/zcode-env-account.json +58 -0
- package/dist/axom-patch/cli/zcode-models.json +9 -10
- package/dist/axom-patch/cli/zcode-subagent-subwork.json +10 -11
- package/dist/cli.js +11 -45
- package/dist/commands/auth.js +4 -1
- package/dist/commands/knowledge.js +47 -0
- package/dist/commands/update.js +126 -3
- package/dist/commands/workstation.js +101 -164
- package/dist/commands/workstationRegistrations.js +116 -0
- package/dist/credentialBroker.js +13 -2
- package/dist/credentials.js +16 -0
- package/dist/install/claude/commands.js +15 -15
- package/dist/keepers/terminal/keeper.mjs +1089 -673
- package/dist/keepers/terminal/manifest.json +4 -4
- package/dist/mcp.js +1 -1
- package/dist/native/darwin-x64/axom-quic +0 -0
- package/dist/workstation/acp/acpAgents.js +113 -102
- package/dist/workstation/acp/acpClient.js +26 -0
- package/dist/workstation/acp/builds/install/npm.js +99 -10
- package/dist/workstation/acp/initializeResponse.js +6 -1
- package/dist/workstation/acp/managedPatches.js +189 -25
- package/dist/workstation/acp/managedRuntime.js +314 -0
- package/dist/workstation/acp/outbox.js +12 -5
- package/dist/workstation/acp/patchAssets.js +40 -0
- package/dist/workstation/acp/permission.js +2 -2
- package/dist/workstation/acp/processor/checkpoint.js +26 -34
- package/dist/workstation/acp/processor/control.js +7 -80
- package/dist/workstation/acp/processor/jsonrpc.js +8 -0
- package/dist/workstation/acp/processor/runtime.js +5 -81
- package/dist/workstation/acp/processor/session.js +54 -82
- package/dist/workstation/acp/processor/state.js +7 -13
- package/dist/workstation/acp/processor/step.js +3 -20
- package/dist/workstation/acp/processor/test/fixtureLog.js +9 -16
- package/dist/workstation/acp/processor/transport/local.js +19 -3
- package/dist/workstation/acp/processor/updates.js +156 -105
- package/dist/workstation/acp/processorSession.js +20 -173
- package/dist/workstation/acp/registry/catalog.js +65 -39
- package/dist/workstation/acp/registry/launch.js +16 -1
- package/dist/workstation/acp/switching.js +0 -3
- package/dist/workstation/acp/test/fakeAgent.js +13 -0
- package/dist/workstation/acp/test/keeperWorld.js +32 -3
- package/dist/workstation/acp/test/raw/sources.js +16 -130
- package/dist/workstation/acp/test/realAdapter.js +1 -47
- package/dist/workstation/acp/test/scriptAgent.js +11 -3
- package/dist/workstation/agentFlow.js +1 -1
- package/dist/workstation/agentSessions.js +114 -166
- package/dist/workstation/channels/agent.js +9 -1
- package/dist/workstation/channels/computer.js +27 -4
- package/dist/workstation/channels/management.js +130 -15
- package/dist/workstation/channels/url.js +1 -1
- package/dist/workstation/config.js +33 -9
- package/dist/workstation/connection/encryptedChannel.js +4 -1
- package/dist/workstation/control/bindingStore.js +204 -0
- package/dist/workstation/control/commands.js +125 -0
- package/dist/workstation/control/daemonIdentity.js +143 -0
- package/dist/workstation/control/deviceCommands.js +191 -0
- package/dist/workstation/control/deviceProtocol.js +68 -0
- package/dist/workstation/control/enrollment.js +106 -0
- package/dist/workstation/control/inspect.js +71 -0
- package/dist/workstation/control/locks.js +110 -0
- package/dist/workstation/control/protocol.js +40 -0
- package/dist/workstation/control/restartGuard.js +46 -0
- package/dist/workstation/control/rootFormat.js +16 -0
- package/dist/workstation/control/runtimeClient.js +152 -0
- package/dist/workstation/control/runtimeHost.js +235 -0
- package/dist/workstation/control/serverIdentity.js +35 -0
- package/dist/workstation/control/updateTransition.js +91 -0
- package/dist/workstation/daemon.js +266 -264
- package/dist/workstation/daemonctl.js +226 -25
- package/dist/workstation/desktopControl/audio.js +99 -0
- package/dist/workstation/desktopControl/automationDevice.js +219 -0
- package/dist/workstation/desktopControl/automationJson.js +74 -0
- package/dist/workstation/desktopControl/automationLocalState.js +23 -0
- package/dist/workstation/desktopControl/automationProtocol.js +253 -0
- package/dist/workstation/desktopControl/automationSchema.generated.js +1 -0
- package/dist/workstation/desktopControl/automationSession.js +81 -0
- package/dist/workstation/desktopControl/automationTransport.js +138 -0
- package/dist/workstation/desktopControl/capabilities.js +93 -0
- package/dist/workstation/desktopControl/captureModes.js +9 -0
- package/dist/workstation/desktopControl/captureTargetLabel.js +9 -0
- package/dist/workstation/desktopControl/deviceAdmission.js +229 -0
- package/dist/workstation/desktopControl/deviceOwnership.js +130 -0
- package/dist/workstation/desktopControl/deviceSockets.js +246 -0
- package/dist/workstation/desktopControl/helperProcess.js +602 -0
- package/dist/workstation/desktopControl/hostGrant.js +65 -0
- package/dist/workstation/desktopControl/lobby.js +131 -0
- package/dist/workstation/desktopControl/macProvider.js +156 -0
- package/dist/workstation/desktopControl/media.js +331 -0
- package/dist/workstation/desktopControl/nativeDeviceAdmission.js +207 -0
- package/dist/workstation/desktopControl/nativeProvider.js +1044 -0
- package/dist/workstation/desktopControl/networkEvidence.js +43 -0
- package/dist/workstation/desktopControl/overview.js +58 -0
- package/dist/workstation/desktopControl/ownershipProtocol.js +31 -0
- package/dist/workstation/desktopControl/protocol.js +311 -0
- package/dist/workstation/desktopControl/provider.js +1 -0
- package/dist/workstation/desktopControl/quicMedia.js +240 -0
- package/dist/workstation/desktopControl/rateController.js +226 -0
- package/dist/workstation/desktopControl/supervisor.js +1774 -0
- package/dist/workstation/desktopControl/transport.js +27 -0
- package/dist/workstation/desktopControl/wallpaper.js +98 -0
- package/dist/workstation/exec/policy.js +5 -0
- package/dist/workstation/files/client.js +10 -1
- package/dist/workstation/files/credentials.js +142 -0
- package/dist/workstation/files/host.js +9 -2
- package/dist/workstation/files/text.js +42 -8
- package/dist/workstation/journal.js +5 -2
- package/dist/workstation/keepers/acp/client.js +38 -2
- package/dist/workstation/keepers/kit/buildVersion.js +35 -11
- package/dist/workstation/keepers/kit/bundle.js +21 -17
- package/dist/workstation/keepers/kit/client.js +2 -2
- package/dist/workstation/keepers/kit/launcher.js +123 -7
- package/dist/workstation/keepers/kit/paths.js +2 -1
- package/dist/workstation/keepers/legacy.js +16 -2
- package/dist/workstation/keepers/policy.js +5 -34
- package/dist/workstation/keepers/terminal/client.js +115 -23
- package/dist/workstation/keepers/terminal/exec.js +6 -3
- package/dist/workstation/keepers/terminal/frames.js +73 -0
- package/dist/workstation/keepers/terminal/generations.js +5 -8
- package/dist/workstation/keepers/terminal/gridProtocol.js +16 -0
- package/dist/workstation/keepers/terminal/input.js +146 -0
- package/dist/workstation/keepers/terminal/keeper.js +22 -8
- package/dist/workstation/keepers/terminal/main.js +1 -1
- package/dist/workstation/keepers/terminal/metadata.js +138 -0
- package/dist/workstation/keepers/terminal/protocol.js +4 -28
- package/dist/workstation/keepers/terminal/pty.js +1 -330
- package/dist/workstation/keepers/terminal/screen.js +246 -0
- package/dist/workstation/keepers/terminal/sessions.js +526 -0
- package/dist/workstation/managedInstall.js +57 -0
- package/dist/workstation/oauth/deviceAuthorization.js +31 -17
- package/dist/workstation/protocol/agent.js +2 -15
- package/dist/workstation/protocol/management.js +113 -5
- package/dist/workstation/protocol/runtimeDiagnostics.js +15 -0
- package/dist/workstation/rd/artifact.js +120 -0
- package/dist/workstation/rd/control.js +117 -0
- package/dist/workstation/rd/display.js +29 -0
- package/dist/workstation/rd/host.js +213 -0
- package/dist/workstation/rd/migrationReadiness.js +46 -0
- package/dist/workstation/rd/provider.js +256 -0
- package/dist/workstation/rd/sessions.js +391 -0
- package/dist/workstation/rd/state.js +57 -0
- package/dist/workstation/rd/update.js +168 -0
- package/dist/workstation/rd/upkeep.js +80 -0
- package/dist/workstation/register.js +27 -37
- package/dist/workstation/registrations/enrollment.js +250 -0
- package/dist/workstation/registrations/enrollmentPending.js +361 -0
- package/dist/workstation/registrations/migration.js +356 -0
- package/dist/workstation/registrations/model.js +34 -0
- package/dist/workstation/registrations/removal.js +222 -0
- package/dist/workstation/registrations/store.js +737 -0
- package/dist/workstation/runtime/desktopBinding.js +20 -0
- package/dist/workstation/runtime/deviceDesktop.js +193 -0
- package/dist/workstation/runtime/deviceRuntime.js +568 -0
- package/dist/workstation/runtime/deviceServices.js +117 -0
- package/dist/workstation/runtime/events.js +20 -0
- package/dist/workstation/runtime/ownership.js +100 -0
- package/dist/workstation/runtime/registrationRuntime.js +575 -0
- package/dist/workstation/runtime/remoteDesktopAdapter.js +186 -0
- package/dist/workstation/runtime/removalActivity.js +184 -0
- package/dist/workstation/service/installer.js +197 -14
- package/dist/workstation/service/launchd.js +2 -0
- package/dist/workstation/service/systemd.js +1 -0
- package/dist/workstation/storageMetrics.js +118 -0
- package/dist/workstation/systemMetrics.js +47 -92
- package/dist/workstation/toolchain.js +94 -0
- package/dist/workstation/tools/browser.js +28 -4
- package/dist/workstation/tools/client.js +8 -27
- package/dist/workstation/tools/info.js +6 -13
- package/dist/workstation/tools/terminalRouter.js +17 -172
- package/dist/workstation/tools/validation.js +55 -1
- package/dist/workstation/update/archiveEvidence.js +206 -0
- package/dist/workstation/update/artifact.js +2 -1
- package/dist/workstation/update/coordinator.js +34 -13
- package/dist/workstation/update/deviceCompatibility.js +119 -0
- package/dist/workstation/update/deviceCoordinator.js +369 -0
- package/dist/workstation/update/deviceHealth.js +104 -0
- package/dist/workstation/update/deviceHealthProtocol.js +26 -0
- package/dist/workstation/update/deviceRecoveryLifecycle.js +46 -0
- package/dist/workstation/update/deviceRollback.js +78 -0
- package/dist/workstation/update/globalInstall.js +186 -0
- package/dist/workstation/update/legacyBridge.js +414 -0
- package/dist/workstation/update/legacyOwner.js +39 -0
- package/dist/workstation/update/npmInstaller.js +51 -25
- package/dist/workstation/update/privateDurability.js +61 -0
- package/dist/workstation/update/rdRollback.js +145 -0
- package/dist/workstation/update/recoveryPlan.js +124 -0
- package/dist/workstation/update/runUpdate.js +10 -3
- package/dist/workstation/update/taskRegistry.js +161 -3
- package/dist/workstation/update/watchdog.js +29806 -333
- package/dist/workstation/update/watchdogProcess.js +72 -0
- package/package.json +11 -7
- package/skills/axom/SKILL.md +11 -11
- package/skills/axom-definition/SKILL.md +3 -3
- package/skills/axom-platform/SKILL.md +3 -3
- package/dist/axom-patch/cli/probes/qoder-steering.mjs +0 -89
- package/dist/axom-patch/cli/probes/qoder-subagent-origin.mjs +0 -61
- package/dist/axom-patch/cli/qoder-steering.json +0 -52
- package/dist/axom-patch/cli/qoder-subagent-origin.json +0 -73
- package/dist/commands/guide.js +0 -38
- package/dist/runtime-deps/qoder-sdk/package-lock.json +0 -1189
- package/dist/runtime-deps/qoder-sdk/package.json +0 -8
- package/dist/workstation/acp/delegationMcp.js +0 -24
- package/dist/workstation/acp/processor/sideChannel.js +0 -80
- package/dist/workstation/acp/qoderStartup.js +0 -45
- package/dist/workstation/acp/test/raw/qoderCaptures.js +0 -125
- package/dist/workstation/qoderAdapter/backgroundProcess.js +0 -118
- package/dist/workstation/qoderAdapter/backgroundTerminal.js +0 -193
- package/dist/workstation/qoderAdapter/backgroundTerminalFold.js +0 -372
- package/dist/workstation/qoderAdapter/callbackContext.js +0 -76
- package/dist/workstation/qoderAdapter/completedContinuation.js +0 -139
- package/dist/workstation/qoderAdapter/contract.js +0 -78
- package/dist/workstation/qoderAdapter/foregroundOutput.js +0 -36
- package/dist/workstation/qoderAdapter/history.js +0 -85
- package/dist/workstation/qoderAdapter/identityIndex.js +0 -191
- package/dist/workstation/qoderAdapter/inputQueue.js +0 -42
- package/dist/workstation/qoderAdapter/main.js +0 -95
- package/dist/workstation/qoderAdapter/mapper.js +0 -117
- package/dist/workstation/qoderAdapter/observerPlugin.js +0 -245
- package/dist/workstation/qoderAdapter/observerReceiver.js +0 -187
- package/dist/workstation/qoderAdapter/permissions.js +0 -61
- package/dist/workstation/qoderAdapter/profile.js +0 -93
- package/dist/workstation/qoderAdapter/sdkDriver.js +0 -48
- package/dist/workstation/qoderAdapter/sdkRuntime.js +0 -93
- package/dist/workstation/qoderAdapter/sdkRuntimeInstall.js +0 -145
- package/dist/workstation/qoderAdapter/session.js +0 -319
- package/dist/workstation/qoderAdapter/subagentMeta.js +0 -28
- package/dist/workstation/qoderAdapter/subwork.js +0 -85
- package/dist/workstation/qoderAdapter/taskControl.js +0 -38
- package/dist/workstation/qoderAdapter/taskLedger.js +0 -163
- package/dist/workstation/qoderAdapter/usage.js +0 -37
- /package/dist/workstation/{qoderAdapter/types.js → desktopControl/automationPreparation.js} +0 -0
|
@@ -1,18 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* A representative raw log that touches every piece of processor state Tasks 16–17b keep: initialize
|
|
3
|
-
* facts and
|
|
4
|
-
* load with history and `historyEnd`),
|
|
3
|
+
* facts and runtime diagnostics, the open session bound from its response (new, then — after a respawn —
|
|
4
|
+
* load with history and `historyEnd`), launch notes, config and steering round trips,
|
|
5
5
|
* every kind of `session/update` route (root, draft, child), message synthesis including an
|
|
6
6
|
* out-of-turn trailing chunk, async tasks, activity, both card kinds and the origin graph, turns
|
|
7
7
|
* (completed, interrupted, cut by an exit), the 17a low-level notes, and the diagnostics.
|
|
8
8
|
*/
|
|
9
9
|
import { createProcessor } from '../step.js';
|
|
10
10
|
import { append, chunk, elicitationRequest, exitEntry, initializeResult, lastPromptId, lastRequestId, newSession, note, permissionRequest, prompt, respond, run, spawnEntry, subagentSpawned, up, update, } from './harness.js';
|
|
11
|
-
export const QODER_PROFILE = {
|
|
12
|
-
cliVersion: '1.1.61', sdkVersion: '0.1.0', executable: '/usr/local/bin/qodercli', persistent: true, agentMessaging: false,
|
|
13
|
-
observerHealthy: true, completedContinuationEnabled: false, sendMessageExposed: false, mode: 'sdk-adapter', transport: 'process',
|
|
14
|
-
capabilities: ['background_tasks_v1'],
|
|
15
|
-
};
|
|
16
11
|
export const LAUNCH = { source: 'registry', executable: '/usr/local/bin/agent', platform: 'darwin', architecture: 'arm64', transport: 'stdio' };
|
|
17
12
|
export function representativeLog() {
|
|
18
13
|
const session = newSession();
|
|
@@ -27,15 +22,13 @@ export function representativeLog() {
|
|
|
27
22
|
agentCapabilities: { loadSession: true, sessionCapabilities: { list: {} } },
|
|
28
23
|
authMethods: [{ id: 'login', name: 'Login', type: 'terminal', args: ['login'] }, { type: 'terminal' }],
|
|
29
24
|
agentInfo: { name: '@agentclientprotocol/claude-agent-acp', version: '1.0.0' },
|
|
30
|
-
_meta: { steering: { supported: true, timings: ['next'] }
|
|
25
|
+
_meta: { steering: { supported: true, timings: ['next'] } },
|
|
31
26
|
})));
|
|
32
27
|
step(
|
|
33
|
-
// Before the open response:
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
mcpServers: [{ type: 'http', name: 'axom-delegation', url: 'https://x.test/mcp/delegation', headers: [{ name: 'Authorization', value: 'secret:delegation' }] }],
|
|
38
|
-
delegation: { expiresAtEpochMs: 1_900_000_000_000, leaseId: 'lease-hash', bindingId: 'b-1', instanceId: 'i-1' },
|
|
28
|
+
// Before the open response: an update with no root yet is dropped.
|
|
29
|
+
note('agent_session_open', {
|
|
30
|
+
mode: 'new', cwd: '/work', redact: ['secret:mcp'],
|
|
31
|
+
mcpServers: [{ type: 'http', name: 'tools', url: 'https://x.test/mcp/tools', headers: [{ name: 'Authorization', value: 'secret:mcp' }] }],
|
|
39
32
|
}), update('S1', { sessionUpdate: 'available_commands_update', availableCommands: [] }));
|
|
40
33
|
step(respond(lastRequestId(session, 'session/new'), {
|
|
41
34
|
sessionId: 'S1',
|
|
@@ -52,14 +45,14 @@ export function representativeLog() {
|
|
|
52
45
|
step(note('agent_steer', { id: 'steer-1', blocksRef: 'steer-1', priority: 'next' }));
|
|
53
46
|
step(respond(lastRequestId(session, '_session/steering'), { outcome: 'injected' }), respond(lastPromptId(session), { stopReason: 'end_turn' }),
|
|
54
47
|
// Trailing, after the response: out of turn, closed before the next turn starts.
|
|
55
|
-
chunk('S1', 'late words'), update('S1', { sessionUpdate: 'async_task_state_update', asyncTaskId: 'task-1', state: 'stopped' }), update('S1', { sessionUpdate: 'async_task_state_update', asyncTaskId: 'task-1', state: 'completed' }),
|
|
48
|
+
chunk('S1', 'late words'), update('S1', { sessionUpdate: 'async_task_state_update', asyncTaskId: 'task-1', state: 'stopped' }), update('S1', { sessionUpdate: 'async_task_state_update', asyncTaskId: 'task-1', state: 'completed' }), prompt(session, 'two'), chunk('S1', 'thinking'), update('S1', { sessionUpdate: 'tool_call', toolCallId: 't2', title: 'Serve', kind: 'execute', rawInput: { command: 'serve', run_in_background: true } }), permissionRequest(10), note('agent_interrupt'));
|
|
56
49
|
step(respond(lastPromptId(session), { stopReason: 'cancelled' }), prompt(session, 'three', { platformTurnId: 'turn-3' }), note('rpc.respond', { requestId: 5, result: { outcome: { outcome: 'cancelled' } } }));
|
|
57
50
|
step(exitEntry(0), spawnEntry('spawn-b'));
|
|
58
51
|
step(respond(lastRequestId(session, 'initialize'), initializeResult({ agentInfo: { name: 'other-agent', version: '2' } })));
|
|
59
52
|
step(note('agent_launch', { launch: LAUNCH }), note('agent_session_open', { mode: 'load', cwd: '/work', sessionId: 'S1' }));
|
|
60
53
|
step(update('S1', { sessionUpdate: 'user_message_chunk', content: { type: 'text', text: 'old question' } }), chunk('S1', 'old answer', 'h1'), subagentSpawned('S1', 'C9'), chunk('C9', 'old child'));
|
|
61
54
|
step(respond(lastRequestId(session, 'session/load'), { configOptions: [] }));
|
|
62
|
-
step(chunk('S1', '
|
|
55
|
+
step(chunk('S1', 'agent replays one more old chunk after its response'), note('historyEnd'));
|
|
63
56
|
step(prompt(session, 'four'), chunk('S1', 'new'), note('historyEnd'));
|
|
64
57
|
return { session, records: session.log.read(0) };
|
|
65
58
|
}
|
|
@@ -19,6 +19,7 @@ import { AcpSession } from '../../../keepers/acp/session.js';
|
|
|
19
19
|
import { KeeperMethodError } from '../../../keepers/kit/protocol.js';
|
|
20
20
|
import { StreamLog } from '../../../keepers/kit/streamLog.js';
|
|
21
21
|
import { DAEMON_FRAME_INDEX } from '../../outbox.js';
|
|
22
|
+
import { assertOwnedDirectory } from '../../../runtime/ownership.js';
|
|
22
23
|
import { RawTruncatedError, checkpointBlob, rawRecordOf, } from './types.js';
|
|
23
24
|
const DEFAULT_WINDOW = 32;
|
|
24
25
|
/** Longest directory name used as is (file systems cap a name at 255 bytes); longer keys get a hashed name. */
|
|
@@ -43,12 +44,15 @@ export class LocalTransport {
|
|
|
43
44
|
logLine;
|
|
44
45
|
constructor(options) {
|
|
45
46
|
this.options = options;
|
|
46
|
-
this.root = path.join(options.configDir, 'agents', 'local');
|
|
47
|
+
this.root = options.outboxDirectory ?? path.join(options.configDir, 'agents', 'local');
|
|
47
48
|
this.now = options.now ?? Date.now;
|
|
48
49
|
this.logLine = options.log ?? (() => { });
|
|
49
50
|
}
|
|
50
51
|
open(sessionKey, label, baseOffset, orphan) {
|
|
51
52
|
const dir = localSessionDir(this.root, sessionKey);
|
|
53
|
+
assertOwnedDirectory(dir, this.options.ownership, 'local-outbox');
|
|
54
|
+
if (this.options.ownership)
|
|
55
|
+
label = { ...label, registrationId: this.options.ownership.registrationId };
|
|
52
56
|
if (path.basename(dir).startsWith('~')) {
|
|
53
57
|
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
54
58
|
writeFileSync(path.join(dir, KEY_FILE), sessionKey, { mode: 0o600 });
|
|
@@ -71,12 +75,15 @@ export class LocalTransport {
|
|
|
71
75
|
async importOutbox(key, records) {
|
|
72
76
|
const sessionKey = sessionKeyOf(key);
|
|
73
77
|
const dir = localSessionDir(this.root, sessionKey);
|
|
74
|
-
if (this.entries.has(sessionKey) || existsSync(dir))
|
|
78
|
+
if (this.entries.has(sessionKey) || existsSync(dir)) {
|
|
79
|
+
assertOwnedDirectory(dir, this.options.ownership, 'local-outbox');
|
|
75
80
|
return false;
|
|
81
|
+
}
|
|
76
82
|
mkdirSync(this.root, { recursive: true, mode: 0o700 });
|
|
77
83
|
const staging = path.join(this.root, `.staging-${randomUUID()}`);
|
|
78
84
|
try {
|
|
79
85
|
mkdirSync(staging, { mode: 0o700 });
|
|
86
|
+
assertOwnedDirectory(staging, this.options.ownership);
|
|
80
87
|
if (path.basename(dir).startsWith('~'))
|
|
81
88
|
writeFileSync(path.join(staging, KEY_FILE), sessionKey, { mode: 0o600 });
|
|
82
89
|
const log = StreamLog.open({ dir: staging, now: this.now, immediateKinds: [] });
|
|
@@ -127,6 +134,13 @@ export class LocalTransport {
|
|
|
127
134
|
catch {
|
|
128
135
|
continue;
|
|
129
136
|
}
|
|
137
|
+
try {
|
|
138
|
+
assertOwnedDirectory(path.join(this.root, name), this.options.ownership, 'local-outbox');
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
this.logLine('ACP local state skipped: registration owner is unknown or mismatched');
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
130
144
|
if (!this.entries.has(key))
|
|
131
145
|
keys.push(key);
|
|
132
146
|
}
|
|
@@ -135,8 +149,10 @@ export class LocalTransport {
|
|
|
135
149
|
entry(key) {
|
|
136
150
|
const sessionKey = sessionKeyOf(key);
|
|
137
151
|
const existing = this.entries.get(sessionKey);
|
|
138
|
-
if (existing)
|
|
152
|
+
if (existing) {
|
|
153
|
+
assertOwnedDirectory(existing.dir, this.options.ownership, 'local-outbox');
|
|
139
154
|
return existing;
|
|
155
|
+
}
|
|
140
156
|
if (this.orphans().includes(sessionKey))
|
|
141
157
|
return this.open(sessionKey, {}, undefined, true);
|
|
142
158
|
throw new KeeperMethodError(AcpErrorCode.notFound, 'ACP session not found');
|
|
@@ -1,69 +1,6 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `session/update` → journaled `agent_event`s, one record at a time, in log order (Task 17b).
|
|
3
|
-
*
|
|
4
|
-
* ## One path, in log order (replaces `draftInterceptor` + the SDK's async chain)
|
|
5
|
-
*
|
|
6
|
-
* Today a draft variant or a child session's update is diverted by `interceptDraftUpdates` and
|
|
7
|
-
* applied **synchronously** while the stream is read, and every other update travels the SDK's
|
|
8
|
-
* **asynchronous** handler chain — so the two orders depend on pipe chunking (analysis B §0.1: the
|
|
9
|
-
* same bytes, written at once or line by line, gave different event orders). Here both kinds are
|
|
10
|
-
* handled on their own record, so the order of events is the order of the log, whatever the
|
|
11
|
-
* chunking. What differs between them is only what `AcpSession` does with each, kept as it is:
|
|
12
|
-
*
|
|
13
|
-
* - **The root session's own ordinary update** is parsed with the SDK's `zSessionNotification` (as the
|
|
14
|
-
* SDK's router does — so unknown keys are stripped and an unknown variant is dropped), mapped with
|
|
15
|
-
* the root scope, and routed like `AcpSession.routeMappedEvent`: background-writer evidence and
|
|
16
|
-
* message-boundary tracking for `agent_message` synthesis.
|
|
17
|
-
* - **A draft variant (any session) or any update on another session** is mapped raw, with that
|
|
18
|
-
* session's own scope, like `AcpSession.applyDivertedUpdate` → `emitEvent`: a child's events carry
|
|
19
|
-
* `acpSessionId`, and none of them touch the root's message boundaries.
|
|
20
|
-
*
|
|
21
|
-
* Both feed the permission-origin graph (`subagent_spawned`) and the unfinished async-task set.
|
|
22
|
-
*
|
|
23
|
-
* ## Binding, and what replaced the pre-bind buffer
|
|
24
|
-
*
|
|
25
|
-
* The root is bound by the open **response** (`control.ts`). An update that arrives before any root
|
|
26
|
-
* is bound — outside a load's history window — has no session to belong to and is dropped
|
|
27
|
-
* (`droppedUpdates`). Today's 32-entry, 5-second pre-bind buffer existed because the SDK processed
|
|
28
|
-
* the `session/new` response *after* an update the agent had written *after* it; in log order that
|
|
29
|
-
* update follows the response and is applied normally (analysis B A3/C1; the raw captures in `tmp/`
|
|
30
|
-
* show Claude's first `available_commands_update` after the response line).
|
|
31
|
-
*
|
|
32
|
-
* ## Load history
|
|
33
|
-
*
|
|
34
|
-
* Between a `session/load` request and the `historyEnd` note, every update (any session) is history — except a level update
|
|
35
|
-
* ({@link LEVEL_UPDATES}) the loaded session sends once its response is in, which is folded live (Task 21):
|
|
36
|
-
* folded with its own scopes and message boundaries exactly as `adoption.ts` `foldScopedReplayEvents`
|
|
37
|
-
* folds a finished list — incrementally here — then validated and emitted **unnumbered** as
|
|
38
|
-
* `processor.history_event` frames for the daemon's backfill, and observed for origins and
|
|
39
|
-
* background writers (`AcpSession.observeReplayEvent`).
|
|
40
|
-
*
|
|
41
|
-
* ## `agent_message` synthesis and out-of-turn chunks
|
|
42
|
-
*
|
|
43
|
-
* `AcpSession`'s rule: an `agent_message` closes the streaming assistant message when its `messageId`
|
|
44
|
-
* changes and when a turn ends (before the turn's `agent_turn_done`). One addition, decided for 17b:
|
|
45
|
-
* chunks that arrive with **no turn running** — after a prompt's response, or an agent working on its
|
|
46
|
-
* own — are emitted as they are (an `agent_event` never carries a turn id; that is what today's path
|
|
47
|
-
* does with them too), and the message they open is closed **before the next turn starts** instead
|
|
48
|
-
* of being merged into that turn's message when neither carries a `messageId`.
|
|
49
|
-
*
|
|
50
|
-
* ## The Qoder adapter boundaries (Task 17c)
|
|
51
|
-
*
|
|
52
|
-
* `AcpSession` passes events through three adapter boundaries, and so does the processor, over its
|
|
53
|
-
* checkpointed data (`state.boundaries`), at the same points:
|
|
54
|
-
* - the root's own update ({@link routeOwnEvent} = `routeMappedEvent`): the foreground-output boundary
|
|
55
|
-
* may hold an unaddressed chunk back (certified qoder-cli), the terminal repair observes it, the
|
|
56
|
-
* subwork boundary maps it (`supportsSubworkMeta`) — and only an unscoped mapped chunk tracks the
|
|
57
|
-
* root's message boundary;
|
|
58
|
-
* - everything `AcpSession.emitEvent` emits ({@link emitSessionEvent}): diverted updates, cards, and the
|
|
59
|
-
* terminal repair's own events (`sideChannel.ts`) — observed by the repair, then subwork-mapped;
|
|
60
|
-
* - load history (`observeReplayEvent`): observed by the repair.
|
|
61
|
-
*/
|
|
1
|
+
/** Session updates map in log order. Root chunks track streaming message boundaries; diverted and child updates retain their session address. Both feed the permission-origin graph and unfinished async-task state. Load history uses separate mapper scopes and emits unnumbered backfill events. */
|
|
62
2
|
import { DRAFT_SESSION_UPDATE_NAMES } from '../draftSubagents.js';
|
|
63
3
|
import { mapSessionUpdate, newMapperScopeData, SessionMapperState } from '../sessionUpdateMapper.js';
|
|
64
|
-
import { certifiedRuntime, ForegroundOutputBoundary } from '../../qoderAdapter/foregroundOutput.js';
|
|
65
|
-
import { SubworkBoundary, supportsSubworkMeta } from '../../qoderAdapter/subwork.js';
|
|
66
|
-
import { TerminalRepairFold } from '../../qoderAdapter/backgroundTerminalFold.js';
|
|
67
4
|
import { validateWireEvent } from '../validate.js';
|
|
68
5
|
import { emitEvent, isRecord } from './emit.js';
|
|
69
6
|
import { observeSubagentSpawned } from './origins.js';
|
|
@@ -80,6 +17,10 @@ const DRAFT_NAMES = new Set(DRAFT_SESSION_UPDATE_NAMES);
|
|
|
80
17
|
const LEVEL_UPDATES = new Set([
|
|
81
18
|
'available_commands_update', 'current_mode_update', 'config_option_update', 'usage_update', 'session_info_update',
|
|
82
19
|
]);
|
|
20
|
+
/** Only configuration snapshots: terminal usage, titles and actual output remain observable. */
|
|
21
|
+
const CLOSED_REPLAY_LEVELS = new Set([
|
|
22
|
+
'available_commands_update', 'current_mode_update', 'config_option_update',
|
|
23
|
+
]);
|
|
83
24
|
const SESSION_NOTIFICATION = INBOUND_NOTIFICATION_SCHEMAS['session/update'];
|
|
84
25
|
function stringField(payload, key) {
|
|
85
26
|
const value = payload[key];
|
|
@@ -112,6 +53,15 @@ export function onSessionUpdate(state, params, out, t) {
|
|
|
112
53
|
state.diagnostics.droppedUpdates += 1;
|
|
113
54
|
return;
|
|
114
55
|
}
|
|
56
|
+
// A load may replay and end a child that the host did not request as backfill.
|
|
57
|
+
// Its later configuration snapshot is still historical, not a live child event.
|
|
58
|
+
// Keep this narrow: unknown scopes and actual content/tool/lifecycle events retain
|
|
59
|
+
// their normal routing and ownership checks. A live spawn removes the tombstone.
|
|
60
|
+
if (sessionId !== root && getOwn(state.closedReplayScopes, sessionId) && CLOSED_REPLAY_LEVELS.has(String(update.sessionUpdate))) {
|
|
61
|
+
state.diagnostics.droppedUpdates += 1;
|
|
62
|
+
out.unjournaled({ t: 'processor.closed_replay_level', sessionId, sessionUpdate: update.sessionUpdate });
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
115
65
|
const variant = update.sessionUpdate;
|
|
116
66
|
const draft = typeof variant === 'string' && DRAFT_NAMES.has(variant);
|
|
117
67
|
if (!draft && sessionId === root) {
|
|
@@ -137,63 +87,128 @@ export function onSessionUpdate(state, params, out, t) {
|
|
|
137
87
|
for (const event of events)
|
|
138
88
|
emitSessionEvent(state, out, sessionId === root ? event : { ...event, acpSessionId: sessionId });
|
|
139
89
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
90
|
+
const CHUNK_CHURN = new WeakMap();
|
|
91
|
+
const CHUNK_TICKS = new WeakMap();
|
|
92
|
+
const canonical = (value) => { try {
|
|
93
|
+
return JSON.stringify(value) ?? 'undefined';
|
|
143
94
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
95
|
+
catch {
|
|
96
|
+
return 'unserializable';
|
|
97
|
+
} };
|
|
98
|
+
const isVolatileMetaKey = (key) => key === 'timestamp' || key.endsWith('.timestamp') || key.endsWith('_timestamp');
|
|
99
|
+
/** Every event through the live/history entry points ticks once; a tracker whose last chunk is not the immediately
|
|
100
|
+
* preceding event starts fresh. A tool call or any other update between two chunks therefore keeps their ids
|
|
101
|
+
* meaningful — which also matches the server, where an unrelated event already flushes the pending text. */
|
|
102
|
+
function tickEvent(state) {
|
|
103
|
+
const ticks = CHUNK_TICKS.get(state) ?? { tick: 0 };
|
|
104
|
+
ticks.tick += 1;
|
|
105
|
+
CHUNK_TICKS.set(state, ticks);
|
|
106
|
+
return ticks.tick;
|
|
147
107
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
108
|
+
function churnStateOf(state, key, tick) {
|
|
109
|
+
let perState = CHUNK_CHURN.get(state);
|
|
110
|
+
if (!perState)
|
|
111
|
+
CHUNK_CHURN.set(state, perState = new Map());
|
|
112
|
+
const fresh = state.turns.length;
|
|
113
|
+
let churn = perState.get(key);
|
|
114
|
+
if (!churn || churn.seenTurns !== fresh || churn.lastTick !== tick - 1)
|
|
115
|
+
perState.set(key, churn = { prevId: null, anchorId: null, trusted: false, prevMeta: null, anchorMeta: {}, churnyMetaKeys: new Set(), trustedMetaKeys: new Set(), seenTurns: fresh, lastTick: tick });
|
|
116
|
+
churn.lastTick = tick;
|
|
117
|
+
return churn;
|
|
154
118
|
}
|
|
155
|
-
/**
|
|
156
|
-
|
|
157
|
-
|
|
119
|
+
/** Absorb per-delta id/meta churn on one assistant chunk (see the block comment above); other events pass through. */
|
|
120
|
+
function absorbAssistantChunkChurn(state, tick, event) {
|
|
121
|
+
if (event.eventName !== 'agent_message_chunk' && event.eventName !== 'agent_thought_chunk')
|
|
122
|
+
return event;
|
|
123
|
+
const payload = event.payload;
|
|
124
|
+
const churn = churnStateOf(state, `${event.acpSessionId ?? 'root'}:${event.eventName}`, tick);
|
|
125
|
+
let changed = false;
|
|
126
|
+
const rawId = typeof payload.messageId === 'string' ? payload.messageId : null;
|
|
127
|
+
if (rawId !== null) {
|
|
128
|
+
if (churn.prevId !== null && rawId === churn.prevId)
|
|
129
|
+
churn.trusted = true;
|
|
130
|
+
if (churn.trusted) {
|
|
131
|
+
churn.anchorId = rawId;
|
|
132
|
+
}
|
|
133
|
+
else if (churn.prevId !== null) {
|
|
134
|
+
// A change with no stable pair ever seen: churn — keep the anchor.
|
|
135
|
+
if (churn.anchorId !== null && churn.anchorId !== rawId) {
|
|
136
|
+
payload.messageId = churn.anchorId;
|
|
137
|
+
changed = true;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
churn.anchorId = rawId;
|
|
142
|
+
}
|
|
143
|
+
churn.prevId = rawId;
|
|
144
|
+
}
|
|
145
|
+
const rawMeta = payload._meta;
|
|
146
|
+
if (rawMeta !== null && typeof rawMeta === 'object') {
|
|
147
|
+
const meta = rawMeta;
|
|
148
|
+
const emitted = {};
|
|
149
|
+
let anchored = false;
|
|
150
|
+
for (const [key, value] of Object.entries(meta)) {
|
|
151
|
+
if (!isVolatileMetaKey(key)) {
|
|
152
|
+
emitted[key] = value;
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
const previous = churn.prevMeta;
|
|
156
|
+
if (previous !== null && key in previous) {
|
|
157
|
+
if (canonical(value) === canonical(previous[key]))
|
|
158
|
+
churn.trustedMetaKeys.add(key);
|
|
159
|
+
else if (!churn.trustedMetaKeys.has(key)) {
|
|
160
|
+
// First sighting of a change on a never-stable key: anchor it to what the previous chunk
|
|
161
|
+
// already emitted, so every chunk of this stream carries the identical map.
|
|
162
|
+
if (!(key in churn.anchorMeta))
|
|
163
|
+
churn.anchorMeta[key] = previous[key];
|
|
164
|
+
churn.churnyMetaKeys.add(key);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
emitted[key] = churn.churnyMetaKeys.has(key) && key in churn.anchorMeta ? churn.anchorMeta[key] : value;
|
|
168
|
+
if (emitted[key] !== value)
|
|
169
|
+
anchored = true;
|
|
170
|
+
}
|
|
171
|
+
churn.prevMeta = meta;
|
|
172
|
+
if (anchored) {
|
|
173
|
+
payload._meta = emitted;
|
|
174
|
+
changed = true;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return changed ? { ...event, payload } : event;
|
|
158
178
|
}
|
|
159
|
-
/** `AcpSession.routeMappedEvent`, Qoder boundaries included. */
|
|
160
179
|
function routeOwnEvent(state, out, event) {
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
if ((!subwork || !mapped.acpSessionId || mapped.acpSessionId === root) &&
|
|
170
|
-
(mapped.eventName === 'agent_message_chunk' || mapped.eventName === 'agent_thought_chunk'))
|
|
171
|
-
trackMessageBoundary(state, out, mapped);
|
|
172
|
-
emitEvent(state, out, mapped);
|
|
173
|
-
}
|
|
180
|
+
const tick = tickEvent(state);
|
|
181
|
+
event = absorbAssistantChunkChurn(state, tick, event);
|
|
182
|
+
observe(state, event);
|
|
183
|
+
observeBackgroundWriter(state, event);
|
|
184
|
+
if ((!event.acpSessionId || event.acpSessionId === state.rootSessionId) &&
|
|
185
|
+
(event.eventName === 'agent_message_chunk' || event.eventName === 'agent_thought_chunk'))
|
|
186
|
+
trackMessageBoundary(state, out, event);
|
|
187
|
+
emitEvent(state, out, event);
|
|
174
188
|
}
|
|
175
|
-
/**
|
|
176
|
-
* `AcpSession.emitEvent` (minus a card's own checks, `session.ts`): a diverted update, a card, or a
|
|
177
|
-
* repair event. The repair observes it as given; a root id on `acpSessionId` is dropped; the subwork
|
|
178
|
-
* boundary maps it; each mapped event feeds the origin graph and is numbered.
|
|
179
|
-
*/
|
|
189
|
+
/** Normalize the root session address, observe the event, then emit. */
|
|
180
190
|
export function emitSessionEvent(state, out, event) {
|
|
181
|
-
|
|
191
|
+
const tick = tickEvent(state);
|
|
192
|
+
event = absorbAssistantChunkChurn(state, tick, event);
|
|
182
193
|
// Inside a load's history window the session being loaded is the root (an attached `AcpSession` knows it).
|
|
183
194
|
const normalized = event.acpSessionId === (state.rootSessionId ?? state.history?.rootSessionId)
|
|
184
195
|
? { eventName: event.eventName, payload: event.payload }
|
|
185
196
|
: event;
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
emitEvent(state, out, mapped);
|
|
189
|
-
}
|
|
197
|
+
observe(state, normalized);
|
|
198
|
+
emitEvent(state, out, normalized);
|
|
190
199
|
}
|
|
191
200
|
/** What every live event feeds besides the outbox: the origin graph and the async-task set. */
|
|
192
201
|
function observe(state, event) {
|
|
193
202
|
if (event.eventName === 'subagent_spawned') {
|
|
194
203
|
observeSubagentSpawned(state, event.acpSessionId ?? state.rootSessionId, event.payload.subagentSessionId);
|
|
204
|
+
const child = event.payload.subagentSessionId;
|
|
205
|
+
if (typeof child === 'string' && getOwn(state.permissionOrigins, child) &&
|
|
206
|
+
getOwn(state.permissionParents, child) === (event.acpSessionId ?? state.rootSessionId)) {
|
|
207
|
+
delete state.closedReplayScopes[child];
|
|
208
|
+
}
|
|
195
209
|
}
|
|
196
210
|
trackAsyncTask(state, event);
|
|
211
|
+
trackDraftSubagent(state, event);
|
|
197
212
|
}
|
|
198
213
|
/** `AcpSession.observeBackgroundWriter`: an explicitly detached writer blocks `executionEnded` for good. */
|
|
199
214
|
function observeBackgroundWriter(state, event) {
|
|
@@ -205,6 +220,33 @@ function observeBackgroundWriter(state, event) {
|
|
|
205
220
|
state.backgroundWriterUnconfirmed = true;
|
|
206
221
|
}
|
|
207
222
|
}
|
|
223
|
+
const DRAFT_CHILD_LIMIT = 4096;
|
|
224
|
+
const DRAFT_TERMINALS = new Set(['completed', 'failed', 'cancelled']);
|
|
225
|
+
/** Keep terminal tombstones: duplicated/reordered spawns cannot resurrect completed children.
|
|
226
|
+
* At capacity retain a conservative blocker rather than forget potentially running work. */
|
|
227
|
+
function trackDraftSubagent(state, event) {
|
|
228
|
+
if (event.eventName !== 'subagent_spawned' && event.eventName !== 'subagent_state_update')
|
|
229
|
+
return;
|
|
230
|
+
const id = event.payload.subagentSessionId;
|
|
231
|
+
if (typeof id !== 'string' || !id.length)
|
|
232
|
+
return;
|
|
233
|
+
const data = state.draftSubagents;
|
|
234
|
+
if (id.length > 256) {
|
|
235
|
+
data.overflow = true;
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
const terminal = event.eventName === 'subagent_state_update' && DRAFT_TERMINALS.has(String(event.payload.state));
|
|
239
|
+
if (getOwn(data.children, id) === false)
|
|
240
|
+
return;
|
|
241
|
+
if (!Object.hasOwn(data.children, id) && Object.keys(data.children).length >= DRAFT_CHILD_LIMIT) {
|
|
242
|
+
data.overflow = true;
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
putOwn(data.children, id, !terminal);
|
|
246
|
+
}
|
|
247
|
+
export function unfinishedDraftSubagentCount(state) {
|
|
248
|
+
return Object.values(state.draftSubagents.children).filter(Boolean).length + Number(state.draftSubagents.overflow);
|
|
249
|
+
}
|
|
208
250
|
const TERMINAL_ASYNC_STATES = new Set(['completed', 'failed', 'stopped']);
|
|
209
251
|
/**
|
|
210
252
|
* The unfinished async tasks (AIR `async_task_*`), per native session: spawned → running; a state
|
|
@@ -296,7 +338,9 @@ function onHistoryUpdate(state, history, sessionId, update, out) {
|
|
|
296
338
|
history.droppedCount += 1;
|
|
297
339
|
return;
|
|
298
340
|
}
|
|
299
|
-
for (const
|
|
341
|
+
for (const rawEvent of events) {
|
|
342
|
+
// Same churn absorption as the live path: a replayed CodeBuddy history fragments identically.
|
|
343
|
+
const event = absorbAssistantChunkChurn(state, tickEvent(state), rawEvent);
|
|
300
344
|
if (event.eventName === 'user_message_chunk')
|
|
301
345
|
flushHistoryMessage(state, history, sessionId, out);
|
|
302
346
|
if (event.eventName === 'agent_message_chunk' || event.eventName === 'agent_thought_chunk') {
|
|
@@ -331,10 +375,17 @@ function emitHistory(state, history, out, event, sessionId) {
|
|
|
331
375
|
return;
|
|
332
376
|
}
|
|
333
377
|
// `AcpSession.observeReplayEvent`: history restores the repair's calls, addresses and background-writer evidence.
|
|
334
|
-
repairFold(state)?.observe(scoped);
|
|
335
378
|
if (scoped.eventName === 'subagent_spawned') {
|
|
336
379
|
observeSubagentSpawned(state, scoped.acpSessionId ?? history.rootSessionId, scoped.payload.subagentSessionId, history.rootSessionId);
|
|
337
380
|
}
|
|
381
|
+
trackDraftSubagent(state, scoped);
|
|
382
|
+
if (scoped.eventName === 'subagent_state_update' && DRAFT_TERMINALS.has(String(scoped.payload.state))) {
|
|
383
|
+
const child = scoped.payload.subagentSessionId;
|
|
384
|
+
// Share the lifecycle tracker's bounded, validated identity set.
|
|
385
|
+
if (typeof child === 'string' && getOwn(state.draftSubagents.children, child) === false) {
|
|
386
|
+
putOwn(state.closedReplayScopes, child, true);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
338
389
|
observeBackgroundWriter(state, scoped);
|
|
339
390
|
out.unjournaled({
|
|
340
391
|
t: 'processor.history_event',
|