@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
|
@@ -0,0 +1,1072 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { observeClaudeDeadProcessTranscriptCompletion } from "./claude-local-transcript-provider.js";
|
|
4
|
+
import { expandHome, resolveOptionalExecutable } from "./cli-command-runtime.js";
|
|
5
|
+
import { cliDependencies, cliRuntimeLog } from "./cli-runtime-context.js";
|
|
6
|
+
import * as deferredAuthorityAdapter from "./deferred-foreground-authority-cli-adapter.js";
|
|
7
|
+
import { isExactNativeThreadId, managedSessionBindingToken, managedSessionRevision, terminalBindingFrom } from "./managed-session.js";
|
|
8
|
+
import { executorForConversation, sessionIdForConversation, turnIdForConversation } from "./protocol.js";
|
|
9
|
+
import { listManagedSessions, loadManagedSession, loadNativeThreadTransition, saveManagedSession, tryLoadManagedSession } from "./session-store.js";
|
|
10
|
+
import { listConversations } from "./store.js";
|
|
11
|
+
import { parseTerminalConversationId } from "./terminal-agent-adapter.js";
|
|
12
|
+
import { exactCodexReadyStyledComposerCapture } from "./terminal-agent-bridge.js";
|
|
13
|
+
import { knownNativeThreadCompanionSet } from "./native-thread-transition-verification-adapter.js";
|
|
14
|
+
import { codexComposerVisible } from "./native-thread-lifecycle-recovery-adapter.js";
|
|
15
|
+
import { associateTerminalEndpointEvidence, hasCanonicalTerminalEndpoint, terminalControlEvidence } from "./terminal-control-ref.js";
|
|
16
|
+
import { codexIdentityFence, decideManagedBindingConflict, exactBoundCodexSendSource, isCodexStatusCardEvidence, isCompleteNativeRollout, processIncarnationRelationship, terminalControlAliasMatches, terminalControlsShareIncarnation, withCodexCompanionFences } from "./terminal-authority-policy.js";
|
|
17
|
+
import { codexCompanionSet, exactLifecycleIdentity, verifiedEmptySourceSnapshotMatches } from "./terminal-identity-authority-service.js";
|
|
18
|
+
import { codexKnownBeforeIdentityForTransition, codexLingeringIdentityMatches, logicalManagedSessionIdentity, managedBindingMatchesLiveTerminal, managedSessionOwnerIsInactive, resolvedTerminalProcessIncarnation as resolvedTerminalProcessIncarnationPolicy, selectBoundManagedSessionForTerminal, selectSoleBoundManagedSessionClaim, terminalRuntimeForLiveIdentity as terminalRuntimeForLiveIdentityPolicy, terminalRuntimeIdentityBase } from "./terminal-dispatch-execution.js";
|
|
19
|
+
import { terminalBridgeSubmission } from "./terminal-dispatch-receipt.js";
|
|
20
|
+
import { detectCodexBoundRolloutCompletion } from "./terminal-submission-acceptance.js";
|
|
21
|
+
import { isRecord, nonBlankString as stringValue } from "./value-guards.js";
|
|
22
|
+
export function createTerminalIdentityAuthorityCliAdapter(ports) {
|
|
23
|
+
return Object.freeze({
|
|
24
|
+
resolveTerminalConversationFromOptions: (options) => resolveTerminalConversationFromOptions(ports, options),
|
|
25
|
+
refineTerminalTurnEndpoint: (input) => refineTerminalTurnEndpoint(ports, input),
|
|
26
|
+
terminalRuntimeIdentityForConversation: (conversation, terminalControl) => terminalRuntimeIdentityForConversation(ports, conversation, terminalControl),
|
|
27
|
+
terminalDurableRequestForConversation: (conversation, terminalControl) => terminalDurableRequestForConversation(ports, conversation, terminalControl),
|
|
28
|
+
migrateLegacyTerminalAgentIdentity: (input) => migrateLegacyTerminalAgentIdentity(ports, input),
|
|
29
|
+
exactLifecycleProcessIdentity: (terminal, identity) => exactLifecycleProcessIdentity(terminal, identity),
|
|
30
|
+
codexProcessBirthForLifecycle,
|
|
31
|
+
codexProcessIncarnationForPid,
|
|
32
|
+
observeDurableCompletionBeforeDeadStall: (input) => observeDurableCompletionBeforeDeadStall(ports, input),
|
|
33
|
+
observeBoundTerminalAgentProcess: (input) => observeBoundTerminalAgentProcess(ports, input),
|
|
34
|
+
resolvedTerminalProcessIncarnation,
|
|
35
|
+
managedSessionOwnerIsConclusivelyInactive: (input) => managedSessionOwnerIsConclusivelyInactive(ports, input),
|
|
36
|
+
codexKnownBeforeIdentityForManagedSession,
|
|
37
|
+
codexLingeringBeforeIdentityMatchesSession,
|
|
38
|
+
logicalIdentityForManagedSession,
|
|
39
|
+
codexAllowedCompanionIdentityForManagedSession,
|
|
40
|
+
codexAllowedCompanionSetForManagedSession,
|
|
41
|
+
codexManagedIdentityResolutionContext: (input) => codexManagedIdentityResolutionContext(ports, input),
|
|
42
|
+
codexKnownRootSetForLifecycleTransition: (input) => codexKnownRootSetForLifecycleTransition(ports, input),
|
|
43
|
+
codexPreMaterializationIdentityForManagedSession,
|
|
44
|
+
assertCodexComposerReadyForAutomatedInput: (input) => assertCodexComposerReadyForAutomatedInput(ports, input),
|
|
45
|
+
verifyCodexPendingManagedSendStatus: (input) => verifyCodexPendingManagedSendStatus(ports, input),
|
|
46
|
+
terminalRuntimeForLiveIdentity,
|
|
47
|
+
managedSessionClaimsResolvedTerminal: (session, terminal) => managedSessionClaimsResolvedTerminal(ports, session, terminal),
|
|
48
|
+
bindingMatchesLiveTerminal: (session, terminal, identity, storeDir) => bindingMatchesLiveTerminal(ports, session, terminal, identity, storeDir),
|
|
49
|
+
boundManagedSessionForTerminal: (input) => boundManagedSessionForTerminal(ports, input),
|
|
50
|
+
managedBindingConflictKindForResolvedTerminal: (input) => managedBindingConflictKindForResolvedTerminal(ports, input),
|
|
51
|
+
soleBoundManagedSessionClaimForTerminal: (storeDir, terminal) => soleBoundManagedSessionClaimForTerminal(ports, storeDir, terminal),
|
|
52
|
+
createBoundManagedSession: (input) => createBoundManagedSession(ports, input),
|
|
53
|
+
materializeCurrentManagedSession: (input) => materializeCurrentManagedSession(ports, input),
|
|
54
|
+
reattachManagedSessionForNativeIdentity: (input) => reattachManagedSessionForNativeIdentity(ports, input),
|
|
55
|
+
deferredForegroundAuthorityAdapterPorts: () => deferredForegroundAuthorityAdapterPorts(ports),
|
|
56
|
+
codexCandidateInventoryHasNoOtherManagedClaim: (options) => codexCandidateInventoryHasNoOtherManagedClaim(ports, options),
|
|
57
|
+
deferredCandidateSourceTurnHistory: (storeDir, session) => deferredCandidateSourceTurnHistory(ports, storeDir, session),
|
|
58
|
+
explicitlyAbandonedCandidateSourceFingerprint: (options) => explicitlyAbandonedCandidateSourceFingerprint(ports, options),
|
|
59
|
+
assertFrozenExplicitlyAbandonedPredecessorAuthority: (options) => assertFrozenExplicitlyAbandonedPredecessorAuthority(ports, options),
|
|
60
|
+
deferredCodexForegroundDispatchSnapshot: (control) => deferredCodexForegroundDispatchSnapshot(ports, control),
|
|
61
|
+
deferredCodexPreviousDispatchSnapshotMatches: (options) => deferredCodexPreviousDispatchSnapshotMatches(ports, options),
|
|
62
|
+
observeDeferredCodexAuthority: (options) => observeDeferredCodexAuthority(ports, options),
|
|
63
|
+
assertVerifiedEmptyCodexHandoffBoundary: (input) => assertVerifiedEmptyCodexHandoffBoundary(ports, input)
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
function refineTerminalTurnEndpoint(ports, input) {
|
|
67
|
+
const takeover = isRecord(input.conversation.native_session_takeover)
|
|
68
|
+
? input.conversation.native_session_takeover
|
|
69
|
+
: undefined;
|
|
70
|
+
const storedControl = ports.store.terminalControlFromTakeover(takeover);
|
|
71
|
+
if (!takeover ||
|
|
72
|
+
!storedControl ||
|
|
73
|
+
takeover.terminal_endpoint !== undefined ||
|
|
74
|
+
!hasCanonicalTerminalEndpoint(input.terminalControl)) {
|
|
75
|
+
return input.conversation;
|
|
76
|
+
}
|
|
77
|
+
if (!terminalControlsShareIncarnation(storedControl, input.terminalControl)) {
|
|
78
|
+
throw new Error(`cannot refine Turn ${turnIdForConversation(input.conversation)} terminal ` +
|
|
79
|
+
"endpoint after its terminal incarnation changed");
|
|
80
|
+
}
|
|
81
|
+
const terminalEndpoint = terminalControlEvidence(input.terminalControl);
|
|
82
|
+
associateTerminalEndpointEvidence(storedControl, terminalEndpoint);
|
|
83
|
+
const refined = {
|
|
84
|
+
...input.conversation,
|
|
85
|
+
native_session_takeover: { ...takeover, terminal_endpoint: terminalEndpoint }
|
|
86
|
+
};
|
|
87
|
+
ports.store.saveTurn(input.statePath, refined);
|
|
88
|
+
return refined;
|
|
89
|
+
}
|
|
90
|
+
function terminalRuntimeIdentityForConversation(ports, conversation, terminalControl) {
|
|
91
|
+
const runtime = terminalRuntimeIdentityBase(conversation, terminalControl);
|
|
92
|
+
if (executorForConversation(conversation).kind !== "codex") {
|
|
93
|
+
return runtime;
|
|
94
|
+
}
|
|
95
|
+
const storeDir = ports.store.storeDirForConversation(conversation);
|
|
96
|
+
if (!storeDir) {
|
|
97
|
+
return runtime;
|
|
98
|
+
}
|
|
99
|
+
const managedSession = tryLoadManagedSession(storeDir, sessionIdForConversation(conversation));
|
|
100
|
+
const binding = managedSession?.binding;
|
|
101
|
+
const expectedThreadId = runtime.nativeSessionId ?? runtime.expectedNativeSessionId;
|
|
102
|
+
if (!managedSession || managedSession.agent !== "codex" ||
|
|
103
|
+
managedSession.status !== "bound" || !binding ||
|
|
104
|
+
binding.binding_id !== stringValue(conversation.terminal_binding_id) ||
|
|
105
|
+
binding.generation !== Number(conversation.terminal_binding_generation) ||
|
|
106
|
+
binding.native_thread_id !== expectedThreadId ||
|
|
107
|
+
binding.native_process.pid !== runtime.pid ||
|
|
108
|
+
!terminalControlsShareIncarnation(binding.terminal_control, terminalControl)) {
|
|
109
|
+
return runtime;
|
|
110
|
+
}
|
|
111
|
+
return withCodexCompanionFences(runtime, codexAllowedCompanionSetForManagedSession({ storeDir, session: managedSession }));
|
|
112
|
+
}
|
|
113
|
+
function terminalDurableRequestForConversation(ports, conversation, terminalControl) {
|
|
114
|
+
const nativeTakeover = isRecord(conversation.native_session_takeover)
|
|
115
|
+
? conversation.native_session_takeover
|
|
116
|
+
: undefined;
|
|
117
|
+
const runtime = terminalRuntimeIdentityForConversation(ports, conversation, terminalControl);
|
|
118
|
+
return {
|
|
119
|
+
sessionId: runtime.sessionId,
|
|
120
|
+
cwd: stringValue(nativeTakeover?.source_cwd),
|
|
121
|
+
requestText: String(nativeTakeover?.terminal_bridge_request_text ?? conversation.user_request ?? ""),
|
|
122
|
+
requestHash: stringValue(nativeTakeover?.terminal_bridge_request_hash),
|
|
123
|
+
startedAt: stringValue(nativeTakeover?.terminal_bridge_started_at),
|
|
124
|
+
context: { conversation, nativeTakeover, ...runtime }
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
async function migrateLegacyTerminalAgentIdentity(ports, input) {
|
|
128
|
+
const nativeTakeover = isRecord(input.conversation.native_session_takeover)
|
|
129
|
+
? input.conversation.native_session_takeover
|
|
130
|
+
: undefined;
|
|
131
|
+
const terminalControl = ports.store.terminalControlFromTakeover(nativeTakeover);
|
|
132
|
+
if (!nativeTakeover || !terminalControl || hasRuntimePid(terminalRuntimeIdentityForConversation(ports, input.conversation, terminalControl))) {
|
|
133
|
+
return input.conversation;
|
|
134
|
+
}
|
|
135
|
+
const nativeSessionId = stringValue(nativeTakeover.native_session_id);
|
|
136
|
+
if (executorForConversation(input.conversation).kind !== "codex" ||
|
|
137
|
+
!nativeSessionId || parseTerminalConversationId(nativeSessionId)) {
|
|
138
|
+
return input.conversation;
|
|
139
|
+
}
|
|
140
|
+
const matchedProcess = await observeLegacyTerminalAgentProcess(ports, input, terminalControl, nativeSessionId);
|
|
141
|
+
if (!matchedProcess) {
|
|
142
|
+
return input.conversation;
|
|
143
|
+
}
|
|
144
|
+
const result = persistLegacyTerminalAgentIdentity(ports, input, terminalControl, nativeSessionId, matchedProcess);
|
|
145
|
+
if (result.migrated) {
|
|
146
|
+
reportLegacyTerminalAgentIdentityMigration(ports, input.logPath, result.conversation, terminalControl, nativeSessionId, matchedProcess);
|
|
147
|
+
}
|
|
148
|
+
return result.conversation;
|
|
149
|
+
}
|
|
150
|
+
function hasRuntimePid(runtime) {
|
|
151
|
+
return Number.isInteger(runtime.pid) && Number(runtime.pid) > 0;
|
|
152
|
+
}
|
|
153
|
+
async function observeLegacyTerminalAgentProcess(ports, input, terminalControl, nativeSessionId) {
|
|
154
|
+
try {
|
|
155
|
+
const adapter = ports.runtime.createAgentRegistry(input.options).require("codex");
|
|
156
|
+
const snapshots = await ports.runtime.createProcessSource(input.options)
|
|
157
|
+
.listProcessSnapshots((snapshot) => adapter.classifyProcess(snapshot) !== undefined, { includeAncestors: true });
|
|
158
|
+
const terminalProvider = ports.runtime.createControlProvider(input.options);
|
|
159
|
+
const resolvedTerminal = await terminalProvider.resolve(terminalProvider.endpoint(terminalControl));
|
|
160
|
+
const candidates = snapshots.flatMap((snapshot) => {
|
|
161
|
+
const classified = adapter.classifyProcess(snapshot);
|
|
162
|
+
return classified ? [{ ...classified, agent: "codex" }] : [];
|
|
163
|
+
});
|
|
164
|
+
const matches = candidates.filter((candidate) => candidate.sessionId === nativeSessionId &&
|
|
165
|
+
terminalProvider.containsProcess(resolvedTerminal, candidate, snapshots));
|
|
166
|
+
return matches.length === 1 ? matches[0] : undefined;
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
cliRuntimeLog("warn", "legacy_terminal_agent_identity_migration_failed", {
|
|
170
|
+
conversation_id: input.conversation.conversation_id,
|
|
171
|
+
terminal_target: terminalControl.target,
|
|
172
|
+
reason: error instanceof Error ? error.message : String(error)
|
|
173
|
+
});
|
|
174
|
+
return undefined;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
function persistLegacyTerminalAgentIdentity(ports, input, terminalControl, nativeSessionId, matchedProcess) {
|
|
178
|
+
const releaseLock = ports.store.acquireStateLock(input.statePath);
|
|
179
|
+
try {
|
|
180
|
+
const current = ports.store.loadTurn(input.statePath);
|
|
181
|
+
const currentTakeover = isRecord(current.native_session_takeover)
|
|
182
|
+
? current.native_session_takeover
|
|
183
|
+
: undefined;
|
|
184
|
+
const currentControl = ports.store.terminalControlFromTakeover(currentTakeover);
|
|
185
|
+
if (!currentTakeover || !currentControl || hasRuntimePid(terminalRuntimeIdentityForConversation(ports, current, currentControl)) || currentTakeover.native_session_id !== nativeSessionId ||
|
|
186
|
+
!terminalControlsShareIncarnation(currentControl, terminalControl)) {
|
|
187
|
+
return { conversation: current, migrated: false };
|
|
188
|
+
}
|
|
189
|
+
const migratedAt = ports.environment.now().toISOString();
|
|
190
|
+
const conversation = {
|
|
191
|
+
...current,
|
|
192
|
+
native_session_takeover: {
|
|
193
|
+
...currentTakeover,
|
|
194
|
+
terminal_agent_pid: matchedProcess.pid,
|
|
195
|
+
terminal_agent_session_id: matchedProcess.sessionId,
|
|
196
|
+
terminal_agent_identity_migrated_at: migratedAt
|
|
197
|
+
},
|
|
198
|
+
updated_at: migratedAt
|
|
199
|
+
};
|
|
200
|
+
ports.store.saveTurn(input.statePath, conversation);
|
|
201
|
+
return { conversation, migrated: true };
|
|
202
|
+
}
|
|
203
|
+
finally {
|
|
204
|
+
releaseLock();
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
function reportLegacyTerminalAgentIdentityMigration(ports, logPath, conversation, terminalControl, nativeSessionId, matchedProcess) {
|
|
208
|
+
ports.store.appendEvent(logPath, {
|
|
209
|
+
ts: ports.environment.now().toISOString(),
|
|
210
|
+
conversation_id: conversation.conversation_id,
|
|
211
|
+
event: "terminal_agent_identity_migrated",
|
|
212
|
+
terminal_target: terminalControl.target,
|
|
213
|
+
terminal_agent_pid: matchedProcess.pid,
|
|
214
|
+
native_session_id: nativeSessionId
|
|
215
|
+
});
|
|
216
|
+
cliRuntimeLog("info", "terminal_agent_identity_migrated", {
|
|
217
|
+
conversation_id: conversation.conversation_id,
|
|
218
|
+
terminal_target: terminalControl.target,
|
|
219
|
+
terminal_agent_pid: matchedProcess.pid
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
async function resolveTerminalConversationFromOptions(ports, options) {
|
|
223
|
+
return ports.runtime.createBridge(options).resolveConversationId(stringValue(options.session ??
|
|
224
|
+
options.turn ??
|
|
225
|
+
options.conversation ??
|
|
226
|
+
options.conversationId));
|
|
227
|
+
}
|
|
228
|
+
function exactLifecycleProcessIdentity(terminal, identity) {
|
|
229
|
+
const codexIncarnation = terminal.agent === "codex" &&
|
|
230
|
+
identity.processBirth === undefined
|
|
231
|
+
? codexProcessIncarnationForPid(terminal.pid)
|
|
232
|
+
: undefined;
|
|
233
|
+
return exactLifecycleIdentity({
|
|
234
|
+
agent: terminal.agent,
|
|
235
|
+
pid: terminal.pid,
|
|
236
|
+
identity,
|
|
237
|
+
codexIncarnation
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
function codexProcessBirthForLifecycle(pid) {
|
|
241
|
+
const injected = cliDependencies().codexProcessBirthForPid;
|
|
242
|
+
if (injected) {
|
|
243
|
+
return injected(pid);
|
|
244
|
+
}
|
|
245
|
+
const ps = resolveOptionalExecutable("ps");
|
|
246
|
+
if (!ps) {
|
|
247
|
+
throw new Error("cannot verify Codex process incarnation because ps is unavailable");
|
|
248
|
+
}
|
|
249
|
+
const result = spawnSync(ps, ["-o", "lstart=", "-p", String(pid)], {
|
|
250
|
+
encoding: "utf8",
|
|
251
|
+
timeout: 5000,
|
|
252
|
+
maxBuffer: 1024 * 1024
|
|
253
|
+
});
|
|
254
|
+
const processBirth = String(result.stdout ?? "").trim();
|
|
255
|
+
if (result.error || result.status !== 0 || !processBirth) {
|
|
256
|
+
throw new Error(String(result.stderr ?? "").trim() ||
|
|
257
|
+
result.error?.message ||
|
|
258
|
+
`cannot verify Codex process incarnation for pid ${pid}`);
|
|
259
|
+
}
|
|
260
|
+
return processBirth;
|
|
261
|
+
}
|
|
262
|
+
function codexProcessIncarnationForPid(pid) {
|
|
263
|
+
const processBirth = codexProcessBirthForLifecycle(pid);
|
|
264
|
+
return {
|
|
265
|
+
processUuid: `codex-pid:${pid}:birth:${processBirth}`,
|
|
266
|
+
processBirth,
|
|
267
|
+
evidence: "codex_process_birth"
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
function codexCompletionIdentity(runtime) {
|
|
271
|
+
const rollout = isRecord(runtime.nativeRollout)
|
|
272
|
+
? runtime.nativeRollout
|
|
273
|
+
: undefined;
|
|
274
|
+
return {
|
|
275
|
+
sessionId: runtime.nativeSessionId ?? runtime.sessionId ?? "",
|
|
276
|
+
processUuid: runtime.nativeProcessUuid,
|
|
277
|
+
processBirth: runtime.nativeProcessBirth,
|
|
278
|
+
...(rollout ? { rollout: {
|
|
279
|
+
fd: String(rollout.fd ?? ""), device: String(rollout.device ?? ""),
|
|
280
|
+
inode: String(rollout.inode ?? ""), path: String(rollout.path ?? "")
|
|
281
|
+
} } : {})
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
function observeExactCodexDeadProcessRolloutCompletion(ports, input) {
|
|
285
|
+
const { conversation, terminalControl } = input;
|
|
286
|
+
const takeover = isRecord(conversation.native_session_takeover)
|
|
287
|
+
? conversation.native_session_takeover
|
|
288
|
+
: undefined;
|
|
289
|
+
const submission = terminalBridgeSubmission(conversation);
|
|
290
|
+
const acceptanceEvidence = isRecord(submission?.acceptance_evidence)
|
|
291
|
+
? submission.acceptance_evidence
|
|
292
|
+
: undefined;
|
|
293
|
+
const anchor = isRecord(takeover?.codex_rollout_acceptance_anchor)
|
|
294
|
+
? takeover.codex_rollout_acceptance_anchor
|
|
295
|
+
: undefined;
|
|
296
|
+
if (submission?.status !== "agent_accepted" ||
|
|
297
|
+
!ports.completion.requiresExactBoundCodexCompletion(conversation) ||
|
|
298
|
+
acceptanceEvidence?.source !== "codex_rollout" ||
|
|
299
|
+
!anchor) {
|
|
300
|
+
return {
|
|
301
|
+
status: "unverifiable",
|
|
302
|
+
reason: "Codex has no exact accepted rollout authority for the dead process"
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
const runtime = terminalRuntimeIdentityForConversation(ports, conversation, terminalControl);
|
|
306
|
+
const request = terminalDurableRequestForConversation(ports, conversation, terminalControl);
|
|
307
|
+
const result = detectCodexBoundRolloutCompletion({
|
|
308
|
+
anchor: anchor,
|
|
309
|
+
acceptanceEvidence: acceptanceEvidence,
|
|
310
|
+
currentIdentity: codexCompletionIdentity(runtime),
|
|
311
|
+
requestHash: stringValue(request.requestHash) ??
|
|
312
|
+
stringValue(takeover?.terminal_bridge_request_hash) ??
|
|
313
|
+
""
|
|
314
|
+
});
|
|
315
|
+
if (result.status === "completed") {
|
|
316
|
+
return {
|
|
317
|
+
status: "present",
|
|
318
|
+
completion: {
|
|
319
|
+
...result.completion,
|
|
320
|
+
metadata: {
|
|
321
|
+
...result.completion.metadata,
|
|
322
|
+
context_match: "exact_bound_rollout",
|
|
323
|
+
detector_code: result.diagnostics.code
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
if (result.status === "pending" &&
|
|
329
|
+
result.diagnostics.code === "exact_turn_not_complete") {
|
|
330
|
+
return { status: "absent" };
|
|
331
|
+
}
|
|
332
|
+
return {
|
|
333
|
+
status: "unverifiable",
|
|
334
|
+
reason: `[codex_exact_bound_rollout:${result.diagnostics.code}] ` +
|
|
335
|
+
(result.diagnostics.detail ??
|
|
336
|
+
"the exact bound rollout is not safely inspectable")
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
async function observeDurableCompletionBeforeDeadStall(ports, input) {
|
|
340
|
+
const { options, conversation, terminalControl } = input;
|
|
341
|
+
try {
|
|
342
|
+
const executor = executorForConversation(conversation);
|
|
343
|
+
const takeover = isRecord(conversation.native_session_takeover)
|
|
344
|
+
? conversation.native_session_takeover
|
|
345
|
+
: undefined;
|
|
346
|
+
if (executor.kind === "claude") {
|
|
347
|
+
const anchor = isRecord(takeover?.claude_transcript_anchor)
|
|
348
|
+
? takeover.claude_transcript_anchor
|
|
349
|
+
: undefined;
|
|
350
|
+
const pid = Number(anchor?.pid);
|
|
351
|
+
const startedAt = Number(anchor?.agent_started_at_ms);
|
|
352
|
+
const sessionId = stringValue(anchor?.session_id);
|
|
353
|
+
const cwd = stringValue(anchor?.cwd);
|
|
354
|
+
if (!Number.isSafeInteger(pid) ||
|
|
355
|
+
pid <= 1 ||
|
|
356
|
+
!Number.isSafeInteger(startedAt) ||
|
|
357
|
+
startedAt <= 0 ||
|
|
358
|
+
!sessionId ||
|
|
359
|
+
!cwd ||
|
|
360
|
+
pid !== Number(takeover?.terminal_agent_pid) ||
|
|
361
|
+
sessionId !== stringValue(takeover?.terminal_agent_session_id) ||
|
|
362
|
+
path.resolve(cwd) !== path.resolve(conversation.workspace)) {
|
|
363
|
+
return {
|
|
364
|
+
status: "unverifiable",
|
|
365
|
+
reason: "Claude has no exact immutable transcript anchor for the dead process"
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
const processUuid = stringValue(takeover?.terminal_agent_process_uuid);
|
|
369
|
+
if (processUuid !== `claude-pid:${pid}:started:${startedAt}`) {
|
|
370
|
+
return {
|
|
371
|
+
status: "unverifiable",
|
|
372
|
+
reason: "Claude transcript anchor does not match the exact process incarnation"
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
const submission = terminalBridgeSubmission(conversation);
|
|
376
|
+
return observeClaudeDeadProcessTranscriptCompletion(terminalDurableRequestForConversation(ports, conversation, terminalControl), {
|
|
377
|
+
claudeHome: expandHome(options.claudeHome),
|
|
378
|
+
agentRows: [{
|
|
379
|
+
pid,
|
|
380
|
+
startedAt,
|
|
381
|
+
sessionId,
|
|
382
|
+
cwd,
|
|
383
|
+
kind: "interactive",
|
|
384
|
+
status: "idle"
|
|
385
|
+
}],
|
|
386
|
+
acceptanceEvidence: submission?.acceptance_evidence
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
return observeExactCodexDeadProcessRolloutCompletion(ports, input);
|
|
390
|
+
}
|
|
391
|
+
catch (error) {
|
|
392
|
+
return {
|
|
393
|
+
status: "unverifiable",
|
|
394
|
+
reason: error instanceof Error ? error.message : String(error)
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
function hasExactBoundProcessEnvelope(context) {
|
|
399
|
+
return context.session.status === "bound" &&
|
|
400
|
+
context.binding !== undefined &&
|
|
401
|
+
Number.isSafeInteger(context.pid) && context.pid > 1 &&
|
|
402
|
+
context.bindingId !== undefined &&
|
|
403
|
+
Number.isSafeInteger(context.bindingGeneration) &&
|
|
404
|
+
context.bindingGeneration >= 1 &&
|
|
405
|
+
context.processUuid !== undefined &&
|
|
406
|
+
context.messageId !== undefined &&
|
|
407
|
+
hasCanonicalTerminalEndpoint(context.terminalControl);
|
|
408
|
+
}
|
|
409
|
+
function exactBoundProcessBindingMatches(context) {
|
|
410
|
+
const { binding, conversation, session, takeover } = context;
|
|
411
|
+
return binding.native_process.pid === context.pid &&
|
|
412
|
+
terminalControlsShareIncarnation(binding.terminal_control, context.terminalControl) &&
|
|
413
|
+
binding.binding_id === context.bindingId &&
|
|
414
|
+
binding.generation === context.bindingGeneration &&
|
|
415
|
+
session.agent === executorForConversation(conversation).kind &&
|
|
416
|
+
path.resolve(session.workspace) === path.resolve(conversation.workspace) &&
|
|
417
|
+
binding.native_thread_id === (stringValue(conversation.native_thread_id) ??
|
|
418
|
+
stringValue(takeover?.terminal_agent_session_id)) &&
|
|
419
|
+
binding.native_process.process_uuid === context.processUuid &&
|
|
420
|
+
stringValue(takeover?.terminal_binding_id) === context.bindingId &&
|
|
421
|
+
Number(takeover?.terminal_binding_generation) ===
|
|
422
|
+
context.bindingGeneration &&
|
|
423
|
+
(binding.native_process.process_birth ?? undefined) ===
|
|
424
|
+
context.processBirth;
|
|
425
|
+
}
|
|
426
|
+
function exactBoundProcessSubmissionMatches(context) {
|
|
427
|
+
const { conversation, submission } = context;
|
|
428
|
+
return submission?.status === "agent_accepted" &&
|
|
429
|
+
stringValue(submission.session_id) ===
|
|
430
|
+
sessionIdForConversation(conversation) &&
|
|
431
|
+
stringValue(submission.turn_id) === turnIdForConversation(conversation) &&
|
|
432
|
+
stringValue(submission.message_id) === context.messageId &&
|
|
433
|
+
stringValue(submission.binding_id) === context.bindingId &&
|
|
434
|
+
Number(submission.binding_generation) === context.bindingGeneration;
|
|
435
|
+
}
|
|
436
|
+
async function observeBoundTerminalAgentProcess(ports, input) {
|
|
437
|
+
const { options, conversation, terminalControl } = input;
|
|
438
|
+
try {
|
|
439
|
+
ports.authority.assertTurnBindingCurrent(conversation, "verify the bound agent process for");
|
|
440
|
+
const storeDir = ports.store.storeDirForConversation(conversation);
|
|
441
|
+
if (!storeDir) {
|
|
442
|
+
return {
|
|
443
|
+
status: "unverifiable",
|
|
444
|
+
reason: "the managed Store is unavailable"
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
const session = loadManagedSession(storeDir, sessionIdForConversation(conversation));
|
|
448
|
+
const binding = session.binding;
|
|
449
|
+
const takeover = isRecord(conversation.native_session_takeover)
|
|
450
|
+
? conversation.native_session_takeover
|
|
451
|
+
: undefined;
|
|
452
|
+
const pid = Number(takeover?.terminal_agent_pid);
|
|
453
|
+
const bindingId = stringValue(conversation.terminal_binding_id);
|
|
454
|
+
const bindingGeneration = Number(conversation.terminal_binding_generation);
|
|
455
|
+
const processUuid = stringValue(takeover?.terminal_agent_process_uuid);
|
|
456
|
+
const processBirth = stringValue(takeover?.terminal_agent_process_birth);
|
|
457
|
+
const messageId = stringValue(takeover?.terminal_bridge_message_id);
|
|
458
|
+
const submission = terminalBridgeSubmission(conversation);
|
|
459
|
+
const context = {
|
|
460
|
+
session, conversation, terminalControl, binding, takeover, submission,
|
|
461
|
+
pid, bindingId, bindingGeneration, processUuid, processBirth, messageId
|
|
462
|
+
};
|
|
463
|
+
if (!hasExactBoundProcessEnvelope(context) ||
|
|
464
|
+
!exactBoundProcessBindingMatches(context) ||
|
|
465
|
+
!exactBoundProcessSubmissionMatches(context)) {
|
|
466
|
+
return {
|
|
467
|
+
status: "unverifiable",
|
|
468
|
+
reason: "the Turn and Session no longer share one exact process binding"
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
const processSource = ports.runtime.createProcessSource(options);
|
|
472
|
+
if (processSource.completeInventoryAuthority !== true) {
|
|
473
|
+
return {
|
|
474
|
+
status: "unverifiable",
|
|
475
|
+
reason: "the configured process source is not complete process-death authority"
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
const snapshots = await processSource.listProcessSnapshots((snapshot) => snapshot.pid === pid, { includeCwd: false, includeAncestors: false });
|
|
479
|
+
const exact = snapshots.filter((snapshot) => snapshot.pid === pid);
|
|
480
|
+
if (exact.length === 1) {
|
|
481
|
+
return { status: "alive", pid };
|
|
482
|
+
}
|
|
483
|
+
if (exact.length !== 0) {
|
|
484
|
+
return {
|
|
485
|
+
status: "unverifiable",
|
|
486
|
+
reason: `the process inventory returned ${exact.length} rows for pid ${pid}`
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
return {
|
|
490
|
+
status: "verified_dead",
|
|
491
|
+
proof: {
|
|
492
|
+
kind: "exact_pid_absent_from_complete_process_inventory",
|
|
493
|
+
agent: session.agent,
|
|
494
|
+
pid,
|
|
495
|
+
process_uuid: context.processUuid,
|
|
496
|
+
process_birth: context.binding.native_process.process_birth,
|
|
497
|
+
conversation_id: conversation.conversation_id,
|
|
498
|
+
session_id: sessionIdForConversation(conversation),
|
|
499
|
+
turn_id: turnIdForConversation(conversation),
|
|
500
|
+
terminal_control: terminalControl,
|
|
501
|
+
terminal_endpoint: terminalControlEvidence(terminalControl),
|
|
502
|
+
binding_id: context.binding.binding_id,
|
|
503
|
+
binding_generation: context.binding.generation,
|
|
504
|
+
message_id: context.messageId,
|
|
505
|
+
observed_at: ports.environment.now().toISOString()
|
|
506
|
+
}
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
catch (error) {
|
|
510
|
+
return {
|
|
511
|
+
status: "unverifiable",
|
|
512
|
+
reason: error instanceof Error ? error.message : String(error)
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
function resolvedTerminalProcessIncarnation(terminal, identity) {
|
|
517
|
+
return resolvedTerminalProcessIncarnationPolicy({
|
|
518
|
+
terminal,
|
|
519
|
+
identity,
|
|
520
|
+
codexProcessIncarnation: codexProcessIncarnationForPid
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
function managedSessionOwnerIsConclusivelyInactive(ports, input) {
|
|
524
|
+
return managedSessionOwnerIsInactive({
|
|
525
|
+
...input,
|
|
526
|
+
isProcessAlive: ports.environment.isProcessAlive,
|
|
527
|
+
codexProcessIncarnation: codexProcessIncarnationForPid
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
function codexKnownBeforeIdentityForManagedSession(input) {
|
|
531
|
+
let transition;
|
|
532
|
+
try {
|
|
533
|
+
transition = input.session.last_transition_id
|
|
534
|
+
? loadNativeThreadTransition(input.storeDir, input.session.last_transition_id)
|
|
535
|
+
: undefined;
|
|
536
|
+
}
|
|
537
|
+
catch {
|
|
538
|
+
return undefined;
|
|
539
|
+
}
|
|
540
|
+
return codexKnownBeforeIdentityForTransition({
|
|
541
|
+
session: input.session,
|
|
542
|
+
transition,
|
|
543
|
+
requireNewThread: input.requireNewThread ?? false
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
function codexLingeringBeforeIdentityMatchesSession(input) {
|
|
547
|
+
if (!codexKnownBeforeIdentityForManagedSession({
|
|
548
|
+
storeDir: input.storeDir,
|
|
549
|
+
session: input.session,
|
|
550
|
+
requireNewThread: true
|
|
551
|
+
})) {
|
|
552
|
+
return false;
|
|
553
|
+
}
|
|
554
|
+
let transition;
|
|
555
|
+
try {
|
|
556
|
+
transition = loadNativeThreadTransition(input.storeDir, input.session.last_transition_id);
|
|
557
|
+
}
|
|
558
|
+
catch {
|
|
559
|
+
return false;
|
|
560
|
+
}
|
|
561
|
+
return codexLingeringIdentityMatches({
|
|
562
|
+
session: input.session,
|
|
563
|
+
identity: input.identity,
|
|
564
|
+
transition,
|
|
565
|
+
companions: codexAllowedCompanionSetForManagedSession({
|
|
566
|
+
storeDir: input.storeDir,
|
|
567
|
+
session: input.session
|
|
568
|
+
})
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
function logicalIdentityForManagedSession(input) {
|
|
572
|
+
return logicalManagedSessionIdentity({
|
|
573
|
+
session: input.session,
|
|
574
|
+
observedIdentity: input.observedIdentity,
|
|
575
|
+
lingeringBeforeMatches: Boolean(input.observedIdentity &&
|
|
576
|
+
codexLingeringBeforeIdentityMatchesSession({
|
|
577
|
+
storeDir: input.storeDir,
|
|
578
|
+
session: input.session,
|
|
579
|
+
identity: input.observedIdentity
|
|
580
|
+
}))
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
function codexAllowedCompanionIdentityForManagedSession(input) {
|
|
584
|
+
return codexIdentityFence(codexKnownBeforeIdentityForManagedSession(input));
|
|
585
|
+
}
|
|
586
|
+
function codexAllowedCompanionSetForManagedSession(input) {
|
|
587
|
+
const primary = codexAllowedCompanionIdentityForManagedSession(input);
|
|
588
|
+
const binding = input.session.binding;
|
|
589
|
+
if (!binding) {
|
|
590
|
+
return { additional: [] };
|
|
591
|
+
}
|
|
592
|
+
const candidates = listManagedSessions(input.storeDir).flatMap((candidate) => {
|
|
593
|
+
const candidateBinding = candidate.binding;
|
|
594
|
+
if (candidate.session_id === input.session.session_id ||
|
|
595
|
+
candidate.agent !== "codex" ||
|
|
596
|
+
candidate.status !== "detached" ||
|
|
597
|
+
!candidateBinding?.native_thread_id ||
|
|
598
|
+
candidateBinding.native_process.pid !== binding.native_process.pid ||
|
|
599
|
+
candidateBinding.native_process.process_uuid !==
|
|
600
|
+
binding.native_process.process_uuid ||
|
|
601
|
+
candidateBinding.native_process.process_birth !==
|
|
602
|
+
binding.native_process.process_birth ||
|
|
603
|
+
!terminalControlAliasMatches(candidateBinding.terminal_id, candidateBinding.terminal_control, binding.terminal_id, binding.terminal_control) ||
|
|
604
|
+
path.resolve(candidate.workspace) !==
|
|
605
|
+
path.resolve(input.session.workspace) ||
|
|
606
|
+
!candidateBinding.native_process.process_uuid ||
|
|
607
|
+
!candidateBinding.native_process.process_birth ||
|
|
608
|
+
!isCompleteNativeRollout(candidateBinding.native_process.rollout)) {
|
|
609
|
+
return [];
|
|
610
|
+
}
|
|
611
|
+
return [{
|
|
612
|
+
sessionId: candidateBinding.native_thread_id,
|
|
613
|
+
processUuid: candidateBinding.native_process.process_uuid,
|
|
614
|
+
processBirth: candidateBinding.native_process.process_birth,
|
|
615
|
+
rollout: candidateBinding.native_process.rollout
|
|
616
|
+
}];
|
|
617
|
+
});
|
|
618
|
+
return codexCompanionSet({ primary, candidates });
|
|
619
|
+
}
|
|
620
|
+
function codexManagedIdentityResolutionContext(ports, input) {
|
|
621
|
+
const claimedSession = soleBoundManagedSessionClaimForTerminal(ports, input.storeDir, input.terminal);
|
|
622
|
+
const companions = claimedSession
|
|
623
|
+
? codexAllowedCompanionSetForManagedSession({
|
|
624
|
+
storeDir: input.storeDir,
|
|
625
|
+
session: claimedSession
|
|
626
|
+
})
|
|
627
|
+
: { additional: [] };
|
|
628
|
+
return {
|
|
629
|
+
claimedSession,
|
|
630
|
+
companions,
|
|
631
|
+
preferredSessionId: companions.primary
|
|
632
|
+
? claimedSession?.binding?.native_thread_id
|
|
633
|
+
: undefined
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
function codexKnownRootSetForLifecycleTransition(ports, input) {
|
|
637
|
+
return knownNativeThreadCompanionSet({
|
|
638
|
+
terminal: input.terminal,
|
|
639
|
+
transition: input.transition,
|
|
640
|
+
managedSessions: listManagedSessions(input.storeDir)
|
|
641
|
+
}, {
|
|
642
|
+
terminalAliasMatches: terminalControlAliasMatches,
|
|
643
|
+
workspaceMatches: ports.environment.workspaceMatches
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
function codexPreMaterializationIdentityForManagedSession(input) {
|
|
647
|
+
if (!codexLingeringBeforeIdentityMatchesSession({
|
|
648
|
+
storeDir: input.storeDir,
|
|
649
|
+
session: input.session,
|
|
650
|
+
identity: input.observedIdentity
|
|
651
|
+
})) {
|
|
652
|
+
return undefined;
|
|
653
|
+
}
|
|
654
|
+
return codexIdentityFence(input.observedIdentity);
|
|
655
|
+
}
|
|
656
|
+
async function assertCodexComposerReadyForAutomatedInput(ports, input) {
|
|
657
|
+
const provider = ports.runtime.createControlProvider(input.options);
|
|
658
|
+
const resolvedTerminal = await provider.resolve(provider.endpoint(input.terminalControl));
|
|
659
|
+
const styledScreen = await provider.capture(resolvedTerminal, { scrollbackLines: 40, preserveEscapes: true });
|
|
660
|
+
if (exactCodexReadyStyledComposerCapture(styledScreen) === undefined) {
|
|
661
|
+
throw new Error("Codex composer contains non-placeholder input; refusing automated terminal input");
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
async function verifyCodexPendingManagedSendStatus(ports, input) {
|
|
665
|
+
const binding = input.session.binding;
|
|
666
|
+
if (input.terminal.agent !== "codex" ||
|
|
667
|
+
!binding?.native_thread_id ||
|
|
668
|
+
(!input.allowedPreMaterializationIdentity &&
|
|
669
|
+
(binding.native_process.rollout ||
|
|
670
|
+
!isCodexStatusCardEvidence(binding.native_process.evidence)))) {
|
|
671
|
+
return;
|
|
672
|
+
}
|
|
673
|
+
if (input.logicalIdentity?.sessionId !== binding.native_thread_id) {
|
|
674
|
+
throw new Error("the pending Codex Session has no exact logical native-thread identity");
|
|
675
|
+
}
|
|
676
|
+
const runtime = {
|
|
677
|
+
...terminalRuntimeForLiveIdentity({
|
|
678
|
+
terminal: input.terminal,
|
|
679
|
+
expectedEmptyNativeSession: true
|
|
680
|
+
}),
|
|
681
|
+
...(binding.native_process.process_uuid
|
|
682
|
+
? { nativeProcessUuid: binding.native_process.process_uuid }
|
|
683
|
+
: {}),
|
|
684
|
+
...(binding.native_process.process_birth
|
|
685
|
+
? { nativeProcessBirth: binding.native_process.process_birth }
|
|
686
|
+
: {}),
|
|
687
|
+
...(binding.native_process.rollout
|
|
688
|
+
? { nativeRollout: binding.native_process.rollout }
|
|
689
|
+
: {}),
|
|
690
|
+
expectedNativeSessionId: binding.native_thread_id,
|
|
691
|
+
allowedPreMaterializationNativeIdentity: input.allowedPreMaterializationIdentity,
|
|
692
|
+
allowedAdditionalNativeIdentities: input.allowedAdditionalIdentities ?? []
|
|
693
|
+
};
|
|
694
|
+
const bridge = ports.runtime.createBridge(input.options);
|
|
695
|
+
const initialStatus = await bridge.status(input.terminal.agent, input.terminal.terminalControl, { runtime });
|
|
696
|
+
ports.authority.assertSafeTerminalSend(input.terminal.agent, initialStatus);
|
|
697
|
+
if (!codexComposerVisible(initialStatus.screen.excerpt)) {
|
|
698
|
+
throw new Error("Codex status-card verification requires a visible idle composer");
|
|
699
|
+
}
|
|
700
|
+
const observed = await ports.runtime.probeCodexCurrentThread({
|
|
701
|
+
options: input.options,
|
|
702
|
+
terminal: input.terminal,
|
|
703
|
+
currentIdentity: input.logicalIdentity,
|
|
704
|
+
runtimeOverride: runtime
|
|
705
|
+
});
|
|
706
|
+
if (observed.sessionId !== binding.native_thread_id) {
|
|
707
|
+
throw new Error(`Codex /status reports native thread ${observed.sessionId}, but managed ` +
|
|
708
|
+
`Session ${input.session.session_id} is bound to ${binding.native_thread_id}`);
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
function terminalRuntimeForLiveIdentity(input) {
|
|
712
|
+
return terminalRuntimeForLiveIdentityPolicy({
|
|
713
|
+
...input,
|
|
714
|
+
codexProcessIncarnation: codexProcessIncarnationForPid
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
function managedSessionClaimsResolvedTerminal(ports, session, terminal) {
|
|
718
|
+
const binding = session.binding;
|
|
719
|
+
return session.status === "bound" && binding !== undefined &&
|
|
720
|
+
session.agent === terminal.agent &&
|
|
721
|
+
binding.native_process.pid === terminal.pid &&
|
|
722
|
+
terminalControlAliasMatches(binding.terminal_id, binding.terminal_control, terminal.conversationId, terminal.terminalControl) && ports.environment.workspaceMatches(session.workspace, terminal.terminalControl.currentPath);
|
|
723
|
+
}
|
|
724
|
+
function bindingMatchesLiveTerminal(ports, session, terminal, identity, storeDir) {
|
|
725
|
+
if (!managedSessionClaimsResolvedTerminal(ports, session, terminal)) {
|
|
726
|
+
return false;
|
|
727
|
+
}
|
|
728
|
+
let processIncarnation = {
|
|
729
|
+
processUuid: identity?.processUuid,
|
|
730
|
+
processBirth: identity?.processBirth
|
|
731
|
+
};
|
|
732
|
+
if (!identity) {
|
|
733
|
+
const binding = session.binding;
|
|
734
|
+
if (terminal.agent !== "codex" ||
|
|
735
|
+
!binding?.native_thread_id ||
|
|
736
|
+
!isCodexStatusCardEvidence(binding.native_process.evidence) ||
|
|
737
|
+
!binding.native_process.process_uuid ||
|
|
738
|
+
!binding.native_process.process_birth) {
|
|
739
|
+
return false;
|
|
740
|
+
}
|
|
741
|
+
try {
|
|
742
|
+
processIncarnation = codexProcessIncarnationForPid(terminal.pid);
|
|
743
|
+
}
|
|
744
|
+
catch {
|
|
745
|
+
return false;
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
return managedBindingMatchesLiveTerminal({
|
|
749
|
+
session,
|
|
750
|
+
terminal,
|
|
751
|
+
identity,
|
|
752
|
+
processIncarnation,
|
|
753
|
+
claimMatches: true,
|
|
754
|
+
codexLingeringBeforeMatches: () => codexLingeringBeforeIdentityMatchesSession({
|
|
755
|
+
storeDir,
|
|
756
|
+
session,
|
|
757
|
+
identity
|
|
758
|
+
})
|
|
759
|
+
});
|
|
760
|
+
}
|
|
761
|
+
function boundManagedSessionForTerminal(ports, input) {
|
|
762
|
+
return selectBoundManagedSessionForTerminal({
|
|
763
|
+
sessions: listManagedSessions(input.storeDir),
|
|
764
|
+
agent: input.terminal.agent,
|
|
765
|
+
pid: input.terminal.pid,
|
|
766
|
+
terminalTarget: input.terminal.terminalControl.target,
|
|
767
|
+
aliasMatches: (session) => Boolean(session.binding &&
|
|
768
|
+
terminalControlAliasMatches(session.binding.terminal_id, session.binding.terminal_control, input.terminal.conversationId, input.terminal.terminalControl)),
|
|
769
|
+
exactMatches: (session) => bindingMatchesLiveTerminal(ports, session, input.terminal, input.identity, input.storeDir),
|
|
770
|
+
sameIncarnation: (session) => Boolean(session.binding &&
|
|
771
|
+
terminalControlsShareIncarnation(session.binding.terminal_control, input.terminal.terminalControl)),
|
|
772
|
+
ownerIsInactive: (session) => managedSessionOwnerIsConclusivelyInactive(ports, {
|
|
773
|
+
session,
|
|
774
|
+
terminal: input.terminal,
|
|
775
|
+
identity: input.identity
|
|
776
|
+
})
|
|
777
|
+
});
|
|
778
|
+
}
|
|
779
|
+
function managedBindingConflictKindForResolvedTerminal(ports, input) {
|
|
780
|
+
if (!managedSessionClaimsResolvedTerminal(ports, input.session, input.terminal) ||
|
|
781
|
+
bindingMatchesLiveTerminal(ports, input.session, input.terminal, input.identity, input.storeDir)) {
|
|
782
|
+
return undefined;
|
|
783
|
+
}
|
|
784
|
+
const binding = input.session.binding;
|
|
785
|
+
if (managedSessionOwnerIsConclusivelyInactive(ports, {
|
|
786
|
+
session: input.session,
|
|
787
|
+
terminal: input.terminal,
|
|
788
|
+
identity: input.identity
|
|
789
|
+
})) {
|
|
790
|
+
return "stale_process_incarnation";
|
|
791
|
+
}
|
|
792
|
+
const incarnation = resolvedTerminalProcessIncarnation(input.terminal, input.identity);
|
|
793
|
+
const relationship = processIncarnationRelationship({
|
|
794
|
+
binding,
|
|
795
|
+
livePid: input.terminal.pid,
|
|
796
|
+
liveProcessUuid: incarnation.processUuid,
|
|
797
|
+
liveProcessBirth: incarnation.processBirth
|
|
798
|
+
});
|
|
799
|
+
return decideManagedBindingConflict({
|
|
800
|
+
session: input.session,
|
|
801
|
+
claimsTerminal: true,
|
|
802
|
+
exactBinding: false,
|
|
803
|
+
ownerConclusivelyInactive: false,
|
|
804
|
+
processRelationship: relationship,
|
|
805
|
+
liveNativeThreadId: input.identity?.sessionId,
|
|
806
|
+
managedTurnCount: ports.authority.provisionalManagedBindingTurnCount(input.storeDir, input.session)
|
|
807
|
+
});
|
|
808
|
+
}
|
|
809
|
+
function soleBoundManagedSessionClaimForTerminal(ports, storeDir, terminal) {
|
|
810
|
+
return selectSoleBoundManagedSessionClaim({
|
|
811
|
+
sessions: listManagedSessions(storeDir),
|
|
812
|
+
terminalTarget: terminal.terminalControl.target,
|
|
813
|
+
claims: (session) => managedSessionClaimsResolvedTerminal(ports, session, terminal),
|
|
814
|
+
ownerIsInactive: (session) => managedSessionOwnerIsConclusivelyInactive(ports, { session, terminal })
|
|
815
|
+
});
|
|
816
|
+
}
|
|
817
|
+
function createBoundManagedSession(ports, input) {
|
|
818
|
+
const nativeThreadId = input.nativeThreadId ?? input.identity?.sessionId;
|
|
819
|
+
const evidence = input.evidence ?? input.identity?.evidence ??
|
|
820
|
+
"native_thread_boundary";
|
|
821
|
+
const generation = input.generation ?? 1;
|
|
822
|
+
const now = input.now ?? ports.environment.now();
|
|
823
|
+
const workspace = input.terminal.terminalControl.currentPath ??
|
|
824
|
+
ports.environment.cwd();
|
|
825
|
+
const codexIncarnation = input.terminal.agent === "codex" && !input.identity
|
|
826
|
+
? codexProcessIncarnationForPid(input.terminal.pid)
|
|
827
|
+
: undefined;
|
|
828
|
+
const binding = terminalBindingFrom({
|
|
829
|
+
terminalId: input.terminal.conversationId,
|
|
830
|
+
terminalControl: input.terminal.terminalControl,
|
|
831
|
+
pid: input.terminal.pid,
|
|
832
|
+
nativeThreadId,
|
|
833
|
+
processUuid: input.identity?.processUuid ?? codexIncarnation?.processUuid,
|
|
834
|
+
processBirth: input.identity?.processBirth ?? codexIncarnation?.processBirth,
|
|
835
|
+
rollout: input.identity?.rollout,
|
|
836
|
+
evidence: input.identity?.evidence ?? codexIncarnation?.evidence ?? evidence,
|
|
837
|
+
generation,
|
|
838
|
+
now
|
|
839
|
+
});
|
|
840
|
+
return {
|
|
841
|
+
schema: "agent-knock-knock/session",
|
|
842
|
+
version: 1,
|
|
843
|
+
session_id: input.sessionId,
|
|
844
|
+
agent: input.terminal.agent,
|
|
845
|
+
workspace,
|
|
846
|
+
status: "bound",
|
|
847
|
+
binding,
|
|
848
|
+
lineage: input.lineage,
|
|
849
|
+
created_at: now.toISOString(),
|
|
850
|
+
updated_at: now.toISOString(),
|
|
851
|
+
last_transition_id: input.lineage.transition_id
|
|
852
|
+
};
|
|
853
|
+
}
|
|
854
|
+
function materializeCurrentManagedSession(ports, input) {
|
|
855
|
+
const storeDir = ports.store.storeDir(input.options);
|
|
856
|
+
const existing = boundManagedSessionForTerminal(ports, {
|
|
857
|
+
storeDir,
|
|
858
|
+
terminal: input.terminal,
|
|
859
|
+
identity: input.identity
|
|
860
|
+
});
|
|
861
|
+
if (existing) {
|
|
862
|
+
return existing;
|
|
863
|
+
}
|
|
864
|
+
const matches = listConversations(storeDir)
|
|
865
|
+
.filter(ports.store.isDiscoverableTurn)
|
|
866
|
+
.filter((conversation) => ports.store.turnMatchesTerminal(conversation, input.terminal, input.identity));
|
|
867
|
+
const sessionIds = [...new Set(matches.map((turn) => sessionIdForConversation(turn)))];
|
|
868
|
+
if (sessionIds.length > 1) {
|
|
869
|
+
throw new Error(`terminal ${input.terminal.terminalControl.target} has ambiguous legacy Session bindings`);
|
|
870
|
+
}
|
|
871
|
+
const sessionId = sessionIds[0];
|
|
872
|
+
if (!sessionId) {
|
|
873
|
+
return undefined;
|
|
874
|
+
}
|
|
875
|
+
const existingById = tryLoadManagedSession(storeDir, sessionId);
|
|
876
|
+
if (existingById) {
|
|
877
|
+
if (!bindingMatchesLiveTerminal(ports, existingById, input.terminal, input.identity, storeDir)) {
|
|
878
|
+
throw new Error(`managed Session ${sessionId} no longer matches its terminal binding`);
|
|
879
|
+
}
|
|
880
|
+
return existingById;
|
|
881
|
+
}
|
|
882
|
+
throw new Error(`Store protocol 3+ has Turn records for Session ${sessionId} but its ` +
|
|
883
|
+
"authoritative Session state is missing");
|
|
884
|
+
}
|
|
885
|
+
async function reattachManagedSessionForNativeIdentity(ports, input) {
|
|
886
|
+
if (!isExactNativeThreadId(input.identity.sessionId)) {
|
|
887
|
+
throw new Error("raw terminal attach requires an exact native thread UUID");
|
|
888
|
+
}
|
|
889
|
+
const nativeThreadId = input.identity.sessionId.toLowerCase();
|
|
890
|
+
const matches = listManagedSessions(input.storeDir).filter((session) => session.agent === input.terminal.agent &&
|
|
891
|
+
session.binding?.native_thread_id?.toLowerCase() === nativeThreadId);
|
|
892
|
+
if (matches.length === 0) {
|
|
893
|
+
return undefined;
|
|
894
|
+
}
|
|
895
|
+
if (matches.length > 1) {
|
|
896
|
+
throw new Error(`native thread ${nativeThreadId} is claimed by multiple managed Sessions`);
|
|
897
|
+
}
|
|
898
|
+
const existing = matches[0];
|
|
899
|
+
if (!existing.binding ||
|
|
900
|
+
!["bound", "detached"].includes(existing.status) ||
|
|
901
|
+
path.resolve(existing.workspace) !==
|
|
902
|
+
path.resolve(input.terminal.terminalControl.currentPath ?? ports.environment.cwd())) {
|
|
903
|
+
throw new Error(`managed Session ${existing.session_id} cannot be rebound from ` +
|
|
904
|
+
`${existing.status} state for native thread ${nativeThreadId}`);
|
|
905
|
+
}
|
|
906
|
+
ports.authority.assertManagedSessionCanStartTurn(ports.store.turnsForSession(input.storeDir, existing.session_id));
|
|
907
|
+
await ports.authority.assertNativeThreadHasExclusiveOwnership({
|
|
908
|
+
options: input.options,
|
|
909
|
+
agent: input.terminal.agent,
|
|
910
|
+
currentPid: input.terminal.pid,
|
|
911
|
+
nativeThreadId,
|
|
912
|
+
storeDir: input.storeDir,
|
|
913
|
+
terminalControl: input.terminal.terminalControl,
|
|
914
|
+
excludedManagedSessionId: existing.session_id
|
|
915
|
+
});
|
|
916
|
+
const fresh = loadManagedSession(input.storeDir, existing.session_id);
|
|
917
|
+
if (fresh.revision !== existing.revision ||
|
|
918
|
+
managedSessionBindingToken(fresh) !== managedSessionBindingToken(existing)) {
|
|
919
|
+
throw new Error(`managed Session ${existing.session_id} changed before raw reattach`);
|
|
920
|
+
}
|
|
921
|
+
const previousPid = fresh.binding.native_process.pid;
|
|
922
|
+
if (fresh.status === "bound" &&
|
|
923
|
+
!managedSessionOwnerIsConclusivelyInactive(ports, {
|
|
924
|
+
session: fresh,
|
|
925
|
+
terminal: input.terminal,
|
|
926
|
+
identity: input.identity
|
|
927
|
+
})) {
|
|
928
|
+
throw new Error(`managed Session ${fresh.session_id} is still bound to process ${previousPid}`);
|
|
929
|
+
}
|
|
930
|
+
const now = ports.environment.now();
|
|
931
|
+
const binding = terminalBindingFrom({
|
|
932
|
+
terminalId: input.terminal.conversationId,
|
|
933
|
+
terminalControl: input.terminal.terminalControl,
|
|
934
|
+
pid: input.terminal.pid,
|
|
935
|
+
nativeThreadId,
|
|
936
|
+
processUuid: input.identity.processUuid,
|
|
937
|
+
processBirth: input.identity.processBirth,
|
|
938
|
+
rollout: input.identity.rollout,
|
|
939
|
+
evidence: `${input.identity.evidence}+raw_reattach`,
|
|
940
|
+
generation: fresh.binding.generation + 1,
|
|
941
|
+
now
|
|
942
|
+
});
|
|
943
|
+
return saveManagedSession(input.storeDir, {
|
|
944
|
+
...fresh,
|
|
945
|
+
status: "bound",
|
|
946
|
+
binding,
|
|
947
|
+
detached_at: undefined,
|
|
948
|
+
quarantine_reason: undefined,
|
|
949
|
+
updated_at: now.toISOString()
|
|
950
|
+
}, { expectedRevision: managedSessionRevision(fresh) });
|
|
951
|
+
}
|
|
952
|
+
function deferredForegroundAuthorityAdapterPorts(ports) {
|
|
953
|
+
return {
|
|
954
|
+
turn: {
|
|
955
|
+
terminalControl: ports.store.terminalControlFromTakeover,
|
|
956
|
+
storeDir: ports.store.storeDirForConversation,
|
|
957
|
+
turnsForSession: ports.store.turnsForSession,
|
|
958
|
+
needsAttention: ports.authority.managedTurnNeedsAttention,
|
|
959
|
+
readEvents: ports.store.readEvents
|
|
960
|
+
},
|
|
961
|
+
ledger: {
|
|
962
|
+
load: ports.store.loadLedger,
|
|
963
|
+
matchesControl: ports.store.ledgerMatchesControl,
|
|
964
|
+
processAnchor: ports.store.ledgerProcessAnchor
|
|
965
|
+
},
|
|
966
|
+
transition: {
|
|
967
|
+
hasUnresolved: ports.authority.hasUnresolvedNativeTransition,
|
|
968
|
+
hasAny: ports.authority.hasAnyNativeTransition
|
|
969
|
+
}
|
|
970
|
+
};
|
|
971
|
+
}
|
|
972
|
+
function codexCandidateInventoryHasNoOtherManagedClaim(ports, options) {
|
|
973
|
+
return deferredAuthorityAdapter.codexCandidateInventoryHasNoOtherManagedClaim(deferredForegroundAuthorityAdapterPorts(ports), options);
|
|
974
|
+
}
|
|
975
|
+
function deferredCandidateSourceTurnHistory(ports, storeDir, session) {
|
|
976
|
+
return deferredAuthorityAdapter.deferredCandidateSourceTurnHistory(deferredForegroundAuthorityAdapterPorts(ports), storeDir, session);
|
|
977
|
+
}
|
|
978
|
+
function explicitlyAbandonedCandidateSourceFingerprint(ports, options) {
|
|
979
|
+
return deferredAuthorityAdapter.explicitlyAbandonedCandidateSourceFingerprint(deferredForegroundAuthorityAdapterPorts(ports), options);
|
|
980
|
+
}
|
|
981
|
+
function assertFrozenExplicitlyAbandonedPredecessorAuthority(ports, options) {
|
|
982
|
+
deferredAuthorityAdapter.assertFrozenExplicitlyAbandonedPredecessorAuthority(deferredForegroundAuthorityAdapterPorts(ports), options);
|
|
983
|
+
}
|
|
984
|
+
function deferredCodexForegroundDispatchSnapshot(ports, terminalControl) {
|
|
985
|
+
return deferredAuthorityAdapter.deferredCodexForegroundDispatchSnapshot(deferredForegroundAuthorityAdapterPorts(ports), terminalControl);
|
|
986
|
+
}
|
|
987
|
+
function deferredCodexPreviousDispatchSnapshotMatches(ports, options) {
|
|
988
|
+
return deferredAuthorityAdapter.deferredCodexPreviousDispatchSnapshotMatches(deferredForegroundAuthorityAdapterPorts(ports), options);
|
|
989
|
+
}
|
|
990
|
+
function observeDeferredCodexAuthority(ports, options) {
|
|
991
|
+
return deferredAuthorityAdapter.observeDeferredCodexAuthority(deferredForegroundAuthorityAdapterPorts(ports), options);
|
|
992
|
+
}
|
|
993
|
+
async function assertVerifiedEmptyCodexHandoffBoundary(ports, input) {
|
|
994
|
+
const storeDir = ports.store.storeDir(input.options);
|
|
995
|
+
const currentSource = loadManagedSession(storeDir, input.sourceSession.session_id);
|
|
996
|
+
if (!verifiedEmptySourceSnapshotMatches({
|
|
997
|
+
expectedStatus: input.expectedSourceStatus,
|
|
998
|
+
currentStatus: currentSource.status,
|
|
999
|
+
expectedRevision: input.sourceSession.revision,
|
|
1000
|
+
currentRevision: currentSource.revision,
|
|
1001
|
+
expectedBindingToken: managedSessionBindingToken(input.sourceSession),
|
|
1002
|
+
currentBindingToken: managedSessionBindingToken(currentSource)
|
|
1003
|
+
})) {
|
|
1004
|
+
throw new Error("the verified-empty source Session changed; refresh AKK list");
|
|
1005
|
+
}
|
|
1006
|
+
const liveIncarnation = codexProcessIncarnationForPid(input.terminal.pid);
|
|
1007
|
+
if (!exactBoundCodexSendSource({
|
|
1008
|
+
kind: "verified_empty",
|
|
1009
|
+
sourceSession: { ...currentSource, status: "bound" },
|
|
1010
|
+
context: {
|
|
1011
|
+
terminalId: input.terminal.conversationId,
|
|
1012
|
+
terminalControl: input.terminal.terminalControl,
|
|
1013
|
+
pid: input.terminal.pid,
|
|
1014
|
+
workspace: input.terminal.terminalControl.currentPath,
|
|
1015
|
+
liveProcessUuid: liveIncarnation.processUuid,
|
|
1016
|
+
liveProcessBirth: liveIncarnation.processBirth
|
|
1017
|
+
}
|
|
1018
|
+
})) {
|
|
1019
|
+
throw new Error("the verified-empty source binding no longer matches the terminal");
|
|
1020
|
+
}
|
|
1021
|
+
const observation = await ports.runtime.observeNativeIdentity({
|
|
1022
|
+
options: input.options,
|
|
1023
|
+
agent: "codex",
|
|
1024
|
+
pid: input.terminal.pid,
|
|
1025
|
+
cwd: input.terminal.terminalControl.currentPath
|
|
1026
|
+
});
|
|
1027
|
+
if (observation.status === "unavailable") {
|
|
1028
|
+
throw new Error(`Codex native identity observation is unavailable: ${observation.reason}`);
|
|
1029
|
+
}
|
|
1030
|
+
if (observation.status !== "verified_absent") {
|
|
1031
|
+
throw new Error(`Codex materialized native thread ${observation.identity.sessionId}; ` +
|
|
1032
|
+
"refresh AKK list before sending");
|
|
1033
|
+
}
|
|
1034
|
+
const status = await ports.runtime.createBridge(input.options).status("codex", input.terminal.terminalControl, {
|
|
1035
|
+
runtime: terminalRuntimeForLiveIdentity({ terminal: input.terminal,
|
|
1036
|
+
expectedEmptyNativeSession: true })
|
|
1037
|
+
});
|
|
1038
|
+
if (status.reachable !== true ||
|
|
1039
|
+
status.approval_state.blocked === true ||
|
|
1040
|
+
!["idle", "unknown"].includes(status.activity_state)) {
|
|
1041
|
+
throw new Error(`terminal ${input.terminal.terminalControl.target} is not at a verified ` +
|
|
1042
|
+
`empty Codex prompt (${status.activity_state}: ${status.activity_reason})`);
|
|
1043
|
+
}
|
|
1044
|
+
if (input.requireNoDispatch) {
|
|
1045
|
+
ports.authority.assertTerminalLifecycleReady({
|
|
1046
|
+
options: input.options,
|
|
1047
|
+
terminal: input.terminal,
|
|
1048
|
+
terminalStatus: { ...status, activity_state: "idle" }
|
|
1049
|
+
});
|
|
1050
|
+
}
|
|
1051
|
+
const freshSource = loadManagedSession(storeDir, currentSource.session_id);
|
|
1052
|
+
if (!verifiedEmptySourceSnapshotMatches({
|
|
1053
|
+
expectedStatus: input.expectedSourceStatus,
|
|
1054
|
+
currentStatus: freshSource.status,
|
|
1055
|
+
expectedRevision: currentSource.revision,
|
|
1056
|
+
currentRevision: freshSource.revision,
|
|
1057
|
+
expectedBindingToken: managedSessionBindingToken(currentSource),
|
|
1058
|
+
currentBindingToken: managedSessionBindingToken(freshSource)
|
|
1059
|
+
})) {
|
|
1060
|
+
throw new Error("the verified-empty source Session changed; refresh AKK list");
|
|
1061
|
+
}
|
|
1062
|
+
if (ports.authority.hasUnresolvedNativeTransition(storeDir, freshSource)) {
|
|
1063
|
+
throw new Error(`managed Session ${freshSource.session_id} has an unresolved native-thread transition`);
|
|
1064
|
+
}
|
|
1065
|
+
if (input.requireEmptyComposer ?? true) {
|
|
1066
|
+
await assertCodexComposerReadyForAutomatedInput(ports, {
|
|
1067
|
+
options: input.options,
|
|
1068
|
+
terminalControl: input.terminal.terminalControl
|
|
1069
|
+
});
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
//# sourceMappingURL=terminal-identity-authority-cli-adapter.js.map
|