@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,974 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { createManagedSessionId, createNativeThreadTransitionId, isExactNativeThreadId, managedSessionBindingToken, managedSessionRevision, nativeThreadTransitionRevision, nativeThreadCommandFingerprint, terminalBindingFrom } from "./managed-session.js";
|
|
3
|
+
import { assertRestorableOriginSessionRelationship as assertRestorableOriginSessionRelationshipFromQuery, requireRestorableLifecycleOrigin as requireRestorableLifecycleOriginFromQuery, resumableNativeThreadCandidates, revalidateNativeThreadCandidate as revalidateNativeThreadCandidateFromQuery } from "./native-thread-lifecycle-query-service.js";
|
|
4
|
+
import { decideResumeCandidateEligibility, decideResumeTargetSession, prepareNativeThreadTransition, reduceNativeThreadTransitionPhase } from "./native-thread-transition-policy.js";
|
|
5
|
+
import { settleFailedNativeThreadTransition, settleVerifiedNativeThreadTransition } from "./native-thread-transition-settlement-service.js";
|
|
6
|
+
import { reconcileLifecycleDispatchLedger as reconcileLifecycleDispatchLedgerService, recoverLifecycleFenceBeforeMutation as recoverLifecycleFenceBeforeMutationService } from "./native-thread-lifecycle-recovery-service.js";
|
|
7
|
+
import { createNativeThreadLifecycleRecoveryProbeAdapter, lifecycleRecoveryRuntime, lifecycleRecoveryTerminalFacts } from "./native-thread-lifecycle-recovery-adapter.js";
|
|
8
|
+
import { nativeThreadTransitionResourceBoundOperation } from "./native-thread-transition-resource-adapter.js";
|
|
9
|
+
import { assertResumedNativeThreadMatchesCandidate as assertResumedCodexRolloutMatchesCandidate, nativeThreadCandidateFileIdentity as codexCandidateFileIdentity, prepareNativeThreadVerification, verifyNativeThreadTransition as verifyNativeThreadTransitionWithRuntime } from "./native-thread-transition-verification-adapter.js";
|
|
10
|
+
import { resolveNativeThreadResumeSelection } from "./native-thread-resume-snapshot.js";
|
|
11
|
+
import { assertResumeSnapshotActionFingerprint, assertResumeSnapshotCandidates, assertResumeSnapshotMatchesTerminal, assertResumeSnapshotNotExpired } from "./native-thread-resume-snapshot-policy.js";
|
|
12
|
+
import { commitVerifiedLifecycleTransition, listNativeThreadTransitions, loadManagedSession, loadNativeThreadTransition, saveManagedSession, saveNativeThreadTransition, tryLoadManagedSession } from "./session-store.js";
|
|
13
|
+
import { TerminalInputNotStartedError } from "./terminal-agent-bridge.js";
|
|
14
|
+
import { codexCompanionsExcludingPreferred, nativeIdentityMatchesCodexPreMaterialization, terminalControlAliasMatches, terminalControlsShareIncarnation } from "./terminal-authority-policy.js";
|
|
15
|
+
import { nativeThreadLifecycleLedger as lifecycleLedger, terminalDispatchLedgerLooksLifecycle } from "./terminal-dispatch-ledger-codec.js";
|
|
16
|
+
import { reconcileTerminalBinding } from "./terminal-binding-reconciliation-service.js";
|
|
17
|
+
import { withCanonicalMutationLocks } from "./mutation-transaction.js";
|
|
18
|
+
import { isSessionSendBlockingStatus, isTerminalDispatchOwnerReleasedStatus, turnIdForConversation } from "./protocol.js";
|
|
19
|
+
import { nonBlankString as stringValue } from "./value-guards.js";
|
|
20
|
+
function createNativeThreadTransitionBindings(ports) {
|
|
21
|
+
const { now: cliNow, nowMs: cliNowMs, pid: cliPid, cwd: cliCwd, sleep: cliSleep, exit: cliExit, log: runtimeLog, print: printJson, summarizeError: textSummary } = ports.runtime;
|
|
22
|
+
const cliEnv = () => new Proxy({}, {
|
|
23
|
+
get: (_target, property) => ports.runtime.env(String(property))
|
|
24
|
+
});
|
|
25
|
+
const nativeThreadLifecycleFacade = ports.lifecycle.facade;
|
|
26
|
+
const assertNativeThreadHasExclusiveOwnership = (input) => nativeThreadLifecycleFacade.assertExclusive(input);
|
|
27
|
+
const resolveLifecycleTerminal = nativeThreadLifecycleFacade.resolveLifecycleTerminal;
|
|
28
|
+
const nativeThreadLifecycleQueryPorts = nativeThreadLifecycleFacade.queryPorts;
|
|
29
|
+
const currentLifecycleSnapshot = nativeThreadLifecycleFacade.currentSnapshot;
|
|
30
|
+
const lifecycleBindingTokens = nativeThreadLifecycleFacade.lifecycleBindingTokens;
|
|
31
|
+
const assertSameNativeInspectionTerminal = nativeThreadLifecycleFacade.assertSameInspectionTerminal;
|
|
32
|
+
const storeDirFromOptions = ports.state.storeDir;
|
|
33
|
+
const terminalBridgeRuntimeDir = ports.state.runtimeDir;
|
|
34
|
+
const loadTerminalBridgeDispatchLedger = ports.state.loadLedger;
|
|
35
|
+
const reconcilePreparedTerminalDispatchLedger = ports.state.reconcilePrepared;
|
|
36
|
+
const resolveTerminalDispatchLedgerPaneIncarnation = ports.state.reconcileIncarnation;
|
|
37
|
+
const terminalDispatchRecordMatchesControl = ports.state.recordMatchesControl;
|
|
38
|
+
const saveLifecycleTerminalDispatchLedger = ports.state.lifecycleLedger.save;
|
|
39
|
+
const loadTerminalDispatchLedgerOwner = (ledger) => {
|
|
40
|
+
const status = ports.state.ordinaryOwnerStatus(ledger);
|
|
41
|
+
return status ? { status } : undefined;
|
|
42
|
+
};
|
|
43
|
+
const terminalListCliFacade = {
|
|
44
|
+
terminalIncarnationBlockingTurns: ports.state.blockingTurns,
|
|
45
|
+
terminalDispatchOwnership: ports.state.dispatchOwnership,
|
|
46
|
+
managedSessionHasUnresolvedNativeTransition: ports.state.hasUnresolvedTransition
|
|
47
|
+
};
|
|
48
|
+
const managedTurnsForSession = ports.state.managedTurns;
|
|
49
|
+
const terminalWriterMutationLocks = ports.mutation.locks;
|
|
50
|
+
const mutationManagedSessions = {
|
|
51
|
+
load: ports.mutation.loadSession,
|
|
52
|
+
save: ports.mutation.saveSession
|
|
53
|
+
};
|
|
54
|
+
const authenticateLifecycleRecoveryResources = ports.mutation.authenticate;
|
|
55
|
+
const createTerminalAgentBridge = (options) => ports.lifecycle.runtime(options).createBridge();
|
|
56
|
+
const loadClaudeAgentRows = (options, settings) => ports.lifecycle.runtime(options).loadClaudeAgentRows(settings);
|
|
57
|
+
const agentVersionForRunningProcess = (agent, pid, options) => ports.lifecycle.runtime(options)
|
|
58
|
+
.agentVersionForRunningProcess(agent, pid);
|
|
59
|
+
const terminalRuntimeForLiveIdentity = ports.lifecycle.runtimeForIdentity;
|
|
60
|
+
const resolveCurrentNativeAgentSessionIdentity = ports.lifecycle.resolveIdentity;
|
|
61
|
+
const assertCodexComposerReadyForAutomatedInput = ports.lifecycle.assertComposerReady;
|
|
62
|
+
const exactLifecycleProcessIdentity = ports.lifecycle.exactIdentity;
|
|
63
|
+
const managedSessionClaimsResolvedTerminal = ports.authority.sessionClaimsTerminal;
|
|
64
|
+
const managedBindingConflictKindForResolvedTerminal = ports.authority.conflictKind;
|
|
65
|
+
const managedSessionOwnerIsConclusivelyInactive = ports.authority.ownerIsInactive;
|
|
66
|
+
const observedExternalHandoffIdentity = ports.authority.observeExternal;
|
|
67
|
+
const recoverDeferredCodexForegroundTransferWhileWriterLease = ports.authority.recoverDeferred;
|
|
68
|
+
const codexKnownRootSetForLifecycleTransition = ports.authority.knownRoots;
|
|
69
|
+
const codexProcessBirthForLifecycle = ports.authority.codexProcessBirth;
|
|
70
|
+
const isProcessAlive = ports.authority.processAlive;
|
|
71
|
+
const matchesConfiguredWorkspace = ports.authority.workspaceMatches;
|
|
72
|
+
return Object.freeze({
|
|
73
|
+
cliNow, cliNowMs, cliPid, cliCwd, cliSleep, cliExit,
|
|
74
|
+
runtimeLog, printJson, textSummary, cliEnv,
|
|
75
|
+
nativeThreadLifecycleFacade, assertNativeThreadHasExclusiveOwnership,
|
|
76
|
+
resolveLifecycleTerminal, nativeThreadLifecycleQueryPorts,
|
|
77
|
+
currentLifecycleSnapshot, lifecycleBindingTokens,
|
|
78
|
+
assertSameNativeInspectionTerminal, storeDirFromOptions,
|
|
79
|
+
terminalBridgeRuntimeDir, loadTerminalBridgeDispatchLedger,
|
|
80
|
+
reconcilePreparedTerminalDispatchLedger,
|
|
81
|
+
resolveTerminalDispatchLedgerPaneIncarnation,
|
|
82
|
+
terminalDispatchRecordMatchesControl,
|
|
83
|
+
saveLifecycleTerminalDispatchLedger, loadTerminalDispatchLedgerOwner,
|
|
84
|
+
terminalListCliFacade, managedTurnsForSession,
|
|
85
|
+
terminalWriterMutationLocks, mutationManagedSessions,
|
|
86
|
+
authenticateLifecycleRecoveryResources, createTerminalAgentBridge,
|
|
87
|
+
loadClaudeAgentRows, agentVersionForRunningProcess,
|
|
88
|
+
terminalRuntimeForLiveIdentity,
|
|
89
|
+
resolveCurrentNativeAgentSessionIdentity,
|
|
90
|
+
assertCodexComposerReadyForAutomatedInput,
|
|
91
|
+
exactLifecycleProcessIdentity, managedSessionClaimsResolvedTerminal,
|
|
92
|
+
managedBindingConflictKindForResolvedTerminal,
|
|
93
|
+
managedSessionOwnerIsConclusivelyInactive,
|
|
94
|
+
observedExternalHandoffIdentity,
|
|
95
|
+
recoverDeferredCodexForegroundTransferWhileWriterLease,
|
|
96
|
+
codexKnownRootSetForLifecycleTransition,
|
|
97
|
+
codexProcessBirthForLifecycle, isProcessAlive,
|
|
98
|
+
matchesConfiguredWorkspace
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
function required(value, message) {
|
|
102
|
+
if (value === undefined || value === "")
|
|
103
|
+
throw new Error(message);
|
|
104
|
+
return value;
|
|
105
|
+
}
|
|
106
|
+
function positiveSafeInteger(value, optionName) {
|
|
107
|
+
if (typeof value !== "string" || !/^[1-9][0-9]*$/u.test(value)) {
|
|
108
|
+
throw new Error(`${optionName} must be a positive integer`);
|
|
109
|
+
}
|
|
110
|
+
const parsed = Number(value);
|
|
111
|
+
if (!Number.isSafeInteger(parsed)) {
|
|
112
|
+
throw new Error(`${optionName} must be a positive safe integer`);
|
|
113
|
+
}
|
|
114
|
+
return parsed;
|
|
115
|
+
}
|
|
116
|
+
function bindingTerminalFacts(terminal) {
|
|
117
|
+
return Object.freeze({
|
|
118
|
+
conversationId: terminal.conversationId,
|
|
119
|
+
agent: terminal.agent,
|
|
120
|
+
pid: terminal.pid,
|
|
121
|
+
terminalControl: terminal.terminalControl
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
export function createNativeThreadTransitionApplication(ports) {
|
|
125
|
+
const bindings = createNativeThreadTransitionBindings(ports);
|
|
126
|
+
let commands;
|
|
127
|
+
let recovery;
|
|
128
|
+
const runTransition = (options, operation) => runNativeThreadTransition(bindings, commands, recovery, options, operation);
|
|
129
|
+
commands = createNativeThreadTransitionCommandApplication(bindings, runTransition, (...args) => recovery.recoverBeforeMutation(...args));
|
|
130
|
+
recovery = createNativeThreadTransitionRecoveryApplication(bindings, commands.assertTargetExclusive);
|
|
131
|
+
return Object.freeze({
|
|
132
|
+
runNewThread: commands.runNewThread,
|
|
133
|
+
runResumeThread: commands.runResumeThread,
|
|
134
|
+
runReconcileBinding: commands.runReconcileBinding,
|
|
135
|
+
verificationPorts: commands.verificationPorts,
|
|
136
|
+
assertTerminalReady: commands.assertTerminalReady,
|
|
137
|
+
recoverBeforeMutation: recovery.recoverBeforeMutation,
|
|
138
|
+
reconcileLedger: recovery.reconcileLedger
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
function createNativeThreadTransitionCommandApplication(bindings, runTransition, recoverBeforeMutation) {
|
|
142
|
+
const { cliNow, cliCwd, cliSleep, runtimeLog, printJson, assertNativeThreadHasExclusiveOwnership, resolveLifecycleTerminal, lifecycleBindingTokens, storeDirFromOptions, terminalBridgeRuntimeDir, loadTerminalBridgeDispatchLedger, reconcilePreparedTerminalDispatchLedger, resolveTerminalDispatchLedgerPaneIncarnation, loadTerminalDispatchLedgerOwner, terminalListCliFacade, managedTurnsForSession, terminalWriterMutationLocks, mutationManagedSessions, createTerminalAgentBridge, loadClaudeAgentRows, agentVersionForRunningProcess, terminalRuntimeForLiveIdentity, resolveCurrentNativeAgentSessionIdentity, managedSessionClaimsResolvedTerminal, managedBindingConflictKindForResolvedTerminal } = bindings;
|
|
143
|
+
function assertTerminalLifecycleReady({ options, terminal, terminalStatus }) {
|
|
144
|
+
if (terminalStatus.reachable !== true ||
|
|
145
|
+
terminalStatus.activity_state !== "idle" ||
|
|
146
|
+
terminalStatus.approval_state.blocked === true) {
|
|
147
|
+
throw new Error(`terminal ${terminal.terminalControl.target} is not at a verified idle prompt ` +
|
|
148
|
+
`(${terminalStatus.activity_state}: ${terminalStatus.activity_reason})`);
|
|
149
|
+
}
|
|
150
|
+
const blockers = terminalListCliFacade.terminalIncarnationBlockingTurns(storeDirFromOptions(options), terminal.terminalControl);
|
|
151
|
+
if (blockers.length > 0) {
|
|
152
|
+
throw new Error(`terminal ${terminal.terminalControl.target} still has unresolved Turn ` +
|
|
153
|
+
`${turnIdForConversation(blockers[0])} (${blockers[0].status})`);
|
|
154
|
+
}
|
|
155
|
+
const ledger = resolveTerminalDispatchLedgerPaneIncarnation(terminal.terminalControl, reconcilePreparedTerminalDispatchLedger(terminal.terminalControl, loadTerminalBridgeDispatchLedger(terminal.terminalControl)));
|
|
156
|
+
if (terminalDispatchLedgerLooksLifecycle(ledger) &&
|
|
157
|
+
ledger?.status !== "resolved") {
|
|
158
|
+
throw new Error(`terminal ${terminal.terminalControl.target} has unresolved lifecycle ` +
|
|
159
|
+
`transition ${stringValue(ledger?.transition_id) ?? "with invalid identity"}`);
|
|
160
|
+
}
|
|
161
|
+
if (ledger &&
|
|
162
|
+
[
|
|
163
|
+
"prepared",
|
|
164
|
+
"text_injected",
|
|
165
|
+
"enter_dispatched",
|
|
166
|
+
"dispatching",
|
|
167
|
+
"not_accepted",
|
|
168
|
+
"uncertain"
|
|
169
|
+
].includes(String(ledger.status))) {
|
|
170
|
+
throw new Error(`terminal ${terminal.terminalControl.target} has an unresolved ` +
|
|
171
|
+
`${String(ledger.kind ?? "turn")} operation (${String(ledger.status)})`);
|
|
172
|
+
}
|
|
173
|
+
if (ledger && ["submitted", "agent_accepted"].includes(String(ledger.status))) {
|
|
174
|
+
const owner = loadTerminalDispatchLedgerOwner(ledger);
|
|
175
|
+
if (!owner || !isTerminalDispatchOwnerReleasedStatus(owner.status)) {
|
|
176
|
+
throw new Error(`terminal ${terminal.terminalControl.target} still has a submitted operation`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
function nativeThreadVerificationAdapterPorts(options, terminal) {
|
|
181
|
+
return {
|
|
182
|
+
createBridge: () => createTerminalAgentBridge(options),
|
|
183
|
+
loadClaudeAgentRows: () => loadClaudeAgentRows(options, { required: true }),
|
|
184
|
+
runningVersion: () => agentVersionForRunningProcess(terminal.agent, terminal.pid, options),
|
|
185
|
+
runtimeForIdentity: (identity) => terminalRuntimeForLiveIdentity({ terminal, identity }),
|
|
186
|
+
emptyRuntime: () => terminalRuntimeForLiveIdentity({
|
|
187
|
+
terminal,
|
|
188
|
+
expectedEmptyNativeSession: true
|
|
189
|
+
}),
|
|
190
|
+
physicalRuntime: () => terminalRuntimeForLiveIdentity({
|
|
191
|
+
terminal,
|
|
192
|
+
physicalOnly: true
|
|
193
|
+
}),
|
|
194
|
+
resolveIdentity: (preferredSessionId, allowedCompanionIdentity, allowedAdditionalIdentities) => resolveCurrentNativeAgentSessionIdentity({
|
|
195
|
+
options,
|
|
196
|
+
agent: terminal.agent,
|
|
197
|
+
pid: terminal.pid,
|
|
198
|
+
cwd: terminal.terminalControl.currentPath,
|
|
199
|
+
preferredSessionId,
|
|
200
|
+
allowedCompanionIdentity,
|
|
201
|
+
allowedAdditionalIdentities: [...allowedAdditionalIdentities]
|
|
202
|
+
}),
|
|
203
|
+
sleep: cliSleep
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
async function assertLifecycleTargetHasExclusiveOwnership({ options, terminal, transition, storeDir }) {
|
|
207
|
+
const afterBinding = transition.after_binding;
|
|
208
|
+
const nativeThreadId = afterBinding?.native_thread_id;
|
|
209
|
+
if (!afterBinding ||
|
|
210
|
+
!nativeThreadId ||
|
|
211
|
+
!isExactNativeThreadId(nativeThreadId) ||
|
|
212
|
+
afterBinding.native_process.pid !== terminal.pid ||
|
|
213
|
+
!terminalControlAliasMatches(afterBinding.terminal_id, afterBinding.terminal_control, terminal.conversationId, terminal.terminalControl)) {
|
|
214
|
+
throw new Error("cannot exclude the current lifecycle process without an exact after_binding");
|
|
215
|
+
}
|
|
216
|
+
await assertNativeThreadHasExclusiveOwnership({
|
|
217
|
+
options,
|
|
218
|
+
agent: transition.agent,
|
|
219
|
+
currentPid: terminal.pid,
|
|
220
|
+
nativeThreadId,
|
|
221
|
+
storeDir,
|
|
222
|
+
terminalControl: terminal.terminalControl,
|
|
223
|
+
excludedManagedSessionId: transition.target_session_id
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
async function runNewThread(options) {
|
|
227
|
+
const requireRestorableOrigin = options.requireRestorableOrigin;
|
|
228
|
+
if (requireRestorableOrigin !== undefined &&
|
|
229
|
+
requireRestorableOrigin !== true) {
|
|
230
|
+
throw new Error("--require-restorable-origin does not take a value");
|
|
231
|
+
}
|
|
232
|
+
return runTransition(options, {
|
|
233
|
+
kind: "new_thread",
|
|
234
|
+
requireRestorableOrigin: requireRestorableOrigin === true
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
async function runResumeThread(options) {
|
|
238
|
+
const exactNativeThreadId = stringValue(options.nativeThread ?? options.nativeThreadId);
|
|
239
|
+
const hasSnapshotSelection = Boolean(options.selectionSnapshot ||
|
|
240
|
+
options.selectionNumber ||
|
|
241
|
+
options.selectionShortId ||
|
|
242
|
+
options.selectionHandle);
|
|
243
|
+
if (exactNativeThreadId && hasSnapshotSelection) {
|
|
244
|
+
throw new Error("--native-thread cannot be combined with a snapshot-bound selection");
|
|
245
|
+
}
|
|
246
|
+
if (exactNativeThreadId) {
|
|
247
|
+
if (!isExactNativeThreadId(exactNativeThreadId)) {
|
|
248
|
+
throw new Error("--native-thread must be a complete native thread UUID");
|
|
249
|
+
}
|
|
250
|
+
return runTransition(options, {
|
|
251
|
+
kind: "resume_thread",
|
|
252
|
+
nativeThreadId: exactNativeThreadId.toLowerCase()
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
if (!hasSnapshotSelection) {
|
|
256
|
+
throw new Error("--native-thread or a snapshot-bound resume selection is required");
|
|
257
|
+
}
|
|
258
|
+
const terminal = await resolveLifecycleTerminal(options);
|
|
259
|
+
const storeDir = storeDirFromOptions(options);
|
|
260
|
+
const selectionScope = required(stringValue(options.selectionScope), "--selection-scope is required for snapshot-bound resume");
|
|
261
|
+
const rawSelectionNumber = options.selectionNumber;
|
|
262
|
+
const selectionNumber = rawSelectionNumber === undefined
|
|
263
|
+
? undefined
|
|
264
|
+
: positiveSafeInteger(rawSelectionNumber, "--selection-number");
|
|
265
|
+
const selection = resolveNativeThreadResumeSelection({
|
|
266
|
+
runtimeDir: terminalBridgeRuntimeDir(),
|
|
267
|
+
storeDir,
|
|
268
|
+
terminalId: terminal.conversationId,
|
|
269
|
+
selectionScope,
|
|
270
|
+
snapshotId: stringValue(options.selectionSnapshot),
|
|
271
|
+
selectionNumber,
|
|
272
|
+
shortId: stringValue(options.selectionShortId),
|
|
273
|
+
selectionHandle: stringValue(options.selectionHandle)
|
|
274
|
+
});
|
|
275
|
+
assertResumeSnapshotMatchesTerminal(selection.snapshot, terminal, cliCwd);
|
|
276
|
+
assertResumeSnapshotActionFingerprint(selection.snapshot, loadTerminalBridgeDispatchLedger(terminal.terminalControl));
|
|
277
|
+
const selectedOptions = {
|
|
278
|
+
...options,
|
|
279
|
+
expectedBindingToken: selection.snapshot.expected_binding_token,
|
|
280
|
+
candidateToken: selection.row.candidate_token
|
|
281
|
+
};
|
|
282
|
+
return runTransition(selectedOptions, {
|
|
283
|
+
kind: "resume_thread",
|
|
284
|
+
nativeThreadId: selection.row.native_thread_id.toLowerCase(),
|
|
285
|
+
selectionSnapshot: selection.snapshot
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
async function runReconcileBinding(options) {
|
|
289
|
+
const initiallyResolved = await resolveLifecycleTerminal(options);
|
|
290
|
+
const resolvedByFacts = new WeakMap();
|
|
291
|
+
const remember = (terminal) => {
|
|
292
|
+
const facts = bindingTerminalFacts(terminal);
|
|
293
|
+
resolvedByFacts.set(facts, terminal);
|
|
294
|
+
return facts;
|
|
295
|
+
};
|
|
296
|
+
const fullTerminal = (facts) => required(resolvedByFacts.get(facts), "binding reconciliation terminal capability is unavailable");
|
|
297
|
+
const initialFacts = remember(initiallyResolved);
|
|
298
|
+
const storeDir = storeDirFromOptions(options);
|
|
299
|
+
const conflictingSessionId = required(stringValue(options.conflictingSession ?? options.conflictingSessionId), "--conflicting-session is required");
|
|
300
|
+
const expectedSessionRevision = positiveSafeInteger(required(stringValue(options.expectedSessionRevision ?? options.sessionRevision), "--expected-session-revision is required"), "--expected-session-revision");
|
|
301
|
+
const expectedBindingToken = required(stringValue(options.expectedBindingToken), "--expected-binding-token is required");
|
|
302
|
+
const expectedTerminalToken = required(stringValue(options.expectedTerminalToken), "--expected-terminal-token is required");
|
|
303
|
+
return reconcileTerminalBinding({
|
|
304
|
+
initialTerminal: initialFacts,
|
|
305
|
+
conflictingSessionId,
|
|
306
|
+
expectedSessionRevision,
|
|
307
|
+
expectedBindingToken,
|
|
308
|
+
expectedTerminalToken
|
|
309
|
+
}, {
|
|
310
|
+
transaction: {
|
|
311
|
+
locks: terminalWriterMutationLocks(storeDir, initiallyResolved.terminalControl),
|
|
312
|
+
recover: (scopes, resources, terminal) => recoverBeforeMutation(scopes, resources, options, fullTerminal(terminal)),
|
|
313
|
+
loadSession: mutationManagedSessions.load,
|
|
314
|
+
saveSession: mutationManagedSessions.save
|
|
315
|
+
},
|
|
316
|
+
terminal: {
|
|
317
|
+
resolve: async () => remember(await resolveLifecycleTerminal(options)),
|
|
318
|
+
sameIncarnation: terminalControlsShareIncarnation,
|
|
319
|
+
identity: (facts) => resolveCurrentNativeAgentSessionIdentity({
|
|
320
|
+
options,
|
|
321
|
+
agent: facts.agent,
|
|
322
|
+
pid: facts.pid,
|
|
323
|
+
cwd: facts.terminalControl.currentPath
|
|
324
|
+
}),
|
|
325
|
+
prepareStatus: () => {
|
|
326
|
+
const bridge = createTerminalAgentBridge(options);
|
|
327
|
+
return async (facts) => {
|
|
328
|
+
const terminal = fullTerminal(facts);
|
|
329
|
+
const status = await bridge.status(terminal.agent, terminal.terminalControl, {
|
|
330
|
+
runtime: terminalRuntimeForLiveIdentity({
|
|
331
|
+
terminal,
|
|
332
|
+
physicalOnly: true
|
|
333
|
+
})
|
|
334
|
+
});
|
|
335
|
+
return Object.freeze({
|
|
336
|
+
reachable: status.reachable,
|
|
337
|
+
activity_state: status.activity_state,
|
|
338
|
+
activity_reason: status.activity_reason,
|
|
339
|
+
approval_state: Object.freeze({
|
|
340
|
+
blocked: status.approval_state.blocked
|
|
341
|
+
})
|
|
342
|
+
});
|
|
343
|
+
};
|
|
344
|
+
},
|
|
345
|
+
assertReady: (facts, terminalStatus) => assertTerminalLifecycleReady({
|
|
346
|
+
options,
|
|
347
|
+
terminal: fullTerminal(facts),
|
|
348
|
+
terminalStatus
|
|
349
|
+
})
|
|
350
|
+
},
|
|
351
|
+
authority: {
|
|
352
|
+
dispatchIsFree: (terminalControl) => terminalListCliFacade.terminalDispatchOwnership(terminalControl).state === "none",
|
|
353
|
+
sessionClaimsTerminal: managedSessionClaimsResolvedTerminal,
|
|
354
|
+
terminalTokenMatches: (terminal, identity, token) => lifecycleBindingTokens({
|
|
355
|
+
terminal: fullTerminal(terminal),
|
|
356
|
+
identity
|
|
357
|
+
}).includes(token),
|
|
358
|
+
hasUnresolvedTransition: (session) => terminalListCliFacade.managedSessionHasUnresolvedNativeTransition(storeDir, session),
|
|
359
|
+
blockingTurn: (sessionId) => {
|
|
360
|
+
const blocker = managedTurnsForSession(storeDir, sessionId)
|
|
361
|
+
.find((turn) => isSessionSendBlockingStatus(turn.status));
|
|
362
|
+
return blocker
|
|
363
|
+
? { turnId: turnIdForConversation(blocker), status: blocker.status }
|
|
364
|
+
: undefined;
|
|
365
|
+
},
|
|
366
|
+
conflictKind: (session, terminal, identity) => managedBindingConflictKindForResolvedTerminal({
|
|
367
|
+
storeDir,
|
|
368
|
+
session,
|
|
369
|
+
terminal: fullTerminal(terminal),
|
|
370
|
+
identity
|
|
371
|
+
})
|
|
372
|
+
},
|
|
373
|
+
now: () => cliNow().toISOString(),
|
|
374
|
+
present: (result) => {
|
|
375
|
+
runtimeLog("info", "managed_binding_reconciled", {
|
|
376
|
+
terminal_id: result.terminal.conversationId,
|
|
377
|
+
terminal_target: result.terminal.terminalControl.target,
|
|
378
|
+
session_id: result.detached.session_id,
|
|
379
|
+
binding_id: result.detached.binding?.binding_id,
|
|
380
|
+
previous_revision: expectedSessionRevision,
|
|
381
|
+
revision: result.detached.revision,
|
|
382
|
+
conflict_kind: result.conflictKind,
|
|
383
|
+
terminal_input_sent: false
|
|
384
|
+
});
|
|
385
|
+
printJson({
|
|
386
|
+
status: "reconciled",
|
|
387
|
+
outcome: "detached_conflicting_binding",
|
|
388
|
+
conflict_kind: result.conflictKind,
|
|
389
|
+
terminal_id: result.terminal.conversationId,
|
|
390
|
+
session_id: result.detached.session_id,
|
|
391
|
+
binding_id: result.detached.binding?.binding_id,
|
|
392
|
+
session_revision: result.detached.revision,
|
|
393
|
+
terminal_input_sent: false,
|
|
394
|
+
turn_created: false,
|
|
395
|
+
refresh_required: true
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
return Object.freeze({
|
|
401
|
+
runNewThread, runResumeThread, runReconcileBinding,
|
|
402
|
+
verificationPorts: nativeThreadVerificationAdapterPorts,
|
|
403
|
+
assertTerminalReady: assertTerminalLifecycleReady,
|
|
404
|
+
assertTargetExclusive: assertLifecycleTargetHasExclusiveOwnership
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
function createNativeThreadTransitionRecoveryApplication(bindings, assertTargetExclusive) {
|
|
408
|
+
const { cliNow, cliSleep, terminalRuntimeForLiveIdentity, resolveCurrentNativeAgentSessionIdentity, assertNativeThreadHasExclusiveOwnership, assertSameNativeInspectionTerminal, authenticateLifecycleRecoveryResources, createTerminalAgentBridge, loadClaudeAgentRows, loadTerminalBridgeDispatchLedger, saveLifecycleTerminalDispatchLedger, observedExternalHandoffIdentity, recoverDeferredCodexForegroundTransferWhileWriterLease, codexKnownRootSetForLifecycleTransition, codexProcessBirthForLifecycle, isProcessAlive, matchesConfiguredWorkspace, terminalDispatchRecordMatchesControl, agentVersionForRunningProcess, exactLifecycleProcessIdentity } = bindings;
|
|
409
|
+
async function freshLifecycleRecoveryTerminal(scopes, resources, options, capturedTerminal) {
|
|
410
|
+
const active = authenticateLifecycleRecoveryResources(scopes, resources);
|
|
411
|
+
const terminal = await createTerminalAgentBridge(options).resolveStoredTerminal(capturedTerminal.agent, capturedTerminal.pid, active.terminalControl, { pid: capturedTerminal.pid });
|
|
412
|
+
assertSameNativeInspectionTerminal(capturedTerminal, terminal, "while waiting for lifecycle recovery");
|
|
413
|
+
if (!terminalControlsShareIncarnation(active.terminalControl, terminal.terminalControl)) {
|
|
414
|
+
throw new Error("terminal process incarnation changed while waiting for lifecycle recovery");
|
|
415
|
+
}
|
|
416
|
+
return { terminal, storeDir: active.storeDir };
|
|
417
|
+
}
|
|
418
|
+
function nativeThreadLifecycleRecoveryPorts({ options, terminal, storeDir }) {
|
|
419
|
+
const scoped = (operation) => nativeThreadTransitionResourceBoundOperation({
|
|
420
|
+
freshTerminal: terminal.terminalControl,
|
|
421
|
+
capturedStoreDir: storeDir
|
|
422
|
+
}, operation);
|
|
423
|
+
const scopedStore = (operation) => scoped((_freshControl, canonicalStoreDir, ...args) => operation(canonicalStoreDir, ...args));
|
|
424
|
+
const freshTerminal = (freshControl) => ({
|
|
425
|
+
...terminal,
|
|
426
|
+
terminalControl: freshControl
|
|
427
|
+
});
|
|
428
|
+
const canonicalOptions = (canonicalStoreDir) => Object.freeze({ ...options, storeDir: canonicalStoreDir });
|
|
429
|
+
const deferredRecoveryRoute = scoped((freshControl, canonicalStoreDir) => ({ freshControl, canonicalStoreDir }));
|
|
430
|
+
const recoveryProbe = createNativeThreadLifecycleRecoveryProbeAdapter({
|
|
431
|
+
agent: terminal.agent,
|
|
432
|
+
lifecycle: terminal.adapter,
|
|
433
|
+
createBridge: (canonicalStoreDir) => createTerminalAgentBridge(canonicalOptions(canonicalStoreDir)),
|
|
434
|
+
runtime: (freshControl, context) => lifecycleRecoveryRuntime(terminalRuntimeForLiveIdentity({
|
|
435
|
+
terminal: freshTerminal(freshControl),
|
|
436
|
+
expectedEmptyNativeSession: context.kind === "codex_recovery",
|
|
437
|
+
physicalOnly: context.kind === "physical"
|
|
438
|
+
}), context),
|
|
439
|
+
loadClaudeRows: (canonicalStoreDir) => loadClaudeAgentRows(canonicalOptions(canonicalStoreDir), { required: true })
|
|
440
|
+
});
|
|
441
|
+
const ledgerDocument = (freshControl, canonicalStoreDir, transition, phase) => lifecycleLedger(transition, canonicalStoreDir, phase.phase === "rebuild"
|
|
442
|
+
? { ...phase, control: freshControl }
|
|
443
|
+
: phase);
|
|
444
|
+
return {
|
|
445
|
+
authority: {
|
|
446
|
+
bind: scoped((freshControl) => ({ terminalControl: freshControl }))
|
|
447
|
+
},
|
|
448
|
+
persistence: {
|
|
449
|
+
listNativeThreadTransitions: scopedStore(listNativeThreadTransitions),
|
|
450
|
+
loadManagedSession: scopedStore(loadManagedSession),
|
|
451
|
+
tryLoadManagedSession: scopedStore(tryLoadManagedSession),
|
|
452
|
+
loadNativeThreadTransition: scopedStore(loadNativeThreadTransition),
|
|
453
|
+
saveManagedSession: scopedStore(saveManagedSession),
|
|
454
|
+
saveNativeThreadTransition: scopedStore(saveNativeThreadTransition),
|
|
455
|
+
commitVerified: scopedStore(commitVerifiedLifecycleTransition),
|
|
456
|
+
loadLedger: scoped((freshControl) => loadTerminalBridgeDispatchLedger(freshControl)),
|
|
457
|
+
buildLedger: scoped((freshControl, canonicalStoreDir, transition, phase) => ledgerDocument(freshControl, canonicalStoreDir, transition, phase)),
|
|
458
|
+
saveLedger: scoped((freshControl, canonicalStoreDir, transition, phase, expectation) => {
|
|
459
|
+
const ledger = ledgerDocument(freshControl, canonicalStoreDir, transition, phase);
|
|
460
|
+
if (ledger.store_dir !== canonicalStoreDir) {
|
|
461
|
+
throw new Error("lifecycle recovery ledger Store differs from its writer capability");
|
|
462
|
+
}
|
|
463
|
+
saveLifecycleTerminalDispatchLedger(freshControl, ledger, expectation);
|
|
464
|
+
}),
|
|
465
|
+
saveFailClosedLedger: scoped((freshControl, _canonicalStoreDir, ledger, transitionId, now, reason) => saveLifecycleTerminalDispatchLedger(freshControl, {
|
|
466
|
+
...ledger,
|
|
467
|
+
kind: "lifecycle",
|
|
468
|
+
generation_id: transitionId,
|
|
469
|
+
transition_id: transitionId,
|
|
470
|
+
status: "uncertain",
|
|
471
|
+
uncertain_at: now,
|
|
472
|
+
reason: reason.slice(0, 2000)
|
|
473
|
+
}, { expectedTransitionId: transitionId }))
|
|
474
|
+
},
|
|
475
|
+
terminal: {
|
|
476
|
+
recoverDeferred: (scopes, resources) => {
|
|
477
|
+
const route = deferredRecoveryRoute(scopes, resources);
|
|
478
|
+
return recoverDeferredCodexForegroundTransferWhileWriterLease({
|
|
479
|
+
options: canonicalOptions(route.canonicalStoreDir),
|
|
480
|
+
terminal: freshTerminal(route.freshControl),
|
|
481
|
+
storeDir: route.canonicalStoreDir,
|
|
482
|
+
scopes,
|
|
483
|
+
resources
|
|
484
|
+
});
|
|
485
|
+
},
|
|
486
|
+
sameTerminalIncarnation: scoped((freshControl, _storeDir, right) => terminalControlsShareIncarnation(freshControl, right)),
|
|
487
|
+
aliasMatches: scoped((freshControl, _storeDir, storedTerminalId, storedControl, currentTerminalId) => terminalControlAliasMatches(storedTerminalId, storedControl, currentTerminalId, freshControl)),
|
|
488
|
+
workspaceMatches: scoped((freshControl, _storeDir, expected) => matchesConfiguredWorkspace(expected, freshControl.currentPath)),
|
|
489
|
+
resolveIdentity: scoped((freshControl, canonicalStoreDir, preferredSessionId, companions) => resolveCurrentNativeAgentSessionIdentity({
|
|
490
|
+
options: canonicalOptions(canonicalStoreDir),
|
|
491
|
+
agent: terminal.agent,
|
|
492
|
+
pid: terminal.pid,
|
|
493
|
+
cwd: freshControl.currentPath,
|
|
494
|
+
preferredSessionId,
|
|
495
|
+
allowedCompanionIdentity: companions.primary,
|
|
496
|
+
allowedAdditionalIdentities: companions.additional
|
|
497
|
+
})),
|
|
498
|
+
observeExternalHandoff: scoped((freshControl, canonicalStoreDir, sourceSession, resolvedIdentity) => observedExternalHandoffIdentity({
|
|
499
|
+
options: canonicalOptions(canonicalStoreDir),
|
|
500
|
+
terminal: freshTerminal(freshControl),
|
|
501
|
+
sourceSession,
|
|
502
|
+
resolvedIdentity
|
|
503
|
+
}).then((observed) => observed.identity)),
|
|
504
|
+
assertExclusive: scoped((freshControl, canonicalStoreDir, nativeThreadId, excludedManagedSessionId) => assertNativeThreadHasExclusiveOwnership({
|
|
505
|
+
options: canonicalOptions(canonicalStoreDir),
|
|
506
|
+
agent: terminal.agent,
|
|
507
|
+
currentPid: terminal.pid,
|
|
508
|
+
nativeThreadId,
|
|
509
|
+
storeDir: canonicalStoreDir,
|
|
510
|
+
terminalControl: freshControl,
|
|
511
|
+
excludedManagedSessionId
|
|
512
|
+
})),
|
|
513
|
+
assertTargetExclusive: scoped((freshControl, canonicalStoreDir, transition) => assertTargetExclusive({
|
|
514
|
+
options: canonicalOptions(canonicalStoreDir),
|
|
515
|
+
terminal: freshTerminal(freshControl),
|
|
516
|
+
transition,
|
|
517
|
+
storeDir: canonicalStoreDir
|
|
518
|
+
})),
|
|
519
|
+
exactIdentity: scoped((freshControl, _storeDir, identity) => exactLifecycleProcessIdentity(freshTerminal(freshControl), identity)),
|
|
520
|
+
isProcessAlive: scoped((_freshControl, _storeDir, pid) => isProcessAlive(pid)),
|
|
521
|
+
recordedStoreMatches: scoped((_freshControl, canonicalStoreDir, recordedStoreDir) => path.resolve(recordedStoreDir) === canonicalStoreDir),
|
|
522
|
+
recordMatchesControl: scoped((freshControl, _storeDir, ledger) => terminalDispatchRecordMatchesControl(ledger, freshControl)),
|
|
523
|
+
runningVersion: scoped((_freshControl, canonicalStoreDir) => agentVersionForRunningProcess(terminal.agent, terminal.pid, canonicalOptions(canonicalStoreDir))),
|
|
524
|
+
probeThreadLifecycle: scoped((_freshControl, _storeDir, version) => recoveryProbe.probe(version)),
|
|
525
|
+
planThreadLifecycle: scoped((_freshControl, _storeDir, operation, facts) => recoveryProbe.plan(operation, facts)),
|
|
526
|
+
observeThreadLifecycle: scoped((freshControl, canonicalStoreDir, request) => recoveryProbe.observe(freshControl, canonicalStoreDir, request)),
|
|
527
|
+
prepareProbeBridge: scoped((_freshControl, canonicalStoreDir) => recoveryProbe.prepare(canonicalStoreDir)),
|
|
528
|
+
status: scoped((freshControl, _storeDir, context, scrollbackLines) => recoveryProbe.status(freshControl, context, scrollbackLines)),
|
|
529
|
+
clearInputLine: scoped((freshControl, _storeDir, context) => recoveryProbe.clearInputLine(freshControl, context)),
|
|
530
|
+
submitCodexStatusProbe: scoped((freshControl, _storeDir, version, context) => recoveryProbe.submitCodexStatusProbe(freshControl, version, context)),
|
|
531
|
+
assertResumedCodexCandidate: scoped((_freshControl, _storeDir, identity, expected) => assertResumedCodexRolloutMatchesCandidate(identity, expected)),
|
|
532
|
+
codexKnownRoots: scoped((freshControl, canonicalStoreDir, transition) => codexKnownRootSetForLifecycleTransition({
|
|
533
|
+
storeDir: canonicalStoreDir,
|
|
534
|
+
terminal: freshTerminal(freshControl),
|
|
535
|
+
transition
|
|
536
|
+
})),
|
|
537
|
+
codexCompanionsExcludingPreferred: scoped((_freshControl, _storeDir, roots, preferredSessionId) => codexCompanionsExcludingPreferred(roots, preferredSessionId)),
|
|
538
|
+
codexProcessBirth: scoped(() => codexProcessBirthForLifecycle(terminal.pid)),
|
|
539
|
+
nativeIdentityMatches: scoped((_freshControl, _storeDir, identity, expected) => nativeIdentityMatchesCodexPreMaterialization(identity, expected))
|
|
540
|
+
},
|
|
541
|
+
runtime: {
|
|
542
|
+
now: cliNow,
|
|
543
|
+
sleep: cliSleep
|
|
544
|
+
}
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
async function recoverLifecycleFenceBeforeMutationScoped(scopes, resources, options, capturedTerminal) {
|
|
548
|
+
const fresh = await freshLifecycleRecoveryTerminal(scopes, resources, options, capturedTerminal);
|
|
549
|
+
const serviceTerminal = lifecycleRecoveryTerminalFacts(fresh.terminal);
|
|
550
|
+
await recoverLifecycleFenceBeforeMutationService({ terminal: serviceTerminal }, scopes, resources, nativeThreadLifecycleRecoveryPorts({
|
|
551
|
+
options,
|
|
552
|
+
terminal: fresh.terminal,
|
|
553
|
+
storeDir: fresh.storeDir
|
|
554
|
+
}));
|
|
555
|
+
}
|
|
556
|
+
async function reconcileLifecycleDispatchLedgerScoped(scopes, resources, options, capturedTerminal, ledger, authority = { kind: "automatic" }) {
|
|
557
|
+
const fresh = await freshLifecycleRecoveryTerminal(scopes, resources, options, capturedTerminal);
|
|
558
|
+
const serviceTerminal = lifecycleRecoveryTerminalFacts(fresh.terminal);
|
|
559
|
+
return reconcileLifecycleDispatchLedgerService({ terminal: serviceTerminal, ledger, authority }, scopes, resources, nativeThreadLifecycleRecoveryPorts({
|
|
560
|
+
options,
|
|
561
|
+
terminal: fresh.terminal,
|
|
562
|
+
storeDir: fresh.storeDir
|
|
563
|
+
}));
|
|
564
|
+
}
|
|
565
|
+
return Object.freeze({
|
|
566
|
+
recoverBeforeMutation: recoverLifecycleFenceBeforeMutationScoped,
|
|
567
|
+
reconcileLedger: reconcileLifecycleDispatchLedgerScoped
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
function nativeThreadTransitionSettlementPorts(bindings, assertTargetExclusive, { options, operation, terminal, storeDir, targetSession, targetSessionId, sourceBefore, beforeIdentity, verificationPorts }) {
|
|
571
|
+
const { saveLifecycleTerminalDispatchLedger, exactLifecycleProcessIdentity, cliCwd, cliNow, cliEnv, cliExit, textSummary, printJson } = bindings;
|
|
572
|
+
const scoped = (operation) => nativeThreadTransitionResourceBoundOperation({
|
|
573
|
+
freshTerminal: terminal.terminalControl,
|
|
574
|
+
capturedStoreDir: storeDir
|
|
575
|
+
}, operation);
|
|
576
|
+
return {
|
|
577
|
+
persistence: {
|
|
578
|
+
saveLedger: scoped((freshTerminal, canonicalStoreDir, value, phase, expectation) => {
|
|
579
|
+
const ledger = lifecycleLedger(value, canonicalStoreDir, phase);
|
|
580
|
+
if (ledger.store_dir !== canonicalStoreDir) {
|
|
581
|
+
throw new Error("lifecycle dispatch ledger Store differs from its writer capability");
|
|
582
|
+
}
|
|
583
|
+
saveLifecycleTerminalDispatchLedger(freshTerminal, ledger, expectation);
|
|
584
|
+
}),
|
|
585
|
+
loadTransition: scoped((_freshTerminal, canonicalStoreDir, id) => loadNativeThreadTransition(canonicalStoreDir, id)),
|
|
586
|
+
saveTransition: scoped((_freshTerminal, canonicalStoreDir, value, expectation) => saveNativeThreadTransition(canonicalStoreDir, value, expectation)),
|
|
587
|
+
saveSession: scoped((_freshTerminal, canonicalStoreDir, value, expectation) => saveManagedSession(canonicalStoreDir, value, expectation)),
|
|
588
|
+
commitVerified: scoped((_freshTerminal, canonicalStoreDir, value, verifiedAt) => commitVerifiedLifecycleTransition(canonicalStoreDir, value, verifiedAt))
|
|
589
|
+
},
|
|
590
|
+
effects: {
|
|
591
|
+
finalizeIdentity: (observed, durableTransition, verifiedAt) => {
|
|
592
|
+
const identity = exactLifecycleProcessIdentity(terminal, observed);
|
|
593
|
+
if (terminal.agent === "codex" &&
|
|
594
|
+
operation.kind === "resume_thread") {
|
|
595
|
+
assertResumedCodexRolloutMatchesCandidate(identity, durableTransition.target_candidate_file_identity);
|
|
596
|
+
}
|
|
597
|
+
return {
|
|
598
|
+
identity,
|
|
599
|
+
binding: terminalBindingFrom({
|
|
600
|
+
terminalId: terminal.conversationId,
|
|
601
|
+
terminalControl: terminal.terminalControl,
|
|
602
|
+
pid: terminal.pid,
|
|
603
|
+
nativeThreadId: identity.sessionId,
|
|
604
|
+
processUuid: identity.processUuid,
|
|
605
|
+
processBirth: identity.processBirth,
|
|
606
|
+
rollout: identity.rollout,
|
|
607
|
+
evidence: identity.evidence,
|
|
608
|
+
generation: (targetSession?.binding?.generation ?? 0) + 1,
|
|
609
|
+
now: verifiedAt
|
|
610
|
+
})
|
|
611
|
+
};
|
|
612
|
+
},
|
|
613
|
+
assertTargetOwnership: scoped((_freshTerminal, canonicalStoreDir, durableTransition) => assertTargetExclusive({
|
|
614
|
+
options,
|
|
615
|
+
terminal,
|
|
616
|
+
transition: durableTransition,
|
|
617
|
+
storeDir: canonicalStoreDir
|
|
618
|
+
})),
|
|
619
|
+
targetConflictWorkspace: () => terminal.terminalControl.currentPath ?? cliCwd()
|
|
620
|
+
},
|
|
621
|
+
runtime: {
|
|
622
|
+
now: cliNow,
|
|
623
|
+
crashAfterVerified: () => {
|
|
624
|
+
if (cliEnv().AKK_TEST_EXIT_AFTER_LIFECYCLE_VERIFIED === "1") {
|
|
625
|
+
cliExit(87);
|
|
626
|
+
}
|
|
627
|
+
},
|
|
628
|
+
injectTargetConflict: () => cliEnv().AKK_TEST_INJECT_LIFECYCLE_TARGET_CONFLICT === "1",
|
|
629
|
+
errorProvesInputNotStarted: (error) => error instanceof TerminalInputNotStartedError,
|
|
630
|
+
summarizeError: textSummary
|
|
631
|
+
},
|
|
632
|
+
verification: scoped((_freshTerminal, _canonicalStoreDir, request) => verifyNativeThreadTransitionWithRuntime(request, terminal, verificationPorts)),
|
|
633
|
+
present: (result) => {
|
|
634
|
+
if (result.status === "committed") {
|
|
635
|
+
printJson({
|
|
636
|
+
status: "committed",
|
|
637
|
+
transition_id: result.transition.transition_id,
|
|
638
|
+
operation: operation.kind,
|
|
639
|
+
terminal_id: terminal.conversationId,
|
|
640
|
+
previous_session_id: sourceBefore?.session_id ?? null,
|
|
641
|
+
session_id: targetSessionId,
|
|
642
|
+
previous_native_thread_id: beforeIdentity.sessionId ??
|
|
643
|
+
sourceBefore?.binding?.native_thread_id ??
|
|
644
|
+
null,
|
|
645
|
+
native_thread_id: result.identity.sessionId,
|
|
646
|
+
binding_id: result.binding.binding_id,
|
|
647
|
+
binding_generation: result.binding.generation,
|
|
648
|
+
binding_token: managedSessionBindingToken(result.committedTarget),
|
|
649
|
+
turn_created: false
|
|
650
|
+
});
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
653
|
+
printJson({
|
|
654
|
+
status: result.status,
|
|
655
|
+
transition_id: result.transitionId,
|
|
656
|
+
operation: operation.kind,
|
|
657
|
+
terminal_id: terminal.conversationId,
|
|
658
|
+
do_not_retry: true,
|
|
659
|
+
turn_created: false,
|
|
660
|
+
reason: result.reason
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
async function runNativeThreadTransition(bindings, commands, recovery, options, operation) {
|
|
666
|
+
const { resolveLifecycleTerminal, storeDirFromOptions, terminalWriterMutationLocks, cliNowMs, cliCwd, loadTerminalBridgeDispatchLedger, currentLifecycleSnapshot, nativeThreadLifecycleFacade, nativeThreadLifecycleQueryPorts, assertNativeThreadHasExclusiveOwnership, assertCodexComposerReadyForAutomatedInput, cliNow, cliPid, saveLifecycleTerminalDispatchLedger, managedTurnsForSession, managedSessionOwnerIsConclusivelyInactive, printJson, cliEnv, cliExit, exactLifecycleProcessIdentity } = bindings;
|
|
667
|
+
const initiallyResolved = await resolveLifecycleTerminal(options);
|
|
668
|
+
const storeDir = storeDirFromOptions(options);
|
|
669
|
+
return withCanonicalMutationLocks(terminalWriterMutationLocks(storeDir, initiallyResolved.terminalControl), async (scopes, resources) => {
|
|
670
|
+
const terminal = await resolveLifecycleTerminal(options);
|
|
671
|
+
if (terminal.pid !== initiallyResolved.pid ||
|
|
672
|
+
!terminalControlsShareIncarnation(terminal.terminalControl, initiallyResolved.terminalControl)) {
|
|
673
|
+
throw new Error("terminal identity changed while waiting for lifecycle control; refresh list");
|
|
674
|
+
}
|
|
675
|
+
if (operation.kind === "resume_thread" && operation.selectionSnapshot) {
|
|
676
|
+
assertResumeSnapshotNotExpired(operation.selectionSnapshot, cliNowMs);
|
|
677
|
+
assertResumeSnapshotMatchesTerminal(operation.selectionSnapshot, terminal, cliCwd);
|
|
678
|
+
assertResumeSnapshotActionFingerprint(operation.selectionSnapshot, loadTerminalBridgeDispatchLedger(terminal.terminalControl));
|
|
679
|
+
}
|
|
680
|
+
await recovery.recoverBeforeMutation(scopes, resources, options, terminal);
|
|
681
|
+
if (operation.kind === "resume_thread" && operation.selectionSnapshot) {
|
|
682
|
+
// Recovery may resolve or otherwise rewrite a durable dispatch fence.
|
|
683
|
+
// A snapshot created before that mutation is stale even when the
|
|
684
|
+
// binding and candidate files themselves did not change.
|
|
685
|
+
assertResumeSnapshotActionFingerprint(operation.selectionSnapshot, loadTerminalBridgeDispatchLedger(terminal.terminalControl));
|
|
686
|
+
}
|
|
687
|
+
const snapshot = await currentLifecycleSnapshot(options, terminal, { materialize: true });
|
|
688
|
+
const expectedToken = required(stringValue(options.expectedBindingToken), "--expected-binding-token is required");
|
|
689
|
+
const verificationPorts = commands.verificationPorts(options, terminal);
|
|
690
|
+
const selectionSnapshot = operation.kind === "resume_thread"
|
|
691
|
+
? operation.selectionSnapshot
|
|
692
|
+
: undefined;
|
|
693
|
+
const preparedVerification = await prepareNativeThreadVerification({
|
|
694
|
+
operation,
|
|
695
|
+
expectedBindingToken: expectedToken,
|
|
696
|
+
bindingTokens: snapshot.bindingTokens,
|
|
697
|
+
capabilities: snapshot.capabilities,
|
|
698
|
+
beforeIdentity: snapshot.identity,
|
|
699
|
+
physicalBeforeIdentity: snapshot.runtimeIdentity,
|
|
700
|
+
allowedCompanionIdentity: snapshot.codexCompanions.primary,
|
|
701
|
+
allowedAdditionalIdentities: snapshot.codexCompanions.additional
|
|
702
|
+
}, terminal, {
|
|
703
|
+
...verificationPorts,
|
|
704
|
+
plan: () => nativeThreadLifecycleFacade.agentAdapter(options, terminal.agent).planThreadLifecycle?.(operation, snapshot.capabilities),
|
|
705
|
+
assertReady: (status) => commands.assertTerminalReady({ options, terminal, terminalStatus: status }),
|
|
706
|
+
...(selectionSnapshot
|
|
707
|
+
? {
|
|
708
|
+
revalidateSelectionSnapshot: async () => {
|
|
709
|
+
const candidates = await resumableNativeThreadCandidates({
|
|
710
|
+
terminal,
|
|
711
|
+
currentIdentity: snapshot.identity
|
|
712
|
+
}, nativeThreadLifecycleQueryPorts(options));
|
|
713
|
+
assertResumeSnapshotCandidates(selectionSnapshot, candidates);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
: {}),
|
|
717
|
+
finalizeIdentity: (identity) => exactLifecycleProcessIdentity(terminal, identity)
|
|
718
|
+
});
|
|
719
|
+
const { bridge, plan, beforeIdentity, beforeRuntime } = preparedVerification;
|
|
720
|
+
const restorableOriginCandidateToken = operation.kind === "new_thread" &&
|
|
721
|
+
operation.requireRestorableOrigin
|
|
722
|
+
? await requireRestorableLifecycleOriginFromQuery({
|
|
723
|
+
terminal,
|
|
724
|
+
currentIdentity: beforeIdentity,
|
|
725
|
+
currentSession: snapshot.session,
|
|
726
|
+
agentVersion: required(stringValue(snapshot.version), "restorable origin requires the exact running agent version")
|
|
727
|
+
}, nativeThreadLifecycleQueryPorts({ ...options, storeDir }))
|
|
728
|
+
: undefined;
|
|
729
|
+
let candidates = [];
|
|
730
|
+
let targetSession;
|
|
731
|
+
let resumeCandidateToken;
|
|
732
|
+
if (operation.kind === "resume_thread") {
|
|
733
|
+
const expectedCandidateToken = required(stringValue(options.candidateToken), "--candidate-token is required");
|
|
734
|
+
if (beforeIdentity.sessionId === operation.nativeThreadId) {
|
|
735
|
+
printJson({
|
|
736
|
+
status: "already_active",
|
|
737
|
+
no_op: true,
|
|
738
|
+
terminal_id: terminal.conversationId,
|
|
739
|
+
session_id: snapshot.session?.session_id ?? null,
|
|
740
|
+
native_thread_id: operation.nativeThreadId,
|
|
741
|
+
binding_token: snapshot.bindingToken
|
|
742
|
+
});
|
|
743
|
+
return;
|
|
744
|
+
}
|
|
745
|
+
candidates = await resumableNativeThreadCandidates({
|
|
746
|
+
terminal,
|
|
747
|
+
currentIdentity: beforeIdentity
|
|
748
|
+
}, nativeThreadLifecycleQueryPorts(options));
|
|
749
|
+
if (operation.selectionSnapshot) {
|
|
750
|
+
// The Codex identity probe and other readiness checks above are
|
|
751
|
+
// asynchronous. Rebind the whole displayed snapshot, not only its
|
|
752
|
+
// selected UUID, after those checks and before any lifecycle state
|
|
753
|
+
// is persisted.
|
|
754
|
+
assertResumeSnapshotCandidates(operation.selectionSnapshot, candidates);
|
|
755
|
+
}
|
|
756
|
+
const candidate = candidates.find((entry) => entry.native_thread_id === operation.nativeThreadId);
|
|
757
|
+
const candidateDecision = decideResumeCandidateEligibility({
|
|
758
|
+
candidate,
|
|
759
|
+
expectedCandidateToken
|
|
760
|
+
});
|
|
761
|
+
if (candidateDecision.action === "reject") {
|
|
762
|
+
throw new Error(candidateDecision.reason === "candidate_not_found"
|
|
763
|
+
? `native thread ${operation.nativeThreadId} is not a verified same-workspace candidate`
|
|
764
|
+
: candidateDecision.reason === "candidate_not_resumable"
|
|
765
|
+
? `native thread ${operation.nativeThreadId} cannot be resumed: ` +
|
|
766
|
+
`${candidate?.unavailable_reason ?? "unavailable"}`
|
|
767
|
+
: "resume candidate changed after it was listed; refresh resumable threads and retry");
|
|
768
|
+
}
|
|
769
|
+
const eligibleCandidate = candidateDecision.candidate;
|
|
770
|
+
resumeCandidateToken = await revalidateNativeThreadCandidateFromQuery({
|
|
771
|
+
terminal,
|
|
772
|
+
nativeThreadId: operation.nativeThreadId,
|
|
773
|
+
encodedToken: expectedCandidateToken,
|
|
774
|
+
agentVersion: snapshot.version
|
|
775
|
+
}, nativeThreadLifecycleQueryPorts(options));
|
|
776
|
+
if (eligibleCandidate.managed_session_id) {
|
|
777
|
+
targetSession = tryLoadManagedSession(storeDir, eligibleCandidate.managed_session_id);
|
|
778
|
+
const targetBlockers = managedTurnsForSession(storeDir, eligibleCandidate.managed_session_id).filter((turn) => isSessionSendBlockingStatus(turn.status));
|
|
779
|
+
const targetDecision = decideResumeTargetSession({
|
|
780
|
+
hasUnresolvedTurn: targetBlockers.length > 0,
|
|
781
|
+
loadedSession: targetSession,
|
|
782
|
+
boundOwnerConclusivelyInactive: Boolean(targetBlockers.length === 0 && targetSession?.status === "bound" &&
|
|
783
|
+
managedSessionOwnerIsConclusivelyInactive({
|
|
784
|
+
session: targetSession,
|
|
785
|
+
terminal,
|
|
786
|
+
identity: beforeIdentity
|
|
787
|
+
}))
|
|
788
|
+
});
|
|
789
|
+
if (targetDecision.action === "reject" &&
|
|
790
|
+
targetDecision.reason === "unresolved_turn") {
|
|
791
|
+
throw new Error(`target Session ${eligibleCandidate.managed_session_id} has unresolved Turn ` +
|
|
792
|
+
`${turnIdForConversation(targetBlockers[0])}`);
|
|
793
|
+
}
|
|
794
|
+
if (targetDecision.action === "reject") {
|
|
795
|
+
throw new Error(`target Session ${eligibleCandidate.managed_session_id} is still bound to a live or unverifiable process`);
|
|
796
|
+
}
|
|
797
|
+
if (targetDecision.action === "detach_stale_binding" &&
|
|
798
|
+
targetSession) {
|
|
799
|
+
const detachedAt = cliNow().toISOString();
|
|
800
|
+
targetSession = saveManagedSession(storeDir, {
|
|
801
|
+
...targetSession,
|
|
802
|
+
status: "detached",
|
|
803
|
+
detached_at: detachedAt,
|
|
804
|
+
updated_at: detachedAt
|
|
805
|
+
}, { expectedRevision: managedSessionRevision(targetSession) });
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
const now = cliNow();
|
|
810
|
+
const transitionId = createNativeThreadTransitionId();
|
|
811
|
+
const targetSessionId = targetSession?.session_id ??
|
|
812
|
+
createManagedSessionId(now);
|
|
813
|
+
const sourceBefore = snapshot.session;
|
|
814
|
+
const previousLedger = loadTerminalBridgeDispatchLedger(terminal.terminalControl);
|
|
815
|
+
let transition = prepareNativeThreadTransition({
|
|
816
|
+
transitionId,
|
|
817
|
+
operation,
|
|
818
|
+
terminalId: terminal.conversationId,
|
|
819
|
+
agent: terminal.agent,
|
|
820
|
+
workspace: terminal.terminalControl.currentPath ?? cliCwd(),
|
|
821
|
+
source: sourceBefore
|
|
822
|
+
? {
|
|
823
|
+
state: sourceBefore,
|
|
824
|
+
revision: managedSessionRevision(sourceBefore)
|
|
825
|
+
}
|
|
826
|
+
: undefined,
|
|
827
|
+
targetSessionId,
|
|
828
|
+
target: targetSession
|
|
829
|
+
? {
|
|
830
|
+
state: targetSession,
|
|
831
|
+
revision: managedSessionRevision(targetSession)
|
|
832
|
+
}
|
|
833
|
+
: undefined,
|
|
834
|
+
candidateFileIdentity: terminal.agent === "codex" && operation.kind === "resume_thread"
|
|
835
|
+
? codexCandidateFileIdentity(resumeCandidateToken)
|
|
836
|
+
: undefined,
|
|
837
|
+
beforeIdentity,
|
|
838
|
+
beforeProcessUuid: beforeIdentity.processUuid,
|
|
839
|
+
beforeBinding: snapshot.session?.binding,
|
|
840
|
+
adapterVersion: snapshot.version,
|
|
841
|
+
commandFingerprint: nativeThreadCommandFingerprint(JSON.stringify(plan.steps)),
|
|
842
|
+
dispatcherPid: cliPid(),
|
|
843
|
+
preparedAt: now.toISOString()
|
|
844
|
+
});
|
|
845
|
+
transition = saveNativeThreadTransition(storeDir, transition, {
|
|
846
|
+
expectedRevision: null
|
|
847
|
+
});
|
|
848
|
+
saveLifecycleTerminalDispatchLedger(terminal.terminalControl, lifecycleLedger(transition, storeDir, { phase: "command_prepared", previous: previousLedger }), {
|
|
849
|
+
expectedTransitionId: null
|
|
850
|
+
});
|
|
851
|
+
const settlementPorts = nativeThreadTransitionSettlementPorts(bindings, commands.assertTargetExclusive, {
|
|
852
|
+
options,
|
|
853
|
+
operation,
|
|
854
|
+
terminal,
|
|
855
|
+
storeDir,
|
|
856
|
+
targetSession,
|
|
857
|
+
targetSessionId,
|
|
858
|
+
sourceBefore,
|
|
859
|
+
beforeIdentity,
|
|
860
|
+
verificationPorts
|
|
861
|
+
});
|
|
862
|
+
let inputStarted = false;
|
|
863
|
+
let sourceTransitioning;
|
|
864
|
+
try {
|
|
865
|
+
if (cliEnv().AKK_TEST_EXIT_AFTER_LIFECYCLE_PREPARED === "1") {
|
|
866
|
+
cliExit(86);
|
|
867
|
+
}
|
|
868
|
+
if (sourceBefore) {
|
|
869
|
+
sourceTransitioning = saveManagedSession(storeDir, {
|
|
870
|
+
...sourceBefore,
|
|
871
|
+
status: "transitioning",
|
|
872
|
+
last_transition_id: transitionId,
|
|
873
|
+
updated_at: now.toISOString()
|
|
874
|
+
}, { expectedRevision: managedSessionRevision(sourceBefore) });
|
|
875
|
+
}
|
|
876
|
+
const dispatchingAt = cliNow().toISOString();
|
|
877
|
+
transition = reduceNativeThreadTransitionPhase(transition, {
|
|
878
|
+
type: "dispatch_started",
|
|
879
|
+
at: dispatchingAt
|
|
880
|
+
});
|
|
881
|
+
transition = saveNativeThreadTransition(storeDir, transition, {
|
|
882
|
+
expectedRevision: nativeThreadTransitionRevision(transition)
|
|
883
|
+
});
|
|
884
|
+
saveLifecycleTerminalDispatchLedger(terminal.terminalControl, lifecycleLedger(transition, storeDir, { phase: "command_dispatching", previous: previousLedger }), {
|
|
885
|
+
expectedTransitionId: transitionId,
|
|
886
|
+
expectedStatus: "prepared"
|
|
887
|
+
});
|
|
888
|
+
const transitionStep = plan.steps.find((step) => step.kind === "transition");
|
|
889
|
+
if (!transitionStep || transitionStep.effect !== "thread_transition") {
|
|
890
|
+
throw new Error("the adapter lifecycle plan has no exact transition step");
|
|
891
|
+
}
|
|
892
|
+
if (restorableOriginCandidateToken) {
|
|
893
|
+
assertRestorableOriginSessionRelationshipFromQuery({
|
|
894
|
+
agent: terminal.agent,
|
|
895
|
+
nativeThreadId: beforeIdentity.sessionId.toLowerCase(),
|
|
896
|
+
currentSession: snapshot.session
|
|
897
|
+
}, nativeThreadLifecycleQueryPorts({ ...options, storeDir }));
|
|
898
|
+
await assertNativeThreadHasExclusiveOwnership({
|
|
899
|
+
options,
|
|
900
|
+
agent: terminal.agent,
|
|
901
|
+
currentPid: terminal.pid,
|
|
902
|
+
nativeThreadId: beforeIdentity.sessionId.toLowerCase(),
|
|
903
|
+
storeDir,
|
|
904
|
+
terminalControl: terminal.terminalControl,
|
|
905
|
+
excludedManagedSessionId: snapshot.session?.session_id
|
|
906
|
+
});
|
|
907
|
+
await revalidateNativeThreadCandidateFromQuery({
|
|
908
|
+
terminal,
|
|
909
|
+
nativeThreadId: beforeIdentity.sessionId.toLowerCase(),
|
|
910
|
+
encodedToken: restorableOriginCandidateToken,
|
|
911
|
+
agentVersion: required(stringValue(snapshot.version), "restorable origin requires the exact running agent version")
|
|
912
|
+
}, nativeThreadLifecycleQueryPorts({ ...options, storeDir }));
|
|
913
|
+
}
|
|
914
|
+
if (terminal.agent === "codex") {
|
|
915
|
+
// Candidate discovery and ownership revalidation can take long
|
|
916
|
+
// enough for a human to start typing after the /status probe. Keep
|
|
917
|
+
// this check immediately adjacent to the terminal mutation.
|
|
918
|
+
try {
|
|
919
|
+
await assertCodexComposerReadyForAutomatedInput({
|
|
920
|
+
options,
|
|
921
|
+
terminalControl: terminal.terminalControl
|
|
922
|
+
});
|
|
923
|
+
}
|
|
924
|
+
catch (error) {
|
|
925
|
+
throw new TerminalInputNotStartedError(error instanceof Error ? error.message : String(error), { cause: error });
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
if (operation.kind === "resume_thread" && operation.selectionSnapshot) {
|
|
929
|
+
assertResumeSnapshotNotExpired(operation.selectionSnapshot, cliNowMs);
|
|
930
|
+
}
|
|
931
|
+
await bridge.send(terminal.agent, terminal.terminalControl, transitionStep.command, {
|
|
932
|
+
runtime: beforeRuntime,
|
|
933
|
+
onTransportStage(event) {
|
|
934
|
+
if (event.stage === "text_injected") {
|
|
935
|
+
inputStarted = true;
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
});
|
|
939
|
+
const submittedAt = cliNow().toISOString();
|
|
940
|
+
transition = reduceNativeThreadTransitionPhase(transition, {
|
|
941
|
+
type: "submission_recorded",
|
|
942
|
+
at: submittedAt
|
|
943
|
+
});
|
|
944
|
+
transition = saveNativeThreadTransition(storeDir, transition, {
|
|
945
|
+
expectedRevision: nativeThreadTransitionRevision(transition)
|
|
946
|
+
});
|
|
947
|
+
saveLifecycleTerminalDispatchLedger(terminal.terminalControl, lifecycleLedger(transition, storeDir, { phase: "command_submitted", previous: previousLedger }), {
|
|
948
|
+
expectedTransitionId: transitionId,
|
|
949
|
+
expectedStatus: "dispatching"
|
|
950
|
+
});
|
|
951
|
+
await settleVerifiedNativeThreadTransition({
|
|
952
|
+
transition,
|
|
953
|
+
verification: {
|
|
954
|
+
operation,
|
|
955
|
+
plan,
|
|
956
|
+
beforeIdentity,
|
|
957
|
+
physicalBeforeIdentity: snapshot.runtimeIdentity,
|
|
958
|
+
allowedCompanionIdentity: snapshot.codexCompanions.primary,
|
|
959
|
+
allowedAdditionalIdentities: snapshot.codexCompanions.additional,
|
|
960
|
+
initialScreenDigest: preparedVerification.initialScreenDigest
|
|
961
|
+
}
|
|
962
|
+
}, scopes, resources, settlementPorts);
|
|
963
|
+
}
|
|
964
|
+
catch (error) {
|
|
965
|
+
await settleFailedNativeThreadTransition({
|
|
966
|
+
transitionId,
|
|
967
|
+
inputStarted,
|
|
968
|
+
sourceBefore,
|
|
969
|
+
sourceTransitioning
|
|
970
|
+
}, error, scopes, resources, settlementPorts);
|
|
971
|
+
}
|
|
972
|
+
});
|
|
973
|
+
}
|
|
974
|
+
//# sourceMappingURL=native-thread-transition-application.js.map
|