@scotthuang/agent-knock-knock 0.13.15 → 0.13.17

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.
Files changed (213) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +2 -1
  3. package/dist/src/claude-terminal-agent-adapter.js +1 -1
  4. package/dist/src/claude-terminal-agent-adapter.js.map +1 -1
  5. package/dist/src/cli-core.js +6 -3
  6. package/dist/src/cli-core.js.map +1 -1
  7. package/dist/src/codex-app-server-interaction-client.js +68 -26
  8. package/dist/src/codex-app-server-interaction-client.js.map +1 -1
  9. package/dist/src/codex-app-server-read-client.js +52 -20
  10. package/dist/src/codex-app-server-read-client.js.map +1 -1
  11. package/dist/src/codex-astra-composer-proof.js +1 -1
  12. package/dist/src/codex-astra-composer-proof.js.map +1 -1
  13. package/dist/src/codex-open-rollout-inventory.d.ts +51 -0
  14. package/dist/src/codex-open-rollout-inventory.js +365 -0
  15. package/dist/src/codex-open-rollout-inventory.js.map +1 -0
  16. package/dist/src/codex-paginated-thread-binding.js +1 -1
  17. package/dist/src/codex-paginated-thread-binding.js.map +1 -1
  18. package/dist/src/codex-permission-surface.d.ts +23 -0
  19. package/dist/src/codex-permission-surface.js +211 -0
  20. package/dist/src/codex-permission-surface.js.map +1 -0
  21. package/dist/src/codex-rollout-bound-completion.d.ts +44 -0
  22. package/dist/src/codex-rollout-bound-completion.js +357 -0
  23. package/dist/src/codex-rollout-bound-completion.js.map +1 -0
  24. package/dist/src/codex-rollout-file.d.ts +15 -0
  25. package/dist/src/codex-rollout-file.js +98 -0
  26. package/dist/src/codex-rollout-file.js.map +1 -0
  27. package/dist/src/codex-rollout-human-task.d.ts +61 -0
  28. package/dist/src/codex-rollout-human-task.js +555 -0
  29. package/dist/src/codex-rollout-human-task.js.map +1 -0
  30. package/dist/src/codex-rollout-interaction-evidence.d.ts +48 -0
  31. package/dist/src/codex-rollout-interaction-evidence.js +521 -0
  32. package/dist/src/codex-rollout-interaction-evidence.js.map +1 -0
  33. package/dist/src/codex-rollout-inventory.d.ts +5 -0
  34. package/dist/src/codex-rollout-inventory.js +81 -0
  35. package/dist/src/codex-rollout-inventory.js.map +1 -0
  36. package/dist/src/codex-rollout-records.d.ts +49 -0
  37. package/dist/src/codex-rollout-records.js +298 -0
  38. package/dist/src/codex-rollout-records.js.map +1 -0
  39. package/dist/src/codex-sqlite-thread-query.d.ts +85 -0
  40. package/dist/src/codex-sqlite-thread-query.js +440 -0
  41. package/dist/src/codex-sqlite-thread-query.js.map +1 -0
  42. package/dist/src/codex-status-history-navigation.d.ts +1 -1
  43. package/dist/src/codex-status-history-navigation.js +1 -1
  44. package/dist/src/codex-status-history-navigation.js.map +1 -1
  45. package/dist/src/codex-store-adapter.d.ts +3 -50
  46. package/dist/src/codex-store-adapter.js +5 -796
  47. package/dist/src/codex-store-adapter.js.map +1 -1
  48. package/dist/src/codex-terminal-agent-adapter.js +1 -1
  49. package/dist/src/codex-terminal-agent-adapter.js.map +1 -1
  50. package/dist/src/native-thread-lifecycle-cli-adapter.d.ts +7 -136
  51. package/dist/src/native-thread-lifecycle-cli-adapter.js +39 -633
  52. package/dist/src/native-thread-lifecycle-cli-adapter.js.map +1 -1
  53. package/dist/src/native-thread-lifecycle-cli-contract.d.ts +138 -0
  54. package/dist/src/native-thread-lifecycle-cli-contract.js +2 -0
  55. package/dist/src/native-thread-lifecycle-cli-contract.js.map +1 -0
  56. package/dist/src/semantic-permission-control.d.ts +19 -0
  57. package/dist/src/semantic-permission-control.js +201 -0
  58. package/dist/src/semantic-permission-control.js.map +1 -0
  59. package/dist/src/semantic-tool-command-helpers.d.ts +7 -0
  60. package/dist/src/semantic-tool-command-helpers.js +29 -1
  61. package/dist/src/semantic-tool-command-helpers.js.map +1 -1
  62. package/dist/src/semantic-tool-descriptions.d.ts +26 -0
  63. package/dist/src/semantic-tool-descriptions.js +259 -0
  64. package/dist/src/semantic-tool-descriptions.js.map +1 -0
  65. package/dist/src/semantic-tool-list-projection.js +4 -0
  66. package/dist/src/semantic-tool-list-projection.js.map +1 -1
  67. package/dist/src/semantic-tool-presentation.js +5 -2
  68. package/dist/src/semantic-tool-presentation.js.map +1 -1
  69. package/dist/src/semantic-tool-runtime.js +50 -36
  70. package/dist/src/semantic-tool-runtime.js.map +1 -1
  71. package/dist/src/semantic-tool-schemas.d.ts +33 -0
  72. package/dist/src/semantic-tool-schemas.js +165 -31
  73. package/dist/src/semantic-tool-schemas.js.map +1 -1
  74. package/dist/src/terminal-action-contracts.d.ts +4 -0
  75. package/dist/src/terminal-action-contracts.js +695 -0
  76. package/dist/src/terminal-action-contracts.js.map +1 -0
  77. package/dist/src/terminal-action-projection.d.ts +1 -1
  78. package/dist/src/terminal-action-projection.js.map +1 -1
  79. package/dist/src/terminal-agent-bridge.d.ts +3 -0
  80. package/dist/src/terminal-agent-bridge.js +11 -0
  81. package/dist/src/terminal-agent-bridge.js.map +1 -1
  82. package/dist/src/terminal-composer-classifier.d.ts +1 -1
  83. package/dist/src/terminal-composer-classifier.js +31 -5
  84. package/dist/src/terminal-composer-classifier.js.map +1 -1
  85. package/dist/src/terminal-interaction-response-bridge.js +1 -1
  86. package/dist/src/terminal-interaction-response-bridge.js.map +1 -1
  87. package/dist/src/terminal-list-cli-adapter.js +7 -12
  88. package/dist/src/terminal-list-cli-adapter.js.map +1 -1
  89. package/dist/src/terminal-list-renderer.d.ts +8 -2
  90. package/dist/src/terminal-list-renderer.js +37 -400
  91. package/dist/src/terminal-list-renderer.js.map +1 -1
  92. package/dist/src/terminal-model-control-action-policy.d.ts +1 -1
  93. package/dist/src/terminal-model-control-availability.d.ts +1 -1
  94. package/dist/src/terminal-model-control-availability.js +1 -1
  95. package/dist/src/terminal-model-control-availability.js.map +1 -1
  96. package/dist/src/terminal-model-control-cli-adapter.d.ts +34 -0
  97. package/dist/src/terminal-model-control-cli-adapter.js +669 -0
  98. package/dist/src/terminal-model-control-cli-adapter.js.map +1 -0
  99. package/dist/src/terminal-model-control-contract.d.ts +198 -0
  100. package/dist/src/terminal-model-control-contract.js +15 -0
  101. package/dist/src/terminal-model-control-contract.js.map +1 -0
  102. package/dist/src/terminal-model-control-input.d.ts +29 -0
  103. package/dist/src/terminal-model-control-input.js +86 -0
  104. package/dist/src/terminal-model-control-input.js.map +1 -0
  105. package/dist/src/terminal-model-control-profile.d.ts +2 -0
  106. package/dist/src/terminal-model-control-profile.js +6 -0
  107. package/dist/src/terminal-model-control-profile.js.map +1 -1
  108. package/dist/src/terminal-model-control-subject.d.ts +52 -0
  109. package/dist/src/terminal-model-control-subject.js +73 -0
  110. package/dist/src/terminal-model-control-subject.js.map +1 -1
  111. package/dist/src/terminal-model-control-surface.d.ts +82 -0
  112. package/dist/src/terminal-model-control-surface.js +688 -0
  113. package/dist/src/terminal-model-control-surface.js.map +1 -0
  114. package/dist/src/terminal-model-control.d.ts +6 -270
  115. package/dist/src/terminal-model-control.js +11 -988
  116. package/dist/src/terminal-model-control.js.map +1 -1
  117. package/dist/src/terminal-model-selection.d.ts +33 -0
  118. package/dist/src/terminal-model-selection.js +143 -0
  119. package/dist/src/terminal-model-selection.js.map +1 -0
  120. package/dist/src/terminal-monitor-application-service.js +38 -59
  121. package/dist/src/terminal-monitor-application-service.js.map +1 -1
  122. package/dist/src/terminal-monitor-attention-store.d.ts +65 -0
  123. package/dist/src/terminal-monitor-attention-store.js +518 -0
  124. package/dist/src/terminal-monitor-attention-store.js.map +1 -0
  125. package/dist/src/terminal-monitor-collateral-recovery.d.ts +12 -0
  126. package/dist/src/terminal-monitor-collateral-recovery.js +415 -0
  127. package/dist/src/terminal-monitor-collateral-recovery.js.map +1 -0
  128. package/dist/src/terminal-monitor-decision-policy.d.ts +1 -1
  129. package/dist/src/terminal-monitor-interaction-projection.d.ts +19 -0
  130. package/dist/src/terminal-monitor-interaction-projection.js +48 -0
  131. package/dist/src/terminal-monitor-interaction-projection.js.map +1 -0
  132. package/dist/src/terminal-monitor-startup-recovery.d.ts +15 -0
  133. package/dist/src/terminal-monitor-startup-recovery.js +767 -0
  134. package/dist/src/terminal-monitor-startup-recovery.js.map +1 -0
  135. package/dist/src/terminal-monitor-state-cli-adapter.d.ts +2 -104
  136. package/dist/src/terminal-monitor-state-cli-adapter.js +28 -1640
  137. package/dist/src/terminal-monitor-state-cli-adapter.js.map +1 -1
  138. package/dist/src/terminal-monitor-state-contract.d.ts +108 -0
  139. package/dist/src/terminal-monitor-state-contract.js +8 -0
  140. package/dist/src/terminal-monitor-state-contract.js.map +1 -0
  141. package/dist/src/terminal-native-control-cli-contract.d.ts +16 -0
  142. package/dist/src/terminal-native-control-cli-contract.js +2 -0
  143. package/dist/src/terminal-native-control-cli-contract.js.map +1 -0
  144. package/dist/src/terminal-native-inspection-bridge.d.ts +4 -153
  145. package/dist/src/terminal-native-inspection-bridge.js +7 -646
  146. package/dist/src/terminal-native-inspection-bridge.js.map +1 -1
  147. package/dist/src/terminal-native-inspection-contract.d.ts +128 -0
  148. package/dist/src/terminal-native-inspection-contract.js +38 -0
  149. package/dist/src/terminal-native-inspection-contract.js.map +1 -0
  150. package/dist/src/terminal-native-inspection-profile.d.ts +15 -0
  151. package/dist/src/terminal-native-inspection-profile.js +185 -0
  152. package/dist/src/terminal-native-inspection-profile.js.map +1 -0
  153. package/dist/src/terminal-native-inspection-proof.d.ts +62 -0
  154. package/dist/src/terminal-native-inspection-proof.js +362 -0
  155. package/dist/src/terminal-native-inspection-proof.js.map +1 -0
  156. package/dist/src/terminal-permission-control-availability.d.ts +31 -0
  157. package/dist/src/terminal-permission-control-availability.js +50 -0
  158. package/dist/src/terminal-permission-control-availability.js.map +1 -0
  159. package/dist/src/terminal-permission-control-bridge.d.ts +16 -0
  160. package/dist/src/terminal-permission-control-bridge.js +93 -0
  161. package/dist/src/terminal-permission-control-bridge.js.map +1 -0
  162. package/dist/src/terminal-permission-control-cli-adapter.d.ts +31 -0
  163. package/dist/src/terminal-permission-control-cli-adapter.js +152 -0
  164. package/dist/src/terminal-permission-control-cli-adapter.js.map +1 -0
  165. package/dist/src/terminal-permission-control-list-projection.d.ts +23 -0
  166. package/dist/src/terminal-permission-control-list-projection.js +47 -0
  167. package/dist/src/terminal-permission-control-list-projection.js.map +1 -0
  168. package/dist/src/terminal-permission-control-runtime.d.ts +28 -0
  169. package/dist/src/terminal-permission-control-runtime.js +88 -0
  170. package/dist/src/terminal-permission-control-runtime.js.map +1 -0
  171. package/dist/src/terminal-permission-control.d.ts +69 -0
  172. package/dist/src/terminal-permission-control.js +217 -0
  173. package/dist/src/terminal-permission-control.js.map +1 -0
  174. package/dist/src/terminal-runtime-cli-adapter.d.ts +2 -1
  175. package/dist/src/terminal-runtime-cli-adapter.js +1 -1
  176. package/dist/src/terminal-runtime-cli-adapter.js.map +1 -1
  177. package/dist/src/terminal-submission-acceptance.d.ts +5 -148
  178. package/dist/src/terminal-submission-acceptance.js +21 -1845
  179. package/dist/src/terminal-submission-acceptance.js.map +1 -1
  180. package/dist/src/terminal-watch-anchor-capture.d.ts +9 -0
  181. package/dist/src/terminal-watch-anchor-capture.js +139 -0
  182. package/dist/src/terminal-watch-anchor-capture.js.map +1 -0
  183. package/dist/src/terminal-watch-cli-adapter.d.ts +3 -117
  184. package/dist/src/terminal-watch-cli-adapter.js +10 -1992
  185. package/dist/src/terminal-watch-cli-adapter.js.map +1 -1
  186. package/dist/src/terminal-watch-cli-contract.d.ts +117 -0
  187. package/dist/src/terminal-watch-cli-contract.js +2 -0
  188. package/dist/src/terminal-watch-cli-contract.js.map +1 -0
  189. package/dist/src/terminal-watch-fallback-observation.d.ts +15 -0
  190. package/dist/src/terminal-watch-fallback-observation.js +249 -0
  191. package/dist/src/terminal-watch-fallback-observation.js.map +1 -0
  192. package/dist/src/terminal-watch-interaction-observation.d.ts +26 -0
  193. package/dist/src/terminal-watch-interaction-observation.js +262 -0
  194. package/dist/src/terminal-watch-interaction-observation.js.map +1 -0
  195. package/dist/src/terminal-watch-observation-adapter.d.ts +4 -0
  196. package/dist/src/terminal-watch-observation-adapter.js +307 -0
  197. package/dist/src/terminal-watch-observation-adapter.js.map +1 -0
  198. package/dist/src/terminal-watch-observation-evidence.d.ts +25 -0
  199. package/dist/src/terminal-watch-observation-evidence.js +184 -0
  200. package/dist/src/terminal-watch-observation-evidence.js.map +1 -0
  201. package/dist/src/terminal-watch-response-authority.d.ts +6 -0
  202. package/dist/src/terminal-watch-response-authority.js +333 -0
  203. package/dist/src/terminal-watch-response-authority.js.map +1 -0
  204. package/dist/src/terminal-watch-response-cli-adapter.d.ts +3 -0
  205. package/dist/src/terminal-watch-response-cli-adapter.js +230 -0
  206. package/dist/src/terminal-watch-response-cli-adapter.js.map +1 -0
  207. package/dist/src/terminal-watch-terminal-identity.d.ts +39 -0
  208. package/dist/src/terminal-watch-terminal-identity.js +380 -0
  209. package/dist/src/terminal-watch-terminal-identity.js.map +1 -0
  210. package/docs/codex-permissions.md +95 -0
  211. package/openclaw.plugin.json +9 -1
  212. package/package.json +3 -2
  213. package/templates/openclaw-skills/agent-knock-knock/SKILL.md +6 -1
