@platless/axom-cli 0.3.13 → 0.6.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -5
- package/dist/axom-patch/cli/README.md +119 -9
- package/dist/axom-patch/cli/account-limits/README.md +27 -0
- package/dist/axom-patch/cli/account-limits/antigravity-proxy.mjs +47 -0
- package/dist/axom-patch/cli/account-limits/antigravity-read.mjs +90 -0
- package/dist/axom-patch/cli/account-limits/antigravity.mjs +46 -0
- package/dist/axom-patch/cli/account-limits/generate.py +123 -0
- package/dist/axom-patch/cli/account-limits/kimi-proxy.mjs +81 -0
- package/dist/axom-patch/cli/account-limits/normalize.mjs +151 -0
- package/dist/axom-patch/cli/claude-account-limits.json +37 -0
- package/dist/axom-patch/cli/codex-account-limits.json +37 -0
- package/dist/axom-patch/cli/codex-terminal-output.json +16 -0
- package/dist/axom-patch/cli/commandcode-account-limits.json +30 -0
- 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 +194 -0
- package/dist/axom-patch/cli/pi-subagent-subwork.json +23 -0
- package/dist/axom-patch/cli/probes/claude-late-async-terminal.mjs +5 -1
- 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/pi-subagent-subwork.mjs +211 -0
- package/dist/axom-patch/cli/probes/zcode-subagent-subwork.mjs +257 -0
- package/dist/axom-patch/cli/qoder/dist/axom-acp-compat.mjs +1591 -0
- package/dist/axom-patch/cli/qoder/releases/1.1.75.json +360 -0
- package/dist/axom-patch/cli/qoder-background-terminal.md +13 -5
- package/dist/axom-patch/cli/qoder-foreground-output.md +14 -7
- 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 +39 -15
- package/dist/axom-patch/cli/zcode-account-limits.json +30 -0
- package/dist/axom-patch/cli/zcode-models.json +37 -0
- package/dist/axom-patch/cli/zcode-subagent-subwork.json +72 -0
- package/dist/cli.js +4 -9
- package/dist/commands/knowledge.js +47 -0
- package/dist/commands/update.js +65 -3
- package/dist/commands/workstation.js +73 -7
- package/dist/install/claude/commands.js +13 -13
- package/dist/keepers/acp/keeper.mjs +3083 -0
- package/dist/keepers/acp/manifest.json +9 -0
- package/dist/keepers/terminal/keeper.mjs +8469 -0
- package/dist/keepers/terminal/manifest.json +9 -0
- package/dist/keepers/terminal/node_modules/node-pty/lib/conpty_console_list_agent.js +25 -0
- package/dist/keepers/terminal/node_modules/node-pty/lib/eventEmitter2.js +47 -0
- package/dist/keepers/terminal/node_modules/node-pty/lib/index.js +52 -0
- package/dist/keepers/terminal/node_modules/node-pty/lib/interfaces.js +7 -0
- package/dist/keepers/terminal/node_modules/node-pty/lib/shared/conout.js +10 -0
- package/dist/keepers/terminal/node_modules/node-pty/lib/terminal.js +190 -0
- package/dist/keepers/terminal/node_modules/node-pty/lib/types.js +7 -0
- package/dist/keepers/terminal/node_modules/node-pty/lib/unixTerminal.js +351 -0
- package/dist/keepers/terminal/node_modules/node-pty/lib/utils.js +38 -0
- package/dist/keepers/terminal/node_modules/node-pty/lib/windowsConoutConnection.js +131 -0
- package/dist/keepers/terminal/node_modules/node-pty/lib/windowsPtyAgent.js +344 -0
- package/dist/keepers/terminal/node_modules/node-pty/lib/windowsTerminal.js +209 -0
- package/dist/keepers/terminal/node_modules/node-pty/lib/worker/conoutSocketWorker.js +22 -0
- package/dist/keepers/terminal/node_modules/node-pty/package.json +66 -0
- package/dist/keepers/terminal/node_modules/node-pty/prebuilds/darwin-arm64/pty.node +0 -0
- package/dist/keepers/terminal/node_modules/node-pty/prebuilds/darwin-arm64/spawn-helper +0 -0
- package/dist/keepers/terminal/node_modules/node-pty/prebuilds/darwin-x64/pty.node +0 -0
- package/dist/keepers/terminal/node_modules/node-pty/prebuilds/darwin-x64/spawn-helper +0 -0
- package/dist/keepers/terminal/node_modules/node-pty/prebuilds/linux-arm64/pty.node +0 -0
- package/dist/keepers/terminal/node_modules/node-pty/prebuilds/linux-x64/pty.node +0 -0
- package/dist/mcp.js +1 -1
- package/dist/native/darwin-arm64/axom-quic +0 -0
- package/dist/test/keeperKit.js +67 -0
- package/dist/workstation/acp/acpAgents.js +2192 -636
- package/dist/workstation/acp/acpClient.js +84 -495
- package/dist/workstation/acp/adoption.js +10 -396
- package/dist/workstation/acp/binaryInstall.js +100 -27
- package/dist/workstation/acp/builds/gc.js +245 -0
- package/dist/workstation/acp/builds/install/binary.js +28 -0
- package/dist/workstation/acp/builds/install/npm.js +324 -0
- package/dist/workstation/acp/builds/install/uv.js +254 -0
- package/dist/workstation/acp/builds/migrate.js +195 -0
- package/dist/workstation/acp/builds/model.js +128 -0
- package/dist/workstation/acp/builds/nodeGate.js +100 -0
- package/dist/workstation/acp/builds/store.js +352 -0
- package/dist/workstation/acp/commandTableProbe.js +68 -0
- package/dist/workstation/acp/configOptions.js +103 -1
- package/dist/workstation/acp/envSecrets.js +27 -0
- package/dist/workstation/acp/initializeResponse.js +290 -0
- package/dist/workstation/acp/installErrors.js +12 -2
- package/dist/workstation/acp/installProbe.js +1 -1
- package/dist/workstation/acp/managedPatches.js +239 -159
- package/dist/workstation/acp/managedRuntime.js +314 -0
- package/dist/workstation/acp/outbox.js +330 -0
- package/dist/workstation/acp/patchAssets.js +60 -3
- package/dist/workstation/acp/permission.js +42 -10
- package/dist/workstation/acp/processor/checkpoint.js +78 -0
- package/dist/workstation/acp/processor/commands.js +35 -0
- package/dist/workstation/acp/processor/control.js +282 -0
- package/dist/workstation/acp/processor/emit.js +26 -0
- package/dist/workstation/acp/processor/jsonrpc.js +172 -0
- package/dist/workstation/acp/processor/memoryLog.js +49 -0
- package/dist/workstation/acp/processor/origins.js +48 -0
- package/dist/workstation/acp/processor/output.js +67 -0
- package/dist/workstation/acp/processor/projection.js +42 -0
- package/dist/workstation/acp/processor/record.js +128 -0
- package/dist/workstation/acp/processor/runtime.js +105 -0
- package/dist/workstation/acp/processor/sdkSchemas.js +61 -0
- package/dist/workstation/acp/processor/session.js +496 -0
- package/dist/workstation/acp/processor/state.js +67 -0
- package/dist/workstation/acp/processor/step.js +442 -0
- package/dist/workstation/acp/processor/test/fixtureLog.js +58 -0
- package/dist/workstation/acp/processor/test/harness.js +120 -0
- package/dist/workstation/acp/processor/test/largeIdEcho.js +50 -0
- package/dist/workstation/acp/processor/test/rawAgent.js +70 -0
- package/dist/workstation/acp/processor/transport/generations.js +76 -0
- package/dist/workstation/acp/processor/transport/keeper.js +130 -0
- package/dist/workstation/acp/processor/transport/local.js +269 -0
- package/dist/workstation/acp/processor/transport/types.js +78 -0
- package/dist/workstation/acp/processor/updates.js +390 -0
- package/dist/workstation/acp/processorSession.js +1348 -0
- package/dist/workstation/acp/registry/catalog.js +189 -41
- package/dist/workstation/acp/registry/launch.js +76 -119
- package/dist/workstation/acp/sessionUpdateMapper.js +18 -7
- package/dist/workstation/acp/steering.js +58 -0
- package/dist/workstation/acp/switching.js +111 -0
- package/dist/workstation/acp/terminalLogin.js +16 -42
- package/dist/workstation/acp/terminalLoginSpec.js +41 -0
- package/dist/workstation/acp/test/adapterFixtures.js +256 -0
- package/dist/workstation/acp/test/daemonLife.js +106 -0
- package/dist/workstation/acp/test/fakeAgent.js +13 -0
- package/dist/workstation/acp/test/keeperWorld.js +295 -0
- package/dist/workstation/acp/test/memoryOutbox.js +55 -0
- package/dist/workstation/acp/test/raw/fixtures.js +196 -0
- package/dist/workstation/acp/test/raw/sanitize.js +344 -0
- package/dist/workstation/acp/test/raw/sources.js +135 -0
- package/dist/workstation/acp/test/realAdapter.js +331 -0
- package/dist/workstation/acp/test/scriptAgent.js +214 -0
- package/dist/workstation/acp/test/sessionFrames.js +42 -0
- package/dist/workstation/acp/timedCommand.js +4 -2
- package/dist/workstation/acp/uvxInstall.js +72 -122
- package/dist/workstation/agentFlow.js +103 -0
- package/dist/workstation/agentSessions.js +1289 -544
- package/dist/workstation/channels/agent.js +7 -5
- package/dist/workstation/channels/computer.js +69 -21
- package/dist/workstation/channels/computerExecs.js +200 -0
- package/dist/workstation/channels/management.js +172 -8
- package/dist/workstation/channels/url.js +1 -1
- package/dist/workstation/config.js +16 -4
- package/dist/workstation/connection/encryptedChannel.js +4 -1
- package/dist/workstation/control/bindingStore.js +200 -0
- package/dist/workstation/control/commands.js +124 -0
- package/dist/workstation/control/daemonIdentity.js +53 -0
- package/dist/workstation/control/enrollment.js +106 -0
- package/dist/workstation/control/inspect.js +71 -0
- package/dist/workstation/control/locks.js +63 -0
- package/dist/workstation/control/protocol.js +40 -0
- package/dist/workstation/control/restartGuard.js +46 -0
- package/dist/workstation/control/runtimeClient.js +97 -0
- package/dist/workstation/control/runtimeHost.js +148 -0
- package/dist/workstation/control/serverIdentity.js +35 -0
- package/dist/workstation/daemon.js +393 -12
- package/dist/workstation/daemonctl.js +103 -14
- package/dist/workstation/desktopControl/audio.js +99 -0
- package/dist/workstation/desktopControl/capabilities.js +92 -0
- package/dist/workstation/desktopControl/captureModes.js +9 -0
- package/dist/workstation/desktopControl/captureTargetLabel.js +9 -0
- package/dist/workstation/desktopControl/deviceSockets.js +246 -0
- package/dist/workstation/desktopControl/helperProcess.js +353 -0
- package/dist/workstation/desktopControl/lobby.js +129 -0
- package/dist/workstation/desktopControl/macProvider.js +155 -0
- package/dist/workstation/desktopControl/media.js +331 -0
- package/dist/workstation/desktopControl/nativeProvider.js +1018 -0
- package/dist/workstation/desktopControl/networkEvidence.js +43 -0
- package/dist/workstation/desktopControl/overview.js +58 -0
- package/dist/workstation/desktopControl/protocol.js +302 -0
- package/dist/workstation/desktopControl/quicMedia.js +240 -0
- package/dist/workstation/desktopControl/rateController.js +226 -0
- package/dist/workstation/desktopControl/supervisor.js +1681 -0
- package/dist/workstation/desktopControl/transport.js +27 -0
- package/dist/workstation/desktopControl/wallpaper.js +78 -0
- package/dist/workstation/exec/attach.js +126 -0
- package/dist/workstation/exec/command.js +40 -0
- package/dist/workstation/exec/durable.js +517 -0
- package/dist/workstation/exec/fallback.js +106 -0
- package/dist/workstation/exec/policy.js +837 -0
- package/dist/workstation/exec/shaping.js +211 -0
- package/dist/workstation/exec.js +3 -112
- package/dist/workstation/files/adapters/computerFileFrames.js +51 -0
- package/dist/workstation/files/adapters/toolRequests.js +121 -0
- package/dist/workstation/files/client.js +455 -0
- package/dist/workstation/files/credentials.js +142 -0
- package/dist/workstation/files/errors.js +28 -0
- package/dist/workstation/files/fence.js +350 -0
- package/dist/workstation/files/find/fileIndex.js +153 -0
- package/dist/workstation/files/find/fuzzy.js +155 -0
- package/dist/workstation/files/host.js +122 -0
- package/dist/workstation/files/hostProtocol.js +2 -0
- package/dist/workstation/files/media/cache.js +620 -0
- package/dist/workstation/files/media/encoding.js +308 -0
- package/dist/workstation/files/media/estimator.js +100 -0
- package/dist/workstation/files/media/media.js +325 -0
- package/dist/workstation/files/media/packedAudio.js +23 -0
- package/dist/workstation/files/media/scheduler.js +551 -0
- package/dist/workstation/files/media/types.js +5 -0
- package/dist/workstation/files/scope.js +45 -0
- package/dist/workstation/files/structured.js +284 -0
- package/dist/workstation/{fileOps.js → files/text.js} +152 -39
- package/dist/workstation/files/uploads.js +351 -0
- package/dist/workstation/journal.js +65 -236
- package/dist/workstation/keepers/acp/client.js +101 -0
- package/dist/workstation/keepers/acp/envelope.js +62 -0
- package/dist/workstation/keepers/acp/host.js +419 -0
- package/dist/workstation/keepers/acp/main.js +18 -0
- package/dist/workstation/keepers/acp/memoryStream.js +121 -0
- package/dist/workstation/keepers/acp/params.js +137 -0
- package/dist/workstation/keepers/acp/protocol.js +66 -0
- package/dist/workstation/keepers/acp/recover.js +120 -0
- package/dist/workstation/keepers/acp/session.js +584 -0
- package/dist/workstation/keepers/acp/stderr.js +154 -0
- package/dist/workstation/keepers/kit/activity.js +103 -0
- package/dist/workstation/keepers/kit/bootstrap.js +164 -0
- package/dist/workstation/keepers/kit/buildVersion.js +51 -0
- package/dist/workstation/keepers/kit/bundle.js +124 -0
- package/dist/workstation/keepers/kit/client.js +432 -0
- package/dist/workstation/keepers/kit/identity.js +21 -0
- package/dist/workstation/keepers/kit/ipc.js +112 -0
- package/dist/workstation/keepers/kit/launchConfig.js +8 -0
- package/dist/workstation/keepers/kit/launcher.js +289 -0
- package/dist/workstation/keepers/kit/lock.js +123 -0
- package/dist/workstation/keepers/kit/orphanSweep.js +86 -0
- package/dist/workstation/keepers/kit/paths.js +76 -0
- package/dist/workstation/keepers/kit/protocol.js +43 -0
- package/dist/workstation/keepers/kit/server.js +518 -0
- package/dist/workstation/keepers/kit/streamLog.js +697 -0
- package/dist/workstation/keepers/legacy.js +296 -0
- package/dist/workstation/keepers/policy.js +576 -0
- package/dist/workstation/keepers/stop.js +34 -0
- package/dist/workstation/keepers/terminal/client.js +213 -0
- package/dist/workstation/keepers/terminal/exec.js +981 -0
- package/dist/workstation/keepers/terminal/generations.js +129 -0
- package/dist/workstation/keepers/terminal/keeper.js +62 -0
- package/dist/workstation/keepers/terminal/main.js +43 -0
- package/dist/workstation/keepers/terminal/orphanSweep.js +3 -0
- package/dist/workstation/keepers/terminal/params.js +27 -0
- package/dist/workstation/keepers/terminal/protocol.js +56 -0
- package/dist/workstation/keepers/terminal/pty.js +330 -0
- package/dist/workstation/managedInstall.js +57 -0
- package/dist/workstation/protocol/accountLimits.js +47 -0
- package/dist/workstation/protocol/agent.js +50 -19
- package/dist/workstation/protocol/common.js +30 -0
- package/dist/workstation/protocol/computer.js +15 -4
- package/dist/workstation/protocol/management.js +141 -4
- package/dist/workstation/protocol/runtimeDiagnostics.js +15 -0
- package/dist/workstation/rd/artifact.js +119 -0
- package/dist/workstation/rd/control.js +119 -0
- package/dist/workstation/rd/display.js +29 -0
- package/dist/workstation/rd/provider.js +222 -0
- package/dist/workstation/rd/sessions.js +391 -0
- package/dist/workstation/rd/state.js +45 -0
- package/dist/workstation/rd/update.js +171 -0
- package/dist/workstation/rd/upkeep.js +38 -0
- package/dist/workstation/register.js +27 -37
- package/dist/workstation/service/installer.js +104 -13
- package/dist/workstation/service/launchd.js +1 -0
- package/dist/workstation/storageMetrics.js +118 -0
- package/dist/workstation/systemMetrics.js +154 -93
- package/dist/workstation/toolchain.js +94 -0
- package/dist/workstation/tools/browser.js +28 -4
- package/dist/workstation/tools/client.js +9 -1
- package/dist/workstation/tools/info.js +50 -0
- package/dist/workstation/tools/keeperProcess.js +15 -5
- package/dist/workstation/tools/router.js +55 -0
- package/dist/workstation/tools/terminalRouter.js +222 -0
- package/dist/workstation/tools/validation.js +37 -1
- package/dist/workstation/update/artifact.js +2 -1
- package/dist/workstation/update/coordinator.js +3 -0
- package/dist/workstation/update/globalInstall.js +116 -0
- package/dist/workstation/update/npmInstaller.js +49 -23
- package/dist/workstation/update/ptyPreflight.js +21 -0
- package/dist/workstation/update/rdRollback.js +145 -0
- package/dist/workstation/update/runUpdate.js +3 -2
- package/dist/workstation/update/taskRegistry.js +105 -11
- package/dist/workstation/update/watchdog.js +16790 -334
- package/package.json +11 -8
- 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 -49
- 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/workstation/acp/acpSession.js +0 -1475
- package/dist/workstation/acp/contract.js +0 -499
- package/dist/workstation/acp/delegationMcp.js +0 -24
- package/dist/workstation/acp/draftInterceptor.js +0 -163
- package/dist/workstation/acp/qoderStartup.js +0 -45
- package/dist/workstation/acp/sessionScope.js +0 -19
- package/dist/workstation/filePaths.js +0 -174
- package/dist/workstation/fileWorker.js +0 -81
- package/dist/workstation/fileWorkerClient.js +0 -121
- package/dist/workstation/qoderAdapter/backgroundProcess.js +0 -106
- package/dist/workstation/qoderAdapter/backgroundTerminal.js +0 -262
- package/dist/workstation/qoderAdapter/completedContinuation.js +0 -139
- package/dist/workstation/qoderAdapter/contract.js +0 -78
- package/dist/workstation/qoderAdapter/foregroundOutput.js +0 -30
- package/dist/workstation/qoderAdapter/history.js +0 -87
- package/dist/workstation/qoderAdapter/identityIndex.js +0 -191
- package/dist/workstation/qoderAdapter/inputQueue.js +0 -42
- package/dist/workstation/qoderAdapter/main.js +0 -93
- 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 -49
- package/dist/workstation/qoderAdapter/session.js +0 -319
- package/dist/workstation/qoderAdapter/subagentMeta.js +0 -28
- package/dist/workstation/qoderAdapter/subwork.js +0 -77
- package/dist/workstation/qoderAdapter/taskControl.js +0 -38
- package/dist/workstation/qoderAdapter/taskEvent.js +0 -26
- package/dist/workstation/qoderAdapter/taskLedger.js +0 -163
- package/dist/workstation/qoderAdapter/usage.js +0 -37
- package/dist/workstation/tools/core.js +0 -50
- package/dist/workstation/tools/files.js +0 -525
- package/dist/workstation/tools/terminal.js +0 -214
- /package/dist/workstation/{qoderAdapter/types.js → desktopControl/provider.js} +0 -0
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
/** Native Qoder does not forward background child SDK messages through ACP. Read only the
|
|
2
|
-
* exact transcript named by a validated root launch receipt; never scan or infer ownership. */
|
|
3
|
-
import { constants } from 'node:fs';
|
|
4
|
-
import { open } from 'node:fs/promises';
|
|
5
|
-
const object = (value) => value && typeof value === 'object' && !Array.isArray(value) ? value : {};
|
|
6
|
-
const id = (value) => typeof value === 'string' && /^[a-zA-Z0-9_-]{1,160}$/.test(value);
|
|
7
|
-
const LIMIT = 2 * 1024 * 1024;
|
|
8
|
-
export class BackgroundProcess {
|
|
9
|
-
options;
|
|
10
|
-
offset = 0;
|
|
11
|
-
tail = Buffer.alloc(0);
|
|
12
|
-
skipLine = false;
|
|
13
|
-
seen = new Set();
|
|
14
|
-
tools = new Map();
|
|
15
|
-
constructor(options) {
|
|
16
|
-
this.options = options;
|
|
17
|
-
}
|
|
18
|
-
async poll() {
|
|
19
|
-
const file = await open(this.options.path, constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
20
|
-
try {
|
|
21
|
-
const stat = await file.stat();
|
|
22
|
-
if (!stat.isFile())
|
|
23
|
-
return true;
|
|
24
|
-
if (stat.size < this.offset) {
|
|
25
|
-
this.offset = 0;
|
|
26
|
-
this.tail = Buffer.alloc(0);
|
|
27
|
-
this.skipLine = false;
|
|
28
|
-
}
|
|
29
|
-
const end = Math.min(stat.size, this.offset + 4 * 1024 * 1024);
|
|
30
|
-
while (this.offset < end) {
|
|
31
|
-
const chunk = Buffer.alloc(Math.min(64 * 1024, end - this.offset));
|
|
32
|
-
const { bytesRead } = await file.read(chunk, 0, chunk.length, this.offset);
|
|
33
|
-
if (!bytesRead)
|
|
34
|
-
break;
|
|
35
|
-
this.offset += bytesRead;
|
|
36
|
-
this.consumeBytes(chunk.subarray(0, bytesRead));
|
|
37
|
-
}
|
|
38
|
-
return this.offset >= stat.size;
|
|
39
|
-
}
|
|
40
|
-
finally {
|
|
41
|
-
await file.close();
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
consumeBytes(bytes) {
|
|
45
|
-
let start = 0;
|
|
46
|
-
for (let end = bytes.indexOf(10); end >= 0; end = bytes.indexOf(10, start)) {
|
|
47
|
-
const part = bytes.subarray(start, end);
|
|
48
|
-
if (!this.skipLine && this.tail.length + part.length <= LIMIT) {
|
|
49
|
-
try {
|
|
50
|
-
this.consume(object(JSON.parse(Buffer.concat([this.tail, part]).toString('utf8'))));
|
|
51
|
-
}
|
|
52
|
-
catch { /* not evidence */ }
|
|
53
|
-
}
|
|
54
|
-
this.tail = Buffer.alloc(0);
|
|
55
|
-
this.skipLine = false;
|
|
56
|
-
start = end + 1;
|
|
57
|
-
}
|
|
58
|
-
const part = bytes.subarray(start);
|
|
59
|
-
if (!this.skipLine && this.tail.length + part.length <= LIMIT)
|
|
60
|
-
this.tail = Buffer.concat([this.tail, part]);
|
|
61
|
-
else {
|
|
62
|
-
this.tail = Buffer.alloc(0);
|
|
63
|
-
this.skipLine = true;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
consume(row) {
|
|
67
|
-
const o = this.options;
|
|
68
|
-
if (row.sessionId !== o.sessionId || row.cwd !== o.cwd || row.agentId !== o.agentId ||
|
|
69
|
-
row.parent_tool_use_id !== o.parentToolUseId || row.isSidechain !== true || row.version !== '1.1.61' ||
|
|
70
|
-
!id(row.uuid) || this.seen.has(row.uuid) || this.seen.size >= 4096)
|
|
71
|
-
return;
|
|
72
|
-
this.seen.add(row.uuid);
|
|
73
|
-
const content = object(row.message).content;
|
|
74
|
-
if (!Array.isArray(content))
|
|
75
|
-
return;
|
|
76
|
-
for (const block of content.map(object)) {
|
|
77
|
-
if (row.type === 'assistant' && block.type === 'tool_use' && id(block.id) && typeof block.name === 'string') {
|
|
78
|
-
if (this.tools.has(block.id) || this.tools.size >= 4096)
|
|
79
|
-
continue;
|
|
80
|
-
const input = object(block.input);
|
|
81
|
-
this.tools.set(block.id, { name: block.name, input });
|
|
82
|
-
this.emit('tool_call', { toolCallId: block.id, name: block.name, title: block.name,
|
|
83
|
-
kind: block.name === 'Read' ? 'read' : block.name === 'Bash' ? 'execute' : block.name === 'Agent' ? 'think' : 'other',
|
|
84
|
-
rawInput: input, status: 'pending' }, block.name);
|
|
85
|
-
}
|
|
86
|
-
else if (row.type === 'user' && block.type === 'tool_result' && id(block.tool_use_id)) {
|
|
87
|
-
const tool = this.tools.get(block.tool_use_id);
|
|
88
|
-
if (!tool)
|
|
89
|
-
continue;
|
|
90
|
-
const raw = row.toolUseResult ?? block.content;
|
|
91
|
-
const text = typeof block.content === 'string' ? block.content : JSON.stringify(block.content ?? '');
|
|
92
|
-
this.emit('tool_call_update', { toolCallId: block.tool_use_id, name: tool.name,
|
|
93
|
-
status: block.is_error === true ? 'failed' : 'completed', rawOutput: raw,
|
|
94
|
-
content: [{ type: 'content', content: { type: 'text', text } }] }, tool.name);
|
|
95
|
-
}
|
|
96
|
-
else if (row.type === 'assistant' && ((block.type === 'text' && typeof block.text === 'string') ||
|
|
97
|
-
(block.type === 'thinking' && typeof block.thinking === 'string'))) {
|
|
98
|
-
this.emit(block.type === 'text' ? 'agent_message_chunk' : 'agent_thought_chunk', { content: { type: 'text', text: block.type === 'text' ? block.text : block.thinking } });
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
emit(eventName, payload, toolName) {
|
|
103
|
-
this.options.emit({ eventName, payload: { ...payload, _meta: { qoder: { version: 1, source: 'correction',
|
|
104
|
-
parentToolUseId: this.options.parentToolUseId, route: 'child', ...(toolName ? { toolName } : {}) } } } });
|
|
105
|
-
}
|
|
106
|
-
}
|
|
@@ -1,262 +0,0 @@
|
|
|
1
|
-
/** Qoder 1.1.61 drops native background task notifications from both live ACP and session/load.
|
|
2
|
-
* This read-only compatibility boundary repairs only a known root Agent tool, never a session.
|
|
3
|
-
* Lifecycle/removal contract: axom-patch/cli/qoder-background-terminal.md.
|
|
4
|
-
*/
|
|
5
|
-
import { constants } from 'node:fs';
|
|
6
|
-
import { open } from 'node:fs/promises';
|
|
7
|
-
import { dirname, isAbsolute, join, resolve } from 'node:path';
|
|
8
|
-
import { BackgroundProcess } from './backgroundProcess.js';
|
|
9
|
-
const CHUNK = 64 * 1024;
|
|
10
|
-
const MAX_LINE = 2 * 1024 * 1024;
|
|
11
|
-
const MAX_POLL = 4 * 1024 * 1024;
|
|
12
|
-
const MAX_TASKS = 512;
|
|
13
|
-
const MAX_RESULT = 128 * 1024;
|
|
14
|
-
const MAX_RESULTS = 4 * 1024 * 1024;
|
|
15
|
-
const TERMINALS = new Set(['completed', 'failed', 'cancelled']);
|
|
16
|
-
function object(value) {
|
|
17
|
-
return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
|
|
18
|
-
}
|
|
19
|
-
function address(value) {
|
|
20
|
-
return typeof value === 'string' && /^[a-zA-Z0-9_-]{1,160}$/.test(value) ? value : undefined;
|
|
21
|
-
}
|
|
22
|
-
export function nativeTerminalHistoryPath(identity, sessionId, cwd, historyHome) {
|
|
23
|
-
if (identity?.name !== 'qoder-cli' || identity.version !== '1.1.61' ||
|
|
24
|
-
!address(sessionId) || !isAbsolute(cwd) || !isAbsolute(historyHome))
|
|
25
|
-
return;
|
|
26
|
-
// Certified ~/.qoder/projects layout. Even a colliding directory name cannot cross session/cwd
|
|
27
|
-
// identity: every accepted row is checked again against the exact native session and directory.
|
|
28
|
-
return join(historyHome, '.qoder', 'projects', resolve(cwd).replace(/[^a-zA-Z0-9]/g, '-'), sessionId + '.jsonl');
|
|
29
|
-
}
|
|
30
|
-
export class BackgroundTerminalRepair {
|
|
31
|
-
options;
|
|
32
|
-
calls = new Map();
|
|
33
|
-
launches = new Map();
|
|
34
|
-
terminals = new Map();
|
|
35
|
-
delivered = new Set();
|
|
36
|
-
processes = new Map();
|
|
37
|
-
pendingProcesses = new Set();
|
|
38
|
-
offset = 0;
|
|
39
|
-
fileIdentity = '';
|
|
40
|
-
pendingLine = Buffer.alloc(0);
|
|
41
|
-
skipLine = false;
|
|
42
|
-
closed = false;
|
|
43
|
-
timer;
|
|
44
|
-
inFlight;
|
|
45
|
-
warned = false;
|
|
46
|
-
resultBytes = 0;
|
|
47
|
-
restoring;
|
|
48
|
-
constructor(options) {
|
|
49
|
-
this.options = options;
|
|
50
|
-
this.restoring = options.restoreCalls === true;
|
|
51
|
-
}
|
|
52
|
-
start() {
|
|
53
|
-
if (this.closed || this.timer)
|
|
54
|
-
return;
|
|
55
|
-
this.timer = setInterval(() => { void this.poll(); }, 1000);
|
|
56
|
-
this.timer.unref();
|
|
57
|
-
}
|
|
58
|
-
observe(event) {
|
|
59
|
-
if (this.closed || (event.acpSessionId && event.acpSessionId !== this.options.nativeSessionId) ||
|
|
60
|
-
!['tool_call', 'tool_call_update'].includes(event.eventName))
|
|
61
|
-
return;
|
|
62
|
-
const payload = event.payload;
|
|
63
|
-
const id = address(payload.toolCallId);
|
|
64
|
-
if (!id)
|
|
65
|
-
return;
|
|
66
|
-
let call = this.calls.get(id);
|
|
67
|
-
const input = object(payload.rawInput);
|
|
68
|
-
if (!call && input.run_in_background === true && typeof input.prompt === 'string' &&
|
|
69
|
-
(input.subagent_type == null || typeof input.subagent_type === 'string') && payload.kind === 'think' &&
|
|
70
|
-
object(object(payload._meta).qoder).toolName === 'Agent' && this.calls.size < MAX_TASKS) {
|
|
71
|
-
const meta = object(object(payload._meta).qoder);
|
|
72
|
-
call = { input, invocationComplete: false, nativeTerminal: false,
|
|
73
|
-
process: this.options.includeProcess === true && meta.version === 1 && meta.source === 'native-acp-patch' };
|
|
74
|
-
this.calls.set(id, call);
|
|
75
|
-
}
|
|
76
|
-
if (!call)
|
|
77
|
-
return;
|
|
78
|
-
if (payload.status === 'completed')
|
|
79
|
-
call.invocationComplete = true;
|
|
80
|
-
const output = object(payload.rawOutput);
|
|
81
|
-
if (output.kind === 'agent-result' && (TERMINALS.has(String(output.state)) || output.state === 'error')) {
|
|
82
|
-
call.nativeTerminal = true; // Upstream's structured evidence always wins.
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
poll() {
|
|
86
|
-
if (this.closed || (!this.restoring && ![...this.calls].some(([id, call]) => !this.delivered.has(id) && !call.nativeTerminal)))
|
|
87
|
-
return Promise.resolve();
|
|
88
|
-
if (!this.inFlight) {
|
|
89
|
-
this.inFlight = this.readIncrement().catch(error => {
|
|
90
|
-
const code = object(error).code;
|
|
91
|
-
if (!['ENOENT', 'ELOOP', 'ENOTDIR', 'EACCES'].includes(String(code)) && !this.warned) {
|
|
92
|
-
this.warned = true;
|
|
93
|
-
console.warn('[acp:qoder] background terminal repair unavailable; retaining native ACP state');
|
|
94
|
-
}
|
|
95
|
-
}).finally(() => { this.inFlight = undefined; });
|
|
96
|
-
}
|
|
97
|
-
return this.inFlight;
|
|
98
|
-
}
|
|
99
|
-
close() {
|
|
100
|
-
this.closed = true;
|
|
101
|
-
if (this.timer)
|
|
102
|
-
clearInterval(this.timer);
|
|
103
|
-
this.timer = undefined;
|
|
104
|
-
this.calls.clear();
|
|
105
|
-
this.launches.clear();
|
|
106
|
-
this.terminals.clear();
|
|
107
|
-
this.delivered.clear();
|
|
108
|
-
this.processes.clear();
|
|
109
|
-
this.pendingProcesses.clear();
|
|
110
|
-
this.pendingLine = Buffer.alloc(0);
|
|
111
|
-
}
|
|
112
|
-
async readIncrement() {
|
|
113
|
-
// O_NOFOLLOW avoids importing a transcript through a replaced symlink. File is never written.
|
|
114
|
-
const file = await open(this.options.path, constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
115
|
-
try {
|
|
116
|
-
const stat = await file.stat();
|
|
117
|
-
if (!stat.isFile() || this.closed)
|
|
118
|
-
return;
|
|
119
|
-
const identity = `${stat.dev}:${stat.ino}`;
|
|
120
|
-
if (identity !== this.fileIdentity || stat.size < this.offset) {
|
|
121
|
-
this.offset = 0;
|
|
122
|
-
this.pendingLine = Buffer.alloc(0);
|
|
123
|
-
this.skipLine = false;
|
|
124
|
-
this.fileIdentity = identity;
|
|
125
|
-
}
|
|
126
|
-
const end = Math.min(stat.size, this.offset + MAX_POLL);
|
|
127
|
-
const chunk = Buffer.alloc(CHUNK);
|
|
128
|
-
while (!this.closed && this.offset < end) {
|
|
129
|
-
const { bytesRead } = await file.read(chunk, 0, Math.min(CHUNK, end - this.offset), this.offset);
|
|
130
|
-
if (!bytesRead)
|
|
131
|
-
break;
|
|
132
|
-
this.offset += bytesRead;
|
|
133
|
-
this.lines(chunk.subarray(0, bytesRead));
|
|
134
|
-
}
|
|
135
|
-
if (this.offset >= stat.size)
|
|
136
|
-
this.restoring = false;
|
|
137
|
-
if (!this.closed) {
|
|
138
|
-
await this.pollProcesses();
|
|
139
|
-
if (!this.closed)
|
|
140
|
-
this.publish();
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
finally {
|
|
144
|
-
await file.close();
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
lines(chunk) {
|
|
148
|
-
let start = 0;
|
|
149
|
-
for (let end = chunk.indexOf(10); end !== -1; end = chunk.indexOf(10, start)) {
|
|
150
|
-
if (!this.skipLine && this.pendingLine.length + end - start <= MAX_LINE) {
|
|
151
|
-
const line = Buffer.concat([this.pendingLine, chunk.subarray(start, end)]).toString('utf8');
|
|
152
|
-
try {
|
|
153
|
-
this.consume(object(JSON.parse(line)));
|
|
154
|
-
}
|
|
155
|
-
catch { /* partial/corrupt non-evidence */ }
|
|
156
|
-
}
|
|
157
|
-
this.pendingLine = Buffer.alloc(0);
|
|
158
|
-
this.skipLine = false;
|
|
159
|
-
start = end + 1;
|
|
160
|
-
}
|
|
161
|
-
const tail = chunk.subarray(start);
|
|
162
|
-
if (!this.skipLine && this.pendingLine.length + tail.length <= MAX_LINE) {
|
|
163
|
-
this.pendingLine = Buffer.concat([this.pendingLine, tail]);
|
|
164
|
-
}
|
|
165
|
-
else {
|
|
166
|
-
this.pendingLine = Buffer.alloc(0);
|
|
167
|
-
this.skipLine = true;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
consume(row) {
|
|
171
|
-
if (row.sessionId !== this.options.nativeSessionId || row.cwd !== this.options.cwd ||
|
|
172
|
-
row.isSidechain !== false)
|
|
173
|
-
return;
|
|
174
|
-
const content = object(row.message).content;
|
|
175
|
-
if (!Array.isArray(content))
|
|
176
|
-
return;
|
|
177
|
-
// resume deliberately sends no historical ACP openings. Recover only certified root Agent
|
|
178
|
-
// background inputs; no opening is emitted and the existing exact-ID notification rules apply.
|
|
179
|
-
if (this.options.restoreCalls && row.type === 'assistant') {
|
|
180
|
-
for (const block of content.map(object)) {
|
|
181
|
-
const input = object(block.input), id = address(block.id);
|
|
182
|
-
if (block.type === 'tool_use' && block.name === 'Agent' && id && input.run_in_background === true &&
|
|
183
|
-
typeof input.prompt === 'string' && (input.subagent_type == null || typeof input.subagent_type === 'string') &&
|
|
184
|
-
!this.calls.has(id) && this.calls.size < MAX_TASKS) {
|
|
185
|
-
this.calls.set(id, { input, invocationComplete: false, nativeTerminal: false, process: false });
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
return;
|
|
189
|
-
}
|
|
190
|
-
if (row.type !== 'user')
|
|
191
|
-
return;
|
|
192
|
-
const output = object(row.toolUseResult);
|
|
193
|
-
if (output.isAsync === true && output.status === 'async_launched') {
|
|
194
|
-
const results = content.map(object).filter(c => c.type === 'tool_result');
|
|
195
|
-
const id = results.length === 1 ? address(results[0].tool_use_id) : undefined;
|
|
196
|
-
const agent = address(output.agentId);
|
|
197
|
-
if (this.options.restoreCalls && id && this.calls.has(id))
|
|
198
|
-
this.calls.get(id).invocationComplete = true;
|
|
199
|
-
if (id && agent && this.launches.size < MAX_TASKS && !this.launches.has(id))
|
|
200
|
-
this.launches.set(id, agent);
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
203
|
-
if (row.isMeta !== true || object(row.origin).kind !== 'task-notification')
|
|
204
|
-
return;
|
|
205
|
-
for (const block of content.map(object)) {
|
|
206
|
-
if (block.type !== 'text' || typeof block.text !== 'string' ||
|
|
207
|
-
!block.text.startsWith('[SYSTEM NOTIFICATION - NOT USER INPUT]\n'))
|
|
208
|
-
continue;
|
|
209
|
-
// Match the authoritative envelope before result text; tags inside a result cannot change IDs.
|
|
210
|
-
const match = block.text.match(/<task-notification>\n<task-id>([a-zA-Z0-9_-]{1,160})<\/task-id>\n<tool-use-id>([a-zA-Z0-9_-]{1,160})<\/tool-use-id>\n(?:<output-file>[^\n]*<\/output-file>\n)?<status>(completed|failed|cancelled)<\/status>\n<summary>[^\n]*<\/summary>\n<result>([\s\S]*)<\/result>\n(?:<usage>[^\n]*<\/usage>\n)?<\/task-notification>\s*$/);
|
|
211
|
-
const bytes = match ? Buffer.byteLength(match[4]) : 0;
|
|
212
|
-
if (match && bytes <= MAX_RESULT && this.resultBytes + bytes <= MAX_RESULTS &&
|
|
213
|
-
this.terminals.size < MAX_TASKS && !this.terminals.has(match[2])) {
|
|
214
|
-
this.resultBytes += bytes;
|
|
215
|
-
this.terminals.set(match[2], { taskId: match[1], state: match[3], result: match[4] });
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
publish() {
|
|
220
|
-
for (const [id, terminal] of this.terminals) {
|
|
221
|
-
const call = this.calls.get(id), agentId = this.launches.get(id);
|
|
222
|
-
if (this.delivered.has(id) || this.pendingProcesses.has(id) || !call?.invocationComplete || call.nativeTerminal || !agentId)
|
|
223
|
-
continue;
|
|
224
|
-
this.delivered.add(id);
|
|
225
|
-
this.options.emit({ eventName: 'tool_call_update', payload: {
|
|
226
|
-
name: 'Agent', kind: 'think', toolCallId: id, rawInput: call.input, status: 'completed',
|
|
227
|
-
rawOutput: { kind: 'agent-result', state: terminal.state, agentId, result: terminal.result },
|
|
228
|
-
content: [{ type: 'content', content: { type: 'text', text: terminal.result } }],
|
|
229
|
-
_meta: { qoder: { toolName: 'Agent' }, axomNativeTerminalRepair: {
|
|
230
|
-
patch: 'qoder-background-terminal', nativeSessionId: this.options.nativeSessionId,
|
|
231
|
-
taskId: terminal.taskId, source: 'native-task-notification',
|
|
232
|
-
} },
|
|
233
|
-
} });
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
async pollProcesses() {
|
|
237
|
-
for (const [id, agentId] of this.launches) {
|
|
238
|
-
const call = this.calls.get(id);
|
|
239
|
-
if (this.closed || this.delivered.has(id) || !call?.process || call.nativeTerminal)
|
|
240
|
-
continue;
|
|
241
|
-
let process = this.processes.get(id);
|
|
242
|
-
if (!process) {
|
|
243
|
-
process = new BackgroundProcess({
|
|
244
|
-
path: join(dirname(this.options.path), this.options.nativeSessionId, 'subagents', `agent-${agentId}.jsonl`),
|
|
245
|
-
sessionId: this.options.nativeSessionId, cwd: this.options.cwd, agentId, parentToolUseId: id,
|
|
246
|
-
emit: event => { if (!this.closed)
|
|
247
|
-
this.options.emit(event); },
|
|
248
|
-
});
|
|
249
|
-
this.processes.set(id, process);
|
|
250
|
-
}
|
|
251
|
-
try {
|
|
252
|
-
if (await process.poll())
|
|
253
|
-
this.pendingProcesses.delete(id);
|
|
254
|
-
else
|
|
255
|
-
this.pendingProcesses.add(id);
|
|
256
|
-
}
|
|
257
|
-
catch {
|
|
258
|
-
this.pendingProcesses.delete(id); /* missing/corrupt transcript is not execution evidence */
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
}
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
import { capabilitiesFor, QODER_CERTIFICATIONS } from './profile.js';
|
|
2
|
-
const MAX_ID_BYTES = 256;
|
|
3
|
-
const MAX_MESSAGE_BYTES = 131072;
|
|
4
|
-
const MAX_RECORDS = 4096;
|
|
5
|
-
const states = new Set(['pending', 'paused', 'running', 'completed', 'stopped', 'failed', 'unknown']);
|
|
6
|
-
const id = (value) => typeof value === 'string' && value.trim().length > 0 &&
|
|
7
|
-
Buffer.byteLength(value, 'utf8') <= MAX_ID_BYTES && !/[\x00-\x1f\x7f]/.test(value);
|
|
8
|
-
const message = (value) => typeof value === 'string' &&
|
|
9
|
-
Buffer.byteLength(value, 'utf8') <= MAX_MESSAGE_BYTES;
|
|
10
|
-
function exactIdentity(value) {
|
|
11
|
-
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
12
|
-
return false;
|
|
13
|
-
const input = value;
|
|
14
|
-
return ['rootSessionId', 'runtimeGeneration', 'sourceTaskId', 'agentId', 'requestId', 'toolUseId']
|
|
15
|
-
.every((key) => id(input[key]));
|
|
16
|
-
}
|
|
17
|
-
function hasVendorResumeClaim(value) {
|
|
18
|
-
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
19
|
-
return false;
|
|
20
|
-
if (Object.prototype.hasOwnProperty.call(value, 'resumedAgentId'))
|
|
21
|
-
return true;
|
|
22
|
-
return Object.values(value).some((entry) => hasVendorResumeClaim(entry));
|
|
23
|
-
}
|
|
24
|
-
export function normalizeContinuationEvidence(value) {
|
|
25
|
-
if (!value || typeof value !== 'object' || Array.isArray(value) || hasVendorResumeClaim(value))
|
|
26
|
-
return undefined;
|
|
27
|
-
const input = value;
|
|
28
|
-
if (!exactIdentity(input.request) || !exactIdentity(input.sendMessage))
|
|
29
|
-
return undefined;
|
|
30
|
-
if (typeof input.sendMessage.success !== 'boolean')
|
|
31
|
-
return undefined;
|
|
32
|
-
const joined = input.joinedTask;
|
|
33
|
-
if (joined === undefined)
|
|
34
|
-
return {
|
|
35
|
-
request: { ...input.request },
|
|
36
|
-
sendMessage: { ...input.sendMessage,
|
|
37
|
-
success: input.sendMessage.success },
|
|
38
|
-
};
|
|
39
|
-
if (!joined || typeof joined !== 'object' || Array.isArray(joined))
|
|
40
|
-
return undefined;
|
|
41
|
-
const task = joined;
|
|
42
|
-
const joinedTask = task;
|
|
43
|
-
if (!exactIdentity(task) || !id(joinedTask.taskId) || joinedTask.taskId === joinedTask.sourceTaskId ||
|
|
44
|
-
!states.has(joinedTask.state) || !['pending', 'running', 'completed'].includes(joinedTask.state) ||
|
|
45
|
-
typeof joinedTask.live !== 'boolean' || typeof joinedTask.stale !== 'boolean')
|
|
46
|
-
return undefined;
|
|
47
|
-
return {
|
|
48
|
-
request: { ...input.request },
|
|
49
|
-
sendMessage: { ...input.sendMessage,
|
|
50
|
-
success: input.sendMessage.success },
|
|
51
|
-
joinedTask: { ...joinedTask },
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
function sameIdentity(a, b) {
|
|
55
|
-
return a.rootSessionId === b.rootSessionId && a.runtimeGeneration === b.runtimeGeneration &&
|
|
56
|
-
a.sourceTaskId === b.sourceTaskId && a.agentId === b.agentId && a.requestId === b.requestId &&
|
|
57
|
-
a.toolUseId === b.toolUseId;
|
|
58
|
-
}
|
|
59
|
-
/** Confirm only an observed, exact new execution. Natural-language claims are intentionally ignored. */
|
|
60
|
-
export function confirmContinuation(expectedAgentId, value) {
|
|
61
|
-
if (!id(expectedAgentId))
|
|
62
|
-
return 'rejected';
|
|
63
|
-
const evidence = normalizeContinuationEvidence(value);
|
|
64
|
-
if (!evidence || !evidence.joinedTask || !evidence.sendMessage.success ||
|
|
65
|
-
evidence.request.agentId !== expectedAgentId || !sameIdentity(evidence.request, evidence.sendMessage) ||
|
|
66
|
-
!sameIdentity(evidence.request, evidence.joinedTask) || evidence.joinedTask.agentId !== expectedAgentId ||
|
|
67
|
-
evidence.joinedTask.stale || !evidence.joinedTask.taskId ||
|
|
68
|
-
evidence.joinedTask.taskId === evidence.request.sourceTaskId)
|
|
69
|
-
return 'rejected';
|
|
70
|
-
return 'confirmed';
|
|
71
|
-
}
|
|
72
|
-
function key(identity) {
|
|
73
|
-
return [identity.rootSessionId, identity.runtimeGeneration, identity.requestId].join('\u0000');
|
|
74
|
-
}
|
|
75
|
-
/** A bounded, request-scoped ledger prevents duplicate confirmations and keeps late proof unknown. */
|
|
76
|
-
export class ContinuationLedger {
|
|
77
|
-
requests = new Map();
|
|
78
|
-
statuses = new Map();
|
|
79
|
-
joinedTaskIds = new Set();
|
|
80
|
-
evidence = new Map();
|
|
81
|
-
request(identity) {
|
|
82
|
-
if (!exactIdentity(identity) || this.requests.size >= MAX_RECORDS)
|
|
83
|
-
return 'rejected';
|
|
84
|
-
const requestKey = key(identity);
|
|
85
|
-
if (this.requests.has(requestKey))
|
|
86
|
-
return 'rejected';
|
|
87
|
-
this.requests.set(requestKey, { ...identity });
|
|
88
|
-
this.statuses.set(requestKey, 'requested');
|
|
89
|
-
return 'requested';
|
|
90
|
-
}
|
|
91
|
-
confirm(expectedAgentId, value) {
|
|
92
|
-
const evidence = normalizeContinuationEvidence(value);
|
|
93
|
-
if (!evidence)
|
|
94
|
-
return 'rejected';
|
|
95
|
-
const requestKey = key(evidence.request);
|
|
96
|
-
const original = this.requests.get(requestKey);
|
|
97
|
-
if (!original || !sameIdentity(original, evidence.request))
|
|
98
|
-
return 'unknown';
|
|
99
|
-
if (!evidence.joinedTask)
|
|
100
|
-
return 'unknown';
|
|
101
|
-
if (this.statuses.get(requestKey) === 'confirmed' || this.joinedTaskIds.has(evidence.joinedTask.taskId)) {
|
|
102
|
-
return 'rejected';
|
|
103
|
-
}
|
|
104
|
-
const result = confirmContinuation(expectedAgentId, evidence);
|
|
105
|
-
if (result === 'confirmed') {
|
|
106
|
-
this.joinedTaskIds.add(evidence.joinedTask.taskId);
|
|
107
|
-
this.statuses.set(requestKey, 'confirmed');
|
|
108
|
-
this.evidence.set(requestKey, evidence);
|
|
109
|
-
}
|
|
110
|
-
else
|
|
111
|
-
this.statuses.set(requestKey, 'rejected');
|
|
112
|
-
return result;
|
|
113
|
-
}
|
|
114
|
-
snapshot() {
|
|
115
|
-
return [...this.evidence.values()].map((entry) => ({
|
|
116
|
-
request: { ...entry.request }, sendMessage: { ...entry.sendMessage },
|
|
117
|
-
...(entry.joinedTask ? { joinedTask: { ...entry.joinedTask } } : {}),
|
|
118
|
-
}));
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
export async function requestCompletedContinuation(rootSessionId, agentId, text, requestId, options) {
|
|
122
|
-
const rejected = () => ({ status: 'rejected', rootSessionId, agentId, requestId });
|
|
123
|
-
if (!options)
|
|
124
|
-
return rejected();
|
|
125
|
-
const profile = options.profile, task = options.task;
|
|
126
|
-
if (!id(rootSessionId) || !id(agentId) || !id(requestId) || !message(text) || options.enabled !== true ||
|
|
127
|
-
options.busy === true || !profile || !task ||
|
|
128
|
-
task.origin.rootSessionId !== rootSessionId || task.origin.agentId !== agentId ||
|
|
129
|
-
task.origin.runtimeGeneration !== profile.runtimeGeneration ||
|
|
130
|
-
!capabilitiesFor(profile, task, options.certifications ?? QODER_CERTIFICATIONS, options.binding).continueCompleted)
|
|
131
|
-
return rejected();
|
|
132
|
-
try {
|
|
133
|
-
await options.prompt(text);
|
|
134
|
-
return { status: 'requested', rootSessionId, agentId, requestId };
|
|
135
|
-
}
|
|
136
|
-
catch {
|
|
137
|
-
return rejected();
|
|
138
|
-
}
|
|
139
|
-
}
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import { spawn } from 'node:child_process';
|
|
2
|
-
import { parseQoderProfile } from './profile.js';
|
|
3
|
-
/**
|
|
4
|
-
* Native SDK/Hooks capabilities are opt-in until one real process has produced
|
|
5
|
-
* a complete, attributable contract record. The fake fixture must never add a
|
|
6
|
-
* profile to QODER_CERTIFICATIONS by itself.
|
|
7
|
-
*/
|
|
8
|
-
export const QODER_NATIVE_FALLBACKS = Object.freeze({
|
|
9
|
-
model: 'Model changes are negotiated by the base ACP session only.',
|
|
10
|
-
mode: 'Mode changes are negotiated by the base ACP session only.',
|
|
11
|
-
media: 'Prompt media and resources are unsupported by the SDK adapter.',
|
|
12
|
-
forms: 'Form elicitation is unsupported until an equivalent public SDK callback is certified.',
|
|
13
|
-
load: 'Load requires an exact native session proof; it never creates a replacement session.',
|
|
14
|
-
fork: 'Fork is unsupported until an exact new native execution can be proved.',
|
|
15
|
-
});
|
|
16
|
-
export function nativeFallback(feature) {
|
|
17
|
-
return { supported: false, reason: QODER_NATIVE_FALLBACKS[feature] };
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Parses the deliberately small output contract of an opt-in native runner.
|
|
21
|
-
* This is validation only: callers still decide whether to persist evidence or
|
|
22
|
-
* open a capability, and the shipping certification allowlist remains closed.
|
|
23
|
-
*/
|
|
24
|
-
export function parseNativeContractEvidence(value) {
|
|
25
|
-
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
26
|
-
return undefined;
|
|
27
|
-
const input = value;
|
|
28
|
-
const profile = parseQoderProfile(input.profile);
|
|
29
|
-
if (input.certified !== true || !profile || input.tasks !== true || input.hooks !== true ||
|
|
30
|
-
input.approvals !== true || input.terminalControl !== true || input.replayFree !== true ||
|
|
31
|
-
profile.cliVersion !== '1.1.64' || profile.sdkVersion !== '1.0.50' || profile.mode !== 'sdk-adapter' ||
|
|
32
|
-
profile.transport !== 'process' || profile.initialized !== true || profile.rootConfirmed !== true ||
|
|
33
|
-
profile.observerHealthy !== true || !profile.capabilities.includes('background_tasks_v1'))
|
|
34
|
-
return undefined;
|
|
35
|
-
return { certified: true, profile, tasks: true, hooks: true, approvals: true,
|
|
36
|
-
terminalControl: true, replayFree: true };
|
|
37
|
-
}
|
|
38
|
-
const MAX_OUTPUT_BYTES = 128 * 1024;
|
|
39
|
-
const TIMEOUT_MS = 120_000;
|
|
40
|
-
/**
|
|
41
|
-
* Runs an external certification command only when explicitly configured. The
|
|
42
|
-
* command is an executable path (no shell), receives no credentials from this
|
|
43
|
-
* helper, and must print NativeContractEvidence JSON to stdout.
|
|
44
|
-
*/
|
|
45
|
-
export async function runOptInNativeContract(command = process.env.AXOM_QODER_SDK_CONTRACT_COMMAND) {
|
|
46
|
-
if (!command)
|
|
47
|
-
return { status: 'not-run', stdout: '', stderr: '' };
|
|
48
|
-
if (command.includes('\0') || command.trim() !== command || command.length > 4096)
|
|
49
|
-
throw new Error('Invalid native contract command');
|
|
50
|
-
return await new Promise((resolve) => {
|
|
51
|
-
const child = spawn(command, [], { shell: false, stdio: ['ignore', 'pipe', 'pipe'], env: { ...process.env } });
|
|
52
|
-
let stdout = '', stderr = '', oversized = false, settled = false;
|
|
53
|
-
const append = (target, chunk) => {
|
|
54
|
-
if (Buffer.byteLength(target === 'stdout' ? stdout : stderr, 'utf8') + chunk.byteLength > MAX_OUTPUT_BYTES) {
|
|
55
|
-
oversized = true;
|
|
56
|
-
child.kill('SIGTERM');
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
if (target === 'stdout')
|
|
60
|
-
stdout += chunk.toString('utf8');
|
|
61
|
-
else
|
|
62
|
-
stderr += chunk.toString('utf8');
|
|
63
|
-
};
|
|
64
|
-
child.stdout.on('data', (chunk) => append('stdout', chunk));
|
|
65
|
-
child.stderr.on('data', (chunk) => append('stderr', chunk));
|
|
66
|
-
const timer = setTimeout(() => child.kill('SIGTERM'), TIMEOUT_MS);
|
|
67
|
-
const finish = (result) => {
|
|
68
|
-
if (settled)
|
|
69
|
-
return;
|
|
70
|
-
settled = true;
|
|
71
|
-
clearTimeout(timer);
|
|
72
|
-
resolve(result);
|
|
73
|
-
};
|
|
74
|
-
child.once('error', (error) => finish({ status: 'failed', command, stdout, stderr: `${stderr}${error.message}` }));
|
|
75
|
-
child.once('close', (code) => finish({ status: oversized ? 'failed' : code === 0 ? 'completed' : 'failed',
|
|
76
|
-
command, exitCode: code, stdout, stderr }));
|
|
77
|
-
});
|
|
78
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
export function certifiedRuntime(identity) {
|
|
2
|
-
return identity?.name === 'qoder-cli' && identity.version === '1.1.61';
|
|
3
|
-
}
|
|
4
|
-
function record(value) {
|
|
5
|
-
return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
|
|
6
|
-
}
|
|
7
|
-
export class ForegroundOutputBoundary {
|
|
8
|
-
nativeSessionId;
|
|
9
|
-
pending = new Set();
|
|
10
|
-
constructor(nativeSessionId) {
|
|
11
|
-
this.nativeSessionId = nativeSessionId;
|
|
12
|
-
}
|
|
13
|
-
accept(event) {
|
|
14
|
-
if (event.acpSessionId && event.acpSessionId !== this.nativeSessionId)
|
|
15
|
-
return true;
|
|
16
|
-
const p = event.payload, input = record(p.rawInput);
|
|
17
|
-
if (event.eventName === 'tool_call' && p.kind === 'think' && record(record(p._meta).qoder).toolName === 'Agent' &&
|
|
18
|
-
typeof p.toolCallId === 'string' && typeof input.prompt === 'string' &&
|
|
19
|
-
(input.run_in_background === undefined || input.run_in_background === false))
|
|
20
|
-
this.pending.add(p.toolCallId);
|
|
21
|
-
if (event.eventName === 'tool_call_update' && typeof p.toolCallId === 'string' &&
|
|
22
|
-
['completed', 'failed', 'cancelled'].includes(String(p.status)))
|
|
23
|
-
this.pending.delete(p.toolCallId);
|
|
24
|
-
// Identified/draft child streams and controls keep their existing handling. Never assign an
|
|
25
|
-
// unaddressed chunk to whichever child happens to finish first.
|
|
26
|
-
return !this.pending.size || !['agent_message_chunk', 'agent_thought_chunk'].includes(event.eventName) ||
|
|
27
|
-
typeof p.messageId === 'string' || p._meta != null;
|
|
28
|
-
}
|
|
29
|
-
endTurn() { this.pending.clear(); }
|
|
30
|
-
}
|