@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.
Files changed (163) hide show
  1. package/README.md +18 -5
  2. package/dist/axom-patch/cli/README.md +100 -12
  3. package/dist/axom-patch/cli/account-limits/generate.py +15 -12
  4. package/dist/axom-patch/cli/account-limits/kimi-proxy.mjs +20 -3
  5. package/dist/axom-patch/cli/claude-account-limits.json +9 -9
  6. package/dist/axom-patch/cli/codex-account-limits.json +11 -11
  7. package/dist/axom-patch/cli/codex-terminal-output.json +16 -0
  8. package/dist/axom-patch/cli/commandcode-account-limits.json +9 -9
  9. package/dist/axom-patch/cli/kimi-quota.mjs +12 -0
  10. package/dist/axom-patch/cli/kimi-subagents.mjs +173 -0
  11. package/dist/axom-patch/cli/nightly-task.md +55 -13
  12. package/dist/axom-patch/cli/probes/codex-terminal-output.mjs +33 -0
  13. package/dist/axom-patch/cli/probes/kimi-subagent-subwork.mjs +82 -0
  14. package/dist/axom-patch/cli/qoder/dist/axom-acp-compat.mjs +1591 -0
  15. package/dist/axom-patch/cli/qoder/releases/1.1.75.json +360 -0
  16. package/dist/axom-patch/cli/qoder-background-terminal.md +13 -6
  17. package/dist/axom-patch/cli/qoder-foreground-output.md +14 -8
  18. package/dist/axom-patch/cli/qoder-native-acp.json +227 -0
  19. package/dist/axom-patch/cli/qoder-steering.md +20 -25
  20. package/dist/axom-patch/cli/qoder-subagent-subwork.md +17 -68
  21. package/dist/axom-patch/cli/runtime.json +16 -18
  22. package/dist/axom-patch/cli/zcode-account-limits.json +10 -11
  23. package/dist/axom-patch/cli/zcode-models.json +1 -2
  24. package/dist/axom-patch/cli/zcode-subagent-subwork.json +9 -10
  25. package/dist/cli.js +4 -36
  26. package/dist/commands/knowledge.js +47 -0
  27. package/dist/commands/update.js +65 -3
  28. package/dist/commands/workstation.js +73 -7
  29. package/dist/install/claude/commands.js +13 -13
  30. package/dist/mcp.js +1 -1
  31. package/dist/native/darwin-arm64/axom-quic +0 -0
  32. package/dist/workstation/acp/acpAgents.js +102 -98
  33. package/dist/workstation/acp/acpClient.js +26 -0
  34. package/dist/workstation/acp/builds/install/npm.js +99 -10
  35. package/dist/workstation/acp/initializeResponse.js +6 -1
  36. package/dist/workstation/acp/managedPatches.js +189 -25
  37. package/dist/workstation/acp/managedRuntime.js +314 -0
  38. package/dist/workstation/acp/patchAssets.js +40 -0
  39. package/dist/workstation/acp/processor/checkpoint.js +25 -34
  40. package/dist/workstation/acp/processor/control.js +5 -78
  41. package/dist/workstation/acp/processor/runtime.js +5 -81
  42. package/dist/workstation/acp/processor/session.js +51 -80
  43. package/dist/workstation/acp/processor/state.js +6 -13
  44. package/dist/workstation/acp/processor/step.js +3 -20
  45. package/dist/workstation/acp/processor/test/fixtureLog.js +9 -16
  46. package/dist/workstation/acp/processor/updates.js +131 -105
  47. package/dist/workstation/acp/processorSession.js +8 -172
  48. package/dist/workstation/acp/registry/catalog.js +65 -39
  49. package/dist/workstation/acp/registry/launch.js +16 -1
  50. package/dist/workstation/acp/switching.js +0 -3
  51. package/dist/workstation/acp/test/fakeAgent.js +13 -0
  52. package/dist/workstation/acp/test/keeperWorld.js +28 -0
  53. package/dist/workstation/acp/test/raw/sources.js +16 -130
  54. package/dist/workstation/acp/test/realAdapter.js +1 -47
  55. package/dist/workstation/acp/test/scriptAgent.js +11 -3
  56. package/dist/workstation/agentFlow.js +1 -1
  57. package/dist/workstation/agentSessions.js +38 -143
  58. package/dist/workstation/channels/management.js +95 -11
  59. package/dist/workstation/channels/url.js +1 -1
  60. package/dist/workstation/config.js +16 -4
  61. package/dist/workstation/connection/encryptedChannel.js +4 -1
  62. package/dist/workstation/control/bindingStore.js +200 -0
  63. package/dist/workstation/control/commands.js +124 -0
  64. package/dist/workstation/control/daemonIdentity.js +53 -0
  65. package/dist/workstation/control/enrollment.js +106 -0
  66. package/dist/workstation/control/inspect.js +71 -0
  67. package/dist/workstation/control/locks.js +63 -0
  68. package/dist/workstation/control/protocol.js +40 -0
  69. package/dist/workstation/control/restartGuard.js +46 -0
  70. package/dist/workstation/control/runtimeClient.js +97 -0
  71. package/dist/workstation/control/runtimeHost.js +148 -0
  72. package/dist/workstation/control/serverIdentity.js +35 -0
  73. package/dist/workstation/daemon.js +106 -6
  74. package/dist/workstation/daemonctl.js +77 -14
  75. package/dist/workstation/desktopControl/audio.js +99 -0
  76. package/dist/workstation/desktopControl/capabilities.js +92 -0
  77. package/dist/workstation/desktopControl/captureModes.js +9 -0
  78. package/dist/workstation/desktopControl/captureTargetLabel.js +9 -0
  79. package/dist/workstation/desktopControl/deviceSockets.js +246 -0
  80. package/dist/workstation/desktopControl/helperProcess.js +353 -0
  81. package/dist/workstation/desktopControl/lobby.js +129 -0
  82. package/dist/workstation/desktopControl/macProvider.js +155 -0
  83. package/dist/workstation/desktopControl/media.js +331 -0
  84. package/dist/workstation/desktopControl/nativeProvider.js +1018 -0
  85. package/dist/workstation/desktopControl/networkEvidence.js +43 -0
  86. package/dist/workstation/desktopControl/overview.js +58 -0
  87. package/dist/workstation/desktopControl/protocol.js +302 -0
  88. package/dist/workstation/desktopControl/quicMedia.js +240 -0
  89. package/dist/workstation/desktopControl/rateController.js +226 -0
  90. package/dist/workstation/desktopControl/supervisor.js +1681 -0
  91. package/dist/workstation/desktopControl/transport.js +27 -0
  92. package/dist/workstation/desktopControl/wallpaper.js +78 -0
  93. package/dist/workstation/files/credentials.js +142 -0
  94. package/dist/workstation/files/text.js +42 -8
  95. package/dist/workstation/keepers/kit/bundle.js +21 -17
  96. package/dist/workstation/keepers/kit/launcher.js +47 -7
  97. package/dist/workstation/keepers/kit/paths.js +2 -1
  98. package/dist/workstation/managedInstall.js +57 -0
  99. package/dist/workstation/protocol/agent.js +2 -15
  100. package/dist/workstation/protocol/management.js +93 -4
  101. package/dist/workstation/protocol/runtimeDiagnostics.js +15 -0
  102. package/dist/workstation/rd/artifact.js +119 -0
  103. package/dist/workstation/rd/control.js +119 -0
  104. package/dist/workstation/rd/display.js +29 -0
  105. package/dist/workstation/rd/provider.js +222 -0
  106. package/dist/workstation/rd/sessions.js +391 -0
  107. package/dist/workstation/rd/state.js +45 -0
  108. package/dist/workstation/rd/update.js +171 -0
  109. package/dist/workstation/rd/upkeep.js +38 -0
  110. package/dist/workstation/register.js +27 -37
  111. package/dist/workstation/service/installer.js +104 -13
  112. package/dist/workstation/service/launchd.js +1 -0
  113. package/dist/workstation/storageMetrics.js +118 -0
  114. package/dist/workstation/systemMetrics.js +44 -92
  115. package/dist/workstation/toolchain.js +94 -0
  116. package/dist/workstation/tools/browser.js +28 -4
  117. package/dist/workstation/update/artifact.js +2 -1
  118. package/dist/workstation/update/globalInstall.js +116 -0
  119. package/dist/workstation/update/npmInstaller.js +49 -23
  120. package/dist/workstation/update/rdRollback.js +145 -0
  121. package/dist/workstation/update/runUpdate.js +3 -2
  122. package/dist/workstation/update/watchdog.js +16790 -334
  123. package/package.json +5 -5
  124. package/skills/axom/SKILL.md +11 -11
  125. package/skills/axom-definition/SKILL.md +3 -3
  126. package/skills/axom-platform/SKILL.md +3 -3
  127. package/dist/axom-patch/cli/probes/qoder-steering.mjs +0 -89
  128. package/dist/axom-patch/cli/probes/qoder-subagent-origin.mjs +0 -61
  129. package/dist/axom-patch/cli/qoder-steering.json +0 -52
  130. package/dist/axom-patch/cli/qoder-subagent-origin.json +0 -73
  131. package/dist/commands/guide.js +0 -38
  132. package/dist/runtime-deps/qoder-sdk/package-lock.json +0 -1189
  133. package/dist/runtime-deps/qoder-sdk/package.json +0 -8
  134. package/dist/workstation/acp/delegationMcp.js +0 -24
  135. package/dist/workstation/acp/processor/sideChannel.js +0 -80
  136. package/dist/workstation/acp/qoderStartup.js +0 -45
  137. package/dist/workstation/acp/test/raw/qoderCaptures.js +0 -125
  138. package/dist/workstation/qoderAdapter/backgroundProcess.js +0 -118
  139. package/dist/workstation/qoderAdapter/backgroundTerminal.js +0 -193
  140. package/dist/workstation/qoderAdapter/backgroundTerminalFold.js +0 -372
  141. package/dist/workstation/qoderAdapter/callbackContext.js +0 -76
  142. package/dist/workstation/qoderAdapter/completedContinuation.js +0 -139
  143. package/dist/workstation/qoderAdapter/contract.js +0 -78
  144. package/dist/workstation/qoderAdapter/foregroundOutput.js +0 -36
  145. package/dist/workstation/qoderAdapter/history.js +0 -85
  146. package/dist/workstation/qoderAdapter/identityIndex.js +0 -191
  147. package/dist/workstation/qoderAdapter/inputQueue.js +0 -42
  148. package/dist/workstation/qoderAdapter/main.js +0 -95
  149. package/dist/workstation/qoderAdapter/mapper.js +0 -117
  150. package/dist/workstation/qoderAdapter/observerPlugin.js +0 -245
  151. package/dist/workstation/qoderAdapter/observerReceiver.js +0 -187
  152. package/dist/workstation/qoderAdapter/permissions.js +0 -61
  153. package/dist/workstation/qoderAdapter/profile.js +0 -93
  154. package/dist/workstation/qoderAdapter/sdkDriver.js +0 -48
  155. package/dist/workstation/qoderAdapter/sdkRuntime.js +0 -93
  156. package/dist/workstation/qoderAdapter/sdkRuntimeInstall.js +0 -145
  157. package/dist/workstation/qoderAdapter/session.js +0 -319
  158. package/dist/workstation/qoderAdapter/subagentMeta.js +0 -28
  159. package/dist/workstation/qoderAdapter/subwork.js +0 -85
  160. package/dist/workstation/qoderAdapter/taskControl.js +0 -38
  161. package/dist/workstation/qoderAdapter/taskLedger.js +0 -163
  162. package/dist/workstation/qoderAdapter/usage.js +0 -37
  163. /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 { getOwn, initialRuntime, PENDING_PROFILE_LIMIT, putOwn } from './state.js';
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: connectionGenerationOf(spawnId), complete,
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, profile } = state.runtime;
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, ...(profile !== undefined ? { profile } : {}),
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 { callbackSnapshot, publishCallbacks, resetRuntime } from './runtime.js';
61
- import { getOwn, initialBoundaries, initialCallbacks, initialMessage, PENDING_PERMISSION_LIMIT, SETTLED_PERMISSION_LIMIT, } from './state.js';
62
- import { emitSessionEvent, endForegroundTurn, endHistory, flushMessage, flushOutOfTurnMessage } from './updates.js';
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: '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
- finishTurn(state, out, index, stopReason === 'cancelled' ? 'interrupted' : 'completed', { stopReason, executionEnded: true });
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
- let payload = event.payload;
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
- * 2: Task 17b's open-session layer. 3: Task 17c (`boundaries`, `callbacks`, settled permissions with
3
- * their reason, `rpcRequestId` on a pending permission). 4: the repair's `polledProcesses`. An older
4
- * checkpoint is refused, never half-read.
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, adapterRevision: 0, pendingProfiles: {} };
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, onDelegationRefreshPendingNote, onDelegationRenewedNote, onHistoryEndNote, onInstallOutcomeNote, onOutputGapNote, onLaunch, onOpenNote, onSetConfigOptionNote, onSteerDown, onSteerNote, settleConfig, settleOpen, settleSteer, } from './control.js';
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, onInitialized, onProcessExit, onProfileNotification } from './runtime.js';
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 the Qoder profile, the open session bound from its response (new, then — after a respawn —
4
- * load with history and `historyEnd`), delegation and launch notes, config and steering round trips,
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'] }, qoderProfile: QODER_PROFILE },
25
+ _meta: { steering: { supported: true, timings: ['next'] } },
31
26
  })));
32
27
  step(
33
- // Before the open response: a profile candidate (kept), an update (no root yet: dropped).
34
- up({ jsonrpc: '2.0', method: '_axom/qoder/profile', params: { sessionId: 'S1', revision: 1,
35
- profile: { ...QODER_PROFILE, rootSessionId: 'S1', runtimeGeneration: 'g1', rootConfirmed: true, initialized: true } } }), note('agent_session_open', {
36
- mode: 'new', cwd: '/work', redact: ['secret:delegation'],
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' }), note('agent_delegation_renewed', { expiresAtEpochMs: 1_900_000_100_000, renewalRevision: 1 }), 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'));
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', 'qoder replays one more old chunk after its response'), note('historyEnd'));
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
  }