@@ -1,33 +1,19 @@
1
- import { assertAutomaticActivityWatchIdentity, automaticSendWatchReceipt, createAutomaticActivityWatchAnchor, isAutomaticSendWatch, unsafeFallbackWatchPreparation } from "./terminal-watch-send-activity.js";
1
+ import { automaticSendWatchReceipt, createAutomaticActivityWatchAnchor, isAutomaticSendWatch, unsafeFallbackWatchPreparation } from "./terminal-watch-send-activity.js";
2
2
  import { isCodexPaginatedReadCandidate } from "./codex-lifecycle-compatibility.js";
3
- import { respondCodexPaginatedAsyncQuestion } from "./codex-paginated-async-response.js";
4
- import { publicTerminalWatch, terminalWatchCapturedAgentVersion } from "./terminal-watch-presentation.js";
5
- import { createHash } from "node:crypto";
6
- import path from "node:path";
7
- import { CodexLegacyThreadHistoryError } from "./codex-paginated-observation.js";
8
- import { capturePaginatedWatchAnchor as capturePaginatedAnchor, paginatedWatchProcessMatches, observePaginatedWatch, refreshPaginatedResponseForeground } from "./codex-paginated-watch.js";
9
- import { observeCodexPaginatedBlockingQuestion, respondCodexPaginatedBlockingQuestion } from "./codex-paginated-questionnaire-runtime.js";
10
- import { selectCodexUserExplicitSendWatchSource } from "./terminal-send-watch-policy.js";
3
+ import { publicTerminalWatch } from "./terminal-watch-presentation.js";
11
4
  import { callbackRouteFingerprint } from "./callback-route-authority.js";
12
5
  import { createTerminalWatchOpenClawCallbackRoute, parseCallbackRoute } from "./callback-transport.js";
13
- import { captureClaudeTranscriptAnchor, captureClaudeHumanStartedActiveTaskAnchor, observeClaudeUserExplicitFallbackTranscript, observeClaudeHumanStartedActiveTask } from "./claude-local-transcript-provider.js";
14
- import { codexAsyncQuestionMainComposerVisible } from "./codex-async-question-adapter.js";
15
6
  import { hostProfileCallbackRouteMatchesRuntime } from "./host-profile-callback-transport.js";
16
- import { captureTerminalInteractionRuntimeOffer, TerminalInteractionDispatchReservedError, TerminalInteractionInputNotStartedError } from "./terminal-agent-bridge.js";
17
- import { executorForConversation, turnIdForConversation } from "./protocol.js";
18
- import { rolloutFileIdentityMatches } from "./terminal-binding-authority.js";
19
- import { captureCodexCandidateSetRolloutAcceptanceAnchor, captureCodexRolloutAcceptanceAnchor, detectCodexBoundQuestionnaireAttribution, detectCodexBoundRolloutCompletion, detectCodexCandidateSetRolloutAcceptance, detectCodexRolloutAcceptance, captureCodexHumanStartedActiveTaskAnchor, observeCodexHumanStartedActiveTask } from "./terminal-submission-acceptance.js";
20
- import { createTerminalInteractionAggregate, hashTerminalInteractionResponse, reduceTerminalInteractionAggregate } from "./terminal-interaction-core.js";
21
- import { selectTerminalInteractionResponder } from "./terminal-interaction-authority.js";
22
- import { validateAnyTerminalInteractionProjection, validateTerminalInteractionSubjectResponse } from "./terminal-interaction-protocol.js";
23
- import { executeTerminalInteractionResponseTransaction } from "./terminal-interaction-response-transaction.js";
24
- import { terminalInteractionRuntimeForWatch } from "./terminal-watch-interaction-runtime.js";
25
- import { consumeWatchInteractionResponse, settleWatchInteractionResponseFailure } from "./terminal-watch-interaction-response-store.js";
26
- import { sameTerminalControlEvidenceIncarnation, terminalControlEvidence } from "./terminal-control-ref.js";
7
+ import { terminalControlEvidence } from "./terminal-control-ref.js";
27
8
  import { createTerminalWatchCallbackCliAdapter, resolveTerminalWatchOpenClawCallback, resolveTerminalWatchOpenClawCallbackContext } from "./terminal-watch-callback-cli-adapter.js";
28
- import { createTerminalWatchService, terminalWatchObservationFence } from "./terminal-watch-service.js";
29
- import { assertTerminalWatchManualInteractionSummary, createClaudeUserExplicitFallbackWatchAnchor, createCodexUserExplicitFallbackWatchAnchor, createTerminalActivityWatchAnchor, createTerminalWatchStore, isTerminalActivityWatch, isUserExplicitFallbackWatch, terminalWatchNotificationOnlyRoute, terminalWatchRevision, terminalUserExplicitFallbackWatchId } from "./terminal-watch-store.js";
9
+ import { createTerminalWatchService } from "./terminal-watch-service.js";
10
+ import { assertTerminalWatchManualInteractionSummary, createTerminalActivityWatchAnchor, createTerminalWatchStore, isUserExplicitFallbackWatch, terminalWatchNotificationOnlyRoute, terminalUserExplicitFallbackWatchId } from "./terminal-watch-store.js";
30
11
  import { isRecord, nonBlankString as stringValue } from "./value-guards.js";
12
+ import { createTerminalWatchInteractionResponder } from "./terminal-watch-response-cli-adapter.js";
13
+ import { watchAnchorVersionWarnings, captureCodexFallbackWatchAnchor, captureClaudeFallbackWatchAnchor, capturePaginatedWatchAnchorWithLock, captureTerminalWatchAnchor } from "./terminal-watch-anchor-capture.js";
14
+ import { assertSameUserExplicitFallbackTerminal, assertPreparedFallbackTerminal, exactTerminalForWatch, bestEffortBindingTokenForWatch, terminalActivityState, sameManualWatchTarget, safeDiagnostic, terminalControlForWatch, terminalWatchIdentity, terminalAgent, terminalWorkspace, requiredWatchId, requiredString, positiveInteger, positiveMinutes } from "./terminal-watch-terminal-identity.js";
15
+ import { observeTerminalWatch } from "./terminal-watch-observation-adapter.js";
16
+ import { approvalFingerprint } from "./terminal-watch-observation-evidence.js";
31
17
  const DEFAULT_TERMINAL_WATCH_HARD_TIMEOUT_MINUTES = 720;
32
18
  export function createTerminalWatchCliAdapter(dependencies) {
33
19
  const callback = dependencies.callback ??
@@ -418,246 +404,6 @@ function createTerminalWatchStatusRunner(dependencies, serviceFor) {
418
404
  });
419
405
  };
420
406
  }
