@platless/axom-cli 0.5.9 → 0.6.36
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 +18 -5
- package/dist/axom-patch/cli/README.md +100 -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/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/codex-terminal-output.mjs +33 -0
- package/dist/axom-patch/cli/probes/kimi-subagent-subwork.mjs +82 -0
- package/dist/axom-patch/cli/qoder/dist/axom-acp-compat.mjs +1591 -0
- package/dist/axom-patch/cli/qoder/releases/1.1.75.json +360 -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 +16 -18
- package/dist/axom-patch/cli/zcode-account-limits.json +10 -11
- package/dist/axom-patch/cli/zcode-models.json +1 -2
- package/dist/axom-patch/cli/zcode-subagent-subwork.json +9 -10
- package/dist/cli.js +4 -36
- package/dist/commands/knowledge.js +47 -0
- package/dist/commands/update.js +65 -3
- package/dist/commands/workstation.js +73 -7
- package/dist/install/claude/commands.js +13 -13
- package/dist/mcp.js +1 -1
- package/dist/native/darwin-arm64/axom-quic +0 -0
- package/dist/workstation/acp/acpAgents.js +102 -98
- 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/patchAssets.js +40 -0
- package/dist/workstation/acp/processor/checkpoint.js +25 -34
- package/dist/workstation/acp/processor/control.js +5 -78
- package/dist/workstation/acp/processor/runtime.js +5 -81
- package/dist/workstation/acp/processor/session.js +51 -80
- package/dist/workstation/acp/processor/state.js +6 -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/updates.js +131 -105
- package/dist/workstation/acp/processorSession.js +8 -172
- 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 +28 -0
- 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 +38 -143
- package/dist/workstation/channels/management.js +95 -11
- package/dist/workstation/channels/url.js +1 -1
- package/dist/workstation/config.js +16 -4
- package/dist/workstation/connection/encryptedChannel.js +4 -1
- package/dist/workstation/control/bindingStore.js +200 -0
- package/dist/workstation/control/commands.js +124 -0
- package/dist/workstation/control/daemonIdentity.js +53 -0
- package/dist/workstation/control/enrollment.js +106 -0
- package/dist/workstation/control/inspect.js +71 -0
- package/dist/workstation/control/locks.js +63 -0
- package/dist/workstation/control/protocol.js +40 -0
- package/dist/workstation/control/restartGuard.js +46 -0
- package/dist/workstation/control/runtimeClient.js +97 -0
- package/dist/workstation/control/runtimeHost.js +148 -0
- package/dist/workstation/control/serverIdentity.js +35 -0
- package/dist/workstation/daemon.js +106 -6
- package/dist/workstation/daemonctl.js +77 -14
- package/dist/workstation/desktopControl/audio.js +99 -0
- package/dist/workstation/desktopControl/capabilities.js +92 -0
- package/dist/workstation/desktopControl/captureModes.js +9 -0
- package/dist/workstation/desktopControl/captureTargetLabel.js +9 -0
- package/dist/workstation/desktopControl/deviceSockets.js +246 -0
- package/dist/workstation/desktopControl/helperProcess.js +353 -0
- package/dist/workstation/desktopControl/lobby.js +129 -0
- package/dist/workstation/desktopControl/macProvider.js +155 -0
- package/dist/workstation/desktopControl/media.js +331 -0
- package/dist/workstation/desktopControl/nativeProvider.js +1018 -0
- package/dist/workstation/desktopControl/networkEvidence.js +43 -0
- package/dist/workstation/desktopControl/overview.js +58 -0
- package/dist/workstation/desktopControl/protocol.js +302 -0
- package/dist/workstation/desktopControl/quicMedia.js +240 -0
- package/dist/workstation/desktopControl/rateController.js +226 -0
- package/dist/workstation/desktopControl/supervisor.js +1681 -0
- package/dist/workstation/desktopControl/transport.js +27 -0
- package/dist/workstation/desktopControl/wallpaper.js +78 -0
- package/dist/workstation/files/credentials.js +142 -0
- package/dist/workstation/files/text.js +42 -8
- package/dist/workstation/keepers/kit/bundle.js +21 -17
- package/dist/workstation/keepers/kit/launcher.js +47 -7
- package/dist/workstation/keepers/kit/paths.js +2 -1
- package/dist/workstation/managedInstall.js +57 -0
- package/dist/workstation/protocol/agent.js +2 -15
- package/dist/workstation/protocol/management.js +93 -4
- package/dist/workstation/protocol/runtimeDiagnostics.js +15 -0
- package/dist/workstation/rd/artifact.js +119 -0
- package/dist/workstation/rd/control.js +119 -0
- package/dist/workstation/rd/display.js +29 -0
- package/dist/workstation/rd/provider.js +222 -0
- package/dist/workstation/rd/sessions.js +391 -0
- package/dist/workstation/rd/state.js +45 -0
- package/dist/workstation/rd/update.js +171 -0
- package/dist/workstation/rd/upkeep.js +38 -0
- package/dist/workstation/register.js +27 -37
- package/dist/workstation/service/installer.js +104 -13
- package/dist/workstation/service/launchd.js +1 -0
- package/dist/workstation/storageMetrics.js +118 -0
- package/dist/workstation/systemMetrics.js +44 -92
- package/dist/workstation/toolchain.js +94 -0
- package/dist/workstation/tools/browser.js +28 -4
- package/dist/workstation/update/artifact.js +2 -1
- package/dist/workstation/update/globalInstall.js +116 -0
- package/dist/workstation/update/npmInstaller.js +49 -23
- package/dist/workstation/update/rdRollback.js +145 -0
- package/dist/workstation/update/runUpdate.js +3 -2
- package/dist/workstation/update/watchdog.js +16790 -334
- package/package.json +5 -5
- 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/provider.js} +0 -0
|
@@ -1,25 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
* The two level signals (spec §5.4: "电平类帧从状态现算"): the agent's self-reported activity
|
|
3
|
-
* (`agent_session_state`) and the runtime diagnostics (`agent_runtime_profile`). Neither is numbered
|
|
4
|
-
* or journaled (analysis B §5); both are folded into state from the log, so after a restart they are
|
|
5
|
-
* recomputed by replay and re-sent with `agent_opened` ({@link activityRaw}, {@link runtimeDiagnostics}).
|
|
6
|
-
*
|
|
7
|
-
* - **Activity** — `connectAgent`'s `_claude/sdkMessage` handler (`claudeActivityRaw`) and the mapper's
|
|
8
|
-
* `codexActivityRaw` on the root session's `session_info_update`, as `AcpSession.reportActivity`
|
|
9
|
-
* joins them. Both sinks are bound only once the session is open, so earlier pushes are dropped,
|
|
10
|
-
* as today.
|
|
11
|
-
* - **Diagnostics** — `generation` is the keeper's `spawnId` (analysis B B2: no `randomUUID`), and
|
|
12
|
-
* `revision` is a fold: +1 on process exit and on every applied Qoder profile (the `connectAgent`
|
|
13
|
-
* closure, `acpClient.ts`). `launch` arrives in an `agent_launch` note the daemon writes after the
|
|
14
|
-
* spawn (analysis B D5: computed once, at spawn). Task 17c: the permission callback snapshot
|
|
15
|
-
* (`callbacks`, {@link callbackSnapshot}) — published, and the revision bumped, on every change to the
|
|
16
|
-
* session's pending/settled permission cards, as `AcpSession.publishCallbacks` does for every agent.
|
|
17
|
-
*/
|
|
1
|
+
const connectionGeneration = (spawnId) => `session:${spawnId}`;
|
|
18
2
|
import { claudeActivityRaw } from '../activityReporting.js';
|
|
19
|
-
import { parseQoderProfile, sameProfileIdentity } from '../../qoderAdapter/profile.js';
|
|
20
|
-
import { connectionGenerationOf } from '../../qoderAdapter/callbackContext.js';
|
|
21
3
|
import { isRecord } from './emit.js';
|
|
22
|
-
import {
|
|
4
|
+
import { initialRuntime } from './state.js';
|
|
23
5
|
function opened(state) {
|
|
24
6
|
return state.open.status === 'open';
|
|
25
7
|
}
|
|
@@ -60,7 +42,7 @@ export function callbackSnapshot(state) {
|
|
|
60
42
|
if (rootSessionId === null || spawnId === undefined)
|
|
61
43
|
return undefined;
|
|
62
44
|
return {
|
|
63
|
-
rootSessionId, connectionGeneration:
|
|
45
|
+
rootSessionId, connectionGeneration: connectionGeneration(spawnId), complete,
|
|
64
46
|
pending: complete ? Object.values(state.permissions).map((card) => ({
|
|
65
47
|
requestId: card.requestId, originNativeSessionId: card.origin, rpcRequestId: card.rpcRequestId,
|
|
66
48
|
})) : [],
|
|
@@ -70,11 +52,11 @@ export function callbackSnapshot(state) {
|
|
|
70
52
|
/** The diagnostics as `agent_runtime_profile`/`agent_opened.runtimeDiagnostics` carry them, or `undefined` before a spawn and its `agent_launch` note. */
|
|
71
53
|
export function runtimeDiagnostics(state) {
|
|
72
54
|
const spawnId = currentSpawnId(state);
|
|
73
|
-
const { revision, available, launch
|
|
55
|
+
const { revision, available, launch } = state.runtime;
|
|
74
56
|
if (spawnId === undefined || launch === undefined)
|
|
75
57
|
return undefined;
|
|
76
58
|
const callbacks = callbackSnapshot(state);
|
|
77
|
-
return { generation: spawnId, revision, available, launch,
|
|
59
|
+
return { generation: spawnId, revision, available, launch,
|
|
78
60
|
...(callbacks !== undefined ? { callbacks } : {}) };
|
|
79
61
|
}
|
|
80
62
|
/**
|
|
@@ -113,69 +95,11 @@ export function onLaunchNote(state, launch) {
|
|
|
113
95
|
state.runtime.launch = { source: source, executable: executable.slice(0, 4096), platform, architecture, transport };
|
|
114
96
|
return true;
|
|
115
97
|
}
|
|
116
|
-
/** The `initialize` result's `_meta.qoderProfile` (`connectAgent`, right after the handshake). */
|
|
117
|
-
export function onInitialized(state, result) {
|
|
118
|
-
const meta = isRecord(result) && isRecord(result._meta) ? result._meta : undefined;
|
|
119
|
-
const profile = parseQoderProfile(meta?.qoderProfile);
|
|
120
|
-
if (profile)
|
|
121
|
-
state.runtime.profile = profile;
|
|
122
|
-
else
|
|
123
|
-
delete state.runtime.profile;
|
|
124
|
-
}
|
|
125
98
|
/** The child exited: unavailable, profile no longer observed (`child.once('exit')` in `connectAgent`). */
|
|
126
99
|
export function onProcessExit(state, out) {
|
|
127
100
|
const runtime = state.runtime;
|
|
128
101
|
runtime.revision += 1;
|
|
129
102
|
runtime.available = false;
|
|
130
|
-
if (runtime.profile)
|
|
131
|
-
runtime.profile = { ...runtime.profile, observerHealthy: false, rootConfirmed: false };
|
|
132
|
-
changed(state, out);
|
|
133
|
-
}
|
|
134
|
-
function applyProfile(state, out, sessionId, revision, profile) {
|
|
135
|
-
const runtime = state.runtime;
|
|
136
|
-
const previous = runtime.profile;
|
|
137
|
-
if (!previous || !runtime.available || sessionId !== state.rootSessionId || revision <= runtime.adapterRevision
|
|
138
|
-
|| !sameProfileIdentity(previous, profile) || profile.rootSessionId !== state.rootSessionId
|
|
139
|
-
|| (previous.runtimeGeneration !== undefined && profile.runtimeGeneration !== previous.runtimeGeneration)
|
|
140
|
-
|| (profile.rootConfirmed === true && !profile.runtimeGeneration))
|
|
141
|
-
return;
|
|
142
|
-
runtime.adapterRevision = revision;
|
|
143
|
-
runtime.revision += 1;
|
|
144
|
-
runtime.profile = profile;
|
|
145
103
|
changed(state, out);
|
|
146
104
|
}
|
|
147
|
-
/** `_axom/qoder/profile{sessionId, revision, profile}` — the `connectAgent` handler, over state. */
|
|
148
|
-
export function onProfileNotification(state, out, params) {
|
|
149
|
-
if (!isRecord(params))
|
|
150
|
-
return;
|
|
151
|
-
const profile = parseQoderProfile(params.profile);
|
|
152
|
-
const { sessionId, revision } = params;
|
|
153
|
-
if (!state.runtime.profile || !profile || typeof sessionId !== 'string' || profile.rootSessionId !== sessionId
|
|
154
|
-
|| !Number.isSafeInteger(revision) || revision <= 0 || !sameProfileIdentity(state.runtime.profile, profile))
|
|
155
|
-
return;
|
|
156
|
-
if (state.rootSessionId !== null) {
|
|
157
|
-
applyProfile(state, out, sessionId, revision, profile);
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
// ACP may notify before its new/load reply: bounded candidates, evidence only once the reply binds.
|
|
161
|
-
const pending = state.runtime.pendingProfiles;
|
|
162
|
-
const previous = getOwn(pending, sessionId);
|
|
163
|
-
if (previous && (revision <= previous.revision || profile.runtimeGeneration !== previous.profile.runtimeGeneration))
|
|
164
|
-
return;
|
|
165
|
-
if (!previous && Object.keys(pending).length >= PENDING_PROFILE_LIMIT)
|
|
166
|
-
return;
|
|
167
|
-
putOwn(pending, sessionId, { revision: revision, profile });
|
|
168
|
-
}
|
|
169
105
|
/** The root just bound (`bindDraftUpdates`' diagnostics half): drop a profile for another root, apply a pending one. */
|
|
170
|
-
export function onRootBound(state, out, sessionId) {
|
|
171
|
-
const runtime = state.runtime;
|
|
172
|
-
const current = runtime.profile;
|
|
173
|
-
if (current?.rootSessionId && current.rootSessionId !== sessionId) {
|
|
174
|
-
const { rootSessionId: _root, runtimeGeneration: _generation, ...rest } = current;
|
|
175
|
-
runtime.profile = { ...rest, rootConfirmed: false, initialized: false, observerHealthy: false };
|
|
176
|
-
}
|
|
177
|
-
const pending = getOwn(runtime.pendingProfiles, sessionId);
|
|
178
|
-
runtime.pendingProfiles = {};
|
|
179
|
-
if (pending)
|
|
180
|
-
applyProfile(state, out, sessionId, pending.revision, pending.profile);
|
|
181
|
-
}
|
|
@@ -1,67 +1,16 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The session layer of the ACP processor (Task 17a): turns, interrupt, permission and elicitation
|
|
3
|
-
* cards, and the permission-origin graph — `AcpSession` + `permission.ts` + the turn half of
|
|
4
|
-
* `agentSessions.ts`, re-expressed as a synchronous fold over the raw log (spec 2026-10-11 §5.4).
|
|
5
|
-
*
|
|
6
|
-
* ## Turns
|
|
7
|
-
*
|
|
8
|
-
* - An `agent_prompt` note asks for a turn. Admission (`TaskRegistry.tryAdmitTurn`, a daemon-wide
|
|
9
|
-
* decision) arrives **in** the note (`admitted: false`), so a refusal lands on the note's offset and
|
|
10
|
-
* replays identically; so do the processor's own refusals (no connection, an overlapping exact
|
|
11
|
-
* turn). Those are the three daemon-initiated `turn_done`s of `agentSessions.ts`'s `agent_prompt`
|
|
12
|
-
* branch, with the same two frame shapes (see {@link refuseTurn} / {@link finishTurn}).
|
|
13
|
-
* - An admitted prompt becomes one `session/prompt` command. The note carries no blocks — only a
|
|
14
|
-
* `blocksRef` the executor resolves (`DownCommand.fill`), so the blocks appear once in the log, in
|
|
15
|
-
* that `down` record (§5.4). `platformTurnId` rides in the command's annotation and is **read back
|
|
16
|
-
* from the `down` record** when the processor folds it: that record is what started the turn on the
|
|
17
|
-
* agent.
|
|
18
|
-
* - A turn ends exactly on the record that settles its request — the agent's response line, a
|
|
19
|
-
* `proc exit`, or an `agent_prompt_failed` note (the executor could not write the line). There is no
|
|
20
|
-
* `settleUpdates` heuristic: log order is the truth (analysis B A4). One consequence, recorded
|
|
21
|
-
* rather than hidden: an update the agent writes **after** its `session/prompt` response belongs to
|
|
22
|
-
* whatever follows, not to the finished turn (analysis B §1.5).
|
|
23
|
-
* - Turns are matched to their response by request id, not FIFO: two overlapping prompts the agent
|
|
24
|
-
* answers out of order keep their own results (`AcpSession.inFlightTurns` documents the swap the
|
|
25
|
-
* FIFO rule had).
|
|
26
|
-
*
|
|
27
|
-
* ## Cards
|
|
28
|
-
*
|
|
29
|
-
* `session/request_permission` and `'form'` `elicitation/create` follow `permission.ts` step for step
|
|
30
|
-
* (the same builders and `validateWireEvent`), with ids derived from the log: a permission card is
|
|
31
|
-
* `perm:<spawnId>:<JSON agent id>` ({@link permissionRequestId}, shared with the live path), an
|
|
32
|
-
* elicitation card keeps the agent's id. A card the user has not answered stays in state with the
|
|
33
|
-
* JSON-RPC id it came on, so after a restore it is still pending under the same id
|
|
34
|
-
* ({@link pendingRequestIds}, spec §5.5.3/4) and the answer is written back on the original id.
|
|
35
|
-
*
|
|
36
|
-
* ## Task 17b
|
|
37
|
-
*
|
|
38
|
-
* `session/update` mapping and `agent_message` synthesis live in `updates.ts` (a turn's end flushes
|
|
39
|
-
* the streaming message before its own frame, and a turn's start closes a message that began
|
|
40
|
-
* outside any turn); the open session, config, steering and delegation in `control.ts`.
|
|
41
|
-
*
|
|
42
|
-
* ## Task 17c: callbacks and the adapter boundaries
|
|
43
|
-
*
|
|
44
|
-
* Every change to the pending/settled permission cards publishes the callback snapshot
|
|
45
|
-
* (`runtime.ts` `publishCallbacks`), at the points `AcpSession` does: a registration (accepted, or
|
|
46
|
-
* refused for its fields/capacity/a reused id — which also makes the snapshot incomplete), a
|
|
47
|
-
* settlement (`answered` by a note, `canceled` by the agent or for want of a callback context), a
|
|
48
|
-
* drain (interrupt, an interrupted turn, the connection closing — incomplete from then on). A Qoder
|
|
49
|
-
* SDK adapter's root card carries its `callbackContext` (`qoderAdapter/callbackContext.ts`, the
|
|
50
|
-
* function `AcpSession.emitEvent` uses), computed from that snapshot and the runtime profile — log
|
|
51
|
-
* data only; without one the card is settled `canceled` and answered `cancelled`, as today. Cards are
|
|
52
|
-
* emitted through the same boundaries as any other event (`updates.ts` `emitSessionEvent`).
|
|
53
|
-
*/
|
|
1
|
+
/** Session turns, permission and elicitation cards, and callback snapshots over checkpointed state. Root and child permission origins are verified before a card is emitted; callback generations derive from the process spawn ID. */
|
|
54
2
|
import { buildElicitationRequestedEvent, buildPermissionRequestedEvent, elicitationFieldKeys, isBlank, permissionRequestId, } from '../permission.js';
|
|
55
3
|
import { validateWireEvent } from '../validate.js';
|
|
56
|
-
import { isSdkCallbackCard, permissionCallbackContext } from '../../qoderAdapter/callbackContext.js';
|
|
57
4
|
import { isRecord, nextSeq } from './emit.js';
|
|
58
5
|
import { inboundKey, settlementErrorCode } from './jsonrpc.js';
|
|
59
6
|
import { sameCause } from './record.js';
|
|
60
|
-
import {
|
|
61
|
-
import { getOwn,
|
|
62
|
-
import { emitSessionEvent,
|
|
7
|
+
import { publishCallbacks, resetRuntime } from './runtime.js';
|
|
8
|
+
import { getOwn, initialCallbacks, initialMessage, PENDING_PERMISSION_LIMIT, SETTLED_PERMISSION_LIMIT, } from './state.js';
|
|
9
|
+
import { emitSessionEvent, endHistory, flushMessage, flushOutOfTurnMessage } from './updates.js';
|
|
63
10
|
/** `agentSessions.ts`'s admission refusal text, verbatim. */
|
|
64
11
|
export const ADMISSION_REFUSED = 'Workstation is not accepting new work';
|
|
12
|
+
/** The agent answered `session/prompt` with `stopReason: 'refusal'` and no words of its own. */
|
|
13
|
+
export const PROMPT_REFUSED = 'The agent refused this prompt.';
|
|
65
14
|
/** `AcpSession.prompt`'s overlap refusal text, verbatim. */
|
|
66
15
|
export const OVERLAPPING_TURN = 'An exact platform turn is still running; overlapping prompts are unavailable';
|
|
67
16
|
/** What the SDK rejects pending requests with when the adapter's stdout ends (`jsonrpc.js` `close()`). */
|
|
@@ -95,13 +44,13 @@ export function resetForSpawn(state) {
|
|
|
95
44
|
state.rootSessionId = null;
|
|
96
45
|
state.open = { status: 'idle' };
|
|
97
46
|
state.configOptions = null;
|
|
98
|
-
state.delegation = null;
|
|
99
47
|
state.history = null;
|
|
100
48
|
state.mapperScopes = {};
|
|
101
49
|
state.message = initialMessage();
|
|
102
50
|
state.steers = {};
|
|
103
51
|
state.steered = [];
|
|
104
52
|
state.asyncTasks = {};
|
|
53
|
+
state.draftSubagents = { children: {}, overflow: false };
|
|
105
54
|
resetRuntime(state);
|
|
106
55
|
state.turns = [];
|
|
107
56
|
state.exactExecutionUnconfirmed = false;
|
|
@@ -111,7 +60,6 @@ export function resetForSpawn(state) {
|
|
|
111
60
|
state.elicitations = {};
|
|
112
61
|
state.settledPermissions = [];
|
|
113
62
|
state.callbacks = initialCallbacks();
|
|
114
|
-
state.boundaries = initialBoundaries();
|
|
115
63
|
state.permissionOrigins = {};
|
|
116
64
|
state.permissionParents = {};
|
|
117
65
|
state.unresolvedPermissionOrigins = {};
|
|
@@ -147,7 +95,8 @@ const STOP_REASONS = {
|
|
|
147
95
|
end_turn: 'end_turn',
|
|
148
96
|
max_tokens: 'max_tokens',
|
|
149
97
|
max_turn_requests: 'max_turns',
|
|
150
|
-
refusal
|
|
98
|
+
// No `refusal`: a refusal result settles 'failed' (see `settlePrompt`), and a completed turn
|
|
99
|
+
// never carries it.
|
|
151
100
|
};
|
|
152
101
|
/**
|
|
153
102
|
* End turn `index` — `AcpSession.finishTurn` + `agentSessions.ts`'s `onAgentTurnDone` frame, in one.
|
|
@@ -158,8 +107,6 @@ function finishTurn(state, out, index, status, finish) {
|
|
|
158
107
|
const [turn] = state.turns.splice(index, 1);
|
|
159
108
|
if (!turn)
|
|
160
109
|
return;
|
|
161
|
-
if (state.turns.length === 0)
|
|
162
|
-
endForegroundTurn(state);
|
|
163
110
|
// Before the turn's own frame, on every path that ends a turn (`flushMessageCompleted`).
|
|
164
111
|
flushMessage(state, out);
|
|
165
112
|
const resolved = turn.interrupted ? 'interrupted' : status;
|
|
@@ -243,6 +190,28 @@ export function onDown(state, record) {
|
|
|
243
190
|
else
|
|
244
191
|
delete turn.platformTurnId;
|
|
245
192
|
}
|
|
193
|
+
/**
|
|
194
|
+
* The measured `_meta` key a refusal *result* carries its would-be error response under: a JSON
|
|
195
|
+
* string of the JSON-RPC error the agent chose not to send (`codebuddy-code` 2.171.0; shape-read,
|
|
196
|
+
* same rule as `acpClient`'s `_codebuddy.ai/authUrl`).
|
|
197
|
+
*/
|
|
198
|
+
const REFUSAL_ERROR_META_KEY = 'codebuddy.ai/errorMessage';
|
|
199
|
+
function refusalErrorDetail(result) {
|
|
200
|
+
if (!isRecord(result) || !isRecord(result._meta))
|
|
201
|
+
return undefined;
|
|
202
|
+
const raw = result._meta[REFUSAL_ERROR_META_KEY];
|
|
203
|
+
if (typeof raw !== 'string')
|
|
204
|
+
return undefined;
|
|
205
|
+
try {
|
|
206
|
+
const parsed = JSON.parse(raw);
|
|
207
|
+
if (!isRecord(parsed) || typeof parsed.message !== 'string')
|
|
208
|
+
return undefined;
|
|
209
|
+
return { ...(typeof parsed.code === 'number' ? { code: parsed.code } : {}), message: parsed.message };
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
return undefined;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
246
215
|
/** A `session/prompt` request settled: its turn ends on this record. */
|
|
247
216
|
export function settlePrompt(state, entry, settlement, out) {
|
|
248
217
|
const index = state.turns.findIndex((turn) => turn.requestId === entry.id);
|
|
@@ -252,7 +221,25 @@ export function settlePrompt(state, entry, settlement, out) {
|
|
|
252
221
|
case 'result': {
|
|
253
222
|
const stopReason = isRecord(settlement.result) ? settlement.result.stopReason : undefined;
|
|
254
223
|
// ACP's own `cancelled` forces `interrupted` even when no interrupt was sent (a backstop).
|
|
255
|
-
|
|
224
|
+
if (stopReason === 'cancelled') {
|
|
225
|
+
finishTurn(state, out, index, 'interrupted', { stopReason, executionEnded: true });
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
// Measured (codebuddy-code 2.171.0): an agent that needs credentials answers `session/prompt`
|
|
229
|
+
// with a *result* `stopReason: 'refusal'` — zero content, no JSON-RPC error — which used to
|
|
230
|
+
// settle 'completed' and render as nothing at all. A refusal result settles exactly like the
|
|
231
|
+
// agent's own refusal below ('failed' + `agentRefused`), with the agent's detail read out of
|
|
232
|
+
// the measured `_meta` key; a code-less detail stays an ordinary failure card server-side,
|
|
233
|
+
// while `-32000` beside the observed auth methods is what turns it into the sign-in card.
|
|
234
|
+
if (stopReason === 'refusal') {
|
|
235
|
+
const detail = refusalErrorDetail(settlement.result);
|
|
236
|
+
finishTurn(state, out, index, 'failed', {
|
|
237
|
+
agentRefused: true, executionEnded: true,
|
|
238
|
+
error: detail?.message ?? PROMPT_REFUSED, ...(detail?.code !== undefined ? { errorCode: detail.code } : {}),
|
|
239
|
+
});
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
finishTurn(state, out, index, 'completed', { stopReason, executionEnded: true });
|
|
256
243
|
return;
|
|
257
244
|
}
|
|
258
245
|
case 'error': {
|
|
@@ -405,23 +392,7 @@ export function onPermissionRequest(state, id, params, out) {
|
|
|
405
392
|
const rpcKey = inboundKey(id);
|
|
406
393
|
state.permissions[requestId] = { requestId, rpcKey, origin, rpcRequestId, optionIds: options.map((option) => option.optionId) };
|
|
407
394
|
publishCallbacks(state, out);
|
|
408
|
-
|
|
409
|
-
const toolCall = payload.toolCall;
|
|
410
|
-
if (isSdkCallbackCard(toolCall)) {
|
|
411
|
-
// `AcpSession.emitEvent`: an SDK callback card is shown only with the evidence the server needs
|
|
412
|
-
// to route its answer back (`callbackContext`); without it, it is cancelled.
|
|
413
|
-
const spawnId = state.process.spawn.spawnId;
|
|
414
|
-
const callbackContext = root === null ? undefined : permissionCallbackContext({
|
|
415
|
-
rootSessionId: root, origin, requestId, toolCall, pendingRpcRequestId: rpcRequestId,
|
|
416
|
-
diagnostics: { generation: spawnId, available: state.runtime.available, profile: state.runtime.profile, callbacks: callbackSnapshot(state) },
|
|
417
|
-
});
|
|
418
|
-
if (!callbackContext) {
|
|
419
|
-
settlePermission(state, out, requestId, PERMISSION_CANCELLED, 'canceled');
|
|
420
|
-
state.diagnostics.refusedRequests += 1;
|
|
421
|
-
return undefined;
|
|
422
|
-
}
|
|
423
|
-
payload = { ...payload, callbackContext };
|
|
424
|
-
}
|
|
395
|
+
const payload = event.payload;
|
|
425
396
|
emitSessionEvent(state, out, { eventName: event.eventName, payload, acpSessionId: origin });
|
|
426
397
|
return requestId;
|
|
427
398
|
}
|
|
@@ -1,22 +1,16 @@
|
|
|
1
|
-
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
|
|
5
|
-
*/
|
|
6
|
-
export const PROCESSOR_STATE_SCHEMA = 4;
|
|
1
|
+
/** State history: 2 — open-session layer; 3 — callback snapshots and adapter fold data;
|
|
2
|
+
* 4 — polled process state; 5 — adapter fold data and retired optional state removed.
|
|
3
|
+
* Schemas before 4 remain unreadable; schema 4 is upgraded by checkpoint.ts. */
|
|
4
|
+
export const PROCESSOR_STATE_SCHEMA = 5;
|
|
7
5
|
export const SETTLED_HISTORY_LIMIT = 16;
|
|
8
6
|
export const SETTLED_PERMISSION_LIMIT = 64;
|
|
9
7
|
export const PENDING_PERMISSION_LIMIT = 64;
|
|
10
8
|
export const UNRESOLVED_ORIGIN_LIMIT = 1024;
|
|
11
|
-
export const PENDING_PROFILE_LIMIT = 8;
|
|
12
9
|
export const STEERED_HISTORY_LIMIT = 64;
|
|
13
10
|
/** Unfinished async tasks tracked per connection, all sessions together. */
|
|
14
11
|
export const ASYNC_TASK_LIMIT = 1024;
|
|
15
12
|
export function initialRuntime() {
|
|
16
|
-
return { revision: 0, available: true
|
|
17
|
-
}
|
|
18
|
-
export function initialBoundaries() {
|
|
19
|
-
return { foreground: { pending: {} }, subwork: { calls: {}, count: 0 }, repair: null, sideChunk: null };
|
|
13
|
+
return { revision: 0, available: true };
|
|
20
14
|
}
|
|
21
15
|
export function initialCallbacks() {
|
|
22
16
|
return { complete: true, rootSessionId: null };
|
|
@@ -48,16 +42,15 @@ export function initialState() {
|
|
|
48
42
|
unresolvedPermissionOrigins: {},
|
|
49
43
|
open: { status: 'idle' },
|
|
50
44
|
configOptions: null,
|
|
51
|
-
delegation: null,
|
|
52
45
|
history: null,
|
|
53
46
|
mapperScopes: {},
|
|
54
47
|
message: initialMessage(),
|
|
55
48
|
steers: {},
|
|
56
49
|
steered: [],
|
|
50
|
+
draftSubagents: { children: {}, overflow: false },
|
|
57
51
|
asyncTasks: {},
|
|
58
52
|
activity: null,
|
|
59
53
|
runtime: initialRuntime(),
|
|
60
|
-
boundaries: initialBoundaries(),
|
|
61
54
|
diagnostics: {
|
|
62
55
|
orphanResponses: 0, protocolErrors: 0, ignoredLines: 0, unknownNotes: 0, droppedNotes: 0, refusedRequests: 0,
|
|
63
56
|
rejectedRelations: 0, droppedUpdates: 0, invalidEvents: 0, untrackedAsyncTasks: 0, settled: [],
|
|
@@ -1,15 +1,14 @@
|
|
|
1
|
+
/** The ACP processor folds records synchronously in log order. No clock, randomness, environment, file I/O or timers: identical records and state produce byte-identical outputs. Commands execute before frames are recorded; IDs derive from log offsets. A failed fold poisons the state; unknown notes are counted for forward compatibility. */
|
|
1
2
|
import { accountLimitsSchema, ACCOUNT_LIMITS_READ, ACCOUNT_LIMITS_UPDATED } from '../../protocol/accountLimits.js';
|
|
2
3
|
import { initializeRequestParams, parseInitializeResult } from '../initializeResponse.js';
|
|
3
4
|
import { STEER_METHOD } from '../steering.js';
|
|
4
5
|
import { checkpointOf } from './checkpoint.js';
|
|
5
|
-
import { failUnsentSteer, onAsyncTaskStopNote,
|
|
6
|
+
import { failUnsentSteer, onAsyncTaskStopNote, onHistoryEndNote, onInstallOutcomeNote, onOutputGapNote, onLaunch, onOpenNote, onSetConfigOptionNote, onSteerDown, onSteerNote, settleConfig, settleOpen, settleSteer, } from './control.js';
|
|
6
7
|
import { CANCEL_REQUEST_METHOD, classifyLine, inboundKey, invalidParams, invalidRequest, isJsonRpcId, methodNotFound, outboundKey, parseError, settlementErrorCode, settlementOf, } from './jsonrpc.js';
|
|
7
8
|
import { RecordOutput } from './output.js';
|
|
8
9
|
import { INBOUND_REQUEST_SCHEMAS } from './sdkSchemas.js';
|
|
9
10
|
import { BATCH_CLOSE_REASON, connected, dropCards, finishAllTurns, forgetCard, onAnswerNote, onDown, onElicitationAnswerNote, onElicitationRequest, onInboundCancelled, onInterruptNote, onPermissionRequest, onPromptFailedNote, onPromptNote, onUndeliverableNote, resetForSpawn, settlePrompt, } from './session.js';
|
|
10
|
-
import { onClaudeSdkMessage,
|
|
11
|
-
import { closeRepair, onRepairNote, onSideNote } from './sideChannel.js';
|
|
12
|
-
import { REPAIR_NOTE, SIDE_NOTE } from '../../qoderAdapter/backgroundTerminalFold.js';
|
|
11
|
+
import { onClaudeSdkMessage, onProcessExit } from './runtime.js';
|
|
13
12
|
import { endHistory, onSessionUpdate } from './updates.js';
|
|
14
13
|
import { initialState, SETTLED_HISTORY_LIMIT, } from './state.js';
|
|
15
14
|
/** Serialized size above which a note is dropped (`droppedNotes`). */
|
|
@@ -73,7 +72,6 @@ function closeConnection(state, reason, out) {
|
|
|
73
72
|
state.outbound = {};
|
|
74
73
|
state.inbound = {};
|
|
75
74
|
dropCards(state, out);
|
|
76
|
-
closeRepair(state);
|
|
77
75
|
state.closed = reason;
|
|
78
76
|
for (const entry of pending)
|
|
79
77
|
settle(state, entry, { outcome: 'closed', reason }, out);
|
|
@@ -151,8 +149,6 @@ function onUp(state, line, o, t, out) {
|
|
|
151
149
|
onSessionUpdate(state, message.params, out, t);
|
|
152
150
|
else if (message.method === '_claude/sdkMessage')
|
|
153
151
|
onClaudeSdkMessage(state, out, message.params, t);
|
|
154
|
-
else if (message.method === '_axom/qoder/profile')
|
|
155
|
-
onProfileNotification(state, out, message.params);
|
|
156
152
|
return;
|
|
157
153
|
case 'request':
|
|
158
154
|
onInboundRequest(state, message.id, message.method, message.params, o, out);
|
|
@@ -265,7 +261,6 @@ function settleInitialize(state, entry, settlement, out) {
|
|
|
265
261
|
}
|
|
266
262
|
const { protocolSupported: _supported, ...facts } = reading;
|
|
267
263
|
state.initialize = { status: 'ready', facts: JSON.parse(JSON.stringify(facts)) };
|
|
268
|
-
onInitialized(state, settlement.result);
|
|
269
264
|
const capability = settlement.result.agentCapabilities?._meta?.['axom/accountRateLimits'];
|
|
270
265
|
if (capability && typeof capability === 'object' && capability.read === true) {
|
|
271
266
|
const quotaCapability = capability;
|
|
@@ -339,12 +334,6 @@ function onNote(state, note, out) {
|
|
|
339
334
|
case 'agent_async_task_stop':
|
|
340
335
|
onAsyncTaskStopNote(state, note, out);
|
|
341
336
|
return;
|
|
342
|
-
case 'agent_delegation_renewed':
|
|
343
|
-
onDelegationRenewedNote(state, note);
|
|
344
|
-
return;
|
|
345
|
-
case 'agent_delegation_refresh_pending':
|
|
346
|
-
onDelegationRefreshPendingNote(state);
|
|
347
|
-
return;
|
|
348
337
|
case 'agent_launch':
|
|
349
338
|
onLaunch(state, note);
|
|
350
339
|
return;
|
|
@@ -354,12 +343,6 @@ function onNote(state, note, out) {
|
|
|
354
343
|
case 'agent_output_gap':
|
|
355
344
|
onOutputGapNote(state, note, out);
|
|
356
345
|
return;
|
|
357
|
-
case REPAIR_NOTE:
|
|
358
|
-
onRepairNote(state, note);
|
|
359
|
-
return;
|
|
360
|
-
case SIDE_NOTE:
|
|
361
|
-
onSideNote(state, note, out);
|
|
362
|
-
return;
|
|
363
346
|
case 'agent_account_read': {
|
|
364
347
|
if (!connected(state)) {
|
|
365
348
|
out.unjournaled({ t: 'agent_account_read_result', id: note.id, ok: false, error: 'Agent is offline' });
|
|
@@ -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
|
}
|