@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,42 +1,13 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The open session and its controls, as notes and their responses (Task 17b): `session/new|load|resume`,
|
|
3
|
-
* `historyEnd`, `session/set_config_option`/`set_mode`, `_session/steering`, `_session/async_task/stop`,
|
|
4
|
-
* delegation's non-secret fields and the runtime `launch` (spec 2026-10-11 §5.4).
|
|
5
|
-
*
|
|
6
|
-
* ## Opening, and binding from the response
|
|
7
|
-
*
|
|
8
|
-
* `agent_session_open{mode, cwd, sessionId?, mcpServers?, redact?, delegation?}` sends one request —
|
|
9
|
-
* the params `AcpSession.open` (`buildSession`), `replayNativeSession` (`session/load`) and the Qoder
|
|
10
|
-
* resume path build, `_meta` included (`newSessionMetaFor`, decided from the initialize reading). The
|
|
11
|
-
* MCP server list arrives finished: the daemon validated the delegation lease (`buildSessionMcpServers`,
|
|
12
|
-
* which reads the clock) before it wrote the note, and the bearer in it is a `secretRef` token the
|
|
13
|
-
* executor substitutes into stdin only (`redact`, spec §13). The **response** binds the root
|
|
14
|
-
* (`bindRootSession`) and sets the config snapshot — no pre-bind buffer, no window. A failed open is
|
|
15
|
-
* reported (`processor.open_result`) and never retried as `session/new` here.
|
|
16
|
-
*
|
|
17
|
-
* ## Replies
|
|
18
|
-
*
|
|
19
|
-
* `agent_config_option_set` and `agent_steered` are RPC replies: unnumbered frames carrying the
|
|
20
|
-
* server's frame `id` from the note, built as `agentSessions.ts` builds them. A steer's blocks travel
|
|
21
|
-
* like a prompt's (`fill: {blocksRef}`); if the executor lost them, the same `agent_prompt_failed` note
|
|
22
|
-
* fails the steer.
|
|
23
|
-
*
|
|
24
|
-
* ## `steeredIds` (spec §5.5.4)
|
|
25
|
-
*
|
|
26
|
-
* {@link steeredIds}: steers whose line reached the agent and that are unanswered, plus the last
|
|
27
|
-
* {@link STEERED_HISTORY_LIMIT} the agent answered `injected`. A `promptRequired`, a refusal, or a
|
|
28
|
-
* line that never reached the agent is not steered: the server delivers that message the ordinary way.
|
|
29
|
-
*/
|
|
1
|
+
/** Open-session binding, configuration, steering and launch notes. A response binds the native root; a failed load never silently creates another session here. The daemon supplies the finished MCP list with secrets replaced by references. */
|
|
30
2
|
import { newSessionMetaFor } from '../activityReporting.js';
|
|
31
3
|
import { flatChoicesOf, modesAlreadyRepresented, modelsAlreadyRepresented, normalizeConfigOptions, SYNTHESIZED_MODES_CONFIG_ID, SYNTHESIZED_MODEL_CONFIG_ID } from '../configOptions.js';
|
|
32
4
|
import { ASYNC_TASK_STOP_METHOD, mapSteerOutcome, STEER_METHOD } from '../steering.js';
|
|
33
5
|
import { emitEvent, isRecord } from './emit.js';
|
|
34
|
-
import { settlementErrorCode } from './jsonrpc.js';
|
|
6
|
+
import { jsonRpcErrorText, settlementErrorCode } from './jsonrpc.js';
|
|
35
7
|
import { sameCause } from './record.js';
|
|
36
|
-
import { onLaunchNote
|
|
8
|
+
import { onLaunchNote } from './runtime.js';
|
|
37
9
|
import { bindRootSession, closeErrorText, connected } from './session.js';
|
|
38
10
|
import { getOwn, putOwn, STEERED_HISTORY_LIMIT, } from './state.js';
|
|
39
|
-
import { closeRepair } from './sideChannel.js';
|
|
40
11
|
import { endHistory } from './updates.js';
|
|
41
12
|
const OPEN_METHODS = { new: 'session/new', load: 'session/load', resume: 'session/resume' };
|
|
42
13
|
export const NOT_INITIALIZED = 'The ACP connection has not finished initialize';
|
|
@@ -51,30 +22,12 @@ function request(state, out, method, params, kind, ctx, extras = {}) {
|
|
|
51
22
|
function isStringArray(value) {
|
|
52
23
|
return Array.isArray(value) && value.every((item) => typeof item === 'string');
|
|
53
24
|
}
|
|
54
|
-
function delegationOf(value) {
|
|
55
|
-
if (value === undefined)
|
|
56
|
-
return null;
|
|
57
|
-
if (!isRecord(value))
|
|
58
|
-
return undefined;
|
|
59
|
-
const { expiresAtEpochMs, leaseId, bindingId, instanceId } = value;
|
|
60
|
-
if (!Number.isSafeInteger(expiresAtEpochMs) || typeof leaseId !== 'string' || leaseId === ''
|
|
61
|
-
|| (bindingId !== undefined && typeof bindingId !== 'string') || (instanceId !== undefined && typeof instanceId !== 'string')
|
|
62
|
-
|| (bindingId !== undefined) !== (instanceId !== undefined))
|
|
63
|
-
return undefined;
|
|
64
|
-
return {
|
|
65
|
-
expiresAtEpochMs: expiresAtEpochMs, leaseId,
|
|
66
|
-
...(bindingId !== undefined ? { bindingId, instanceId: instanceId, renewalRevision: 0 } : {}),
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
25
|
// ---- open ---------------------------------------------------------------------------------------
|
|
70
|
-
/** `agent_session_open{mode, cwd, sessionId?, mcpServers?, redact?, delegation?}`. */
|
|
71
26
|
export function onOpenNote(state, note, out) {
|
|
72
27
|
const { mode, cwd, sessionId, mcpServers, redact } = note;
|
|
73
|
-
const delegation = delegationOf(note.delegation);
|
|
74
28
|
if ((mode !== 'new' && mode !== 'load' && mode !== 'resume') || typeof cwd !== 'string' || cwd === ''
|
|
75
29
|
|| (mode === 'new' ? sessionId !== undefined : typeof sessionId !== 'string' || sessionId === '')
|
|
76
|
-
|| (mcpServers !== undefined && !Array.isArray(mcpServers)) || (redact !== undefined && !isStringArray(redact))
|
|
77
|
-
|| delegation === undefined) {
|
|
30
|
+
|| (mcpServers !== undefined && !Array.isArray(mcpServers)) || (redact !== undefined && !isStringArray(redact))) {
|
|
78
31
|
state.diagnostics.droppedNotes += 1;
|
|
79
32
|
return;
|
|
80
33
|
}
|
|
@@ -92,7 +45,7 @@ export function onOpenNote(state, note, out) {
|
|
|
92
45
|
: mode === 'load'
|
|
93
46
|
? { sessionId, cwd, mcpServers: servers, ...(meta ? { _meta: meta } : {}) }
|
|
94
47
|
: { sessionId, cwd, mcpServers: servers };
|
|
95
|
-
const pending = request(state, out, OPEN_METHODS[mode], params, 'open', { mode, ...(sessionId !== undefined ? { sessionId } : {})
|
|
48
|
+
const pending = request(state, out, OPEN_METHODS[mode], params, 'open', { mode, ...(sessionId !== undefined ? { sessionId } : {}) }, redact !== undefined && redact.length > 0 ? { redact } : {});
|
|
96
49
|
state.open = { status: 'pending', requestId: pending.id, mode };
|
|
97
50
|
if (mode === 'load')
|
|
98
51
|
state.history = { rootSessionId: sessionId, scopes: {}, messages: [], droppedCount: 0 };
|
|
@@ -105,11 +58,10 @@ export function settleOpen(state, entry, settlement, out) {
|
|
|
105
58
|
const { mode } = ctx;
|
|
106
59
|
if (settlement.outcome !== 'result') {
|
|
107
60
|
const code = settlementErrorCode(settlement);
|
|
108
|
-
const error = settlement.outcome === 'error' ? settlement.error
|
|
61
|
+
const error = settlement.outcome === 'error' ? jsonRpcErrorText(settlement.error) : closeErrorText(state);
|
|
109
62
|
state.open = { status: 'failed', mode, error, ...(code !== undefined ? { code } : {}) };
|
|
110
63
|
endHistory(state, out, false);
|
|
111
64
|
// A failed open closes the attached session, and its repair with it (`AcpSession.close`).
|
|
112
|
-
closeRepair(state);
|
|
113
65
|
out.unjournaled({ t: 'processor.open_result', ok: false, mode, error, ...(code !== undefined ? { code } : {}) });
|
|
114
66
|
return;
|
|
115
67
|
}
|
|
@@ -117,10 +69,8 @@ export function settleOpen(state, entry, settlement, out) {
|
|
|
117
69
|
const nativeSessionId = mode === 'new' ? (typeof result.sessionId === 'string' ? result.sessionId : '') : ctx.sessionId;
|
|
118
70
|
if (nativeSessionId.length > 0) {
|
|
119
71
|
bindRootSession(state, nativeSessionId);
|
|
120
|
-
onRootBound(state, out, nativeSessionId);
|
|
121
72
|
}
|
|
122
73
|
state.configOptions = normalizeConfigOptions(result.configOptions, result.modes, result.models);
|
|
123
|
-
state.delegation = ctx.delegation ?? null;
|
|
124
74
|
state.open = { status: 'open', mode };
|
|
125
75
|
out.unjournaled({
|
|
126
76
|
t: 'processor.open_result', ok: true, mode,
|
|
@@ -280,7 +230,7 @@ export function steeredIds(state) {
|
|
|
280
230
|
const inFlight = Object.entries(state.steers).filter(([, steer]) => steer.sent).map(([id]) => id);
|
|
281
231
|
return [...state.steered, ...inFlight];
|
|
282
232
|
}
|
|
283
|
-
// ---- background tasks,
|
|
233
|
+
// ---- background tasks, launch -------------------------------------------------------
|
|
284
234
|
/** `agent_async_task_stop{asyncTaskId}` — `asyncTaskStopRequest`; its answer is diagnostics only. */
|
|
285
235
|
export function onAsyncTaskStopNote(state, note, out) {
|
|
286
236
|
const { asyncTaskId } = note;
|
|
@@ -290,29 +240,6 @@ export function onAsyncTaskStopNote(state, note, out) {
|
|
|
290
240
|
}
|
|
291
241
|
request(state, out, ASYNC_TASK_STOP_METHOD, { sessionId: state.rootSessionId, asyncTaskId }, 'rpc', undefined);
|
|
292
242
|
}
|
|
293
|
-
/**
|
|
294
|
-
* `agent_delegation_renewed{expiresAtEpochMs, renewalRevision}` — a renewal the daemon accepted
|
|
295
|
-
* (`agent_delegation_renew`'s checks read the clock, so the decision is the daemon's; the note is
|
|
296
|
-
* its record).
|
|
297
|
-
*/
|
|
298
|
-
export function onDelegationRenewedNote(state, note) {
|
|
299
|
-
const { expiresAtEpochMs, renewalRevision } = note;
|
|
300
|
-
const delegation = state.delegation;
|
|
301
|
-
if (!delegation || delegation.bindingId === undefined || !Number.isSafeInteger(expiresAtEpochMs) || !Number.isSafeInteger(renewalRevision)) {
|
|
302
|
-
state.diagnostics.droppedNotes += 1;
|
|
303
|
-
return;
|
|
304
|
-
}
|
|
305
|
-
delegation.expiresAtEpochMs = expiresAtEpochMs;
|
|
306
|
-
delegation.renewalRevision = renewalRevision;
|
|
307
|
-
}
|
|
308
|
-
/** `agent_delegation_refresh_pending{}` — a refreshed lease waits for the session to go idle. */
|
|
309
|
-
export function onDelegationRefreshPendingNote(state) {
|
|
310
|
-
if (!state.delegation) {
|
|
311
|
-
state.diagnostics.droppedNotes += 1;
|
|
312
|
-
return;
|
|
313
|
-
}
|
|
314
|
-
state.delegation.refreshPending = true;
|
|
315
|
-
}
|
|
316
243
|
/** `agent_launch{launch}` (see `runtime.ts`). */
|
|
317
244
|
export function onLaunch(state, note) {
|
|
318
245
|
if (!onLaunchNote(state, note.launch))
|
|
@@ -132,6 +132,14 @@ export function settlementOf(response) {
|
|
|
132
132
|
const { code, message, data } = response.error;
|
|
133
133
|
return { outcome: 'error', error: { code, message, data } };
|
|
134
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* The text an agent's error is shown as. The ACP SDK's `RequestError.internalError(details)` puts the
|
|
137
|
+
* real cause only in `data.details` behind a generic "Internal error", so a string detail is kept.
|
|
138
|
+
*/
|
|
139
|
+
export function jsonRpcErrorText(error) {
|
|
140
|
+
const details = isRecord(error.data) && typeof error.data.details === 'string' ? error.data.details.trim().slice(0, 2000) : '';
|
|
141
|
+
return details && !error.message.includes(details) ? `${error.message}: ${details}` : error.message;
|
|
142
|
+
}
|
|
135
143
|
/** `jsonRpcErrorCode(err)` for a settlement: the code an agent answered with, if a frame may carry it. */
|
|
136
144
|
export function settlementErrorCode(settlement) {
|
|
137
145
|
return settlement.outcome === 'error' ? jsonRpcErrorCodeOf(settlement.error.code) : undefined;
|
|
@@ -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
|
-
import { inboundKey, settlementErrorCode } from './jsonrpc.js';
|
|
5
|
+
import { inboundKey, jsonRpcErrorText, 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,14 @@ 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 };
|
|
54
|
+
state.closedReplayScopes = {};
|
|
105
55
|
resetRuntime(state);
|
|
106
56
|
state.turns = [];
|
|
107
57
|
state.exactExecutionUnconfirmed = false;
|
|
@@ -111,7 +61,6 @@ export function resetForSpawn(state) {
|
|
|
111
61
|
state.elicitations = {};
|
|
112
62
|
state.settledPermissions = [];
|
|
113
63
|
state.callbacks = initialCallbacks();
|
|
114
|
-
state.boundaries = initialBoundaries();
|
|
115
64
|
state.permissionOrigins = {};
|
|
116
65
|
state.permissionParents = {};
|
|
117
66
|
state.unresolvedPermissionOrigins = {};
|
|
@@ -147,7 +96,8 @@ const STOP_REASONS = {
|
|
|
147
96
|
end_turn: 'end_turn',
|
|
148
97
|
max_tokens: 'max_tokens',
|
|
149
98
|
max_turn_requests: 'max_turns',
|
|
150
|
-
refusal
|
|
99
|
+
// No `refusal`: a refusal result settles 'failed' (see `settlePrompt`), and a completed turn
|
|
100
|
+
// never carries it.
|
|
151
101
|
};
|
|
152
102
|
/**
|
|
153
103
|
* End turn `index` — `AcpSession.finishTurn` + `agentSessions.ts`'s `onAgentTurnDone` frame, in one.
|
|
@@ -158,8 +108,6 @@ function finishTurn(state, out, index, status, finish) {
|
|
|
158
108
|
const [turn] = state.turns.splice(index, 1);
|
|
159
109
|
if (!turn)
|
|
160
110
|
return;
|
|
161
|
-
if (state.turns.length === 0)
|
|
162
|
-
endForegroundTurn(state);
|
|
163
111
|
// Before the turn's own frame, on every path that ends a turn (`flushMessageCompleted`).
|
|
164
112
|
flushMessage(state, out);
|
|
165
113
|
const resolved = turn.interrupted ? 'interrupted' : status;
|
|
@@ -243,6 +191,28 @@ export function onDown(state, record) {
|
|
|
243
191
|
else
|
|
244
192
|
delete turn.platformTurnId;
|
|
245
193
|
}
|
|
194
|
+
/**
|
|
195
|
+
* The measured `_meta` key a refusal *result* carries its would-be error response under: a JSON
|
|
196
|
+
* string of the JSON-RPC error the agent chose not to send (`codebuddy-code` 2.171.0; shape-read,
|
|
197
|
+
* same rule as `acpClient`'s `_codebuddy.ai/authUrl`).
|
|
198
|
+
*/
|
|
199
|
+
const REFUSAL_ERROR_META_KEY = 'codebuddy.ai/errorMessage';
|
|
200
|
+
function refusalErrorDetail(result) {
|
|
201
|
+
if (!isRecord(result) || !isRecord(result._meta))
|
|
202
|
+
return undefined;
|
|
203
|
+
const raw = result._meta[REFUSAL_ERROR_META_KEY];
|
|
204
|
+
if (typeof raw !== 'string')
|
|
205
|
+
return undefined;
|
|
206
|
+
try {
|
|
207
|
+
const parsed = JSON.parse(raw);
|
|
208
|
+
if (!isRecord(parsed) || typeof parsed.message !== 'string')
|
|
209
|
+
return undefined;
|
|
210
|
+
return { ...(typeof parsed.code === 'number' ? { code: parsed.code } : {}), message: parsed.message };
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
return undefined;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
246
216
|
/** A `session/prompt` request settled: its turn ends on this record. */
|
|
247
217
|
export function settlePrompt(state, entry, settlement, out) {
|
|
248
218
|
const index = state.turns.findIndex((turn) => turn.requestId === entry.id);
|
|
@@ -252,14 +222,32 @@ export function settlePrompt(state, entry, settlement, out) {
|
|
|
252
222
|
case 'result': {
|
|
253
223
|
const stopReason = isRecord(settlement.result) ? settlement.result.stopReason : undefined;
|
|
254
224
|
// ACP's own `cancelled` forces `interrupted` even when no interrupt was sent (a backstop).
|
|
255
|
-
|
|
225
|
+
if (stopReason === 'cancelled') {
|
|
226
|
+
finishTurn(state, out, index, 'interrupted', { stopReason, executionEnded: true });
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
// Measured (codebuddy-code 2.171.0): an agent that needs credentials answers `session/prompt`
|
|
230
|
+
// with a *result* `stopReason: 'refusal'` — zero content, no JSON-RPC error — which used to
|
|
231
|
+
// settle 'completed' and render as nothing at all. A refusal result settles exactly like the
|
|
232
|
+
// agent's own refusal below ('failed' + `agentRefused`), with the agent's detail read out of
|
|
233
|
+
// the measured `_meta` key; a code-less detail stays an ordinary failure card server-side,
|
|
234
|
+
// while `-32000` beside the observed auth methods is what turns it into the sign-in card.
|
|
235
|
+
if (stopReason === 'refusal') {
|
|
236
|
+
const detail = refusalErrorDetail(settlement.result);
|
|
237
|
+
finishTurn(state, out, index, 'failed', {
|
|
238
|
+
agentRefused: true, executionEnded: true,
|
|
239
|
+
error: detail?.message ?? PROMPT_REFUSED, ...(detail?.code !== undefined ? { errorCode: detail.code } : {}),
|
|
240
|
+
});
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
finishTurn(state, out, index, 'completed', { stopReason, executionEnded: true });
|
|
256
244
|
return;
|
|
257
245
|
}
|
|
258
246
|
case 'error': {
|
|
259
247
|
// The one exit where the agent itself answered (`TurnResult.agentRefused`).
|
|
260
248
|
const errorCode = settlementErrorCode(settlement);
|
|
261
249
|
finishTurn(state, out, index, 'failed', {
|
|
262
|
-
error: settlement.error
|
|
250
|
+
error: jsonRpcErrorText(settlement.error), agentRefused: true, errorCode, executionEnded: errorCode !== undefined,
|
|
263
251
|
});
|
|
264
252
|
return;
|
|
265
253
|
}
|
|
@@ -405,23 +393,7 @@ export function onPermissionRequest(state, id, params, out) {
|
|
|
405
393
|
const rpcKey = inboundKey(id);
|
|
406
394
|
state.permissions[requestId] = { requestId, rpcKey, origin, rpcRequestId, optionIds: options.map((option) => option.optionId) };
|
|
407
395
|
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
|
-
}
|
|
396
|
+
const payload = event.payload;
|
|
425
397
|
emitSessionEvent(state, out, { eventName: event.eventName, payload, acpSessionId: origin });
|
|
426
398
|
return requestId;
|
|
427
399
|
}
|
|
@@ -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,16 @@ export function initialState() {
|
|
|
48
42
|
unresolvedPermissionOrigins: {},
|
|
49
43
|
open: { status: 'idle' },
|
|
50
44
|
configOptions: null,
|
|
51
|
-
delegation: null,
|
|
52
45
|
history: null,
|
|
46
|
+
closedReplayScopes: {},
|
|
53
47
|
mapperScopes: {},
|
|
54
48
|
message: initialMessage(),
|
|
55
49
|
steers: {},
|
|
56
50
|
steered: [],
|
|
51
|
+
draftSubagents: { children: {}, overflow: false },
|
|
57
52
|
asyncTasks: {},
|
|
58
53
|
activity: null,
|
|
59
54
|
runtime: initialRuntime(),
|
|
60
|
-
boundaries: initialBoundaries(),
|
|
61
55
|
diagnostics: {
|
|
62
56
|
orphanResponses: 0, protocolErrors: 0, ignoredLines: 0, unknownNotes: 0, droppedNotes: 0, refusedRequests: 0,
|
|
63
57
|
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' });
|