421
- function createTerminalWatchInteractionResponder(dependencies, serviceFor) {
422
- return async function runRespondInteraction(options) {
423
- const watchId = requiredWatchId(options.watch);
424
- const interactionId = requiredString(options.interaction, "--interaction is required");
425
- const expectedFingerprint = requiredSha256(options.expectedInteractionFingerprint, "interaction prompt fingerprint");
426
- const expectedExpiresAt = requiredTimestamp(options.expectedInteractionExpiresAt, "--expected-interaction-expires-at");
427
- const responseValue = parseWatchInteractionResponse(options.responseJson);
428
- const storeDir = dependencies.storeDirFromOptions(options);
429
- const repository = createTerminalWatchStore(storeDir, {
430
- acquire: dependencies.acquireFileLock
431
- });
432
- const service = serviceFor(options);
433
- const displayed = await service.reconcile(watchId);
434
- assertWatchControllerSession(displayed, options.openclawSession);
435
- const displayedInteraction = requiredExecutableWatchInteraction(displayed, interactionId, expectedFingerprint);
436
- const response = validateTerminalInteractionSubjectResponse(responseValue, displayedInteraction.projection, { now: dependencies.now(), allowExpiredForLiveRecapture: true });
437
- if (response.subject.kind !== "terminal_watch" ||
438
- response.subject.watch_id !== watchId) {
439
- throw new Error("--response-json subject watch_id does not match --watch");
440
- }
441
- const exact = await exactTerminalForWatch(displayed.terminal.terminal_id, options, dependencies);
442
- if (!terminalMatchesWatch(exact.rawTerminal, displayed)) {
443
- throw new Error("terminal Watch identity changed before interaction response; refresh status");
444
- }
445
- const terminalControl = terminalControlForWatch(exact.rawTerminal);
446
- const releaseTerminal = dependencies.acquireTerminalLock(storeDir, terminalControl);
447
- try {
448
- const withWriterLease = dependencies.withStoreWriterLeaseAsync ??
449
- (async (_storeDir, operation) => operation());
450
- await withWriterLease(storeDir, async () => {
451
- let current = repository.load(watchId);
452
- assertWatchControllerSession(current, options.openclawSession);
453
- const executableInteraction = requiredExecutableWatchInteraction(current, interactionId, expectedFingerprint);
454
- const decision = terminalWatchResponseDecision(current, exact.rawTerminal, executableInteraction.projection.surface_id, executableInteraction.projection.prompt_fingerprint, options, dependencies);
455
- if (!decision.executable || decision.suppress) {
456
- throw new Error("a higher-priority terminal interaction responder owns this live questionnaire; refresh status");
457
- }
458
- const version = terminalWatchCapturedAgentVersion(current) ??
459
- requiredString(exact.rawTerminal.agent_version, "running coding-agent version");
460
- const runtime = terminalInteractionRuntimeForWatch({
461
- watch: current,
462
- rawTerminal: exact.rawTerminal,
463
- checkpoint: current.observation_checkpoint,
464
- version,
465
- terminalTarget: terminalControl.target,
466
- responseAuthority: "executable"
467
- });
468
- const bridge = dependencies.createBridge?.(options);
469
- if (!bridge) {
470
- throw new Error("terminal interaction bridge is unavailable");
471
- }
472
- if (runtime.codexPaginatedThread && executableInteraction.projection.kind === "async_question") {
473
- await refreshPaginatedResponseForeground(bridge, terminalControl, runtime, dependencies.now);
474
- }
475
- const attemptId = dependencies.randomUUID();
476
- const responseHash = hashTerminalInteractionResponse(response);
477
- const reservation = { attemptId, responseHash };
478
- const result = await executeTerminalInteractionResponseTransaction({
479
- reservationFailureFence: "confirmed",
480
- dispatch: (hooks) => "schema" in current.observation_checkpoint &&
481
- current.observation_checkpoint.schema === "agent-knock-knock/codex-paginated-task-checkpoint" &&
482
- executableInteraction.projection.kind === "questionnaire"
483
- ? respondCodexPaginatedBlockingQuestion({
484
- watch: current, checkpoint: current.observation_checkpoint, terminalControl,
485
- response, expectedFingerprint, now: dependencies.now,
486
- authorize: hooks.authorize, beforeDispatch: hooks.beforeDispatch,
487
- persistDraft: (draft) => {
488
- current = repository.withWriterLease((scope) => scope.withWatchLock(watchId, () => {
489
- const latest = scope.load(watchId);
490
- const checkpoint = latest.observation_checkpoint;
491
- if (!("schema" in checkpoint) || checkpoint.schema !== "agent-knock-knock/codex-paginated-task-checkpoint") {
492
- throw new Error("Codex blocking question checkpoint changed");
493
- }
494
- const nextCheckpoint = { ...checkpoint };
495
- if (draft)
496
- nextCheckpoint.blocking_question_draft = draft;
497
- else
498
- delete nextCheckpoint.blocking_question_draft;
499
- return scope.save({ ...latest,
500
- observation_checkpoint: nextCheckpoint,
501
- updated_at: dependencies.now().toISOString()
502
- }, { expectedRevision: terminalWatchRevision(latest) });
503
- }));
504
- }
505
- })
506
- : runtime.codexPaginatedThread && executableInteraction.projection.kind === "async_question"
507
- ? respondCodexPaginatedAsyncQuestion({
508
- bridge, terminalControl, terminalEvidence: current.terminal.terminal_endpoint,
509
- runtime, response, now: dependencies.now,
510
- options: { agentVersion: version, expectedFingerprint, expectedExpiresAt,
511
- scrollbackLines: Number(options.scrollbackLines ?? 120), runtime,
512
- authorize: hooks.authorize, beforeDispatch: hooks.beforeDispatch }
513
- })
514
- : bridge.respondInteraction(current.agent, terminalControl, response, {
515
- agentVersion: version,
516
- expectedFingerprint,
517
- expectedExpiresAt,
518
- scrollbackLines: Number(options.scrollbackLines ?? 120),
519
- runtime,
520
- authorize: hooks.authorize,
521
- beforeDispatch: hooks.beforeDispatch
522
- }),
523
- authorize: ({ projection, fingerprint }) => {
524
- const latest = repository.load(watchId);
525
- const interaction = requiredExecutableWatchInteraction(latest, interactionId, expectedFingerprint);
526
- const latestDecision = terminalWatchResponseDecision(latest, exact.rawTerminal, interaction.projection.surface_id, interaction.projection.prompt_fingerprint, options, dependencies);
527
- return projection.interaction_id === interactionId &&
528
- fingerprint === expectedFingerprint &&
529
- interaction.projection.subject.kind === "terminal_watch" &&
530
- projection.version === 2 &&
531
- projection.subject.kind === "terminal_watch" &&
532
- projection.subject.watch_id === watchId &&
533
- latestDecision.executable && !latestDecision.suppress
534
- ? { approved: true }
535
- : {
536
- approved: false,
537
- reason: "terminal Watch interaction authority changed before response"
538
- };
539
- },
540
- createReservation: () => reservation,
541
- reserve: ({ projection, fingerprint }, receipt, confirm) => {
542
- current = repository.withWriterLease((scope) => scope.withWatchLock(watchId, () => {
543
- const latest = scope.load(watchId);
544
- const interaction = requiredExecutableWatchInteraction(latest, interactionId, expectedFingerprint);
545
- if (projection.interaction_id !== interactionId ||
546
- fingerprint !== expectedFingerprint ||
547
- interaction.aggregate.state !== "pending") {
548
- throw new Error("terminal Watch interaction authority changed before dispatch");
549
- }
550
- const dispatchDecision = terminalWatchResponseDecision(latest, exact.rawTerminal, interaction.projection.surface_id, interaction.projection.prompt_fingerprint, options, dependencies);
551
- if (!dispatchDecision.executable || dispatchDecision.suppress) {
552
- throw new TerminalInteractionInputNotStartedError("terminal Watch interaction ownership changed before dispatch");
553
- }
554
- const reservedAt = dependencies.now().toISOString();
555
- const aggregate = reduceTerminalInteractionAggregate(interaction.aggregate, {
556
- type: "reserve",
557
- attempt_id: receipt.attemptId,
558
- response_hash: receipt.responseHash,
559
- at: reservedAt
560
- });
561
- const saved = scope.save({
562
- ...latest,
563
- current_interaction: {
564
- projection: interaction.projection,
565
- aggregate
566
- },
567
- updated_at: reservedAt
568
- }, { expectedRevision: terminalWatchRevision(latest) });
569
- // Match the predecessor's commit boundary: a later lock
570
- // release failure must still settle this durable receipt.
571
- confirm();
572
- return saved;
573
- }));
574
- },
575
- release: (receipt) => {
576
- current = settleWatchInteractionResponseFailure(repository, {
577
- watchId,
578
- interactionId,
579
- reservation: receipt,
580
- state: "release",
581
- now: dependencies.now
582
- });
583
- },
584
- releaseFailure: (_receipt, _error, releaseError) => releaseError,
585
- markUncertain: (receipt) => {
586
- current = settleWatchInteractionResponseFailure(repository, {
587
- watchId,
588
- interactionId,
589
- reservation: receipt,
590
- state: "response_uncertain",
591
- now: dependencies.now
592
- });
593
- },
594
- consume: (receipt) => {
595
- current = consumeWatchInteractionResponse(repository, {
596
- watchId,
597
- interactionId,
598
- reservation: receipt,
599
- now: dependencies.now,
600
- notificationFingerprint: sha256
601
- });
602
- },
603
- responded: (execution) => execution.responded,
604
- isInputNotStarted: (error) => error instanceof TerminalInteractionInputNotStartedError,
605
- duplicateReservationError: () => new Error("terminal Watch interaction response was already reserved"),
606
- inputNotStartedError: (error) => error,
607
- missingReservationError: () => new TerminalInteractionDispatchReservedError("reservation_uncertain", "terminal Watch response was dispatched without a durable reservation")
608
- });
609
- const execution = result.execution;
610
- if (result.state === "blocked") {
611
- dependencies.printJson({
612
- watch: publicTerminalWatch(current, [], true),
613
- interaction_id: interactionId,
614
- responded: false,
615
- blocked: execution.blocked,
616
- reason: execution.reason
617
- });
618
- return;
619
- }
620
- dependencies.printJson({
621
- watch: publicTerminalWatch(current, [], true),
622
- interaction_id: interactionId,
623
- responded: true,
624
- blocked: false,
625
- question_id: execution.questionId,
626
- response_kind: execution.responseKind,
627
- outcome: execution.outcome
628
- });
629
- });
630
- }
631
- finally {
632
- releaseTerminal();
633
- }
634
- };
635
- }
636
- function watchAnchorVersionWarnings(anchor, terminal) {
637
- const runningVersion = stringValue(terminal.agent_version);
638
- if (!runningVersion) {
639
- return [
640
- "running_agent_version_unavailable: exact task observation remains enabled"
641
- ];
642
- }
643
- const artifactVersion = anchor.schema ===
644
- "agent-knock-knock/codex-human-started-active-task-anchor"
645
- ? anchor.codex_version
646
- : anchor.schema ===
647
- "agent-knock-knock/claude-human-started-active-task-anchor"
648
- ? anchor.claude_version
649
- : undefined;
650
- if (!artifactVersion)
651
- return [];
652
- if (artifactVersion !== runningVersion) {
653
- return [
654
- `the active task artifact reports ${artifactVersion}, not the running ` +
655
- `coding-agent version ${runningVersion}; Watch remains enabled because ` +
656
- "version evidence is advisory"
657
- ];
658
- }
659
- return [];
660
- }
661
407
  /**
662
408
  * A shared native-Host lifecycle owns no single controller session. Bind its
663
409
  * trusted Profile template to the exact session captured when this Watch was
@@ -694,125 +440,6 @@ function callbackRouteForUserExplicitFallback(options) {
694
440
  respond: true
695
441
  });
696
442
  }
697
- function assertSameUserExplicitFallbackTerminal(expected, observed) {
698
- if (requiredString(observed.id, "terminal id") !== expected.conversationId ||
699
- terminalAgent(observed) !== expected.agent ||
700
- positiveInteger(observed.pid, "terminal PID") !== expected.pid ||
701
- !isRecord(observed.terminal_control) ||
702
- !sameTerminalControlEvidenceIncarnation(expected.terminalControl, observed.terminal_control)) {
703
- throw new Error("terminal changed before the user-explicit fallback Watch anchor was captured");
704
- }
705
- }
706
- function assertPreparedFallbackTerminal(prepared, observed) {
707
- if (requiredString(observed.id, "terminal id") !== prepared.terminalId ||
708
- terminalAgent(observed) !== prepared.agent ||
709
- positiveInteger(observed.pid, "terminal PID") !== prepared.pid ||
710
- !isRecord(observed.terminal_control) ||
711
- !sameTerminalControlEvidenceIncarnation(prepared.terminalEndpoint, observed.terminal_control)) {
712
- throw new Error("terminal changed before the user-explicit fallback Watch was attached");
713
- }
714
- if (isTerminalActivityWatch(prepared)) {
715
- assertAutomaticActivityWatchIdentity(prepared.anchor, observed, terminalWorkspace(observed), prepared.terminalIdentity.workspace);
716
- }
717
- }
718
- async function exactTerminalForWatch(terminalId, options, dependencies) {
719
- const observation = await dependencies.observeExactTerminal({
720
- options,
721
- terminalId
722
- });
723
- if (observation.state === "unavailable") {
724
- throw new Error("authoritative observation of the exact terminal is temporarily " +
725
- "unavailable; retry after refreshing AKK list");
726
- }
727
- if (observation.state === "absent") {
728
- throw new Error(`expected exact terminal ${terminalId}; observed none`);
729
- }
730
- return observation;
731
- }
732
- function bindingTokenForWatch(terminal) {
733
- return requiredSha256(terminal.lifecycle_binding_token, "current terminal binding token");
734
- }
735
- function bestEffortBindingTokenForWatch(terminal) {
736
- try {
737
- return { token: bindingTokenForWatch(terminal) };
738
- }
739
- catch {
740
- const terminalControl = terminalControlForWatch(terminal);
741
- return {
742
- token: sha256({
743
- schema: "agent-knock-knock/terminal-watch-observation-binding",
744
- version: 1,
745
- terminal_id: requiredString(terminal.id, "terminal id"),
746
- agent: terminalAgent(terminal),
747
- pid: positiveInteger(terminal.pid, "terminal PID"),
748
- endpoint: terminalControlEvidence(terminalControl),
749
- process_uuid: stringValue(terminal.native_agent_process_uuid) ?? null,
750
- process_birth: stringValue(terminal.native_agent_process_birth) ?? null
751
- }),
752
- warning: "lifecycle_binding_token_unavailable: Watch used read-only terminal/process observation identity"
753
- };
754
- }
755
- }
756
- function terminalActivityState(terminal) {
757
- const state = stringValue(terminal.activity_state);
758
- return state === "awaiting_approval" || state === "working" ||
759
- state === "idle"
760
- ? state
761
- : "unknown";
762
- }
763
- function sameManualWatchTarget(watch, agent, terminal, anchor) {
764
- return watch.status === "active" &&
765
- watch.agent === agent &&
766
- watch.terminal.terminal_id === terminal.terminal_id &&
767
- watch.terminal.workspace === terminal.workspace &&
768
- sameTerminalControlEvidenceIncarnation(watch.terminal.terminal_endpoint, terminal.terminal_endpoint) &&
769
- sameManualWatchAnchorTarget(watch.anchor, anchor);
770
- }
771
- function sameManualWatchAnchorTarget(left, right) {
772
- if (left.schema !== right.schema)
773
- return false;
774
- if (left.schema === "agent-knock-knock/codex-paginated-task-anchor" && right.schema === left.schema) {
775
- return samePaginatedWatchTask(left, right);
776
- }
777
- if (left.schema ===
778
- "agent-knock-knock/codex-human-started-active-task-anchor" &&
779
- right.schema === left.schema) {
780
- return left.turn_id === right.turn_id &&
781
- left.process_uuid === right.process_uuid &&
782
- left.process_birth === right.process_birth &&
783
- left.rollout.device === right.rollout.device &&
784
- left.rollout.inode === right.rollout.inode;
785
- }
786
- if (left.schema ===
787
- "agent-knock-knock/claude-human-started-active-task-anchor" &&
788
- right.schema === left.schema) {
789
- return sameClaudeWatchTask(left, right);
790
- }
791
- if (left.schema === "agent-knock-knock/terminal-activity-watch-anchor" &&
792
- right.schema === left.schema) {
793
- return left.origin === right.origin &&
794
- left.request_hash === right.request_hash &&
795
- left.pid === right.pid &&
796
- optionalIdentityCompatible(left.native_process_uuid, right.native_process_uuid) &&
797
- optionalIdentityCompatible(left.native_process_birth, right.native_process_birth);
798
- }
799
- return left.anchor_fingerprint === right.anchor_fingerprint;
800
- }
801
- function sameClaudeWatchTask(left, right) {
802
- return left.prompt_uuid === right.prompt_uuid &&
803
- left.pid === right.pid &&
804
- left.agent_started_at_ms === right.agent_started_at_ms &&
805
- left.device === right.device &&
806
- left.inode === right.inode;
807
- }
808
- function samePaginatedWatchTask(left, right) {
809
- return left.native_thread_id === right.native_thread_id && left.turn_id === right.turn_id &&
810
- left.process_uuid === right.process_uuid && left.process_birth === right.process_birth &&
811
- left.origin === right.origin && left.codex_home === right.codex_home;
812
- }
813
- function optionalIdentityCompatible(left, right) {
814
- return left === undefined || right === undefined || left === right;
815
- }
816
443
  function sameManualWatchCallbackAuthority(watch, callbackRoute, openclawSession, openclawBin) {
817
444
  if (watch.openclaw_session !== openclawSession)
818
445
  return false;
@@ -824,1497 +451,6 @@ function sameManualWatchCallbackAuthority(watch, callbackRoute, openclawSession,
824
451
  callbackRouteFingerprint(watch.callback_route) ===
825
452
  callbackRouteFingerprint(callbackRoute);
826
453
  }
827
- function safeDiagnostic(error) {
828
- const message = error instanceof Error ? error.message : String(error);
829
- return message.replace(/[\r\n\0]+/gu, " ").trim().slice(0, 500) ||
830
- "unknown provider observation error";
831
- }
832
- function terminalControlForWatch(terminal) {
833
- if (!isRecord(terminal.terminal_control)) {
834
- throw new Error("the exact terminal has no terminal control authority");
835
- }
836
- return terminal.terminal_control;
837
- }
838
- async function captureCodexFallbackWatchAnchor(input, rawTerminal, agentVersion, dependencies) {
839
- let inventory = rawTerminal._codex_open_root_rollout_inventory;
840
- let paginated;
841
- if (isCodexPaginatedReadCandidate(agentVersion)) {
842
- try {
843
- paginated = await capturePaginatedWatchAnchor(rawTerminal, input.options, dependencies, input.requestText === undefined ? input.requestHash
844
- : createHash("sha256").update(input.requestText.replace(/\r\n?/gu, "\n").trim()).digest("hex"));
845
- }
846
- catch (error) {
847
- if (!(error instanceof CodexLegacyThreadHistoryError) || !isRecord(inventory) || !Array.isArray(inventory.roots))
848
- throw error;
849
- const roots = inventory.roots.filter((root) => root.sessionId === error.threadId);
850
- if (roots.length !== 1)
851
- throw error;
852
- inventory = { ...inventory, roots };
853
- }
854
- }
855
- const source = selectCodexUserExplicitSendWatchSource({
856
- agentVersion,
857
- legacyRootCount: isRecord(inventory) && Array.isArray(inventory.roots) ? inventory.roots.length : 0,
858
- paginatedAnchorAvailable: paginated !== undefined
859
- });
860
- if (source.source === "none")
861
- throw new Error(source.warning);
862
- if (paginated) {
863
- return paginated;
864
- }
865
- else {
866
- const acceptanceAnchor = isRecord(inventory)
867
- ? captureCodexCandidateSetRolloutAcceptanceAnchor({
868
- inventory: inventory,
869
- now: dependencies.now()
870
- })
871
- : captureCodexRolloutAcceptanceAnchor({
872
- nativeThreadId: requiredString(rawTerminal.native_agent_session_id, "Codex native thread id"),
873
- processUuid: requiredString(rawTerminal.native_agent_process_uuid, "Codex process UUID"),
874
- processBirth: requiredString(rawTerminal.native_agent_process_birth, "Codex process birth"),
875
- mode: "existing",
876
- rollout: codexIdentity(rawTerminal).rollout,
877
- now: dependencies.now()
878
- });
879
- return createCodexUserExplicitFallbackWatchAnchor({
880
- acceptanceAnchor,
881
- requestHash: input.requestHash,
882
- codexVersion: agentVersion
883
- });
884
- }
885
- }
886
- function captureClaudeFallbackWatchAnchor(input, rawTerminal, agentVersion, dependencies) {
887
- const transcriptAnchor = captureClaudeTranscriptAnchor({
888
- sessionId: requiredString(rawTerminal.native_agent_session_id, "Claude native session id"),
889
- cwd: terminalWorkspace(rawTerminal),
890
- pid: positiveInteger(rawTerminal.pid, "Claude PID"),
891
- claudeHome: stringValue(input.options.claudeHome),
892
- agentRows: dependencies.loadClaudeAgentRows(input.options, { required: true }),
893
- now: dependencies.now()
894
- });
895
- if (!transcriptAnchor) {
896
- throw new Error("Claude transcript anchor is unavailable before terminal input");
897
- }
898
- return createClaudeUserExplicitFallbackWatchAnchor({
899
- transcriptAnchor,
900
- requestHash: input.requestHash,
901
- claudeVersion: agentVersion
902
- });
903
- }
904
- async function capturePaginatedWatchAnchor(terminal, options, dependencies, requestHash) {
905
- const bridge = dependencies.createBridge?.(options);
906
- if (dependencies.capturePaginatedAnchor) {
907
- return dependencies.capturePaginatedAnchor({ terminal, bridge: bridge, requestHash,
908
- codexHome: stringValue(options.codexHome), now: dependencies.now });
909
- }
910
- if (!bridge)
911
- throw new Error("Codex paginated terminal inspection is unavailable");
912
- return capturePaginatedAnchor({ terminal, bridge, requestHash,
913
- codexHome: stringValue(options.codexHome), now: dependencies.now });
914
- }
915
- async function capturePaginatedWatchAnchorWithLock(terminal, options, dependencies) {
916
- const release = dependencies.acquireTerminalLock(dependencies.storeDirFromOptions(options), terminalControlForWatch(terminal));
917
- try {
918
- return await capturePaginatedWatchAnchor(terminal, options, dependencies);
919
- }
920
- finally {
921
- release();
922
- }
923
- }
924
- function captureTerminalWatchAnchor(agent, terminal, options, dependencies) {
925
- if (agent === "codex") {
926
- return captureCodexHumanStartedActiveTaskAnchor({
927
- currentIdentity: codexIdentity(terminal),
928
- now: dependencies.now()
929
- });
930
- }
931
- return captureClaudeHumanStartedActiveTaskAnchor({
932
- sessionId: requiredString(terminal.native_agent_session_id, "Claude native session id"),
933
- cwd: terminalWorkspace(terminal),
934
- pid: positiveInteger(terminal.pid, "Claude PID"),
935
- claudeHome: stringValue(options.claudeHome),
936
- agentRows: dependencies.loadClaudeAgentRows(options, { required: true }),
937
- now: dependencies.now()
938
- });
939
- }
940
- async function observeTerminalWatch(watch, options, dependencies) {
941
- const observedAt = dependencies.now().toISOString();
942
- const fence = terminalWatchObservationFence(watch);
943
- const exactTerminal = await currentTerminalForWatch(watch, options, dependencies);
944
- const rawTerminal = exactTerminal.state === "available"
945
- ? exactTerminal.rawTerminal
946
- : undefined;
947
- if (watch.anchor.schema === "agent-knock-knock/codex-paginated-task-anchor") {
948
- const terminalMatches = Boolean(rawTerminal && terminalMatchesWatch(rawTerminal, watch) && paginatedWatchProcessMatches(rawTerminal, watch.anchor));
949
- return observePaginatedWatch({ watch, observedAt, terminalMatches,
950
- terminalAvailable: exactTerminal.state === "available",
951
- readSnapshot: dependencies.readPaginatedSnapshot,
952
- blockingQuestionnaire: (checkpoint) => observeCodexPaginatedBlockingQuestion({
953
- watch, checkpoint, now: new Date(observedAt),
954
- responseDecision: (surfaceId, fingerprint) => rawTerminal
955
- ? terminalWatchResponseDecision({ ...watch, observation_checkpoint: checkpoint }, rawTerminal, surfaceId, fingerprint, options, dependencies)
956
- : { executable: false, suppress: false }
957
- }),
958
- questionnaire: (checkpoint, questions, allowResponses) => exactTerminal.state === "available"
959
- ? terminalWatchQuestionnaireObservation({ watch: allowResponses ? watch : { ...watch, interaction_policy: "notify_only" }, observedAt, options, dependencies,
960
- rawTerminal, projectedTerminal: exactTerminal.terminal, terminalMatches,
961
- observationCheckpoint: checkpoint, codexAsyncQuestionEvidence: questions })
962
- : undefined });
963
- }
964
- const projectedTerminal = exactTerminal.state === "available"
965
- ? exactTerminal.terminal
966
- : undefined;
967
- const activityTerminalIdentity = rawTerminal && isTerminalActivityWatch(watch)
968
- ? terminalActivityWatchIdentityMatch(rawTerminal, watch)
969
- : undefined;
970
- const terminalMatches = rawTerminal
971
- ? isUserExplicitFallbackWatch(watch)
972
- ? terminalMatchesUserExplicitFallbackWatch(rawTerminal, watch)
973
- : isTerminalActivityWatch(watch)
974
- ? activityTerminalIdentity === "match"
975
- : terminalMatchesWatch(rawTerminal, watch)
976
- : false;
977
- if (isUserExplicitFallbackWatch(watch)) {
978
- return observeUserExplicitFallbackTerminalWatch({
979
- watch,
980
- exactTerminal,
981
- rawTerminal,
982
- projectedTerminal,
983
- terminalMatches,
984
- observedAt,
985
- options,
986
- dependencies
987
- });
988
- }
989
- if (isTerminalActivityWatch(watch)) {
990
- return observeTerminalActivityWatch({
991
- watch,
992
- exactTerminal,
993
- rawTerminal,
994
- projectedTerminal,
995
- terminalIdentityMatch: activityTerminalIdentity ?? "mismatch",
996
- observedAt,
997
- options,
998
- dependencies
999
- });
1000
- }
1001
- const observation = watch.anchor.schema ===
1002
- "agent-knock-knock/codex-human-started-active-task-anchor"
1003
- ? observeCodexHumanStartedActiveTask({
1004
- anchor: watch.anchor,
1005
- currentIdentity: rawTerminal
1006
- ? codexIdentity(rawTerminal)
1007
- : codexIdentityForWatch(watch),
1008
- resumeOffsetBytes: watch.observation_checkpoint.safe_resume_offset_bytes
1009
- })
1010
- : watch.anchor.schema ===
1011
- "agent-knock-knock/claude-human-started-active-task-anchor"
1012
- ? observeClaudeHumanStartedActiveTask({
1013
- anchor: watch.anchor,
1014
- claudeHome: stringValue(options.claudeHome),
1015
- agentRows: dependencies.loadClaudeAgentRows(options, { required: true }),
1016
- resumeOffsetBytes: watch.observation_checkpoint.safe_resume_offset_bytes,
1017
- checkpoint: claudeObservationCheckpoint(watch)
1018
- })
1019
- : (() => {
1020
- throw new Error("terminal Watch anchor is unsupported");
1021
- })();
1022
- if (observation.status === "invalidated") {
1023
- return invalidatedObservation(watch, observedAt, "task_anchor_invalidated");
1024
- }
1025
- if (observation.status === "completed") {
1026
- const completion = observation.completion;
1027
- const kind = completion.outcome === "failure" ? "failed" : "completed";
1028
- return {
1029
- ...fence,
1030
- kind,
1031
- observed_at: observedAt,
1032
- evidence_fingerprint: sha256({
1033
- kind,
1034
- watch_id: watch.watch_id,
1035
- completion_id: completion.id ?? null,
1036
- completion_timestamp: completion.timestamp ?? null,
1037
- anchor_fingerprint: watch.anchor.anchor_fingerprint
1038
- }),
1039
- reason_code: completion.outcome === "failure"
1040
- ? "anchored_task_failed"
1041
- : "anchored_task_completed",
1042
- completion_text: completion.text.slice(0, 4000),
1043
- completion_id: completion.id,
1044
- completion_timestamp: completion.timestamp
1045
- };
1046
- }
1047
- if (observation.status === "unavailable") {
1048
- return {
1049
- ...fence,
1050
- kind: "unavailable",
1051
- observed_at: observedAt,
1052
- reason_code: "provider_observation_unavailable"
1053
- };
1054
- }
1055
- const safeResumeOffsetBytes = observation.safeResumeOffsetBytes;
1056
- const observationCheckpoint = "checkpoint" in observation
1057
- ? observation.checkpoint
1058
- : undefined;
1059
- if (exactTerminal.state === "unavailable") {
1060
- return {
1061
- ...fence,
1062
- kind: "unavailable",
1063
- observed_at: observedAt,
1064
- safe_resume_offset_bytes: safeResumeOffsetBytes,
1065
- observation_checkpoint: observationCheckpoint,
1066
- reason_code: "terminal_observation_unavailable"
1067
- };
1068
- }
1069
- if (rawTerminal && !terminalMatches) {
1070
- return invalidatedObservation(watch, observedAt, "terminal_identity_changed");
1071
- }
1072
- if (exactTerminal.state === "absent") {
1073
- return invalidatedObservation(watch, observedAt, "terminal_process_unavailable");
1074
- }
1075
- if (!projectedTerminal) {
1076
- throw new Error("exact terminal observation is inconsistent");
1077
- }
1078
- const approval = approvalFingerprint(projectedTerminal);
1079
- if (approval) {
1080
- return {
1081
- ...fence,
1082
- kind: "approval",
1083
- observed_at: observedAt,
1084
- last_activity_at: observedAt,
1085
- safe_resume_offset_bytes: safeResumeOffsetBytes,
1086
- observation_checkpoint: observationCheckpoint,
1087
- evidence_fingerprint: approval,
1088
- reason_code: "terminal_waiting_for_approval"
1089
- };
1090
- }
1091
- const interaction = terminalWatchQuestionnaireObservation({
1092
- watch,
1093
- rawTerminal,
1094
- projectedTerminal,
1095
- terminalMatches: true,
1096
- observedAt,
1097
- observationCheckpoint,
1098
- options,
1099
- dependencies
1100
- });
1101
- if (interaction)
1102
- return interaction;
1103
- return {
1104
- ...fence,
1105
- kind: "pending",
1106
- observed_at: observedAt,
1107
- safe_resume_offset_bytes: safeResumeOffsetBytes,
1108
- observation_checkpoint: observationCheckpoint
1109
- };
1110
- }
1111
- function observeTerminalActivityWatch(input) {
1112
- const { watch, exactTerminal, projectedTerminal, rawTerminal, terminalIdentityMatch, observedAt } = input;
1113
- const fence = terminalWatchObservationFence(watch);
1114
- if (exactTerminal.state === "unavailable") {
1115
- return {
1116
- ...fence,
1117
- kind: "unavailable",
1118
- observed_at: observedAt,
1119
- reason_code: "terminal_observation_unavailable"
1120
- };
1121
- }
1122
- if (exactTerminal.state === "absent") {
1123
- return invalidatedObservation(watch, observedAt, "terminal_process_unavailable");
1124
- }
1125
- if (terminalIdentityMatch === "unavailable") {
1126
- return {
1127
- ...fence,
1128
- kind: "unavailable",
1129
- observed_at: observedAt,
1130
- reason_code: "terminal_identity_observation_incomplete"
1131
- };
1132
- }
1133
- if (terminalIdentityMatch === "mismatch") {
1134
- return invalidatedObservation(watch, observedAt, "terminal_identity_changed");
1135
- }
1136
- if (!projectedTerminal) {
1137
- return {
1138
- ...fence,
1139
- kind: "unavailable",
1140
- observed_at: observedAt,
1141
- reason_code: "terminal_activity_unavailable"
1142
- };
1143
- }
1144
- if (rawTerminal) {
1145
- const interaction = terminalWatchQuestionnaireObservation({
1146
- watch,
1147
- rawTerminal,
1148
- projectedTerminal,
1149
- terminalMatches: true,
1150
- observedAt,
1151
- observationCheckpoint: terminalActivityObservationCheckpoint(watch),
1152
- options: input.options,
1153
- dependencies: input.dependencies
1154
- });
1155
- if (interaction)
1156
- return interaction;
1157
- }
1158
- const state = terminalActivityState(projectedTerminal);
1159
- const current = terminalActivityObservationCheckpoint(watch);
1160
- const active = state === "working" || state === "awaiting_approval";
1161
- const hasSeenActivity = current.has_seen_activity || active;
1162
- const consecutiveIdle = state === "idle" && hasSeenActivity
1163
- ? current.last_activity_state === "idle"
1164
- ? current.consecutive_idle_observations + 1
1165
- : 1
1166
- : 0;
1167
- const checkpoint = {
1168
- ...current,
1169
- has_seen_activity: hasSeenActivity,
1170
- consecutive_idle_observations: consecutiveIdle,
1171
- last_activity_state: state
1172
- };
1173
- if (state === "unknown") {
1174
- return {
1175
- ...fence,
1176
- kind: "unavailable",
1177
- observed_at: observedAt,
1178
- observation_checkpoint: checkpoint,
1179
- reason_code: "terminal_activity_unknown"
1180
- };
1181
- }
1182
- if (state === "awaiting_approval") {
1183
- const approval = approvalFingerprint(projectedTerminal);
1184
- if (approval) {
1185
- return {
1186
- ...fence,
1187
- kind: "approval",
1188
- observed_at: observedAt,
1189
- last_activity_at: observedAt,
1190
- observation_checkpoint: checkpoint,
1191
- evidence_fingerprint: approval,
1192
- reason_code: "terminal_waiting_for_approval"
1193
- };
1194
- }
1195
- }
1196
- if (state === "idle" && hasSeenActivity && consecutiveIdle >= 2) {
1197
- return {
1198
- ...fence,
1199
- kind: "completed",
1200
- observed_at: observedAt,
1201
- observation_checkpoint: checkpoint,
1202
- evidence_fingerprint: sha256({
1203
- watch_id: watch.watch_id,
1204
- reason_code: "terminal_activity_became_stably_idle",
1205
- anchor_fingerprint: watch.anchor.anchor_fingerprint
1206
- }),
1207
- reason_code: "terminal_activity_became_stably_idle"
1208
- };
1209
- }
1210
- return {
1211
- ...fence,
1212
- kind: "pending",
1213
- observed_at: observedAt,
1214
- ...(active ? { last_activity_at: observedAt } : {}),
1215
- observation_checkpoint: checkpoint
1216
- };
1217
- }
1218
- async function observeUserExplicitFallbackTerminalWatch(input) {
1219
- const { watch, exactTerminal, rawTerminal, projectedTerminal, terminalMatches, observedAt, options, dependencies } = input;
1220
- const fence = terminalWatchObservationFence(watch);
1221
- if (watch.anchor.schema ===
1222
- "agent-knock-knock/codex-user-explicit-fallback-watch-anchor") {
1223
- const anchor = watch.anchor.acceptance_anchor;
1224
- const persistedCheckpoint = codexFallbackObservationCheckpoint(watch);
1225
- let acceptance;
1226
- let currentIdentity;
1227
- let acceptedCheckpoint;
1228
- if (persistedCheckpoint.acceptance_evidence &&
1229
- persistedCheckpoint.accepted_identity) {
1230
- acceptance = persistedCheckpoint.acceptance_evidence;
1231
- currentIdentity = {
1232
- sessionId: persistedCheckpoint.accepted_identity.native_thread_id,
1233
- processUuid: persistedCheckpoint.accepted_identity.process_uuid,
1234
- processBirth: persistedCheckpoint.accepted_identity.process_birth,
1235
- rollout: persistedCheckpoint.accepted_identity.rollout
1236
- };
1237
- acceptedCheckpoint = persistedCheckpoint;
1238
- }
1239
- else if (anchor.version === 3) {
1240
- const inventory = rawTerminal?._codex_open_root_rollout_inventory;
1241
- if (!isRecord(inventory)) {
1242
- return fallbackPendingOrTerminalObservation({
1243
- watch,
1244
- exactTerminal,
1245
- rawTerminal,
1246
- terminalMatches,
1247
- observedAt,
1248
- reasonCode: "codex_rollout_inventory_unavailable"
1249
- });
1250
- }
1251
- const result = detectCodexCandidateSetRolloutAcceptance({
1252
- anchor,
1253
- currentInventory: inventory,
1254
- requestHash: watch.anchor.request_hash
1255
- });
1256
- if (result.status === "uncertain") {
1257
- if (result.code === "candidate_scan_invalid") {
1258
- return {
1259
- ...fence,
1260
- kind: "unavailable",
1261
- observed_at: observedAt,
1262
- reason_code: "native_acceptance_scan_unavailable"
1263
- };
1264
- }
1265
- return invalidatedObservation(watch, observedAt, `native_acceptance_${result.code}`);
1266
- }
1267
- if (result.status === "pending") {
1268
- return fallbackPendingOrTerminalObservation({
1269
- watch,
1270
- exactTerminal,
1271
- rawTerminal,
1272
- terminalMatches,
1273
- observedAt
1274
- });
1275
- }
1276
- acceptance = result.evidence;
1277
- currentIdentity = result.identity;
1278
- acceptedCheckpoint = codexFallbackAcceptedCheckpoint(watch, acceptance, currentIdentity);
1279
- }
1280
- else {
1281
- try {
1282
- currentIdentity = anchor.version === 1
1283
- ? {
1284
- sessionId: anchor.native_thread_id,
1285
- processUuid: anchor.process_uuid,
1286
- processBirth: anchor.process_birth,
1287
- rollout: anchor.rollout
1288
- }
1289
- : rawTerminal
1290
- ? codexIdentity(rawTerminal)
1291
- : (() => {
1292
- throw new Error("Codex pre-materialization identity is unavailable");
1293
- })();
1294
- acceptance = detectCodexRolloutAcceptance({
1295
- anchor,
1296
- currentIdentity,
1297
- requestHash: watch.anchor.request_hash
1298
- });
1299
- }
1300
- catch (error) {
1301
- if (retryableProviderError(error)) {
1302
- return {
1303
- ...fence,
1304
- kind: "unavailable",
1305
- observed_at: observedAt,
1306
- reason_code: "native_acceptance_scan_unavailable"
1307
- };
1308
- }
1309
- return invalidatedObservation(watch, observedAt, "native_acceptance_identity_changed");
1310
- }
1311
- if (!acceptance) {
1312
- return fallbackPendingOrTerminalObservation({
1313
- watch,
1314
- exactTerminal,
1315
- rawTerminal,
1316
- terminalMatches,
1317
- observedAt
1318
- });
1319
- }
1320
- acceptedCheckpoint = codexFallbackAcceptedCheckpoint(watch, acceptance, currentIdentity);
1321
- }
1322
- if (!acceptance || !acceptedCheckpoint) {
1323
- throw new Error("Codex fallback Watch acceptance checkpoint is incomplete");
1324
- }
1325
- const completion = detectCodexBoundRolloutCompletion({
1326
- anchor,
1327
- acceptanceEvidence: acceptance,
1328
- currentIdentity,
1329
- requestHash: watch.anchor.request_hash
1330
- });
1331
- if (completion.status === "completed") {
1332
- return fallbackCompletionObservation(watch, observedAt, completion.completion, completion.diagnostics.observed_end_offset_bytes, codexFallbackAcceptedCheckpoint(watch, acceptance, currentIdentity));
1333
- }
1334
- if (completion.status === "failure") {
1335
- if (completion.diagnostics.code === "rollout_unreadable") {
1336
- return {
1337
- ...fence,
1338
- kind: "unavailable",
1339
- observed_at: observedAt,
1340
- safe_resume_offset_bytes: acceptedCheckpoint.safe_resume_offset_bytes,
1341
- observation_checkpoint: acceptedCheckpoint,
1342
- reason_code: "accepted_rollout_unavailable"
1343
- };
1344
- }
1345
- return invalidatedObservation(watch, observedAt, `native_completion_${completion.diagnostics.code}`);
1346
- }
1347
- const interaction = fallbackQuestionnaireObservation({
1348
- watch,
1349
- exactTerminal,
1350
- rawTerminal,
1351
- projectedTerminal,
1352
- terminalMatches,
1353
- observedAt,
1354
- observationCheckpoint: codexFallbackAcceptedCheckpoint(watch, acceptance, currentIdentity),
1355
- options,
1356
- dependencies
1357
- });
1358
- if (interaction)
1359
- return interaction;
1360
- return fallbackPendingOrTerminalObservation({
1361
- watch,
1362
- exactTerminal,
1363
- rawTerminal,
1364
- terminalMatches,
1365
- observedAt,
1366
- observedEndOffsetBytes: completion.diagnostics.observed_end_offset_bytes,
1367
- observationCheckpoint: codexFallbackAcceptedCheckpoint(watch, acceptance, currentIdentity)
1368
- });
1369
- }
1370
- if (watch.anchor.schema !==
1371
- "agent-knock-knock/claude-user-explicit-fallback-watch-anchor") {
1372
- throw new Error("user-explicit fallback Watch anchor is unsupported");
1373
- }
1374
- const request = {
1375
- sessionId: watch.anchor.transcript_anchor.session_id,
1376
- cwd: watch.anchor.transcript_anchor.cwd,
1377
- requestHash: watch.anchor.request_hash,
1378
- startedAt: watch.anchor.captured_at,
1379
- context: {
1380
- claudeTranscriptAnchor: watch.anchor.transcript_anchor,
1381
- pid: watch.anchor.transcript_anchor.pid
1382
- }
1383
- };
1384
- const persistedCheckpoint = claudeFallbackObservationCheckpoint(watch);
1385
- const observation = observeClaudeUserExplicitFallbackTranscript(request, {
1386
- claudeHome: stringValue(options.claudeHome),
1387
- // This observer is transcript-authoritative by design. Completion must
1388
- // remain recoverable after the exact pane/process leaves discovery.
1389
- acceptanceEvidence: persistedCheckpoint.acceptance_evidence
1390
- });
1391
- if (observation.status === "unavailable") {
1392
- return {
1393
- ...fence,
1394
- kind: "unavailable",
1395
- observed_at: observedAt,
1396
- reason_code: "native_acceptance_scan_unavailable"
1397
- };
1398
- }
1399
- const acceptance = observation.acceptance;
1400
- if (!acceptance) {
1401
- return fallbackPendingOrTerminalObservation({
1402
- watch,
1403
- exactTerminal,
1404
- rawTerminal,
1405
- terminalMatches,
1406
- observedAt
1407
- });
1408
- }
1409
- const acceptedCheckpoint = claudeFallbackAcceptedCheckpoint(watch, acceptance);
1410
- if (observation.status === "completed") {
1411
- return fallbackCompletionObservation(watch, observedAt, observation.completion, observation.observedEndOffsetBytes, acceptedCheckpoint);
1412
- }
1413
- const interaction = fallbackQuestionnaireObservation({
1414
- watch,
1415
- exactTerminal,
1416
- rawTerminal,
1417
- projectedTerminal,
1418
- terminalMatches,
1419
- observedAt,
1420
- observationCheckpoint: acceptedCheckpoint,
1421
- options,
1422
- dependencies
1423
- });
1424
- if (interaction)
1425
- return interaction;
1426
- return fallbackPendingOrTerminalObservation({
1427
- watch,
1428
- exactTerminal,
1429
- rawTerminal,
1430
- terminalMatches,
1431
- observedAt,
1432
- observedEndOffsetBytes: observation.observedEndOffsetBytes,
1433
- observationCheckpoint: acceptedCheckpoint
1434
- });
1435
- }
1436
- function fallbackQuestionnaireObservation(input) {
1437
- if (input.exactTerminal.state !== "available" ||
1438
- !input.rawTerminal ||
1439
- !input.projectedTerminal ||
1440
- !input.terminalMatches) {
1441
- return undefined;
1442
- }
1443
- return terminalWatchQuestionnaireObservation({
1444
- ...input,
1445
- rawTerminal: input.rawTerminal,
1446
- projectedTerminal: input.projectedTerminal
1447
- }, true);
1448
- }
1449
- function terminalWatchQuestionnaireObservation(input, requireFallbackAttribution = false) {
1450
- if (!input.rawTerminal || !input.terminalMatches)
1451
- return undefined;
1452
- const rawTerminal = input.rawTerminal;
1453
- const screen = terminalWatchScreenExcerpt(rawTerminal, input.projectedTerminal);
1454
- const version = terminalWatchCapturedAgentVersion(input.watch) ??
1455
- stringValue(rawTerminal.agent_version);
1456
- if (!screen || !version)
1457
- return undefined;
1458
- const responseWatch = input.observationCheckpoint
1459
- ? {
1460
- ...input.watch,
1461
- observation_checkpoint: input.observationCheckpoint
1462
- }
1463
- : input.watch;
1464
- const captureOffer = (responseAuthority) => captureTerminalInteractionRuntimeOffer({
1465
- agent: input.watch.agent,
1466
- terminalControl: terminalControlForWatch(rawTerminal),
1467
- screen,
1468
- runtime: terminalInteractionRuntimeForWatch({
1469
- watch: responseWatch,
1470
- rawTerminal,
1471
- checkpoint: responseWatch.observation_checkpoint,
1472
- version,
1473
- terminalTarget: terminalControlForWatch(rawTerminal).target,
1474
- responseAuthority
1475
- }),
1476
- now: new Date(input.observedAt),
1477
- approvalBlocked: Boolean(approvalFingerprint(input.projectedTerminal)),
1478
- trustedTerminalEvidence: input.watch.terminal.terminal_endpoint,
1479
- codexAsyncQuestionEvidence: input.codexAsyncQuestionEvidence
1480
- });
1481
- let offer = captureOffer("notify_only");
1482
- if (!offer)
1483
- return absentAsyncQuestionObservation(input, screen, version);
1484
- if (input.watch.current_interaction?.projection.kind === "async_question" &&
1485
- offer.projection.kind === "questionnaire" &&
1486
- offer.nativeInspection.status !== "actionable") {
1487
- return absentAsyncQuestionObservation(input, screen, version);
1488
- }
1489
- if (requireFallbackAttribution &&
1490
- !fallbackQuestionnaireContextMatches(input.watch, rawTerminal, input.observationCheckpoint ?? input.watch.observation_checkpoint, offer.nativeInspection)) {
1491
- return undefined;
1492
- }
1493
- const responseDecision = terminalWatchResponseDecision(responseWatch, rawTerminal, offer.surfaceId, offer.promptFingerprint, input.options, input.dependencies);
1494
- if (responseDecision.executable && !(responseWatch.anchor.schema === "agent-knock-knock/codex-paginated-task-anchor" && offer.projection.kind === "questionnaire")) {
1495
- const executableOffer = captureOffer("executable");
1496
- if (!executableOffer ||
1497
- executableOffer.surfaceId !== offer.surfaceId ||
1498
- executableOffer.projection.interaction_id !==
1499
- offer.projection.interaction_id) {
1500
- throw new Error("terminal Watch interaction surface changed during responder arbitration");
1501
- }
1502
- offer = executableOffer;
1503
- }
1504
- const currentInteraction = {
1505
- projection: offer.projection.version === 2 &&
1506
- offer.projection.subject.kind === "terminal_watch"
1507
- ? offer.projection
1508
- : (() => {
1509
- throw new Error("Watch interaction projection lost its Watch subject");
1510
- })(),
1511
- aggregate: createTerminalInteractionAggregate(offer.projection, input.observedAt)
1512
- };
1513
- const manualInteraction = terminalWatchManualInteractionSummary(offer.nativeInspection);
1514
- const interactionReasonPrefix = offer.projection.kind === "async_question"
1515
- ? "terminal_async_question"
1516
- : "terminal_questionnaire";
1517
- return {
1518
- ...terminalWatchObservationFence(input.watch),
1519
- kind: "interaction",
1520
- observed_at: input.observedAt,
1521
- last_activity_at: input.observedAt,
1522
- ...(input.observationCheckpoint
1523
- ? { observation_checkpoint: input.observationCheckpoint }
1524
- : {}),
1525
- evidence_fingerprint: sha256({
1526
- schema: "agent-knock-knock/terminal-watch-interaction-event",
1527
- version: 1,
1528
- watch_id: input.watch.watch_id,
1529
- interaction_id: offer.projection.interaction_id,
1530
- surface_id: offer.surfaceId
1531
- }),
1532
- reason_code: responseDecision.suppress
1533
- ? `${interactionReasonPrefix}_managed_responder_precedence`
1534
- : currentInteraction.projection.capabilities.respond
1535
- ? `${interactionReasonPrefix}_response_requested`
1536
- : `${interactionReasonPrefix}_requires_manual_response`,
1537
- current_interaction: currentInteraction,
1538
- ...(responseDecision.suppress
1539
- ? { suppress_notification: true }
1540
- : {}),
1541
- ...(!responseDecision.suppress &&
1542
- !currentInteraction.projection.capabilities.respond
1543
- ? { manual_interaction: manualInteraction }
1544
- : {})
1545
- };
1546
- }
1547
- function absentAsyncQuestionObservation(input, screen, version) {
1548
- const prior = input.watch.current_interaction;
1549
- if (prior?.projection.kind !== "async_question" ||
1550
- prior.aggregate.state !== "pending" ||
1551
- !input.rawTerminal ||
1552
- !terminalWatchCandidateMatchesLiveContext(input.watch, input.rawTerminal) ||
1553
- !codexAsyncQuestionMainComposerVisible({ version, screen })) {
1554
- return undefined;
1555
- }
1556
- return {
1557
- ...terminalWatchObservationFence(input.watch),
1558
- kind: "pending",
1559
- observed_at: input.observedAt,
1560
- ...(input.observationCheckpoint
1561
- ? { observation_checkpoint: input.observationCheckpoint }
1562
- : {}),
1563
- async_interaction_absent: {
1564
- interaction_id: prior.projection.interaction_id,
1565
- prompt_fingerprint: prior.projection.prompt_fingerprint
1566
- }
1567
- };
1568
- }
1569
- /**
1570
- * Durable fallback completion remains bound to its accepted provider artifact
1571
- * even after the pane moves elsewhere. A live questionnaire is different: it
1572
- * may be attributed to a Watch only while that exact accepted native context
1573
- * is still the one rendered in the pane.
1574
- */
1575
- function fallbackQuestionnaireContextMatches(watch, terminal, checkpoint, inspection) {
1576
- if (watch.anchor.schema !==
1577
- "agent-knock-knock/codex-user-explicit-fallback-watch-anchor") {
1578
- return true;
1579
- }
1580
- if (!("schema" in checkpoint) ||
1581
- checkpoint.schema !==
1582
- "agent-knock-knock/codex-user-explicit-fallback-watch-checkpoint" ||
1583
- !checkpoint.accepted_identity ||
1584
- !checkpoint.acceptance_evidence) {
1585
- return false;
1586
- }
1587
- const accepted = checkpoint.accepted_identity;
1588
- const exactLiveContext = stringValue(terminal.native_agent_session_id)?.toLowerCase() ===
1589
- accepted.native_thread_id &&
1590
- stringValue(terminal.native_agent_process_uuid) === accepted.process_uuid &&
1591
- stringValue(terminal.native_agent_process_birth) === accepted.process_birth &&
1592
- rolloutFileIdentityMatches(terminal.native_agent_rollout, accepted.rollout);
1593
- if (exactLiveContext)
1594
- return true;
1595
- if ("interaction_kind" in inspection) {
1596
- // Async-question rollout evidence is already exact-file bound, but unlike
1597
- // blocking request_user_input it has no cross-rollout attribution format
1598
- // in Codex 0.154/0.155.1. Never follow it to a different live context.
1599
- return false;
1600
- }
1601
- const inventory = terminal._codex_open_root_rollout_inventory;
1602
- if (!isRecord(inventory))
1603
- return false;
1604
- const attribution = detectCodexBoundQuestionnaireAttribution({
1605
- currentInventory: inventory,
1606
- acceptedIdentity: {
1607
- sessionId: accepted.native_thread_id,
1608
- processUuid: accepted.process_uuid,
1609
- processBirth: accepted.process_birth,
1610
- rollout: accepted.rollout
1611
- },
1612
- acceptanceId: checkpoint.acceptance_evidence.acceptanceId,
1613
- screen: {
1614
- currentStep: inspection.current_step,
1615
- totalSteps: inspection.total_steps,
1616
- prompt: inspection.question.prompt,
1617
- responseKind: inspection.question.response_kind,
1618
- ...(inspection.question.options
1619
- ? {
1620
- options: inspection.question.options.map((option) => ({
1621
- label: option.label,
1622
- ...(option.description
1623
- ? { description: option.description }
1624
- : {})
1625
- }))
1626
- }
1627
- : {}),
1628
- exactShape: inspection.status === "actionable" &&
1629
- inspection.question.response_kind !== "multi_select"
1630
- }
1631
- });
1632
- return attribution.status === "matched";
1633
- }
1634
- function terminalWatchResponseDecision(watch, rawTerminal, surfaceId, promptFingerprint, options, dependencies) {
1635
- if (watch.status !== "active" ||
1636
- watch.interaction_policy !== "respond_when_exact" ||
1637
- isTerminalActivityWatch(watch) ||
1638
- !terminalWatchCandidateMatchesLiveContext(watch, rawTerminal) ||
1639
- (watch.callback_route !== undefined &&
1640
- watch.callback_route.capabilities?.respond !== true)) {
1641
- return { executable: false, suppress: false };
1642
- }
1643
- const terminalControl = terminalControlForWatch(rawTerminal);
1644
- let blockers;
1645
- try {
1646
- blockers = dependencies.terminalIncarnationBlockingTurns(dependencies.storeDirFromOptions(options), terminalControl);
1647
- }
1648
- catch {
1649
- return { executable: false, suppress: false };
1650
- }
1651
- let storedWatches;
1652
- try {
1653
- storedWatches = createTerminalWatchStore(dependencies.storeDirFromOptions(options), { acquire: dependencies.acquireFileLock }).list();
1654
- }
1655
- catch {
1656
- return { executable: false, suppress: false };
1657
- }
1658
- const currentIndex = storedWatches.findIndex((candidate) => candidate.watch_id === watch.watch_id);
1659
- if (currentIndex < 0) {
1660
- storedWatches.push(watch);
1661
- }
1662
- else {
1663
- storedWatches[currentIndex] = watch;
1664
- }
1665
- const candidates = storedWatches.flatMap((candidate) => {
1666
- const surfaceCandidate = terminalWatchSurfaceCandidate(candidate, watch, rawTerminal, surfaceId, promptFingerprint);
1667
- return surfaceCandidate ? [surfaceCandidate] : [];
1668
- });
1669
- const fences = [
1670
- ...blockers.flatMap((turn) => {
1671
- const fence = managedTerminalInteractionFence(turn, surfaceId, promptFingerprint, watch.agent);
1672
- return fence ? [fence] : [];
1673
- }),
1674
- ...candidates
1675
- .filter((candidate) => candidate.disposition === "fence")
1676
- .map((candidate) => ({
1677
- owner_session: candidate.watch.openclaw_session
1678
- }))
1679
- ];
1680
- if (fences.length > 0) {
1681
- return {
1682
- executable: false,
1683
- suppress: fences.some((fence) => fence.owner_session === watch.openclaw_session)
1684
- };
1685
- }
1686
- const claims = [
1687
- ...blockers.flatMap((turn) => {
1688
- const claim = managedTerminalInteractionResponderClaim(turn, surfaceId, promptFingerprint, watch.agent);
1689
- return claim ? [claim] : [];
1690
- }),
1691
- ...candidates
1692
- .filter((candidate) => candidate.disposition === "responder")
1693
- .map(({ watch: candidate }) => ({
1694
- owner_id: candidate.watch_id,
1695
- owner_session: candidate.openclaw_session,
1696
- surface_id: surfaceId,
1697
- responder_class: isUserExplicitFallbackWatch(candidate)
1698
- ? "exact_request_watch"
1699
- : "exact_task_watch",
1700
- response_authority: "executable",
1701
- created_at: candidate.created_at
1702
- }))
1703
- ];
1704
- const winner = selectTerminalInteractionResponder(claims);
1705
- const executable = winner?.owner_id === watch.watch_id;
1706
- return {
1707
- executable,
1708
- suppress: Boolean(winner &&
1709
- !executable &&
1710
- winner.owner_session === watch.openclaw_session)
1711
- };
1712
- }
1713
- function terminalWatchSurfaceCandidate(candidate, current, rawTerminal, surfaceId, promptFingerprint) {
1714
- if (candidate.status !== "active" ||
1715
- candidate.interaction_policy !== "respond_when_exact" ||
1716
- isTerminalActivityWatch(candidate) ||
1717
- candidate.terminal.terminal_id !== current.terminal.terminal_id ||
1718
- !terminalWatchCandidateMatchesLiveContext(candidate, rawTerminal) ||
1719
- (candidate.callback_route !== undefined &&
1720
- candidate.callback_route.capabilities?.respond !== true)) {
1721
- return undefined;
1722
- }
1723
- const persisted = candidate.current_interaction;
1724
- const samePersistedSurface = Boolean(persisted &&
1725
- persisted.projection.surface_id === surfaceId &&
1726
- persisted.projection.prompt_fingerprint === promptFingerprint);
1727
- if (candidate.watch_id !== current.watch_id) {
1728
- if (!samePersistedSurface ||
1729
- !terminalWatchesShareExactTask(candidate, current) ||
1730
- (persisted.aggregate.state === "pending" &&
1731
- (persisted.projection.response_authority !== "executable" ||
1732
- persisted.projection.capabilities.respond !== true ||
1733
- persisted.aggregate.response_authority !== "executable"))) {
1734
- return undefined;
1735
- }
1736
- }
1737
- return {
1738
- watch: candidate,
1739
- disposition: samePersistedSurface && persisted.aggregate.state !== "pending"
1740
- ? "fence"
1741
- : "responder"
1742
- };
1743
- }
1744
- function terminalWatchesShareExactTask(left, right) {
1745
- const leftKey = terminalWatchExactTaskKey(left);
1746
- const rightKey = terminalWatchExactTaskKey(right);
1747
- return leftKey !== undefined && leftKey === rightKey;
1748
- }
1749
- function terminalWatchExactTaskKey(watch) {
1750
- const anchor = watch.anchor;
1751
- if (anchor.schema === "agent-knock-knock/codex-paginated-task-anchor") {
1752
- const checkpoint = watch.observation_checkpoint;
1753
- const turnId = "schema" in checkpoint && checkpoint.schema === "agent-knock-knock/codex-paginated-task-checkpoint"
1754
- ? checkpoint.acceptance_evidence?.acceptanceId ?? anchor.turn_id : anchor.turn_id;
1755
- return turnId ? sha256({ source: "codex_paginated", thread: anchor.native_thread_id,
1756
- task: turnId, home: anchor.codex_home, process: anchor.process_uuid }) : undefined;
1757
- }
1758
- if (anchor.schema ===
1759
- "agent-knock-knock/codex-human-started-active-task-anchor") {
1760
- return codexWatchTaskKey({
1761
- taskId: anchor.turn_id,
1762
- nativeSessionId: anchor.native_thread_id,
1763
- processUuid: anchor.process_uuid,
1764
- processBirth: anchor.process_birth,
1765
- rollout: anchor.rollout,
1766
- requestHash: anchor.request_hash
1767
- });
1768
- }
1769
- if (anchor.schema ===
1770
- "agent-knock-knock/codex-user-explicit-fallback-watch-anchor") {
1771
- const checkpoint = watch.observation_checkpoint;
1772
- if (!("schema" in checkpoint) ||
1773
- checkpoint.schema !==
1774
- "agent-knock-knock/codex-user-explicit-fallback-watch-checkpoint" ||
1775
- !checkpoint.accepted_identity ||
1776
- !checkpoint.acceptance_evidence)
1777
- return undefined;
1778
- return codexWatchTaskKey({
1779
- taskId: checkpoint.acceptance_evidence.acceptanceId,
1780
- nativeSessionId: checkpoint.accepted_identity.native_thread_id,
1781
- processUuid: checkpoint.accepted_identity.process_uuid,
1782
- processBirth: checkpoint.accepted_identity.process_birth,
1783
- rollout: checkpoint.accepted_identity.rollout,
1784
- requestHash: anchor.request_hash
1785
- });
1786
- }
1787
- if (anchor.schema ===
1788
- "agent-knock-knock/claude-human-started-active-task-anchor") {
1789
- return claudeWatchTaskKey({
1790
- taskId: anchor.prompt_uuid,
1791
- nativeSessionId: anchor.session_id,
1792
- pid: anchor.pid,
1793
- agentStartedAtMs: anchor.agent_started_at_ms,
1794
- transcriptFileId: anchor.transcript_file_id,
1795
- cwd: anchor.cwd,
1796
- requestHash: anchor.request_hash
1797
- });
1798
- }
1799
- if (anchor.schema ===
1800
- "agent-knock-knock/claude-user-explicit-fallback-watch-anchor") {
1801
- const checkpoint = watch.observation_checkpoint;
1802
- if (!("schema" in checkpoint) ||
1803
- checkpoint.schema !==
1804
- "agent-knock-knock/claude-user-explicit-fallback-watch-checkpoint" ||
1805
- !checkpoint.accepted_prompt_uuid ||
1806
- !checkpoint.acceptance_evidence)
1807
- return undefined;
1808
- const transcriptFileId = stringValue(checkpoint.acceptance_evidence.metadata?.transcript_file_id);
1809
- if (!transcriptFileId)
1810
- return undefined;
1811
- return claudeWatchTaskKey({
1812
- taskId: checkpoint.accepted_prompt_uuid,
1813
- nativeSessionId: anchor.transcript_anchor.session_id,
1814
- pid: anchor.transcript_anchor.pid,
1815
- agentStartedAtMs: anchor.transcript_anchor.agent_started_at_ms,
1816
- transcriptFileId,
1817
- cwd: anchor.transcript_anchor.cwd,
1818
- requestHash: anchor.request_hash
1819
- });
1820
- }
1821
- return undefined;
1822
- }
1823
- function codexWatchTaskKey(input) {
1824
- const rollout = isRecord(input.rollout) ? input.rollout : undefined;
1825
- const device = stringValue(rollout?.device);
1826
- const inode = stringValue(rollout?.inode);
1827
- const rolloutPath = stringValue(rollout?.path);
1828
- if (!device || !inode || !rolloutPath)
1829
- return undefined;
1830
- return sha256({
1831
- agent: "codex",
1832
- task_id: input.taskId,
1833
- native_session_id: input.nativeSessionId.toLowerCase(),
1834
- process_uuid: input.processUuid,
1835
- process_birth: input.processBirth,
1836
- rollout: { device, inode, path: rolloutPath },
1837
- request_hash: input.requestHash
1838
- });
1839
- }
1840
- function claudeWatchTaskKey(input) {
1841
- if (!Number.isSafeInteger(input.pid) || input.pid <= 0 ||
1842
- !Number.isSafeInteger(input.agentStartedAtMs) ||
1843
- input.agentStartedAtMs <= 0)
1844
- return undefined;
1845
- return sha256({
1846
- agent: "claude",
1847
- task_id: input.taskId,
1848
- native_session_id: input.nativeSessionId,
1849
- pid: input.pid,
1850
- agent_started_at_ms: input.agentStartedAtMs,
1851
- transcript_file_id: input.transcriptFileId,
1852
- cwd: input.cwd,
1853
- request_hash: input.requestHash
1854
- });
1855
- }
1856
- function managedTerminalInteractionFence(turn, surfaceId, promptFingerprint, agent) {
1857
- const ownerSession = stringValue(turn.openclaw_session);
1858
- const takeover = isRecord(turn.native_session_takeover)
1859
- ? turn.native_session_takeover
1860
- : undefined;
1861
- if (ownerSession &&
1862
- executorForConversation(turn).kind === agent &&
1863
- stringValue(takeover?.terminal_bridge_last_interaction_surface_id) ===
1864
- surfaceId &&
1865
- stringValue(takeover?.terminal_bridge_last_interaction_fingerprint) ===
1866
- promptFingerprint &&
1867
- stringValue(takeover?.terminal_bridge_last_interaction_id) !== undefined &&
1868
- canonicalTimestamp(takeover?.terminal_bridge_last_interaction_at) !== undefined) {
1869
- return { owner_session: ownerSession };
1870
- }
1871
- const evidence = managedTerminalInteractionEvidence(turn, surfaceId, promptFingerprint, agent);
1872
- if (!evidence)
1873
- return undefined;
1874
- const dispatch = isRecord(evidence.takeover.terminal_bridge_interaction_dispatch)
1875
- ? evidence.takeover.terminal_bridge_interaction_dispatch
1876
- : undefined;
1877
- const dispatchState = stringValue(dispatch?.state);
1878
- const dispatchMatches = (dispatchState === "reserved" || dispatchState === "uncertain") &&
1879
- stringValue(dispatch?.interaction_id) ===
1880
- evidence.projection.interaction_id &&
1881
- stringValue(dispatch?.interaction_prompt_fingerprint) ===
1882
- promptFingerprint;
1883
- return evidence.projection.state === "response_uncertain" || dispatchMatches
1884
- ? { owner_session: evidence.ownerSession }
1885
- : undefined;
1886
- }
1887
- function canonicalTimestamp(value) {
1888
- const timestamp = stringValue(value);
1889
- return timestamp &&
1890
- Number.isFinite(Date.parse(timestamp)) &&
1891
- new Date(timestamp).toISOString() === timestamp
1892
- ? timestamp
1893
- : undefined;
1894
- }
1895
- function managedTerminalInteractionResponderClaim(turn, surfaceId, promptFingerprint, agent) {
1896
- const evidence = managedTerminalInteractionEvidence(turn, surfaceId, promptFingerprint, agent);
1897
- if (!evidence)
1898
- return undefined;
1899
- const createdAt = stringValue(evidence.notification.notified_at);
1900
- if (!createdAt ||
1901
- !Number.isFinite(Date.parse(createdAt)) ||
1902
- new Date(createdAt).toISOString() !== createdAt)
1903
- return undefined;
1904
- if (evidence.projection.state !== "pending" ||
1905
- evidence.projection.capabilities.respond !== true) {
1906
- return undefined;
1907
- }
1908
- return {
1909
- owner_id: evidence.ownerId,
1910
- owner_session: evidence.ownerSession,
1911
- surface_id: surfaceId,
1912
- responder_class: "managed_turn",
1913
- response_authority: "executable",
1914
- created_at: createdAt
1915
- };
1916
- }
1917
- function managedTerminalInteractionEvidence(turn, surfaceId, promptFingerprint, agent) {
1918
- const takeover = isRecord(turn.native_session_takeover)
1919
- ? turn.native_session_takeover
1920
- : undefined;
1921
- const notification = isRecord(takeover?.terminal_bridge_interaction_notification)
1922
- ? takeover.terminal_bridge_interaction_notification
1923
- : undefined;
1924
- if (!takeover ||
1925
- !notification ||
1926
- stringValue(notification.surface_id) !== surfaceId ||
1927
- stringValue(notification.prompt_fingerprint) !== promptFingerprint)
1928
- return undefined;
1929
- let ownerId;
1930
- try {
1931
- ownerId = turnIdForConversation(turn);
1932
- }
1933
- catch {
1934
- return undefined;
1935
- }
1936
- const ownerSession = stringValue(turn.openclaw_session);
1937
- if (!ownerSession)
1938
- return undefined;
1939
- const projection = (() => {
1940
- try {
1941
- return validateAnyTerminalInteractionProjection(notification.interaction_state);
1942
- }
1943
- catch {
1944
- return undefined;
1945
- }
1946
- })();
1947
- if (!projection ||
1948
- projection.interaction_id !== stringValue(notification.interaction_id) ||
1949
- projection.agent !== agent ||
1950
- projection.questions.length !== 1 ||
1951
- projection.questions[0]?.question_id !==
1952
- stringValue(notification.question_id) ||
1953
- (projection.version === 1
1954
- ? projection.turn_id !== ownerId
1955
- : projection.subject.kind !== "managed_turn" ||
1956
- projection.subject.turn_id !== ownerId))
1957
- return undefined;
1958
- return { ownerId, ownerSession, takeover, notification, projection };
1959
- }
1960
- function terminalWatchCandidateMatchesLiveContext(watch, terminal) {
1961
- if (!terminalMatchesWatch(terminal, watch))
1962
- return false;
1963
- if (watch.anchor.schema === "agent-knock-knock/codex-paginated-task-anchor") {
1964
- return paginatedWatchProcessMatches(terminal, watch.anchor);
1965
- }
1966
- const sessionId = stringValue(terminal.native_agent_session_id);
1967
- if (watch.anchor.schema ===
1968
- "agent-knock-knock/codex-human-started-active-task-anchor") {
1969
- return sessionId?.toLowerCase() === watch.anchor.native_thread_id &&
1970
- rolloutFileIdentityMatches(terminal.native_agent_rollout, watch.anchor.rollout);
1971
- }
1972
- if (watch.anchor.schema ===
1973
- "agent-knock-knock/codex-user-explicit-fallback-watch-anchor") {
1974
- const checkpoint = watch.observation_checkpoint;
1975
- return "schema" in checkpoint &&
1976
- checkpoint.schema ===
1977
- "agent-knock-knock/codex-user-explicit-fallback-watch-checkpoint" &&
1978
- checkpoint.accepted_identity !== undefined &&
1979
- sessionId?.toLowerCase() === checkpoint.accepted_identity.native_thread_id &&
1980
- rolloutFileIdentityMatches(terminal.native_agent_rollout, checkpoint.accepted_identity.rollout);
1981
- }
1982
- if (watch.anchor.schema ===
1983
- "agent-knock-knock/claude-human-started-active-task-anchor") {
1984
- return sessionId === watch.anchor.session_id;
1985
- }
1986
- if (watch.anchor.schema ===
1987
- "agent-knock-knock/claude-user-explicit-fallback-watch-anchor") {
1988
- return sessionId === watch.anchor.transcript_anchor.session_id;
1989
- }
1990
- return false;
1991
- }
1992
- function terminalWatchScreenExcerpt(rawTerminal, projectedTerminal) {
1993
- const direct = stringValue(projectedTerminal.screen_excerpt) ??
1994
- stringValue(rawTerminal.screen_excerpt);
1995
- if (direct)
1996
- return direct;
1997
- const status = isRecord(rawTerminal._terminal_status_snapshot)
1998
- ? rawTerminal._terminal_status_snapshot
1999
- : undefined;
2000
- const screen = status && isRecord(status.screen) ? status.screen : undefined;
2001
- return stringValue(screen?.excerpt);
2002
- }
2003
- function terminalWatchManualInteractionSummary(inspection) {
2004
- const exposeQuestion = inspection.status === "actionable";
2005
- return {
2006
- kind: "interaction_kind" in inspection
2007
- ? inspection.interaction_kind
2008
- : "questionnaire",
2009
- response_kind: inspection.question.response_kind,
2010
- required: inspection.question.required,
2011
- current_step: inspection.current_step,
2012
- total_steps: inspection.total_steps,
2013
- parser_status: inspection.status,
2014
- ...(exposeQuestion
2015
- ? {
2016
- prompt: boundedInteractionText(inspection.question.prompt, 1_000),
2017
- ...(inspection.question.options &&
2018
- inspection.question.options.length > 0
2019
- ? {
2020
- options: inspection.question.options.slice(0, 8).map((option) => ({
2021
- label: boundedInteractionText(option.label, 300),
2022
- ...(option.description
2023
- ? {
2024
- description: boundedInteractionText(option.description, 600)
2025
- }
2026
- : {})
2027
- }))
2028
- }
2029
- : {})
2030
- }
2031
- : { manual_reason: inspection.reason })
2032
- };
2033
- }
2034
- function boundedInteractionText(value, maxCharacters) {
2035
- const normalized = value
2036
- .replace(/[\u0000-\u001F\u007F-\u009F]+/gu, " ")
2037
- .replace(/\s+/gu, " ")
2038
- .trim()
2039
- .slice(0, maxCharacters);
2040
- return normalized || "Native questionnaire";
2041
- }
2042
- function fallbackPendingOrTerminalObservation(input) {
2043
- if (input.exactTerminal.state === "unavailable") {
2044
- return {
2045
- ...terminalWatchObservationFence(input.watch),
2046
- kind: "unavailable",
2047
- observed_at: input.observedAt,
2048
- reason_code: input.reasonCode ?? "terminal_observation_unavailable"
2049
- };
2050
- }
2051
- if (input.exactTerminal.state === "absent" || !input.rawTerminal) {
2052
- return invalidatedObservation(input.watch, input.observedAt, "terminal_process_unavailable");
2053
- }
2054
- if (!input.terminalMatches) {
2055
- return invalidatedObservation(input.watch, input.observedAt, "terminal_identity_changed");
2056
- }
2057
- if (input.reasonCode) {
2058
- return {
2059
- ...terminalWatchObservationFence(input.watch),
2060
- kind: "unavailable",
2061
- observed_at: input.observedAt,
2062
- reason_code: input.reasonCode
2063
- };
2064
- }
2065
- return fallbackPendingObservation(input.watch, input.observedAt, input.observedEndOffsetBytes, input.observationCheckpoint);
2066
- }
2067
- function fallbackPendingObservation(watch, observedAt, observedEndOffsetBytes, observationCheckpoint) {
2068
- const effectiveCheckpoint = observationCheckpoint ?? (watch.anchor.schema ===
2069
- "agent-knock-knock/codex-user-explicit-fallback-watch-anchor"
2070
- ? watch.observation_checkpoint
2071
- : undefined);
2072
- const safeResumeOffsetBytes = effectiveCheckpoint
2073
- ?.safe_resume_offset_bytes ?? fallbackResumeOffset(watch, observedEndOffsetBytes);
2074
- return {
2075
- ...terminalWatchObservationFence(watch),
2076
- kind: "pending",
2077
- observed_at: observedAt,
2078
- safe_resume_offset_bytes: safeResumeOffsetBytes,
2079
- ...(effectiveCheckpoint
2080
- ? { observation_checkpoint: effectiveCheckpoint }
2081
- : {})
2082
- };
2083
- }
2084
- function codexFallbackObservationCheckpoint(watch) {
2085
- const checkpoint = watch.observation_checkpoint;
2086
- if (!("schema" in checkpoint) ||
2087
- checkpoint.schema !==
2088
- "agent-knock-knock/codex-user-explicit-fallback-watch-checkpoint") {
2089
- throw new Error("Codex fallback Watch has no exact acceptance checkpoint");
2090
- }
2091
- return checkpoint;
2092
- }
2093
- function claudeFallbackObservationCheckpoint(watch) {
2094
- const checkpoint = watch.observation_checkpoint;
2095
- if (!("schema" in checkpoint) ||
2096
- checkpoint.schema !==
2097
- "agent-knock-knock/claude-user-explicit-fallback-watch-checkpoint") {
2098
- throw new Error("Claude fallback Watch has no exact acceptance checkpoint");
2099
- }
2100
- return checkpoint;
2101
- }
2102
- function codexFallbackAcceptedCheckpoint(watch, acceptanceEvidence, currentIdentity) {
2103
- const nativeThreadId = requiredString(currentIdentity.sessionId, "accepted Codex native thread id");
2104
- const processUuid = requiredString(currentIdentity.processUuid, "accepted Codex process UUID");
2105
- const processBirth = requiredString(currentIdentity.processBirth, "accepted Codex process birth");
2106
- const rollout = currentIdentity.rollout;
2107
- if (!rollout) {
2108
- throw new Error("accepted Codex rollout identity is unavailable");
2109
- }
2110
- const acceptanceOffset = numericMetadata(acceptanceEvidence.metadata, "observed_end_offset_bytes");
2111
- if (acceptanceOffset === undefined) {
2112
- throw new Error("accepted Codex rollout boundary is unavailable");
2113
- }
2114
- return {
2115
- schema: "agent-knock-knock/codex-user-explicit-fallback-watch-checkpoint",
2116
- version: 1,
2117
- safe_resume_offset_bytes: fallbackResumeOffset(watch, acceptanceOffset),
2118
- acceptance_evidence: acceptanceEvidence,
2119
- accepted_identity: {
2120
- native_thread_id: nativeThreadId,
2121
- process_uuid: processUuid,
2122
- process_birth: processBirth,
2123
- rollout
2124
- }
2125
- };
2126
- }
2127
- function claudeFallbackAcceptedCheckpoint(watch, acceptanceEvidence) {
2128
- const acceptanceOffset = numericMetadata(acceptanceEvidence.metadata, "observed_end_offset_bytes");
2129
- if (acceptanceOffset === undefined) {
2130
- throw new Error("accepted Claude transcript boundary is unavailable");
2131
- }
2132
- const promptUuid = requiredString(acceptanceEvidence.acceptanceId, "accepted Claude prompt UUID");
2133
- return {
2134
- schema: "agent-knock-knock/claude-user-explicit-fallback-watch-checkpoint",
2135
- version: 1,
2136
- safe_resume_offset_bytes: fallbackResumeOffset(watch, acceptanceOffset),
2137
- acceptance_evidence: acceptanceEvidence,
2138
- accepted_prompt_uuid: promptUuid
2139
- };
2140
- }
2141
- function fallbackCompletionObservation(watch, observedAt, completion, observedEndOffsetBytes, observationCheckpoint) {
2142
- const safeResumeOffsetBytes = observationCheckpoint
2143
- ?.safe_resume_offset_bytes ?? fallbackResumeOffset(watch, observedEndOffsetBytes);
2144
- const kind = completion.outcome === "failure" ? "failed" : "completed";
2145
- return {
2146
- ...terminalWatchObservationFence(watch),
2147
- kind,
2148
- observed_at: observedAt,
2149
- safe_resume_offset_bytes: safeResumeOffsetBytes,
2150
- ...(observationCheckpoint
2151
- ? { observation_checkpoint: observationCheckpoint }
2152
- : {}),
2153
- evidence_fingerprint: sha256({
2154
- kind,
2155
- watch_id: watch.watch_id,
2156
- completion_id: completion.id ?? null,
2157
- completion_timestamp: completion.timestamp ?? null,
2158
- anchor_fingerprint: watch.anchor.anchor_fingerprint
2159
- }),
2160
- reason_code: completion.outcome === "failure"
2161
- ? "anchored_task_failed"
2162
- : "anchored_task_completed",
2163
- completion_text: completion.text.slice(0, 4000),
2164
- completion_id: completion.id,
2165
- completion_timestamp: completion.timestamp
2166
- };
2167
- }
2168
- function fallbackResumeOffset(watch, observedEndOffsetBytes) {
2169
- return Math.max(watch.observation_checkpoint.safe_resume_offset_bytes, Number.isSafeInteger(observedEndOffsetBytes) &&
2170
- Number(observedEndOffsetBytes) >= 0
2171
- ? Number(observedEndOffsetBytes)
2172
- : 0);
2173
- }
2174
- function numericMetadata(metadata, field) {
2175
- const value = metadata?.[field];
2176
- return Number.isSafeInteger(value) && Number(value) >= 0
2177
- ? Number(value)
2178
- : undefined;
2179
- }
2180
- function retryableProviderError(error) {
2181
- return isRecord(error) && typeof error.code === "string" && new Set([
2182
- "EACCES", "EAGAIN", "EBUSY", "EIO", "EMFILE", "ENFILE", "ENOENT",
2183
- "EPERM", "ESTALE", "ETIMEDOUT"
2184
- ]).has(error.code);
2185
- }
2186
- function claudeObservationCheckpoint(watch) {
2187
- const checkpoint = watch.observation_checkpoint;
2188
- if (!("schema" in checkpoint) ||
2189
- checkpoint.schema !==
2190
- "agent-knock-knock/claude-human-started-active-task-checkpoint") {
2191
- throw new Error("Claude terminal Watch has no continuation checkpoint");
2192
- }
2193
- return checkpoint;
2194
- }
2195
- async function currentTerminalForWatch(watch, options, dependencies) {
2196
- return dependencies.observeExactTerminal({
2197
- options,
2198
- terminalId: watch.terminal.terminal_id
2199
- });
2200
- }
2201
- function terminalMatchesWatch(terminal, watch) {
2202
- try {
2203
- return terminalAgent(terminal) === watch.agent &&
2204
- requiredString(terminal.id, "terminal id") ===
2205
- watch.terminal.terminal_id &&
2206
- terminalWorkspace(terminal) === watch.terminal.workspace &&
2207
- isRecord(terminal.terminal_control) &&
2208
- sameTerminalControlEvidenceIncarnation(terminal.terminal_control, watch.terminal.terminal_endpoint);
2209
- }
2210
- catch {
2211
- return false;
2212
- }
2213
- }
2214
- function terminalActivityWatchIdentityMatch(terminal, watch) {
2215
- try {
2216
- if (!isTerminalActivityWatch(watch) ||
2217
- terminalAgent(terminal) !== watch.agent ||
2218
- requiredString(terminal.id, "terminal id") !==
2219
- watch.terminal.terminal_id ||
2220
- terminalWorkspace(terminal) !== watch.terminal.workspace ||
2221
- positiveInteger(terminal.pid, "terminal PID") !== watch.anchor.pid) {
2222
- return "mismatch";
2223
- }
2224
- if (!isRecord(terminal.terminal_control)) {
2225
- return "unavailable";
2226
- }
2227
- if (!sameTerminalControlEvidenceIncarnation(terminal.terminal_control, watch.terminal.terminal_endpoint)) {
2228
- return "mismatch";
2229
- }
2230
- const processUuid = stringValue(terminal.native_agent_process_uuid);
2231
- const processBirth = stringValue(terminal.native_agent_process_birth);
2232
- if ((watch.anchor.native_process_uuid !== undefined && !processUuid) ||
2233
- (watch.anchor.native_process_birth !== undefined && !processBirth)) {
2234
- return "unavailable";
2235
- }
2236
- return (watch.anchor.native_process_uuid !== undefined &&
2237
- processUuid !== watch.anchor.native_process_uuid) || (watch.anchor.native_process_birth !== undefined &&
2238
- processBirth !== watch.anchor.native_process_birth)
2239
- ? "mismatch"
2240
- : "match";
2241
- }
2242
- catch {
2243
- return "unavailable";
2244
- }
2245
- }
2246
- function terminalMatchesUserExplicitFallbackWatch(terminal, watch) {
2247
- try {
2248
- if (!isUserExplicitFallbackWatch(watch) ||
2249
- terminalAgent(terminal) !== watch.agent ||
2250
- requiredString(terminal.id, "terminal id") !==
2251
- watch.terminal.terminal_id ||
2252
- terminalWorkspace(terminal) !== watch.terminal.workspace ||
2253
- !isRecord(terminal.terminal_control) ||
2254
- !sameTerminalControlEvidenceIncarnation(terminal.terminal_control, watch.terminal.terminal_endpoint)) {
2255
- return false;
2256
- }
2257
- if (watch.anchor.schema ===
2258
- "agent-knock-knock/codex-user-explicit-fallback-watch-anchor") {
2259
- return requiredString(terminal.native_agent_process_uuid, "Codex process UUID") === watch.anchor.acceptance_anchor.process_uuid &&
2260
- requiredString(terminal.native_agent_process_birth, "Codex process birth") === watch.anchor.acceptance_anchor.process_birth;
2261
- }
2262
- return watch.anchor.schema ===
2263
- "agent-knock-knock/claude-user-explicit-fallback-watch-anchor" &&
2264
- positiveInteger(terminal.pid, "Claude PID") ===
2265
- watch.anchor.transcript_anchor.pid;
2266
- }
2267
- catch {
2268
- return false;
2269
- }
2270
- }
2271
- function terminalActivityObservationCheckpoint(watch) {
2272
- const checkpoint = watch.observation_checkpoint;
2273
- if (!("schema" in checkpoint) ||
2274
- checkpoint.schema !==
2275
- "agent-knock-knock/terminal-activity-watch-checkpoint") {
2276
- throw new Error("terminal activity Watch has no activity checkpoint");
2277
- }
2278
- return checkpoint;
2279
- }
2280
- function terminalWatchIdentity(terminal, bindingToken) {
2281
- const terminalControl = terminal.terminal_control;
2282
- return {
2283
- terminal_id: requiredString(terminal.id, "terminal id"),
2284
- terminal_endpoint: terminalControlEvidence(terminalControl),
2285
- workspace: terminalWorkspace(terminal),
2286
- binding_token: bindingToken
2287
- };
2288
- }
2289
- function codexIdentity(terminal) {
2290
- if (!isRecord(terminal.native_agent_rollout)) {
2291
- throw new Error("Codex terminal has no exact rollout identity");
2292
- }
2293
- return {
2294
- sessionId: requiredString(terminal.native_agent_session_id, "Codex native thread id"),
2295
- processUuid: requiredString(terminal.native_agent_process_uuid, "Codex process UUID"),
2296
- processBirth: requiredString(terminal.native_agent_process_birth, "Codex process birth"),
2297
- rollout: {
2298
- fd: requiredString(terminal.native_agent_rollout.fd, "Codex rollout fd"),
2299
- device: requiredString(terminal.native_agent_rollout.device, "Codex rollout device"),
2300
- inode: requiredString(terminal.native_agent_rollout.inode, "Codex rollout inode"),
2301
- path: requiredString(terminal.native_agent_rollout.path, "Codex rollout path")
2302
- }
2303
- };
2304
- }
2305
- function codexIdentityForWatch(watch) {
2306
- if (watch.agent !== "codex" ||
2307
- watch.anchor.schema !==
2308
- "agent-knock-knock/codex-human-started-active-task-anchor") {
2309
- throw new Error("terminal Watch has no Codex identity");
2310
- }
2311
- return {
2312
- sessionId: watch.anchor.native_thread_id,
2313
- processUuid: watch.anchor.process_uuid,
2314
- processBirth: watch.anchor.process_birth,
2315
- rollout: watch.anchor.rollout
2316
- };
2317
- }
2318
454
  function watchStatusInteractionAccess(watch, options) {
2319
455
  if (!Object.hasOwn(options, "openclawSession"))
2320
456
  return false;
@@ -2324,122 +460,4 @@ function watchStatusInteractionAccess(watch, options) {
2324
460
  }
2325
461
  return true;
2326
462
  }
