@scotthuang/agent-knock-knock 0.12.11 → 0.12.13
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/CHANGELOG.md +40 -0
- package/README.md +43 -30
- package/dist/src/approval-policy.d.ts +11 -2
- package/dist/src/approval-policy.js +33 -7
- package/dist/src/approval-policy.js.map +1 -1
- package/dist/src/callback-cli-adapter.d.ts +47 -0
- package/dist/src/callback-cli-adapter.js +156 -0
- package/dist/src/callback-cli-adapter.js.map +1 -0
- package/dist/src/callback-outbox-policy.d.ts +95 -0
- package/dist/src/callback-outbox-policy.js +149 -0
- package/dist/src/callback-outbox-policy.js.map +1 -0
- package/dist/src/callback-outbox-service.d.ts +247 -0
- package/dist/src/callback-outbox-service.js +1053 -0
- package/dist/src/callback-outbox-service.js.map +1 -0
- package/dist/src/callback-outbox-settlement.d.ts +54 -0
- package/dist/src/callback-outbox-settlement.js +241 -0
- package/dist/src/callback-outbox-settlement.js.map +1 -0
- package/dist/src/canonical-json.d.ts +1 -0
- package/dist/src/canonical-json.js +14 -0
- package/dist/src/canonical-json.js.map +1 -0
- package/dist/src/claude-lifecycle-compatibility.js +15 -0
- package/dist/src/claude-lifecycle-compatibility.js.map +1 -1
- package/dist/src/claude-local-transcript-provider.d.ts +82 -1
- package/dist/src/claude-local-transcript-provider.js +873 -23
- package/dist/src/claude-local-transcript-provider.js.map +1 -1
- package/dist/src/cli-command-runtime.d.ts +14 -0
- package/dist/src/cli-command-runtime.js +164 -0
- package/dist/src/cli-command-runtime.js.map +1 -0
- package/dist/src/cli-core.d.ts +4 -42
- package/dist/src/cli-core.js +958 -28684
- package/dist/src/cli-core.js.map +1 -1
- package/dist/src/cli-runtime-context.d.ts +57 -0
- package/dist/src/cli-runtime-context.js +118 -0
- package/dist/src/cli-runtime-context.js.map +1 -0
- package/dist/src/codex-lifecycle-compatibility.js +2 -1
- package/dist/src/codex-lifecycle-compatibility.js.map +1 -1
- package/dist/src/codex-local-session-provider.d.ts +18 -0
- package/dist/src/codex-local-session-provider.js +67 -0
- package/dist/src/codex-local-session-provider.js.map +1 -1
- package/dist/src/codex-session-provider.js +1 -3
- package/dist/src/codex-session-provider.js.map +1 -1
- package/dist/src/conversation-trace.d.ts +30 -0
- package/dist/src/conversation-trace.js +185 -0
- package/dist/src/conversation-trace.js.map +1 -0
- package/dist/src/deferred-foreground-application-service.d.ts +84 -0
- package/dist/src/deferred-foreground-application-service.js +742 -0
- package/dist/src/deferred-foreground-application-service.js.map +1 -0
- package/dist/src/deferred-foreground-authority-cli-adapter.d.ts +78 -0
- package/dist/src/deferred-foreground-authority-cli-adapter.js +795 -0
- package/dist/src/deferred-foreground-authority-cli-adapter.js.map +1 -0
- package/dist/src/deferred-foreground-boundary.d.ts +61 -0
- package/dist/src/deferred-foreground-boundary.js +2 -0
- package/dist/src/deferred-foreground-boundary.js.map +1 -0
- package/dist/src/deferred-foreground-capability.d.ts +4 -0
- package/dist/src/deferred-foreground-capability.js +184 -0
- package/dist/src/deferred-foreground-capability.js.map +1 -0
- package/dist/src/deferred-foreground-preparation-cli-adapter.d.ts +53 -0
- package/dist/src/deferred-foreground-preparation-cli-adapter.js +210 -0
- package/dist/src/deferred-foreground-preparation-cli-adapter.js.map +1 -0
- package/dist/src/deferred-foreground-preparation-service.d.ts +72 -0
- package/dist/src/deferred-foreground-preparation-service.js +179 -0
- package/dist/src/deferred-foreground-preparation-service.js.map +1 -0
- package/dist/src/deferred-foreground-recovery-cli-adapter.d.ts +139 -0
- package/dist/src/deferred-foreground-recovery-cli-adapter.js +1125 -0
- package/dist/src/deferred-foreground-recovery-cli-adapter.js.map +1 -0
- package/dist/src/deferred-foreground-recovery-service.d.ts +48 -0
- package/dist/src/deferred-foreground-recovery-service.js +91 -0
- package/dist/src/deferred-foreground-recovery-service.js.map +1 -0
- package/dist/src/deferred-foreground-transfer-policy.d.ts +2 -0
- package/dist/src/deferred-foreground-transfer-policy.js +10 -0
- package/dist/src/deferred-foreground-transfer-policy.js.map +1 -0
- package/dist/src/deferred-foreground-transfer.js +46 -105
- package/dist/src/deferred-foreground-transfer.js.map +1 -1
- package/dist/src/doctor-capabilities.d.ts +9 -0
- package/dist/src/doctor-capabilities.js +22 -0
- package/dist/src/doctor-capabilities.js.map +1 -1
- package/dist/src/durable-json-file.d.ts +22 -0
- package/dist/src/durable-json-file.js +127 -0
- package/dist/src/durable-json-file.js.map +1 -0
- package/dist/src/file-lock-cli-adapter.d.ts +43 -0
- package/dist/src/file-lock-cli-adapter.js +150 -0
- package/dist/src/file-lock-cli-adapter.js.map +1 -0
- package/dist/src/herdr-terminal-control-provider.js +1 -3
- package/dist/src/herdr-terminal-control-provider.js.map +1 -1
- package/dist/src/install-doctor-command-adapter.d.ts +17 -0
- package/dist/src/install-doctor-command-adapter.js +225 -0
- package/dist/src/install-doctor-command-adapter.js.map +1 -0
- package/dist/src/live-lifecycle-evidence.js +76 -46
- package/dist/src/live-lifecycle-evidence.js.map +1 -1
- package/dist/src/live-lifecycle-smoke.js +74 -50
- package/dist/src/live-lifecycle-smoke.js.map +1 -1
- package/dist/src/managed-session.d.ts +2 -0
- package/dist/src/managed-session.js +24 -0
- package/dist/src/managed-session.js.map +1 -1
- package/dist/src/managed-turn-recovery-service.d.ts +86 -0
- package/dist/src/managed-turn-recovery-service.js +145 -0
- package/dist/src/managed-turn-recovery-service.js.map +1 -0
- package/dist/src/mutation-transaction.d.ts +48 -0
- package/dist/src/mutation-transaction.js +73 -0
- package/dist/src/mutation-transaction.js.map +1 -0
- package/dist/src/native-thread-lifecycle-cli-adapter.d.ts +154 -0
- package/dist/src/native-thread-lifecycle-cli-adapter.js +786 -0
- package/dist/src/native-thread-lifecycle-cli-adapter.js.map +1 -0
- package/dist/src/native-thread-lifecycle-ledger-cli-adapter.d.ts +25 -0
- package/dist/src/native-thread-lifecycle-ledger-cli-adapter.js +50 -0
- package/dist/src/native-thread-lifecycle-ledger-cli-adapter.js.map +1 -0
- package/dist/src/native-thread-lifecycle-query-service.d.ts +75 -0
- package/dist/src/native-thread-lifecycle-query-service.js +297 -0
- package/dist/src/native-thread-lifecycle-query-service.js.map +1 -0
- package/dist/src/native-thread-lifecycle-recovery-adapter.d.ts +45 -0
- package/dist/src/native-thread-lifecycle-recovery-adapter.js +154 -0
- package/dist/src/native-thread-lifecycle-recovery-adapter.js.map +1 -0
- package/dist/src/native-thread-lifecycle-recovery-service.d.ts +211 -0
- package/dist/src/native-thread-lifecycle-recovery-service.js +1093 -0
- package/dist/src/native-thread-lifecycle-recovery-service.js.map +1 -0
- package/dist/src/native-thread-resume-snapshot-policy.d.ts +73 -0
- package/dist/src/native-thread-resume-snapshot-policy.js +218 -0
- package/dist/src/native-thread-resume-snapshot-policy.js.map +1 -0
- package/dist/src/native-thread-resume-snapshot.d.ts +3 -61
- package/dist/src/native-thread-resume-snapshot.js +2 -143
- package/dist/src/native-thread-resume-snapshot.js.map +1 -1
- package/dist/src/native-thread-transition-application.d.ts +147 -0
- package/dist/src/native-thread-transition-application.js +974 -0
- package/dist/src/native-thread-transition-application.js.map +1 -0
- package/dist/src/native-thread-transition-policy.d.ts +127 -0
- package/dist/src/native-thread-transition-policy.js +154 -0
- package/dist/src/native-thread-transition-policy.js.map +1 -0
- package/dist/src/native-thread-transition-resource-adapter.d.ts +12 -0
- package/dist/src/native-thread-transition-resource-adapter.js +44 -0
- package/dist/src/native-thread-transition-resource-adapter.js.map +1 -0
- package/dist/src/native-thread-transition-settlement-service.d.ts +94 -0
- package/dist/src/native-thread-transition-settlement-service.js +134 -0
- package/dist/src/native-thread-transition-settlement-service.js.map +1 -0
- package/dist/src/native-thread-transition-verification-adapter.d.ts +62 -0
- package/dist/src/native-thread-transition-verification-adapter.js +451 -0
- package/dist/src/native-thread-transition-verification-adapter.js.map +1 -0
- package/dist/src/openclaw-callback-transport.d.ts +69 -0
- package/dist/src/openclaw-callback-transport.js +583 -0
- package/dist/src/openclaw-callback-transport.js.map +1 -0
- package/dist/src/openclaw-plugin-callback-adapter.d.ts +1 -0
- package/dist/src/openclaw-plugin-callback-adapter.js +423 -0
- package/dist/src/openclaw-plugin-callback-adapter.js.map +1 -0
- package/dist/src/openclaw-plugin-command-adapter.d.ts +16 -0
- package/dist/src/openclaw-plugin-command-adapter.js +1868 -0
- package/dist/src/openclaw-plugin-command-adapter.js.map +1 -0
- package/dist/src/openclaw-plugin-helpers.d.ts +27 -4
- package/dist/src/openclaw-plugin-helpers.js +356 -81
- package/dist/src/openclaw-plugin-helpers.js.map +1 -1
- package/dist/src/openclaw-plugin-schemas.d.ts +358 -0
- package/dist/src/openclaw-plugin-schemas.js +366 -0
- package/dist/src/openclaw-plugin-schemas.js.map +1 -0
- package/dist/src/openclaw-plugin-supervisor.d.ts +6 -0
- package/dist/src/openclaw-plugin-supervisor.js +123 -0
- package/dist/src/openclaw-plugin-supervisor.js.map +1 -0
- package/dist/src/openclaw-plugin.js +7 -2212
- package/dist/src/openclaw-plugin.js.map +1 -1
- package/dist/src/openclaw-private-authority-offers.d.ts +22 -0
- package/dist/src/openclaw-private-authority-offers.js +177 -0
- package/dist/src/openclaw-private-authority-offers.js.map +1 -0
- package/dist/src/protocol.d.ts +5 -0
- package/dist/src/protocol.js +59 -3
- package/dist/src/protocol.js.map +1 -1
- package/dist/src/session-store.d.ts +1 -0
- package/dist/src/session-store.js +84 -106
- package/dist/src/session-store.js.map +1 -1
- package/dist/src/store.d.ts +1 -0
- package/dist/src/store.js +5 -2
- package/dist/src/store.js.map +1 -1
- package/dist/src/terminal-acceptance-application-service.d.ts +65 -0
- package/dist/src/terminal-acceptance-application-service.js +63 -0
- package/dist/src/terminal-acceptance-application-service.js.map +1 -0
- package/dist/src/terminal-acceptance-cli-adapter.d.ts +210 -0
- package/dist/src/terminal-acceptance-cli-adapter.js +957 -0
- package/dist/src/terminal-acceptance-cli-adapter.js.map +1 -0
- package/dist/src/terminal-action-projection.d.ts +309 -0
- package/dist/src/terminal-action-projection.js +376 -0
- package/dist/src/terminal-action-projection.js.map +1 -0
- package/dist/src/terminal-agent-bridge.d.ts +3 -0
- package/dist/src/terminal-agent-bridge.js +29 -8
- package/dist/src/terminal-agent-bridge.js.map +1 -1
- package/dist/src/terminal-authority-policy.d.ts +141 -0
- package/dist/src/terminal-authority-policy.js +367 -0
- package/dist/src/terminal-authority-policy.js.map +1 -0
- package/dist/src/terminal-binding-authority.d.ts +118 -0
- package/dist/src/terminal-binding-authority.js +245 -0
- package/dist/src/terminal-binding-authority.js.map +1 -0
- package/dist/src/terminal-binding-reconciliation-service.d.ts +75 -0
- package/dist/src/terminal-binding-reconciliation-service.js +73 -0
- package/dist/src/terminal-binding-reconciliation-service.js.map +1 -0
- package/dist/src/terminal-command-cli-adapter.d.ts +380 -0
- package/dist/src/terminal-command-cli-adapter.js +3288 -0
- package/dist/src/terminal-command-cli-adapter.js.map +1 -0
- package/dist/src/terminal-control-ref.d.ts +4 -0
- package/dist/src/terminal-control-ref.js +11 -8
- package/dist/src/terminal-control-ref.js.map +1 -1
- package/dist/src/terminal-delegate-cli-adapter.d.ts +41 -0
- package/dist/src/terminal-delegate-cli-adapter.js +242 -0
- package/dist/src/terminal-delegate-cli-adapter.js.map +1 -0
- package/dist/src/terminal-dispatch-abort.d.ts +22 -0
- package/dist/src/terminal-dispatch-abort.js +30 -0
- package/dist/src/terminal-dispatch-abort.js.map +1 -0
- package/dist/src/terminal-dispatch-application.d.ts +115 -0
- package/dist/src/terminal-dispatch-application.js +478 -0
- package/dist/src/terminal-dispatch-application.js.map +1 -0
- package/dist/src/terminal-dispatch-capability.d.ts +63 -0
- package/dist/src/terminal-dispatch-capability.js +133 -0
- package/dist/src/terminal-dispatch-capability.js.map +1 -0
- package/dist/src/terminal-dispatch-completion-cli-adapter.d.ts +22 -0
- package/dist/src/terminal-dispatch-completion-cli-adapter.js +96 -0
- package/dist/src/terminal-dispatch-completion-cli-adapter.js.map +1 -0
- package/dist/src/terminal-dispatch-composition.d.ts +79 -0
- package/dist/src/terminal-dispatch-composition.js +2 -0
- package/dist/src/terminal-dispatch-composition.js.map +1 -0
- package/dist/src/terminal-dispatch-execution.d.ts +290 -0
- package/dist/src/terminal-dispatch-execution.js +916 -0
- package/dist/src/terminal-dispatch-execution.js.map +1 -0
- package/dist/src/terminal-dispatch-ledger-codec.d.ts +110 -0
- package/dist/src/terminal-dispatch-ledger-codec.js +399 -0
- package/dist/src/terminal-dispatch-ledger-codec.js.map +1 -0
- package/dist/src/terminal-dispatch-policy.d.ts +93 -0
- package/dist/src/terminal-dispatch-policy.js +122 -0
- package/dist/src/terminal-dispatch-policy.js.map +1 -0
- package/dist/src/terminal-dispatch-presenter.d.ts +65 -0
- package/dist/src/terminal-dispatch-presenter.js +258 -0
- package/dist/src/terminal-dispatch-presenter.js.map +1 -0
- package/dist/src/terminal-dispatch-receipt.d.ts +79 -0
- package/dist/src/terminal-dispatch-receipt.js +314 -0
- package/dist/src/terminal-dispatch-receipt.js.map +1 -0
- package/dist/src/terminal-dispatch-recovery-cli-adapter.d.ts +132 -0
- package/dist/src/terminal-dispatch-recovery-cli-adapter.js +1330 -0
- package/dist/src/terminal-dispatch-recovery-cli-adapter.js.map +1 -0
- package/dist/src/terminal-dispatch-recovery-service.d.ts +271 -0
- package/dist/src/terminal-dispatch-recovery-service.js +374 -0
- package/dist/src/terminal-dispatch-recovery-service.js.map +1 -0
- package/dist/src/terminal-dispatch-repository-cli-adapter.d.ts +35 -0
- package/dist/src/terminal-dispatch-repository-cli-adapter.js +483 -0
- package/dist/src/terminal-dispatch-repository-cli-adapter.js.map +1 -0
- package/dist/src/terminal-handoff-application-service.d.ts +7 -0
- package/dist/src/terminal-handoff-application-service.js +18 -0
- package/dist/src/terminal-handoff-application-service.js.map +1 -0
- package/dist/src/terminal-handoff-cli-adapter.d.ts +229 -0
- package/dist/src/terminal-handoff-cli-adapter.js +1389 -0
- package/dist/src/terminal-handoff-cli-adapter.js.map +1 -0
- package/dist/src/terminal-handoff-facts.d.ts +12 -0
- package/dist/src/terminal-handoff-facts.js +16 -0
- package/dist/src/terminal-handoff-facts.js.map +1 -0
- package/dist/src/terminal-identity-authority-cli-adapter.d.ts +318 -0
- package/dist/src/terminal-identity-authority-cli-adapter.js +1072 -0
- package/dist/src/terminal-identity-authority-cli-adapter.js.map +1 -0
- package/dist/src/terminal-identity-authority-service.d.ts +48 -0
- package/dist/src/terminal-identity-authority-service.js +45 -0
- package/dist/src/terminal-identity-authority-service.js.map +1 -0
- package/dist/src/terminal-list-cli-adapter.d.ts +274 -0
- package/dist/src/terminal-list-cli-adapter.js +2444 -0
- package/dist/src/terminal-list-cli-adapter.js.map +1 -0
- package/dist/src/terminal-list-renderer.d.ts +34 -0
- package/dist/src/terminal-list-renderer.js +643 -0
- package/dist/src/terminal-list-renderer.js.map +1 -0
- package/dist/src/terminal-maintenance-cli-adapter.d.ts +102 -0
- package/dist/src/terminal-maintenance-cli-adapter.js +1059 -0
- package/dist/src/terminal-maintenance-cli-adapter.js.map +1 -0
- package/dist/src/terminal-monitor-application-service.d.ts +275 -0
- package/dist/src/terminal-monitor-application-service.js +982 -0
- package/dist/src/terminal-monitor-application-service.js.map +1 -0
- package/dist/src/terminal-monitor-cli-adapter.d.ts +130 -0
- package/dist/src/terminal-monitor-cli-adapter.js +442 -0
- package/dist/src/terminal-monitor-cli-adapter.js.map +1 -0
- package/dist/src/terminal-monitor-decision-policy.d.ts +98 -0
- package/dist/src/terminal-monitor-decision-policy.js +246 -0
- package/dist/src/terminal-monitor-decision-policy.js.map +1 -0
- package/dist/src/terminal-monitor-launch-plan.d.ts +45 -0
- package/dist/src/terminal-monitor-launch-plan.js +94 -0
- package/dist/src/terminal-monitor-launch-plan.js.map +1 -0
- package/dist/src/terminal-monitor-ownership-policy.d.ts +31 -0
- package/dist/src/terminal-monitor-ownership-policy.js +64 -0
- package/dist/src/terminal-monitor-ownership-policy.js.map +1 -0
- package/dist/src/terminal-monitor-poll-policy.d.ts +48 -0
- package/dist/src/terminal-monitor-poll-policy.js +67 -0
- package/dist/src/terminal-monitor-poll-policy.js.map +1 -0
- package/dist/src/terminal-monitor-reconciliation-eligibility.d.ts +53 -0
- package/dist/src/terminal-monitor-reconciliation-eligibility.js +114 -0
- package/dist/src/terminal-monitor-reconciliation-eligibility.js.map +1 -0
- package/dist/src/terminal-monitor-state-cli-adapter.d.ts +106 -0
- package/dist/src/terminal-monitor-state-cli-adapter.js +1492 -0
- package/dist/src/terminal-monitor-state-cli-adapter.js.map +1 -0
- package/dist/src/terminal-monitor-state-reconciliation-service.d.ts +87 -0
- package/dist/src/terminal-monitor-state-reconciliation-service.js +106 -0
- package/dist/src/terminal-monitor-state-reconciliation-service.js.map +1 -0
- package/dist/src/terminal-monitor-supervision-cli-adapter.d.ts +122 -0
- package/dist/src/terminal-monitor-supervision-cli-adapter.js +685 -0
- package/dist/src/terminal-monitor-supervision-cli-adapter.js.map +1 -0
- package/dist/src/terminal-process-facts.d.ts +2 -0
- package/dist/src/terminal-process-facts.js +19 -0
- package/dist/src/terminal-process-facts.js.map +1 -0
- package/dist/src/terminal-process-source.d.ts +6 -4
- package/dist/src/terminal-process-source.js +25 -20
- package/dist/src/terminal-process-source.js.map +1 -1
- package/dist/src/terminal-runtime-cli-adapter.d.ts +80 -0
- package/dist/src/terminal-runtime-cli-adapter.js +488 -0
- package/dist/src/terminal-runtime-cli-adapter.js.map +1 -0
- package/dist/src/terminal-scoped-approval-authority.d.ts +62 -0
- package/dist/src/terminal-scoped-approval-authority.js +281 -0
- package/dist/src/terminal-scoped-approval-authority.js.map +1 -0
- package/dist/src/terminal-selector-projection-service.d.ts +26 -0
- package/dist/src/terminal-selector-projection-service.js +81 -0
- package/dist/src/terminal-selector-projection-service.js.map +1 -0
- package/dist/src/terminal-status-cli-adapter.d.ts +126 -0
- package/dist/src/terminal-status-cli-adapter.js +577 -0
- package/dist/src/terminal-status-cli-adapter.js.map +1 -0
- package/dist/src/terminal-status-facts.d.ts +56 -0
- package/dist/src/terminal-status-facts.js +199 -0
- package/dist/src/terminal-status-facts.js.map +1 -0
- package/dist/src/terminal-submission-acceptance.d.ts +62 -145
- package/dist/src/terminal-submission-acceptance.js +555 -222
- package/dist/src/terminal-submission-acceptance.js.map +1 -1
- package/dist/src/terminal-submission-facts.d.ts +123 -0
- package/dist/src/terminal-submission-facts.js +205 -0
- package/dist/src/terminal-submission-facts.js.map +1 -0
- package/dist/src/terminal-turn-binding-authority-cli-adapter.d.ts +14 -0
- package/dist/src/terminal-turn-binding-authority-cli-adapter.js +90 -0
- package/dist/src/terminal-turn-binding-authority-cli-adapter.js.map +1 -0
- package/dist/src/terminal-watch-callback-cli-adapter.d.ts +20 -0
- package/dist/src/terminal-watch-callback-cli-adapter.js +55 -0
- package/dist/src/terminal-watch-callback-cli-adapter.js.map +1 -0
- package/dist/src/terminal-watch-cli-adapter.d.ts +64 -0
- package/dist/src/terminal-watch-cli-adapter.js +508 -0
- package/dist/src/terminal-watch-cli-adapter.js.map +1 -0
- package/dist/src/terminal-watch-service.d.ts +97 -0
- package/dist/src/terminal-watch-service.js +714 -0
- package/dist/src/terminal-watch-service.js.map +1 -0
- package/dist/src/terminal-watch-store.d.ts +149 -0
- package/dist/src/terminal-watch-store.js +861 -0
- package/dist/src/terminal-watch-store.js.map +1 -0
- package/dist/src/value-guards.d.ts +4 -0
- package/dist/src/value-guards.js +12 -0
- package/dist/src/value-guards.js.map +1 -0
- package/dist/src/verified-dead-agent-policy.d.ts +192 -0
- package/dist/src/verified-dead-agent-policy.js +262 -0
- package/dist/src/verified-dead-agent-policy.js.map +1 -0
- package/docs/quickstart-herdr.md +8 -0
- package/docs/quickstart-tmux.md +33 -6
- package/openclaw.plugin.json +11 -3
- package/package.json +3 -1
- package/templates/openclaw-skills/agent-knock-knock/SKILL.md +58 -40
|
@@ -4,7 +4,8 @@ import os from "node:os";
|
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { claudeLifecycleBehaviorProfile, claudeLifecycleSourceVersionSupported, DEFAULT_CLAUDE_LIFECYCLE_VERSION, supportedClaudeLifecycleVersions } from "./claude-lifecycle-compatibility.js";
|
|
6
6
|
import { redactString } from "./runtime-log.js";
|
|
7
|
-
import {
|
|
7
|
+
import { isRecord } from "./value-guards.js";
|
|
8
|
+
import { validateTerminalSubmissionAcceptanceEvidence } from "./terminal-submission-facts.js";
|
|
8
9
|
const CLAUDE_TRANSCRIPT_ANCHOR_VERSION = 1;
|
|
9
10
|
const CLAUDE_TRANSCRIPT_MAX_TURN_BYTES = 64 * 1024 * 1024;
|
|
10
11
|
const CLAUDE_SESSION_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/iu;
|
|
@@ -429,6 +430,770 @@ export function captureClaudeTranscriptAnchor(options) {
|
|
|
429
430
|
fs.closeSync(located.fd);
|
|
430
431
|
}
|
|
431
432
|
}
|
|
433
|
+
/** Capture the latest exact, unfinished root prompt in a live Claude process. */
|
|
434
|
+
export function captureClaudeHumanStartedActiveTaskAnchor(options) {
|
|
435
|
+
const sessionId = nonEmptyString(options.sessionId);
|
|
436
|
+
const cwd = nonEmptyString(options.cwd);
|
|
437
|
+
const pid = positiveInteger(options.pid);
|
|
438
|
+
if (!sessionId || !cwd || pid === undefined || !CLAUDE_SESSION_ID_PATTERN.test(sessionId)) {
|
|
439
|
+
return undefined;
|
|
440
|
+
}
|
|
441
|
+
const agent = exactInteractiveAgent(options.agentRows, pid);
|
|
442
|
+
const agentStartedAtMs = positiveInteger(agent?.startedAt);
|
|
443
|
+
if (!agent ||
|
|
444
|
+
agentStartedAtMs === undefined ||
|
|
445
|
+
agent.sessionId !== sessionId ||
|
|
446
|
+
normalizePath(agent.cwd) !== normalizePath(cwd) ||
|
|
447
|
+
!isActiveClaudeAgentState(agent)) {
|
|
448
|
+
return undefined;
|
|
449
|
+
}
|
|
450
|
+
const projectsRoot = projectsRootPath(path.resolve(options.claudeHome ?? defaultClaudeHome()));
|
|
451
|
+
if (!isRealDirectory(projectsRoot)) {
|
|
452
|
+
return undefined;
|
|
453
|
+
}
|
|
454
|
+
const opened = locateTranscript(projectsRoot, sessionId);
|
|
455
|
+
if (!opened) {
|
|
456
|
+
return undefined;
|
|
457
|
+
}
|
|
458
|
+
try {
|
|
459
|
+
const before = opened.stat;
|
|
460
|
+
const maxBytes = positiveClaudeByteLimit(options.maxTurnBytes);
|
|
461
|
+
if (before.size === 0) {
|
|
462
|
+
return undefined;
|
|
463
|
+
}
|
|
464
|
+
if (!fileEndsWithNewline(opened.fd, before.size)) {
|
|
465
|
+
throw new Error("Claude active-task transcript has an incomplete JSONL tail; retry");
|
|
466
|
+
}
|
|
467
|
+
const recordEntries = readBoundedCompleteJsonlTail(opened.fd, before.size, maxBytes);
|
|
468
|
+
const records = recordEntries.map((entry) => entry.record);
|
|
469
|
+
const after = fs.fstatSync(opened.fd);
|
|
470
|
+
if (!sameStableTranscriptFile(before, after)) {
|
|
471
|
+
throw new Error("Claude active-task transcript changed while it was captured; retry");
|
|
472
|
+
}
|
|
473
|
+
const promptEntry = latestClaudeRootHumanPromptEntry(recordEntries);
|
|
474
|
+
if (!promptEntry) {
|
|
475
|
+
if (before.size > maxBytes) {
|
|
476
|
+
throw new Error("Claude active task exceeded the bounded local read limit");
|
|
477
|
+
}
|
|
478
|
+
return undefined;
|
|
479
|
+
}
|
|
480
|
+
const prompt = promptEntry.record;
|
|
481
|
+
assertSupportedRecord(prompt, sessionId, cwd);
|
|
482
|
+
const promptUuid = uuidValue(prompt.uuid);
|
|
483
|
+
const promptText = userPromptText(prompt);
|
|
484
|
+
const requestHash = exactRequestFingerprint(promptText);
|
|
485
|
+
const claudeVersion = nonEmptyString(prompt.version);
|
|
486
|
+
if (!promptUuid || !promptText || !requestHash || !claudeVersion) {
|
|
487
|
+
throw new Error("Claude active root prompt has no exact supported identity");
|
|
488
|
+
}
|
|
489
|
+
const device = String(before.dev);
|
|
490
|
+
const inode = String(before.ino);
|
|
491
|
+
const fileId = transcriptFileId(sessionId, `${device}:${inode}`);
|
|
492
|
+
const transcriptAnchor = {
|
|
493
|
+
schema_version: CLAUDE_TRANSCRIPT_ANCHOR_VERSION,
|
|
494
|
+
session_id: sessionId,
|
|
495
|
+
cwd: path.resolve(cwd),
|
|
496
|
+
pid,
|
|
497
|
+
agent_started_at_ms: agentStartedAtMs,
|
|
498
|
+
captured_at: (options.now ?? new Date()).toISOString(),
|
|
499
|
+
relative_path: opened.relativePath,
|
|
500
|
+
offset_bytes: 0,
|
|
501
|
+
file_existed: true,
|
|
502
|
+
device,
|
|
503
|
+
inode
|
|
504
|
+
};
|
|
505
|
+
const snapshot = {
|
|
506
|
+
anchor: transcriptAnchor,
|
|
507
|
+
sessionId,
|
|
508
|
+
cwd,
|
|
509
|
+
expectedRequestHash: requestHash,
|
|
510
|
+
expectedPromptUuid: promptUuid,
|
|
511
|
+
records,
|
|
512
|
+
transcriptFileId: fileId,
|
|
513
|
+
observedEndOffsetBytes: before.size
|
|
514
|
+
};
|
|
515
|
+
if (completionFromRecords(snapshot, {
|
|
516
|
+
requireVerifiableCompletionSignal: false
|
|
517
|
+
})) {
|
|
518
|
+
return undefined;
|
|
519
|
+
}
|
|
520
|
+
if (!pendingApprovalTurn(snapshot)) {
|
|
521
|
+
throw new Error("Claude active root prompt has no unique unfinished foreground chain");
|
|
522
|
+
}
|
|
523
|
+
const base = {
|
|
524
|
+
schema: "agent-knock-knock/claude-human-started-active-task-anchor",
|
|
525
|
+
version: 1,
|
|
526
|
+
session_id: sessionId,
|
|
527
|
+
cwd: path.resolve(cwd),
|
|
528
|
+
pid,
|
|
529
|
+
agent_started_at_ms: agentStartedAtMs,
|
|
530
|
+
captured_at: transcriptAnchor.captured_at,
|
|
531
|
+
relative_path: opened.relativePath,
|
|
532
|
+
device,
|
|
533
|
+
inode,
|
|
534
|
+
prompt_uuid: promptUuid,
|
|
535
|
+
request_hash: requestHash,
|
|
536
|
+
claude_version: claudeVersion,
|
|
537
|
+
transcript_file_id: fileId,
|
|
538
|
+
turn_start_offset_bytes: promptEntry.offsetBytes,
|
|
539
|
+
observed_end_offset_bytes: before.size
|
|
540
|
+
};
|
|
541
|
+
return {
|
|
542
|
+
...base,
|
|
543
|
+
anchor_fingerprint: sha256Hex(JSON.stringify(base))
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
finally {
|
|
547
|
+
fs.closeSync(opened.fd);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
/** Observe only the exact root prompt named by a human-started task anchor. */
|
|
551
|
+
export function observeClaudeHumanStartedActiveTask(options) {
|
|
552
|
+
try {
|
|
553
|
+
const anchor = validateClaudeHumanStartedActiveTaskAnchor(options.anchor);
|
|
554
|
+
const checkpoint = claudeActiveTaskObservationCheckpoint(anchor, options);
|
|
555
|
+
const resumeOffsetBytes = checkpoint.safe_resume_offset_bytes;
|
|
556
|
+
const projectsRoot = projectsRootPath(path.resolve(options.claudeHome ?? defaultClaudeHome()));
|
|
557
|
+
if (!isRealDirectory(projectsRoot)) {
|
|
558
|
+
return claudeActiveTaskUnavailable("Claude transcript projects directory is unavailable");
|
|
559
|
+
}
|
|
560
|
+
let opened;
|
|
561
|
+
try {
|
|
562
|
+
opened = openAnchoredTranscript(projectsRoot, claudeTranscriptAnchorForHumanStartedTask(anchor));
|
|
563
|
+
}
|
|
564
|
+
catch (error) {
|
|
565
|
+
if (isRetryableClaudeProviderIoError(error) ||
|
|
566
|
+
(error instanceof Error &&
|
|
567
|
+
error.message === "Claude transcript disappeared after the managed send")) {
|
|
568
|
+
return claudeActiveTaskUnavailable(error instanceof Error ? error.message : String(error));
|
|
569
|
+
}
|
|
570
|
+
throw error;
|
|
571
|
+
}
|
|
572
|
+
if (!opened) {
|
|
573
|
+
return claudeActiveTaskUnavailable("the anchored Claude transcript is unavailable");
|
|
574
|
+
}
|
|
575
|
+
try {
|
|
576
|
+
const before = opened.stat;
|
|
577
|
+
if (String(before.dev) !== anchor.device ||
|
|
578
|
+
String(before.ino) !== anchor.inode) {
|
|
579
|
+
throw new Error("Claude active-task transcript identity changed after capture");
|
|
580
|
+
}
|
|
581
|
+
if (before.size < anchor.observed_end_offset_bytes ||
|
|
582
|
+
before.size <= anchor.turn_start_offset_bytes) {
|
|
583
|
+
throw new Error("Claude active-task transcript was truncated after capture");
|
|
584
|
+
}
|
|
585
|
+
if (before.size < resumeOffsetBytes) {
|
|
586
|
+
throw new Error("Claude active-task transcript was truncated after observation");
|
|
587
|
+
}
|
|
588
|
+
assertClaudeActiveTaskResumeBoundary(opened.fd, resumeOffsetBytes);
|
|
589
|
+
const maxTurnBytes = positiveClaudeByteLimit(options.maxTurnBytes);
|
|
590
|
+
const availableBytes = before.size - resumeOffsetBytes;
|
|
591
|
+
const bytesToRead = Math.min(availableBytes, maxTurnBytes);
|
|
592
|
+
if (bytesToRead === 0) {
|
|
593
|
+
assertObservableClaudeActiveTaskAgent(options.agentRows, anchor);
|
|
594
|
+
return {
|
|
595
|
+
status: "pending",
|
|
596
|
+
observedEndOffsetBytes: before.size,
|
|
597
|
+
safeResumeOffsetBytes: resumeOffsetBytes,
|
|
598
|
+
checkpoint
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
const suffixBuffer = readExactTranscriptBytes(opened.fd, resumeOffsetBytes, bytesToRead);
|
|
602
|
+
const completeLength = completeTranscriptJsonlPrefixLength(suffixBuffer);
|
|
603
|
+
if (completeLength === 0 && availableBytes > maxTurnBytes) {
|
|
604
|
+
throw new Error("Claude active-task JSONL record exceeded the bounded local read limit");
|
|
605
|
+
}
|
|
606
|
+
const safeResumeOffsetBytes = resumeOffsetBytes + completeLength;
|
|
607
|
+
const suffixRecords = readCompleteJsonlRecordEntries(opened.fd, resumeOffsetBytes, completeLength, { preReadBuffer: suffixBuffer.subarray(0, completeLength) }).map((entry) => entry.record);
|
|
608
|
+
const after = fs.fstatSync(opened.fd);
|
|
609
|
+
if (!sameStableTranscriptFile(before, after)) {
|
|
610
|
+
assertObservableClaudeActiveTaskAgent(options.agentRows, anchor);
|
|
611
|
+
return {
|
|
612
|
+
status: "pending",
|
|
613
|
+
observedEndOffsetBytes: after.size,
|
|
614
|
+
safeResumeOffsetBytes: resumeOffsetBytes,
|
|
615
|
+
checkpoint
|
|
616
|
+
};
|
|
617
|
+
}
|
|
618
|
+
const advanced = advanceClaudeHumanStartedActiveTaskCheckpoint(anchor, checkpoint, suffixRecords, safeResumeOffsetBytes);
|
|
619
|
+
if (advanced.completion) {
|
|
620
|
+
return { status: "completed", completion: advanced.completion };
|
|
621
|
+
}
|
|
622
|
+
assertObservableClaudeActiveTaskAgent(options.agentRows, anchor);
|
|
623
|
+
return {
|
|
624
|
+
status: "pending",
|
|
625
|
+
observedEndOffsetBytes: before.size,
|
|
626
|
+
safeResumeOffsetBytes,
|
|
627
|
+
checkpoint: advanced.checkpoint
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
finally {
|
|
631
|
+
fs.closeSync(opened.fd);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
catch (error) {
|
|
635
|
+
if (isRetryableClaudeProviderIoError(error)) {
|
|
636
|
+
return claudeActiveTaskUnavailable(error instanceof Error ? error.message : String(error));
|
|
637
|
+
}
|
|
638
|
+
return {
|
|
639
|
+
status: "invalidated",
|
|
640
|
+
reason: error instanceof Error ? error.message : String(error)
|
|
641
|
+
};
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
export function validateClaudeHumanStartedActiveTaskAnchor(value) {
|
|
645
|
+
if (!isRecord(value) ||
|
|
646
|
+
value.schema !==
|
|
647
|
+
"agent-knock-knock/claude-human-started-active-task-anchor" ||
|
|
648
|
+
value.version !== 1) {
|
|
649
|
+
throw new Error("Claude human-started active-task anchor is invalid");
|
|
650
|
+
}
|
|
651
|
+
const allowedKeys = new Set([
|
|
652
|
+
"schema", "version", "session_id", "cwd", "pid", "agent_started_at_ms",
|
|
653
|
+
"captured_at", "relative_path", "device", "inode", "prompt_uuid",
|
|
654
|
+
"request_hash", "claude_version", "transcript_file_id",
|
|
655
|
+
"turn_start_offset_bytes", "observed_end_offset_bytes", "anchor_fingerprint"
|
|
656
|
+
]);
|
|
657
|
+
if (Object.keys(value).some((key) => !allowedKeys.has(key))) {
|
|
658
|
+
throw new Error("Claude human-started active-task anchor has unsupported fields");
|
|
659
|
+
}
|
|
660
|
+
const sessionId = uuidValue(value.session_id);
|
|
661
|
+
const promptUuid = uuidValue(value.prompt_uuid);
|
|
662
|
+
const cwd = nonEmptyString(value.cwd);
|
|
663
|
+
const pid = positiveInteger(value.pid);
|
|
664
|
+
const startedAt = positiveInteger(value.agent_started_at_ms);
|
|
665
|
+
const relativePath = nonEmptyString(value.relative_path);
|
|
666
|
+
const device = nonEmptyString(value.device);
|
|
667
|
+
const inode = nonEmptyString(value.inode);
|
|
668
|
+
const turnStartOffset = nonNegativeInteger(value.turn_start_offset_bytes);
|
|
669
|
+
const observedEndOffset = positiveInteger(value.observed_end_offset_bytes);
|
|
670
|
+
const capturedAt = typeof value.captured_at === "string"
|
|
671
|
+
? value.captured_at
|
|
672
|
+
: undefined;
|
|
673
|
+
if (!sessionId ||
|
|
674
|
+
sessionId !== value.session_id ||
|
|
675
|
+
!promptUuid ||
|
|
676
|
+
promptUuid !== value.prompt_uuid ||
|
|
677
|
+
!cwd ||
|
|
678
|
+
!path.isAbsolute(cwd) ||
|
|
679
|
+
path.resolve(cwd) !== cwd ||
|
|
680
|
+
typeof value.pid !== "number" ||
|
|
681
|
+
pid === undefined ||
|
|
682
|
+
typeof value.agent_started_at_ms !== "number" ||
|
|
683
|
+
startedAt === undefined ||
|
|
684
|
+
!capturedAt ||
|
|
685
|
+
validTimestampMs(capturedAt) === undefined ||
|
|
686
|
+
new Date(capturedAt).toISOString() !== capturedAt ||
|
|
687
|
+
!relativePath ||
|
|
688
|
+
relativePath.includes("\0") ||
|
|
689
|
+
path.posix.isAbsolute(relativePath) ||
|
|
690
|
+
path.win32.isAbsolute(relativePath) ||
|
|
691
|
+
relativePath.split(/[\\/]/u).length !== 2 ||
|
|
692
|
+
relativePath.split(/[\\/]/u).some((segment) => !segment || segment === "." || segment === "..") ||
|
|
693
|
+
path.basename(relativePath) !== `${sessionId}.jsonl` ||
|
|
694
|
+
typeof value.turn_start_offset_bytes !== "number" ||
|
|
695
|
+
turnStartOffset === undefined ||
|
|
696
|
+
typeof value.observed_end_offset_bytes !== "number" ||
|
|
697
|
+
observedEndOffset === undefined ||
|
|
698
|
+
turnStartOffset >= observedEndOffset ||
|
|
699
|
+
!decimalFileIdentity(device) ||
|
|
700
|
+
!decimalFileIdentity(inode) ||
|
|
701
|
+
!/^[0-9a-f]{64}$/u.test(String(value.request_hash)) ||
|
|
702
|
+
!isCompatibleClaudeVersion(value.claude_version) ||
|
|
703
|
+
!/^[0-9a-f]{24}$/u.test(String(value.transcript_file_id)) ||
|
|
704
|
+
!/^[0-9a-f]{64}$/u.test(String(value.anchor_fingerprint))) {
|
|
705
|
+
throw new Error("Claude human-started active-task anchor identity is invalid");
|
|
706
|
+
}
|
|
707
|
+
const { anchor_fingerprint: _anchorFingerprint, ...base } = value;
|
|
708
|
+
if (sha256Hex(JSON.stringify(base)) !== value.anchor_fingerprint) {
|
|
709
|
+
throw new Error("Claude human-started active-task anchor fingerprint does not match");
|
|
710
|
+
}
|
|
711
|
+
const expectedFileId = transcriptFileId(sessionId, `${device}:${inode}`);
|
|
712
|
+
if (expectedFileId !== value.transcript_file_id) {
|
|
713
|
+
throw new Error("Claude human-started active-task transcript identity is invalid");
|
|
714
|
+
}
|
|
715
|
+
return value;
|
|
716
|
+
}
|
|
717
|
+
function claudeTranscriptAnchorForHumanStartedTask(anchor) {
|
|
718
|
+
return {
|
|
719
|
+
schema_version: CLAUDE_TRANSCRIPT_ANCHOR_VERSION,
|
|
720
|
+
session_id: anchor.session_id,
|
|
721
|
+
cwd: anchor.cwd,
|
|
722
|
+
pid: anchor.pid,
|
|
723
|
+
agent_started_at_ms: anchor.agent_started_at_ms,
|
|
724
|
+
captured_at: anchor.captured_at,
|
|
725
|
+
relative_path: anchor.relative_path,
|
|
726
|
+
offset_bytes: 0,
|
|
727
|
+
file_existed: true,
|
|
728
|
+
device: anchor.device,
|
|
729
|
+
inode: anchor.inode
|
|
730
|
+
};
|
|
731
|
+
}
|
|
732
|
+
function latestClaudeRootHumanPromptEntry(entries) {
|
|
733
|
+
return [...entries].reverse().find((entry) => isClaudeRootHumanPrompt(entry.record));
|
|
734
|
+
}
|
|
735
|
+
function isClaudeRootHumanPrompt(record) {
|
|
736
|
+
return record.type === "user" &&
|
|
737
|
+
isRecord(record.message) &&
|
|
738
|
+
record.message.role === "user" &&
|
|
739
|
+
record.isSidechain !== true &&
|
|
740
|
+
nonEmptyString(record.agentId) === undefined &&
|
|
741
|
+
userPromptText(record) !== undefined;
|
|
742
|
+
}
|
|
743
|
+
function isActiveClaudeAgentState(agent) {
|
|
744
|
+
return agent.status === "working" ||
|
|
745
|
+
agent.status === "busy" ||
|
|
746
|
+
(agent.status === "waiting" && agent.waitingFor === "dialog open");
|
|
747
|
+
}
|
|
748
|
+
function isObservableClaudeAgentState(agent) {
|
|
749
|
+
return agent.status === "idle" || isActiveClaudeAgentState(agent);
|
|
750
|
+
}
|
|
751
|
+
function assertObservableClaudeActiveTaskAgent(rows, anchor) {
|
|
752
|
+
const agent = exactInteractiveAgent(rows, anchor.pid);
|
|
753
|
+
if (!agent ||
|
|
754
|
+
agent.startedAt !== anchor.agent_started_at_ms ||
|
|
755
|
+
agent.sessionId !== anchor.session_id ||
|
|
756
|
+
normalizePath(agent.cwd) !== normalizePath(anchor.cwd) ||
|
|
757
|
+
!isObservableClaudeAgentState(agent)) {
|
|
758
|
+
throw new Error("Claude process identity changed after active-task capture");
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
function positiveClaudeByteLimit(value) {
|
|
762
|
+
const result = value ?? CLAUDE_TRANSCRIPT_MAX_TURN_BYTES;
|
|
763
|
+
if (!Number.isSafeInteger(result) || result <= 0) {
|
|
764
|
+
throw new Error("Claude active-task byte limit is invalid");
|
|
765
|
+
}
|
|
766
|
+
return result;
|
|
767
|
+
}
|
|
768
|
+
const CLAUDE_ACTIVE_TASK_CHECKPOINT_SCHEMA = "agent-knock-knock/claude-human-started-active-task-checkpoint";
|
|
769
|
+
const CLAUDE_ACTIVE_TASK_MAX_UNRESOLVED_TOOLS = 64;
|
|
770
|
+
const CLAUDE_ACTIVE_TASK_MAX_OPAQUE_ID_LENGTH = 256;
|
|
771
|
+
export function initialClaudeHumanStartedActiveTaskCheckpoint(value) {
|
|
772
|
+
const anchor = validateClaudeHumanStartedActiveTaskAnchor(value);
|
|
773
|
+
return signedClaudeActiveTaskCheckpoint({
|
|
774
|
+
schema: CLAUDE_ACTIVE_TASK_CHECKPOINT_SCHEMA,
|
|
775
|
+
version: 1,
|
|
776
|
+
anchor_fingerprint: anchor.anchor_fingerprint,
|
|
777
|
+
safe_resume_offset_bytes: anchor.turn_start_offset_bytes,
|
|
778
|
+
last_record_uuid: anchor.prompt_uuid,
|
|
779
|
+
record_count: 0,
|
|
780
|
+
chain_fingerprint: sha256Hex(`claude-active-task-checkpoint\0${anchor.anchor_fingerprint}`),
|
|
781
|
+
unresolved_tool_uses: []
|
|
782
|
+
});
|
|
783
|
+
}
|
|
784
|
+
export function validateClaudeHumanStartedActiveTaskCheckpoint(value, anchorValue) {
|
|
785
|
+
const anchor = validateClaudeHumanStartedActiveTaskAnchor(anchorValue);
|
|
786
|
+
if (!isRecord(value)) {
|
|
787
|
+
throw new Error("Claude active-task checkpoint is invalid");
|
|
788
|
+
}
|
|
789
|
+
const allowedKeys = new Set([
|
|
790
|
+
"schema", "version", "anchor_fingerprint", "safe_resume_offset_bytes",
|
|
791
|
+
"last_record_uuid", "record_count", "chain_fingerprint",
|
|
792
|
+
"unresolved_tool_uses", "assistant_message_id", "assistant_text",
|
|
793
|
+
"end_turn_message_id", "end_turn_assistant_uuid",
|
|
794
|
+
"checkpoint_fingerprint"
|
|
795
|
+
]);
|
|
796
|
+
if (Object.keys(value).some((key) => !allowedKeys.has(key))) {
|
|
797
|
+
throw new Error("Claude active-task checkpoint has unsupported fields");
|
|
798
|
+
}
|
|
799
|
+
const safeOffset = nonNegativeInteger(value.safe_resume_offset_bytes);
|
|
800
|
+
const recordCount = nonNegativeInteger(value.record_count);
|
|
801
|
+
const lastRecordUuid = uuidValue(value.last_record_uuid);
|
|
802
|
+
const assistantMessageId = value.assistant_message_id === undefined
|
|
803
|
+
? undefined
|
|
804
|
+
: uuidValue(value.assistant_message_id);
|
|
805
|
+
const endTurnMessageId = value.end_turn_message_id === undefined
|
|
806
|
+
? undefined
|
|
807
|
+
: uuidValue(value.end_turn_message_id);
|
|
808
|
+
const endTurnAssistantUuid = value.end_turn_assistant_uuid === undefined
|
|
809
|
+
? undefined
|
|
810
|
+
: uuidValue(value.end_turn_assistant_uuid);
|
|
811
|
+
const assistantText = value.assistant_text;
|
|
812
|
+
if (value.schema !== CLAUDE_ACTIVE_TASK_CHECKPOINT_SCHEMA ||
|
|
813
|
+
value.version !== 1 ||
|
|
814
|
+
value.anchor_fingerprint !== anchor.anchor_fingerprint ||
|
|
815
|
+
typeof value.safe_resume_offset_bytes !== "number" ||
|
|
816
|
+
safeOffset === undefined ||
|
|
817
|
+
safeOffset < anchor.turn_start_offset_bytes ||
|
|
818
|
+
typeof value.record_count !== "number" ||
|
|
819
|
+
recordCount === undefined ||
|
|
820
|
+
!lastRecordUuid ||
|
|
821
|
+
lastRecordUuid !== value.last_record_uuid ||
|
|
822
|
+
(recordCount === 0 && lastRecordUuid !== anchor.prompt_uuid) ||
|
|
823
|
+
!sha256String(value.chain_fingerprint) ||
|
|
824
|
+
!sha256String(value.checkpoint_fingerprint) ||
|
|
825
|
+
(value.assistant_message_id !== undefined && !assistantMessageId) ||
|
|
826
|
+
(value.end_turn_message_id !== undefined && !endTurnMessageId) ||
|
|
827
|
+
(value.end_turn_assistant_uuid !== undefined && !endTurnAssistantUuid) ||
|
|
828
|
+
(endTurnMessageId === undefined) !== (endTurnAssistantUuid === undefined) ||
|
|
829
|
+
(endTurnMessageId !== undefined &&
|
|
830
|
+
assistantMessageId !== endTurnMessageId) ||
|
|
831
|
+
(assistantText !== undefined &&
|
|
832
|
+
(typeof assistantText !== "string" ||
|
|
833
|
+
!assistantText ||
|
|
834
|
+
assistantText.length > 4000 ||
|
|
835
|
+
boundedRedactedText(assistantText) !== assistantText ||
|
|
836
|
+
assistantMessageId === undefined))) {
|
|
837
|
+
throw new Error("Claude active-task checkpoint identity is invalid");
|
|
838
|
+
}
|
|
839
|
+
if (!Array.isArray(value.unresolved_tool_uses) ||
|
|
840
|
+
value.unresolved_tool_uses.length >
|
|
841
|
+
CLAUDE_ACTIVE_TASK_MAX_UNRESOLVED_TOOLS) {
|
|
842
|
+
throw new Error("Claude active-task checkpoint tool state is invalid");
|
|
843
|
+
}
|
|
844
|
+
const seenToolIds = new Set();
|
|
845
|
+
for (const entry of value.unresolved_tool_uses) {
|
|
846
|
+
if (!isRecord(entry) ||
|
|
847
|
+
Object.keys(entry).some((key) => key !== "tool_use_id" && key !== "owner_uuid") ||
|
|
848
|
+
!checkpointOpaqueId(entry.tool_use_id) ||
|
|
849
|
+
!uuidValue(entry.owner_uuid) ||
|
|
850
|
+
uuidValue(entry.owner_uuid) !== entry.owner_uuid ||
|
|
851
|
+
seenToolIds.has(String(entry.tool_use_id))) {
|
|
852
|
+
throw new Error("Claude active-task checkpoint tool state is invalid");
|
|
853
|
+
}
|
|
854
|
+
seenToolIds.add(String(entry.tool_use_id));
|
|
855
|
+
}
|
|
856
|
+
const candidate = value;
|
|
857
|
+
if (claudeActiveTaskCheckpointFingerprint(candidate) !==
|
|
858
|
+
candidate.checkpoint_fingerprint) {
|
|
859
|
+
throw new Error("Claude active-task checkpoint fingerprint does not match");
|
|
860
|
+
}
|
|
861
|
+
return candidate;
|
|
862
|
+
}
|
|
863
|
+
function claudeActiveTaskObservationCheckpoint(anchor, options) {
|
|
864
|
+
const checkpoint = options.checkpoint === undefined
|
|
865
|
+
? initialClaudeHumanStartedActiveTaskCheckpoint(anchor)
|
|
866
|
+
: validateClaudeHumanStartedActiveTaskCheckpoint(options.checkpoint, anchor);
|
|
867
|
+
if (options.resumeOffsetBytes !== undefined &&
|
|
868
|
+
(!Number.isSafeInteger(options.resumeOffsetBytes) ||
|
|
869
|
+
options.resumeOffsetBytes !== checkpoint.safe_resume_offset_bytes)) {
|
|
870
|
+
throw new Error("Claude active-task resume offset does not match its continuation checkpoint");
|
|
871
|
+
}
|
|
872
|
+
return checkpoint;
|
|
873
|
+
}
|
|
874
|
+
function signedClaudeActiveTaskCheckpoint(base) {
|
|
875
|
+
const checkpoint = {
|
|
876
|
+
...base,
|
|
877
|
+
checkpoint_fingerprint: ""
|
|
878
|
+
};
|
|
879
|
+
return {
|
|
880
|
+
...checkpoint,
|
|
881
|
+
checkpoint_fingerprint: claudeActiveTaskCheckpointFingerprint(checkpoint)
|
|
882
|
+
};
|
|
883
|
+
}
|
|
884
|
+
function claudeActiveTaskCheckpointFingerprint(value) {
|
|
885
|
+
return sha256Hex(JSON.stringify({
|
|
886
|
+
schema: value.schema,
|
|
887
|
+
version: value.version,
|
|
888
|
+
anchor_fingerprint: value.anchor_fingerprint,
|
|
889
|
+
safe_resume_offset_bytes: value.safe_resume_offset_bytes,
|
|
890
|
+
last_record_uuid: value.last_record_uuid,
|
|
891
|
+
record_count: value.record_count,
|
|
892
|
+
chain_fingerprint: value.chain_fingerprint,
|
|
893
|
+
unresolved_tool_uses: value.unresolved_tool_uses,
|
|
894
|
+
...(value.assistant_message_id === undefined
|
|
895
|
+
? {}
|
|
896
|
+
: { assistant_message_id: value.assistant_message_id }),
|
|
897
|
+
...(value.assistant_text === undefined
|
|
898
|
+
? {}
|
|
899
|
+
: { assistant_text: value.assistant_text }),
|
|
900
|
+
...(value.end_turn_message_id === undefined
|
|
901
|
+
? {}
|
|
902
|
+
: { end_turn_message_id: value.end_turn_message_id }),
|
|
903
|
+
...(value.end_turn_assistant_uuid === undefined
|
|
904
|
+
? {}
|
|
905
|
+
: { end_turn_assistant_uuid: value.end_turn_assistant_uuid })
|
|
906
|
+
}));
|
|
907
|
+
}
|
|
908
|
+
function sha256String(value) {
|
|
909
|
+
return typeof value === "string" && /^[0-9a-f]{64}$/u.test(value);
|
|
910
|
+
}
|
|
911
|
+
function checkpointOpaqueId(value) {
|
|
912
|
+
return typeof value === "string" &&
|
|
913
|
+
value.length > 0 &&
|
|
914
|
+
value.length <= CLAUDE_ACTIVE_TASK_MAX_OPAQUE_ID_LENGTH &&
|
|
915
|
+
/^[A-Za-z0-9_.:-]+$/u.test(value);
|
|
916
|
+
}
|
|
917
|
+
function advanceClaudeHumanStartedActiveTaskCheckpoint(anchor, checkpoint, records, safeResumeOffsetBytes) {
|
|
918
|
+
let lastRecordUuid = checkpoint.last_record_uuid;
|
|
919
|
+
let recordCount = checkpoint.record_count;
|
|
920
|
+
let chainFingerprint = checkpoint.chain_fingerprint;
|
|
921
|
+
const unresolvedToolUses = new Map(checkpoint.unresolved_tool_uses.map((entry) => [entry.tool_use_id, {
|
|
922
|
+
tool_use_id: entry.tool_use_id,
|
|
923
|
+
owner_uuid: entry.owner_uuid
|
|
924
|
+
}]));
|
|
925
|
+
let assistantMessageId = checkpoint.assistant_message_id;
|
|
926
|
+
let assistantText = checkpoint.assistant_text;
|
|
927
|
+
let endTurnMessageId = checkpoint.end_turn_message_id;
|
|
928
|
+
let endTurnAssistantUuid = checkpoint.end_turn_assistant_uuid;
|
|
929
|
+
let completion;
|
|
930
|
+
let durationSeen = false;
|
|
931
|
+
for (const record of records) {
|
|
932
|
+
if (recordCount === 0) {
|
|
933
|
+
assertClaudeActiveTaskCheckpointPrompt(record, anchor);
|
|
934
|
+
lastRecordUuid = anchor.prompt_uuid;
|
|
935
|
+
({ recordCount, chainFingerprint } = advanceClaudeCheckpointDigest(recordCount, chainFingerprint, record));
|
|
936
|
+
continue;
|
|
937
|
+
}
|
|
938
|
+
if (isClaudeRootHumanPrompt(record)) {
|
|
939
|
+
if (completion) {
|
|
940
|
+
break;
|
|
941
|
+
}
|
|
942
|
+
throw new Error("a later Claude human prompt appeared before the anchored task completed");
|
|
943
|
+
}
|
|
944
|
+
const recordUuid = claudeCheckpointRecordUuid(record, anchor, lastRecordUuid);
|
|
945
|
+
if (recordUuid) {
|
|
946
|
+
lastRecordUuid = recordUuid;
|
|
947
|
+
}
|
|
948
|
+
if (hasUnresolvedBackgroundWork(record) ||
|
|
949
|
+
record.subtype === "stop_hook_summary") {
|
|
950
|
+
throw new Error("Claude anchored task acquired unsupported background or stop-hook work");
|
|
951
|
+
}
|
|
952
|
+
advanceClaudeCheckpointToolState(record, recordUuid, unresolvedToolUses);
|
|
953
|
+
if (record.type === "assistant") {
|
|
954
|
+
const message = isRecord(record.message) ? record.message : undefined;
|
|
955
|
+
if (message?.role !== "assistant") {
|
|
956
|
+
throw new Error("Claude assistant record has an unsupported message role");
|
|
957
|
+
}
|
|
958
|
+
const rawMessageId = nonEmptyString(message.id);
|
|
959
|
+
const messageId = rawMessageId === undefined
|
|
960
|
+
? undefined
|
|
961
|
+
: uuidValue(rawMessageId);
|
|
962
|
+
if (rawMessageId !== undefined && !messageId) {
|
|
963
|
+
throw new Error("Claude assistant message has no stable UUID");
|
|
964
|
+
}
|
|
965
|
+
if (messageId && messageId !== assistantMessageId) {
|
|
966
|
+
if (endTurnMessageId !== undefined) {
|
|
967
|
+
throw new Error("Claude assistant message changed after an end-turn signal");
|
|
968
|
+
}
|
|
969
|
+
assistantMessageId = messageId;
|
|
970
|
+
assistantText = undefined;
|
|
971
|
+
}
|
|
972
|
+
const recordText = textFromAssistantRecord(record);
|
|
973
|
+
if (recordText) {
|
|
974
|
+
if (!messageId || messageId !== assistantMessageId) {
|
|
975
|
+
throw new Error("Claude assistant text has no stable message identity");
|
|
976
|
+
}
|
|
977
|
+
assistantText = appendBoundedRedactedClaudeText(assistantText, recordText);
|
|
978
|
+
}
|
|
979
|
+
if (record.isApiErrorMessage === true) {
|
|
980
|
+
if (!recordUuid || validTimestampMs(record.timestamp) === undefined) {
|
|
981
|
+
throw new Error("Claude API-error completion identity is invalid");
|
|
982
|
+
}
|
|
983
|
+
if (unresolvedToolUses.size > 0) {
|
|
984
|
+
throw new Error("Claude API error left unresolved tool use state");
|
|
985
|
+
}
|
|
986
|
+
const error = safeErrorCode(record.error);
|
|
987
|
+
completion = {
|
|
988
|
+
source: "durable",
|
|
989
|
+
outcome: "failure",
|
|
990
|
+
text: assistantText || `Claude Code stopped with ${error}.`,
|
|
991
|
+
timestamp: nonEmptyString(record.timestamp),
|
|
992
|
+
id: recordUuid,
|
|
993
|
+
confidence: "high",
|
|
994
|
+
metadata: {
|
|
995
|
+
match: "claude_transcript_api_error",
|
|
996
|
+
session_id: anchor.session_id,
|
|
997
|
+
prompt_uuid: anchor.prompt_uuid,
|
|
998
|
+
error,
|
|
999
|
+
transcript_schema: "claude_code_jsonl_v2",
|
|
1000
|
+
transcript_file_id: anchor.transcript_file_id
|
|
1001
|
+
}
|
|
1002
|
+
};
|
|
1003
|
+
}
|
|
1004
|
+
else {
|
|
1005
|
+
const stopReason = nonEmptyString(message.stop_reason);
|
|
1006
|
+
if (stopReason === "end_turn") {
|
|
1007
|
+
if (!recordUuid || !messageId) {
|
|
1008
|
+
throw new Error("Claude end-turn signal has no stable identity");
|
|
1009
|
+
}
|
|
1010
|
+
if (endTurnMessageId !== undefined &&
|
|
1011
|
+
endTurnMessageId !== messageId) {
|
|
1012
|
+
throw new Error("Claude active task contains multiple end-turn message identities");
|
|
1013
|
+
}
|
|
1014
|
+
endTurnMessageId = messageId;
|
|
1015
|
+
endTurnAssistantUuid = recordUuid;
|
|
1016
|
+
}
|
|
1017
|
+
else if (stopReason !== undefined && stopReason !== "tool_use") {
|
|
1018
|
+
throw new Error("Claude active task contains an unsupported terminal stop reason");
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
if (record.type === "system" && record.subtype === "turn_duration") {
|
|
1023
|
+
if (durationSeen) {
|
|
1024
|
+
throw new Error("Claude transcript turn contains multiple turn_duration records");
|
|
1025
|
+
}
|
|
1026
|
+
durationSeen = true;
|
|
1027
|
+
if (!recordUuid ||
|
|
1028
|
+
validTimestampMs(record.timestamp) === undefined ||
|
|
1029
|
+
!endTurnMessageId ||
|
|
1030
|
+
!endTurnAssistantUuid ||
|
|
1031
|
+
assistantMessageId !== endTurnMessageId ||
|
|
1032
|
+
!assistantText ||
|
|
1033
|
+
unresolvedToolUses.size > 0) {
|
|
1034
|
+
throw new Error("Claude turn_duration has no complete incremental completion chain");
|
|
1035
|
+
}
|
|
1036
|
+
completion = {
|
|
1037
|
+
source: "durable",
|
|
1038
|
+
outcome: "success",
|
|
1039
|
+
text: assistantText,
|
|
1040
|
+
timestamp: nonEmptyString(record.timestamp),
|
|
1041
|
+
id: recordUuid,
|
|
1042
|
+
confidence: "high",
|
|
1043
|
+
metadata: {
|
|
1044
|
+
match: "claude_transcript_turn_duration",
|
|
1045
|
+
session_id: anchor.session_id,
|
|
1046
|
+
prompt_uuid: anchor.prompt_uuid,
|
|
1047
|
+
assistant_message_id: endTurnMessageId,
|
|
1048
|
+
claude_version: anchor.claude_version,
|
|
1049
|
+
transcript_schema: "claude_code_jsonl_v2",
|
|
1050
|
+
transcript_file_id: anchor.transcript_file_id
|
|
1051
|
+
}
|
|
1052
|
+
};
|
|
1053
|
+
}
|
|
1054
|
+
({ recordCount, chainFingerprint } = advanceClaudeCheckpointDigest(recordCount, chainFingerprint, record));
|
|
1055
|
+
}
|
|
1056
|
+
const nextCheckpoint = signedClaudeActiveTaskCheckpoint({
|
|
1057
|
+
schema: CLAUDE_ACTIVE_TASK_CHECKPOINT_SCHEMA,
|
|
1058
|
+
version: 1,
|
|
1059
|
+
anchor_fingerprint: anchor.anchor_fingerprint,
|
|
1060
|
+
safe_resume_offset_bytes: safeResumeOffsetBytes,
|
|
1061
|
+
last_record_uuid: lastRecordUuid,
|
|
1062
|
+
record_count: recordCount,
|
|
1063
|
+
chain_fingerprint: chainFingerprint,
|
|
1064
|
+
unresolved_tool_uses: [...unresolvedToolUses.values()],
|
|
1065
|
+
...(assistantMessageId === undefined
|
|
1066
|
+
? {}
|
|
1067
|
+
: { assistant_message_id: assistantMessageId }),
|
|
1068
|
+
...(assistantText === undefined ? {} : { assistant_text: assistantText }),
|
|
1069
|
+
...(endTurnMessageId === undefined
|
|
1070
|
+
? {}
|
|
1071
|
+
: { end_turn_message_id: endTurnMessageId }),
|
|
1072
|
+
...(endTurnAssistantUuid === undefined
|
|
1073
|
+
? {}
|
|
1074
|
+
: { end_turn_assistant_uuid: endTurnAssistantUuid })
|
|
1075
|
+
});
|
|
1076
|
+
return { checkpoint: nextCheckpoint, ...(completion ? { completion } : {}) };
|
|
1077
|
+
}
|
|
1078
|
+
function assertClaudeActiveTaskCheckpointPrompt(record, anchor) {
|
|
1079
|
+
assertSupportedRecord(record, anchor.session_id, anchor.cwd);
|
|
1080
|
+
const promptText = userPromptText(record);
|
|
1081
|
+
if (!isClaudeRootHumanPrompt(record) ||
|
|
1082
|
+
uuidValue(record.uuid) !== anchor.prompt_uuid ||
|
|
1083
|
+
exactRequestFingerprint(promptText) !== anchor.request_hash ||
|
|
1084
|
+
nonEmptyString(record.version) !== anchor.claude_version) {
|
|
1085
|
+
throw new Error("Claude anchored root prompt identity changed");
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
function claudeCheckpointRecordUuid(record, anchor, expectedParentUuid) {
|
|
1089
|
+
assertSupportedRecord(record, anchor.session_id, anchor.cwd);
|
|
1090
|
+
const recordUuid = uuidValue(record.uuid);
|
|
1091
|
+
const requiresIdentity = record.type === "user" ||
|
|
1092
|
+
record.type === "assistant" ||
|
|
1093
|
+
record.type === "system" ||
|
|
1094
|
+
record.type === "attachment" ||
|
|
1095
|
+
hasTurnCompletionSignal(record);
|
|
1096
|
+
if (!recordUuid) {
|
|
1097
|
+
if (requiresIdentity) {
|
|
1098
|
+
throw new Error("Claude active-task record has no stable UUID");
|
|
1099
|
+
}
|
|
1100
|
+
return undefined;
|
|
1101
|
+
}
|
|
1102
|
+
if (nonEmptyString(record.version) !== anchor.claude_version) {
|
|
1103
|
+
throw new Error("Claude transcript turn changed schema versions while it was running");
|
|
1104
|
+
}
|
|
1105
|
+
if (uuidValue(record.parentUuid) !== expectedParentUuid) {
|
|
1106
|
+
throw new Error("Claude active-task record is not the next exact descendant");
|
|
1107
|
+
}
|
|
1108
|
+
return recordUuid;
|
|
1109
|
+
}
|
|
1110
|
+
function advanceClaudeCheckpointToolState(record, recordUuid, unresolved) {
|
|
1111
|
+
const message = isRecord(record.message) ? record.message : undefined;
|
|
1112
|
+
const content = Array.isArray(message?.content) ? message.content : [];
|
|
1113
|
+
for (const block of content) {
|
|
1114
|
+
if (!isRecord(block)) {
|
|
1115
|
+
continue;
|
|
1116
|
+
}
|
|
1117
|
+
if (block.type === "tool_use") {
|
|
1118
|
+
const id = checkpointOpaqueId(block.id) ? block.id : undefined;
|
|
1119
|
+
if (!id || !recordUuid || unresolved.has(id)) {
|
|
1120
|
+
throw new Error("Claude active task contains invalid tool-use identity");
|
|
1121
|
+
}
|
|
1122
|
+
if (unresolved.size >= CLAUDE_ACTIVE_TASK_MAX_UNRESOLVED_TOOLS) {
|
|
1123
|
+
throw new Error("Claude active task has too many unresolved tool uses");
|
|
1124
|
+
}
|
|
1125
|
+
unresolved.set(id, { tool_use_id: id, owner_uuid: recordUuid });
|
|
1126
|
+
}
|
|
1127
|
+
if (block.type === "tool_result") {
|
|
1128
|
+
const id = checkpointOpaqueId(block.tool_use_id)
|
|
1129
|
+
? block.tool_use_id
|
|
1130
|
+
: undefined;
|
|
1131
|
+
const pending = id ? unresolved.get(id) : undefined;
|
|
1132
|
+
if (!id ||
|
|
1133
|
+
!pending ||
|
|
1134
|
+
uuidValue(record.parentUuid) !== pending.owner_uuid ||
|
|
1135
|
+
uuidValue(record.sourceToolAssistantUUID) !== pending.owner_uuid) {
|
|
1136
|
+
throw new Error("Claude active task contains invalid tool-result identity");
|
|
1137
|
+
}
|
|
1138
|
+
unresolved.delete(id);
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
function advanceClaudeCheckpointDigest(recordCount, chainFingerprint, record) {
|
|
1143
|
+
if (recordCount >= Number.MAX_SAFE_INTEGER) {
|
|
1144
|
+
throw new Error("Claude active-task checkpoint record count overflowed");
|
|
1145
|
+
}
|
|
1146
|
+
return {
|
|
1147
|
+
recordCount: recordCount + 1,
|
|
1148
|
+
chainFingerprint: sha256Hex(`${chainFingerprint}\0${sha256Hex(JSON.stringify(record))}`)
|
|
1149
|
+
};
|
|
1150
|
+
}
|
|
1151
|
+
function appendBoundedRedactedClaudeText(current, value) {
|
|
1152
|
+
const redacted = boundedRedactedText(value);
|
|
1153
|
+
if (!redacted) {
|
|
1154
|
+
return current;
|
|
1155
|
+
}
|
|
1156
|
+
return boundedRedactedText(current ? `${current}\n${redacted}` : redacted);
|
|
1157
|
+
}
|
|
1158
|
+
function assertClaudeActiveTaskResumeBoundary(fd, offset) {
|
|
1159
|
+
if (offset > 0 && byteAt(fd, offset - 1) !== 0x0a) {
|
|
1160
|
+
throw new Error("Claude active-task resume offset is not a JSONL boundary");
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
function readExactTranscriptBytes(fd, offset, length) {
|
|
1164
|
+
const buffer = Buffer.allocUnsafe(length);
|
|
1165
|
+
let readTotal = 0;
|
|
1166
|
+
while (readTotal < length) {
|
|
1167
|
+
const bytesRead = fs.readSync(fd, buffer, readTotal, length - readTotal, offset + readTotal);
|
|
1168
|
+
if (bytesRead === 0) {
|
|
1169
|
+
break;
|
|
1170
|
+
}
|
|
1171
|
+
readTotal += bytesRead;
|
|
1172
|
+
}
|
|
1173
|
+
if (readTotal !== length) {
|
|
1174
|
+
throw new Error("Claude transcript changed while it was being read");
|
|
1175
|
+
}
|
|
1176
|
+
return buffer;
|
|
1177
|
+
}
|
|
1178
|
+
function completeTranscriptJsonlPrefixLength(buffer) {
|
|
1179
|
+
if (buffer.length === 0) {
|
|
1180
|
+
return 0;
|
|
1181
|
+
}
|
|
1182
|
+
const newline = buffer.lastIndexOf(0x0a);
|
|
1183
|
+
return newline < 0 ? 0 : newline + 1;
|
|
1184
|
+
}
|
|
1185
|
+
function claudeActiveTaskUnavailable(reason) {
|
|
1186
|
+
return { status: "unavailable", reason, retryable: true };
|
|
1187
|
+
}
|
|
1188
|
+
function isRetryableClaudeProviderIoError(error) {
|
|
1189
|
+
if (!isRecord(error) || typeof error.code !== "string") {
|
|
1190
|
+
return false;
|
|
1191
|
+
}
|
|
1192
|
+
return new Set([
|
|
1193
|
+
"EACCES", "EAGAIN", "EBUSY", "EIO", "EMFILE", "ENFILE", "ENOENT",
|
|
1194
|
+
"EPERM", "ESTALE", "ETIMEDOUT"
|
|
1195
|
+
]).has(error.code);
|
|
1196
|
+
}
|
|
432
1197
|
/**
|
|
433
1198
|
* Detects one completed Claude Code turn from the append-only local transcript.
|
|
434
1199
|
* The detector intentionally fails closed on identity, schema, rotation, prompt,
|
|
@@ -675,6 +1440,10 @@ function readClaudeTranscriptTurnSnapshot(request, options, requiredAgentStatus,
|
|
|
675
1440
|
function matchingManagedPrompt(snapshot) {
|
|
676
1441
|
const promptCandidates = snapshot.records.filter((record) => {
|
|
677
1442
|
const promptText = userPromptText(record);
|
|
1443
|
+
const exactPromptIdentity = snapshot.expectedPromptUuid
|
|
1444
|
+
? uuidValue(record.uuid) === snapshot.expectedPromptUuid
|
|
1445
|
+
: snapshot.expectedPromptText !== undefined &&
|
|
1446
|
+
exactPromptText(promptText) === snapshot.expectedPromptText;
|
|
678
1447
|
return record.type === "user" &&
|
|
679
1448
|
isRecord(record.message) &&
|
|
680
1449
|
record.message.role === "user" &&
|
|
@@ -684,7 +1453,7 @@ function matchingManagedPrompt(snapshot) {
|
|
|
684
1453
|
normalizePath(record.cwd) === normalizePath(snapshot.cwd) &&
|
|
685
1454
|
validTimestampMs(record.timestamp) !== undefined &&
|
|
686
1455
|
promptText !== undefined &&
|
|
687
|
-
|
|
1456
|
+
exactPromptIdentity &&
|
|
688
1457
|
exactRequestFingerprint(promptText) === snapshot.expectedRequestHash;
|
|
689
1458
|
});
|
|
690
1459
|
if (promptCandidates.length === 0) {
|
|
@@ -860,6 +1629,17 @@ function completionFromRecords(snapshot, options = {}) {
|
|
|
860
1629
|
};
|
|
861
1630
|
}
|
|
862
1631
|
function pendingApprovalFromRecords(snapshot) {
|
|
1632
|
+
const turn = pendingApprovalTurn(snapshot);
|
|
1633
|
+
if (!turn) {
|
|
1634
|
+
return undefined;
|
|
1635
|
+
}
|
|
1636
|
+
const pending = pendingApprovalToolUse(turn.descendants);
|
|
1637
|
+
if (!pending) {
|
|
1638
|
+
return undefined;
|
|
1639
|
+
}
|
|
1640
|
+
return pendingApprovalEvidence(snapshot, turn.promptUuid, turn.lastDescendant, pending);
|
|
1641
|
+
}
|
|
1642
|
+
function pendingApprovalTurn(snapshot) {
|
|
863
1643
|
const prompt = matchingManagedPrompt(snapshot);
|
|
864
1644
|
if (!prompt) {
|
|
865
1645
|
return undefined;
|
|
@@ -916,8 +1696,12 @@ function pendingApprovalFromRecords(snapshot) {
|
|
|
916
1696
|
hasUnresolvedBackgroundWork(record))) {
|
|
917
1697
|
return undefined;
|
|
918
1698
|
}
|
|
919
|
-
if (descendants.some((record) =>
|
|
920
|
-
|
|
1699
|
+
if (descendants.some((record) => record.subtype === "stop_hook_summary")) {
|
|
1700
|
+
return undefined;
|
|
1701
|
+
}
|
|
1702
|
+
const completionSignals = descendants.filter(hasTurnCompletionSignal);
|
|
1703
|
+
if (completionSignals.length > 0 &&
|
|
1704
|
+
!isPendingClaudeTurnDurationAppend(descendants, completionSignals)) {
|
|
921
1705
|
return undefined;
|
|
922
1706
|
}
|
|
923
1707
|
const lastDescendant = descendants.at(-1);
|
|
@@ -928,6 +1712,32 @@ function pendingApprovalFromRecords(snapshot) {
|
|
|
928
1712
|
if (!linearChain || linearChain.length !== descendants.length) {
|
|
929
1713
|
return undefined;
|
|
930
1714
|
}
|
|
1715
|
+
return { promptUuid, descendants, lastDescendant };
|
|
1716
|
+
}
|
|
1717
|
+
function isPendingClaudeTurnDurationAppend(descendants, completionSignals) {
|
|
1718
|
+
if (completionSignals.length === 0 || hasUnresolvedToolUse(descendants)) {
|
|
1719
|
+
return false;
|
|
1720
|
+
}
|
|
1721
|
+
const messageIds = new Set();
|
|
1722
|
+
for (const candidate of completionSignals) {
|
|
1723
|
+
const message = isRecord(candidate.message) ? candidate.message : undefined;
|
|
1724
|
+
const messageId = uuidValue(message?.id);
|
|
1725
|
+
if (candidate.type !== "assistant" ||
|
|
1726
|
+
candidate.isApiErrorMessage === true ||
|
|
1727
|
+
message?.role !== "assistant" ||
|
|
1728
|
+
message.stop_reason !== "end_turn" ||
|
|
1729
|
+
uuidValue(candidate.uuid) === undefined ||
|
|
1730
|
+
!messageId) {
|
|
1731
|
+
return false;
|
|
1732
|
+
}
|
|
1733
|
+
messageIds.add(messageId);
|
|
1734
|
+
}
|
|
1735
|
+
const finalSignal = completionSignals.at(-1);
|
|
1736
|
+
return messageIds.size === 1 &&
|
|
1737
|
+
finalSignal === descendants.at(-1) &&
|
|
1738
|
+
finalSignal !== undefined;
|
|
1739
|
+
}
|
|
1740
|
+
function pendingApprovalToolUse(descendants) {
|
|
931
1741
|
const toolUses = new Map();
|
|
932
1742
|
const toolResults = new Map();
|
|
933
1743
|
for (const record of descendants) {
|
|
@@ -988,7 +1798,9 @@ function pendingApprovalFromRecords(snapshot) {
|
|
|
988
1798
|
if (unresolved.length !== 1) {
|
|
989
1799
|
return undefined;
|
|
990
1800
|
}
|
|
991
|
-
|
|
1801
|
+
return unresolved[0];
|
|
1802
|
+
}
|
|
1803
|
+
function pendingApprovalEvidence(snapshot, promptUuid, lastDescendant, pending) {
|
|
992
1804
|
const pendingMessage = isRecord(pending.owner.message)
|
|
993
1805
|
? pending.owner.message
|
|
994
1806
|
: undefined;
|
|
@@ -1191,24 +2003,59 @@ function openRelativeTranscript(projectsRoot, relativePath) {
|
|
|
1191
2003
|
}
|
|
1192
2004
|
}
|
|
1193
2005
|
function readCompleteJsonlRecords(fd, offset, length) {
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
2006
|
+
return readCompleteJsonlRecordEntries(fd, offset, length)
|
|
2007
|
+
.map((entry) => entry.record);
|
|
2008
|
+
}
|
|
2009
|
+
function readBoundedCompleteJsonlTail(fd, endOffset, maxBytes) {
|
|
2010
|
+
const offset = Math.max(0, endOffset - maxBytes);
|
|
2011
|
+
return readCompleteJsonlRecordEntries(fd, offset, endOffset - offset, {
|
|
2012
|
+
allowLeadingPartial: offset > 0
|
|
2013
|
+
});
|
|
2014
|
+
}
|
|
2015
|
+
function readCompleteJsonlRecordEntries(fd, offset, length, options = {}) {
|
|
2016
|
+
const buffer = options.preReadBuffer ??
|
|
2017
|
+
readExactTranscriptBytes(fd, offset, length);
|
|
2018
|
+
if (buffer.length !== length) {
|
|
2019
|
+
throw new Error("Claude transcript pre-read boundary is invalid");
|
|
1205
2020
|
}
|
|
1206
|
-
if (buffer.length === 0
|
|
2021
|
+
if (buffer.length === 0) {
|
|
1207
2022
|
return [];
|
|
1208
2023
|
}
|
|
1209
|
-
|
|
2024
|
+
let start = 0;
|
|
2025
|
+
if (options.allowLeadingPartial === true &&
|
|
2026
|
+
offset > 0 &&
|
|
2027
|
+
byteAt(fd, offset - 1) !== 0x0a) {
|
|
2028
|
+
const firstNewline = buffer.indexOf(0x0a);
|
|
2029
|
+
if (firstNewline < 0) {
|
|
2030
|
+
return [];
|
|
2031
|
+
}
|
|
2032
|
+
start = firstNewline + 1;
|
|
2033
|
+
}
|
|
2034
|
+
let end = buffer.length;
|
|
2035
|
+
if (buffer[end - 1] !== 0x0a) {
|
|
2036
|
+
if (options.allowTrailingPartial !== true) {
|
|
2037
|
+
return [];
|
|
2038
|
+
}
|
|
2039
|
+
const lastNewline = buffer.lastIndexOf(0x0a);
|
|
2040
|
+
if (lastNewline < start) {
|
|
2041
|
+
return [];
|
|
2042
|
+
}
|
|
2043
|
+
end = lastNewline + 1;
|
|
2044
|
+
}
|
|
1210
2045
|
const records = [];
|
|
1211
|
-
|
|
2046
|
+
let lineStart = start;
|
|
2047
|
+
while (lineStart < end) {
|
|
2048
|
+
const newline = buffer.indexOf(0x0a, lineStart);
|
|
2049
|
+
if (newline < 0 || newline >= end) {
|
|
2050
|
+
break;
|
|
2051
|
+
}
|
|
2052
|
+
let lineBuffer = buffer.subarray(lineStart, newline);
|
|
2053
|
+
const recordOffset = offset + lineStart;
|
|
2054
|
+
lineStart = newline + 1;
|
|
2055
|
+
if (lineBuffer.at(-1) === 0x0d) {
|
|
2056
|
+
lineBuffer = lineBuffer.subarray(0, lineBuffer.length - 1);
|
|
2057
|
+
}
|
|
2058
|
+
const line = lineBuffer.toString("utf8");
|
|
1212
2059
|
if (!line.trim()) {
|
|
1213
2060
|
continue;
|
|
1214
2061
|
}
|
|
@@ -1222,10 +2069,16 @@ function readCompleteJsonlRecords(fd, offset, length) {
|
|
|
1222
2069
|
if (!isRecord(parsed)) {
|
|
1223
2070
|
throw new Error("Claude transcript contains a non-object JSONL record");
|
|
1224
2071
|
}
|
|
1225
|
-
records.push(parsed);
|
|
2072
|
+
records.push({ record: parsed, offsetBytes: recordOffset });
|
|
1226
2073
|
}
|
|
1227
2074
|
return records;
|
|
1228
2075
|
}
|
|
2076
|
+
function byteAt(fd, offset) {
|
|
2077
|
+
const buffer = Buffer.allocUnsafe(1);
|
|
2078
|
+
return fs.readSync(fd, buffer, 0, 1, offset) === 1
|
|
2079
|
+
? buffer[0]
|
|
2080
|
+
: undefined;
|
|
2081
|
+
}
|
|
1229
2082
|
function descendantRecords(records, recordsByUuid, ancestorUuid, predicate) {
|
|
1230
2083
|
return records.filter((record) => predicate(record) &&
|
|
1231
2084
|
descendantChain(recordsByUuid, ancestorUuid, record) !== undefined);
|
|
@@ -1594,7 +2447,4 @@ function uuidValue(value) {
|
|
|
1594
2447
|
function nonEmptyString(value) {
|
|
1595
2448
|
return typeof value === "string" && value.trim().length > 0 ? value : undefined;
|
|
1596
2449
|
}
|
|
1597
|
-
function isRecord(value) {
|
|
1598
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1599
|
-
}
|
|
1600
2450
|
//# sourceMappingURL=claude-local-transcript-provider.js.map
|