2327
- function approvalFingerprint(terminal) {
2328
- if (terminal.activity_state !== "awaiting_approval")
2329
- return undefined;
2330
- const approval = isRecord(terminal.approval_state)
2331
- ? terminal.approval_state
2332
- : undefined;
2333
- const fingerprint = stringValue(approval?.fingerprint);
2334
- return fingerprint && /^[a-f0-9]{64}$/u.test(fingerprint)
2335
- ? fingerprint
2336
- : undefined;
2337
- }
2338
- function invalidatedObservation(watch, observedAt, reasonCode) {
2339
- return {
2340
- ...terminalWatchObservationFence(watch),
2341
- kind: "invalidated",
2342
- observed_at: observedAt,
2343
- evidence_fingerprint: sha256({
2344
- watch_id: watch.watch_id,
2345
- reason_code: reasonCode,
2346
- anchor_fingerprint: watch.anchor.anchor_fingerprint
2347
- }),
2348
- reason_code: reasonCode
2349
- };
2350
- }
2351
- function terminalAgent(terminal) {
2352
- const agent = stringValue(terminal.agent);
2353
- if (agent !== "codex" && agent !== "claude") {
2354
- throw new Error("terminal Watch supports only Codex or Claude Code");
2355
- }
2356
- return agent;
2357
- }
2358
- function terminalWorkspace(terminal) {
2359
- const workspace = requiredString(terminal.workspace ?? terminal.cwd, "terminal workspace");
2360
- if (!path.isAbsolute(workspace)) {
2361
- throw new Error("terminal workspace must be absolute");
2362
- }
2363
- return path.resolve(workspace);
2364
- }
2365
- function requiredWatchId(value) {
2366
- const watchId = requiredString(value, "--watch");
2367
- if (!/^terminal-watch-[A-Za-z0-9._:-]+$/u.test(watchId)) {
2368
- throw new Error("--watch must be an exact Terminal Watch id");
2369
- }
2370
- return watchId;
2371
- }
2372
- function requiredString(value, label) {
2373
- const result = stringValue(value);
2374
- if (!result || result.includes("\0")) {
2375
- throw new Error(`${label} is required`);
2376
- }
2377
- return result;
2378
- }
2379
- function requiredSha256(value, label) {
2380
- const result = requiredString(value, label);
2381
- if (!/^[a-f0-9]{64}$/u.test(result)) {
2382
- throw new Error(`${label} must be exactly 64 lowercase ASCII hexadecimal characters`);
2383
- }
2384
- return result;
2385
- }
2386
- function requiredTimestamp(value, label) {
2387
- const result = requiredString(value, label);
2388
- if (!Number.isFinite(Date.parse(result))) {
2389
- throw new Error(`${label} must be a valid timestamp`);
2390
- }
2391
- return result;
2392
- }
2393
- function parseWatchInteractionResponse(value) {
2394
- const serialized = requiredString(value, "--response-json");
2395
- try {
2396
- const parsed = JSON.parse(serialized);
2397
- if (!isRecord(parsed)) {
2398
- throw new Error("response must be an object");
2399
- }
2400
- return parsed;
2401
- }
2402
- catch (error) {
2403
- throw new Error(`--response-json is invalid: ${safeDiagnostic(error)}`);
2404
- }
2405
- }
2406
- function assertWatchControllerSession(watch, requestedValue) {
2407
- const requested = stringValue(requestedValue);
2408
- if (!requested || requested !== watch.openclaw_session) {
2409
- throw new Error(`terminal Watch ${watch.watch_id} belongs to a different controller session; no terminal input was sent`);
2410
- }
2411
- }
2412
- function requiredExecutableWatchInteraction(watch, interactionId, expectedFingerprint) {
2413
- if (watch.status !== "active") {
2414
- throw new Error(`terminal Watch ${watch.watch_id} is ${watch.status}`);
2415
- }
2416
- const interaction = watch.current_interaction;
2417
- if (watch.interaction_policy !== "respond_when_exact" ||
2418
- !interaction ||
2419
- interaction.aggregate.state !== "pending" ||
2420
- interaction.projection.interaction_id !== interactionId ||
2421
- interaction.projection.prompt_fingerprint !== expectedFingerprint ||
2422
- interaction.projection.response_authority !== "executable" ||
2423
- !interaction.projection.capabilities.respond) {
2424
- throw new Error("terminal Watch has no matching executable interaction offer; refresh status");
2425
- }
2426
- return interaction;
2427
- }
2428
- function positiveInteger(value, label) {
2429
- const result = Number(value);
2430
- if (!Number.isSafeInteger(result) || result <= 0) {
2431
- throw new Error(`${label} must be a positive integer`);
2432
- }
2433
- return result;
2434
- }
2435
- function positiveMinutes(value, fallback) {
2436
- const result = value === undefined ? fallback : Number(value);
2437
- if (!Number.isFinite(result) || result <= 0) {
2438
- throw new Error("--hard-timeout-minutes must be a positive number");
2439
- }
2440
- return result;
2441
- }
2442
- function sha256(value) {
2443
- return createHash("sha256").update(JSON.stringify(value)).digest("hex");
2444
- }
2445
463
  //# sourceMappingURL=terminal-watch-cli-adapter.js.map