@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.
- package/CHANGELOG.md +13 -0
- package/README.md +2 -1
- package/dist/src/claude-terminal-agent-adapter.js +1 -1
- package/dist/src/claude-terminal-agent-adapter.js.map +1 -1
- package/dist/src/cli-core.js +6 -3
- package/dist/src/cli-core.js.map +1 -1
- package/dist/src/codex-app-server-interaction-client.js +68 -26
- package/dist/src/codex-app-server-interaction-client.js.map +1 -1
- package/dist/src/codex-app-server-read-client.js +52 -20
- package/dist/src/codex-app-server-read-client.js.map +1 -1
- package/dist/src/codex-astra-composer-proof.js +1 -1
- package/dist/src/codex-astra-composer-proof.js.map +1 -1
- package/dist/src/codex-open-rollout-inventory.d.ts +51 -0
- package/dist/src/codex-open-rollout-inventory.js +365 -0
- package/dist/src/codex-open-rollout-inventory.js.map +1 -0
- package/dist/src/codex-paginated-thread-binding.js +1 -1
- package/dist/src/codex-paginated-thread-binding.js.map +1 -1
- package/dist/src/codex-permission-surface.d.ts +23 -0
- package/dist/src/codex-permission-surface.js +211 -0
- package/dist/src/codex-permission-surface.js.map +1 -0
- package/dist/src/codex-rollout-bound-completion.d.ts +44 -0
- package/dist/src/codex-rollout-bound-completion.js +357 -0
- package/dist/src/codex-rollout-bound-completion.js.map +1 -0
- package/dist/src/codex-rollout-file.d.ts +15 -0
- package/dist/src/codex-rollout-file.js +98 -0
- package/dist/src/codex-rollout-file.js.map +1 -0
- package/dist/src/codex-rollout-human-task.d.ts +61 -0
- package/dist/src/codex-rollout-human-task.js +555 -0
- package/dist/src/codex-rollout-human-task.js.map +1 -0
- package/dist/src/codex-rollout-interaction-evidence.d.ts +48 -0
- package/dist/src/codex-rollout-interaction-evidence.js +521 -0
- package/dist/src/codex-rollout-interaction-evidence.js.map +1 -0
- package/dist/src/codex-rollout-inventory.d.ts +5 -0
- package/dist/src/codex-rollout-inventory.js +81 -0
- package/dist/src/codex-rollout-inventory.js.map +1 -0
- package/dist/src/codex-rollout-records.d.ts +49 -0
- package/dist/src/codex-rollout-records.js +298 -0
- package/dist/src/codex-rollout-records.js.map +1 -0
- package/dist/src/codex-sqlite-thread-query.d.ts +85 -0
- package/dist/src/codex-sqlite-thread-query.js +440 -0
- package/dist/src/codex-sqlite-thread-query.js.map +1 -0
- package/dist/src/codex-status-history-navigation.d.ts +1 -1
- package/dist/src/codex-status-history-navigation.js +1 -1
- package/dist/src/codex-status-history-navigation.js.map +1 -1
- package/dist/src/codex-store-adapter.d.ts +3 -50
- package/dist/src/codex-store-adapter.js +5 -796
- package/dist/src/codex-store-adapter.js.map +1 -1
- package/dist/src/codex-terminal-agent-adapter.js +1 -1
- package/dist/src/codex-terminal-agent-adapter.js.map +1 -1
- package/dist/src/native-thread-lifecycle-cli-adapter.d.ts +7 -136
- package/dist/src/native-thread-lifecycle-cli-adapter.js +39 -633
- package/dist/src/native-thread-lifecycle-cli-adapter.js.map +1 -1
- package/dist/src/native-thread-lifecycle-cli-contract.d.ts +138 -0
- package/dist/src/native-thread-lifecycle-cli-contract.js +2 -0
- package/dist/src/native-thread-lifecycle-cli-contract.js.map +1 -0
- package/dist/src/semantic-permission-control.d.ts +19 -0
- package/dist/src/semantic-permission-control.js +201 -0
- package/dist/src/semantic-permission-control.js.map +1 -0
- package/dist/src/semantic-tool-command-helpers.d.ts +7 -0
- package/dist/src/semantic-tool-command-helpers.js +29 -1
- package/dist/src/semantic-tool-command-helpers.js.map +1 -1
- package/dist/src/semantic-tool-descriptions.d.ts +26 -0
- package/dist/src/semantic-tool-descriptions.js +259 -0
- package/dist/src/semantic-tool-descriptions.js.map +1 -0
- package/dist/src/semantic-tool-list-projection.js +4 -0
- package/dist/src/semantic-tool-list-projection.js.map +1 -1
- package/dist/src/semantic-tool-presentation.js +5 -2
- package/dist/src/semantic-tool-presentation.js.map +1 -1
- package/dist/src/semantic-tool-runtime.js +50 -36
- package/dist/src/semantic-tool-runtime.js.map +1 -1
- package/dist/src/semantic-tool-schemas.d.ts +33 -0
- package/dist/src/semantic-tool-schemas.js +165 -31
- package/dist/src/semantic-tool-schemas.js.map +1 -1
- package/dist/src/terminal-action-contracts.d.ts +4 -0
- package/dist/src/terminal-action-contracts.js +695 -0
- package/dist/src/terminal-action-contracts.js.map +1 -0
- package/dist/src/terminal-action-projection.d.ts +1 -1
- package/dist/src/terminal-action-projection.js.map +1 -1
- package/dist/src/terminal-agent-bridge.d.ts +3 -0
- package/dist/src/terminal-agent-bridge.js +11 -0
- package/dist/src/terminal-agent-bridge.js.map +1 -1
- package/dist/src/terminal-composer-classifier.d.ts +1 -1
- package/dist/src/terminal-composer-classifier.js +31 -5
- package/dist/src/terminal-composer-classifier.js.map +1 -1
- package/dist/src/terminal-interaction-response-bridge.js +1 -1
- package/dist/src/terminal-interaction-response-bridge.js.map +1 -1
- package/dist/src/terminal-list-cli-adapter.js +7 -12
- package/dist/src/terminal-list-cli-adapter.js.map +1 -1
- package/dist/src/terminal-list-renderer.d.ts +8 -2
- package/dist/src/terminal-list-renderer.js +37 -400
- package/dist/src/terminal-list-renderer.js.map +1 -1
- package/dist/src/terminal-model-control-action-policy.d.ts +1 -1
- package/dist/src/terminal-model-control-availability.d.ts +1 -1
- package/dist/src/terminal-model-control-availability.js +1 -1
- package/dist/src/terminal-model-control-availability.js.map +1 -1
- package/dist/src/terminal-model-control-cli-adapter.d.ts +34 -0
- package/dist/src/terminal-model-control-cli-adapter.js +669 -0
- package/dist/src/terminal-model-control-cli-adapter.js.map +1 -0
- package/dist/src/terminal-model-control-contract.d.ts +198 -0
- package/dist/src/terminal-model-control-contract.js +15 -0
- package/dist/src/terminal-model-control-contract.js.map +1 -0
- package/dist/src/terminal-model-control-input.d.ts +29 -0
- package/dist/src/terminal-model-control-input.js +86 -0
- package/dist/src/terminal-model-control-input.js.map +1 -0
- package/dist/src/terminal-model-control-profile.d.ts +2 -0
- package/dist/src/terminal-model-control-profile.js +6 -0
- package/dist/src/terminal-model-control-profile.js.map +1 -1
- package/dist/src/terminal-model-control-subject.d.ts +52 -0
- package/dist/src/terminal-model-control-subject.js +73 -0
- package/dist/src/terminal-model-control-subject.js.map +1 -1
- package/dist/src/terminal-model-control-surface.d.ts +82 -0
- package/dist/src/terminal-model-control-surface.js +688 -0
- package/dist/src/terminal-model-control-surface.js.map +1 -0
- package/dist/src/terminal-model-control.d.ts +6 -270
- package/dist/src/terminal-model-control.js +11 -988
- package/dist/src/terminal-model-control.js.map +1 -1
- package/dist/src/terminal-model-selection.d.ts +33 -0
- package/dist/src/terminal-model-selection.js +143 -0
- package/dist/src/terminal-model-selection.js.map +1 -0
- package/dist/src/terminal-monitor-application-service.js +38 -59
- package/dist/src/terminal-monitor-application-service.js.map +1 -1
- package/dist/src/terminal-monitor-attention-store.d.ts +65 -0
- package/dist/src/terminal-monitor-attention-store.js +518 -0
- package/dist/src/terminal-monitor-attention-store.js.map +1 -0
- package/dist/src/terminal-monitor-collateral-recovery.d.ts +12 -0
- package/dist/src/terminal-monitor-collateral-recovery.js +415 -0
- package/dist/src/terminal-monitor-collateral-recovery.js.map +1 -0
- package/dist/src/terminal-monitor-decision-policy.d.ts +1 -1
- package/dist/src/terminal-monitor-interaction-projection.d.ts +19 -0
- package/dist/src/terminal-monitor-interaction-projection.js +48 -0
- package/dist/src/terminal-monitor-interaction-projection.js.map +1 -0
- package/dist/src/terminal-monitor-startup-recovery.d.ts +15 -0
- package/dist/src/terminal-monitor-startup-recovery.js +767 -0
- package/dist/src/terminal-monitor-startup-recovery.js.map +1 -0
- package/dist/src/terminal-monitor-state-cli-adapter.d.ts +2 -104
- package/dist/src/terminal-monitor-state-cli-adapter.js +28 -1640
- package/dist/src/terminal-monitor-state-cli-adapter.js.map +1 -1
- package/dist/src/terminal-monitor-state-contract.d.ts +108 -0
- package/dist/src/terminal-monitor-state-contract.js +8 -0
- package/dist/src/terminal-monitor-state-contract.js.map +1 -0
- package/dist/src/terminal-native-control-cli-contract.d.ts +16 -0
- package/dist/src/terminal-native-control-cli-contract.js +2 -0
- package/dist/src/terminal-native-control-cli-contract.js.map +1 -0
- package/dist/src/terminal-native-inspection-bridge.d.ts +4 -153
- package/dist/src/terminal-native-inspection-bridge.js +7 -646
- package/dist/src/terminal-native-inspection-bridge.js.map +1 -1
- package/dist/src/terminal-native-inspection-contract.d.ts +128 -0
- package/dist/src/terminal-native-inspection-contract.js +38 -0
- package/dist/src/terminal-native-inspection-contract.js.map +1 -0
- package/dist/src/terminal-native-inspection-profile.d.ts +15 -0
- package/dist/src/terminal-native-inspection-profile.js +185 -0
- package/dist/src/terminal-native-inspection-profile.js.map +1 -0
- package/dist/src/terminal-native-inspection-proof.d.ts +62 -0
- package/dist/src/terminal-native-inspection-proof.js +362 -0
- package/dist/src/terminal-native-inspection-proof.js.map +1 -0
- package/dist/src/terminal-permission-control-availability.d.ts +31 -0
- package/dist/src/terminal-permission-control-availability.js +50 -0
- package/dist/src/terminal-permission-control-availability.js.map +1 -0
- package/dist/src/terminal-permission-control-bridge.d.ts +16 -0
- package/dist/src/terminal-permission-control-bridge.js +93 -0
- package/dist/src/terminal-permission-control-bridge.js.map +1 -0
- package/dist/src/terminal-permission-control-cli-adapter.d.ts +31 -0
- package/dist/src/terminal-permission-control-cli-adapter.js +152 -0
- package/dist/src/terminal-permission-control-cli-adapter.js.map +1 -0
- package/dist/src/terminal-permission-control-list-projection.d.ts +23 -0
- package/dist/src/terminal-permission-control-list-projection.js +47 -0
- package/dist/src/terminal-permission-control-list-projection.js.map +1 -0
- package/dist/src/terminal-permission-control-runtime.d.ts +28 -0
- package/dist/src/terminal-permission-control-runtime.js +88 -0
- package/dist/src/terminal-permission-control-runtime.js.map +1 -0
- package/dist/src/terminal-permission-control.d.ts +69 -0
- package/dist/src/terminal-permission-control.js +217 -0
- package/dist/src/terminal-permission-control.js.map +1 -0
- package/dist/src/terminal-runtime-cli-adapter.d.ts +2 -1
- package/dist/src/terminal-runtime-cli-adapter.js +1 -1
- package/dist/src/terminal-runtime-cli-adapter.js.map +1 -1
- package/dist/src/terminal-submission-acceptance.d.ts +5 -148
- package/dist/src/terminal-submission-acceptance.js +21 -1845
- package/dist/src/terminal-submission-acceptance.js.map +1 -1
- package/dist/src/terminal-watch-anchor-capture.d.ts +9 -0
- package/dist/src/terminal-watch-anchor-capture.js +139 -0
- package/dist/src/terminal-watch-anchor-capture.js.map +1 -0
- package/dist/src/terminal-watch-cli-adapter.d.ts +3 -117
- package/dist/src/terminal-watch-cli-adapter.js +10 -1992
- package/dist/src/terminal-watch-cli-adapter.js.map +1 -1
- package/dist/src/terminal-watch-cli-contract.d.ts +117 -0
- package/dist/src/terminal-watch-cli-contract.js +2 -0
- package/dist/src/terminal-watch-cli-contract.js.map +1 -0
- package/dist/src/terminal-watch-fallback-observation.d.ts +15 -0
- package/dist/src/terminal-watch-fallback-observation.js +249 -0
- package/dist/src/terminal-watch-fallback-observation.js.map +1 -0
- package/dist/src/terminal-watch-interaction-observation.d.ts +26 -0
- package/dist/src/terminal-watch-interaction-observation.js +262 -0
- package/dist/src/terminal-watch-interaction-observation.js.map +1 -0
- package/dist/src/terminal-watch-observation-adapter.d.ts +4 -0
- package/dist/src/terminal-watch-observation-adapter.js +307 -0
- package/dist/src/terminal-watch-observation-adapter.js.map +1 -0
- package/dist/src/terminal-watch-observation-evidence.d.ts +25 -0
- package/dist/src/terminal-watch-observation-evidence.js +184 -0
- package/dist/src/terminal-watch-observation-evidence.js.map +1 -0
- package/dist/src/terminal-watch-response-authority.d.ts +6 -0
- package/dist/src/terminal-watch-response-authority.js +333 -0
- package/dist/src/terminal-watch-response-authority.js.map +1 -0
- package/dist/src/terminal-watch-response-cli-adapter.d.ts +3 -0
- package/dist/src/terminal-watch-response-cli-adapter.js +230 -0
- package/dist/src/terminal-watch-response-cli-adapter.js.map +1 -0
- package/dist/src/terminal-watch-terminal-identity.d.ts +39 -0
- package/dist/src/terminal-watch-terminal-identity.js +380 -0
- package/dist/src/terminal-watch-terminal-identity.js.map +1 -0
- package/docs/codex-permissions.md +95 -0
- package/openclaw.plugin.json +9 -1
- package/package.json +3 -2
- package/templates/openclaw-skills/agent-knock-knock/SKILL.md +6 -1
|
@@ -1,33 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { automaticSendWatchReceipt, createAutomaticActivityWatchAnchor, isAutomaticSendWatch, unsafeFallbackWatchPreparation } from "./terminal-watch-send-activity.js";
|
|
2
2
|
import { isCodexPaginatedReadCandidate } from "./codex-lifecycle-compatibility.js";
|
|
3
|
-
import {
|
|
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 {
|
|
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
|
|
29
|
-
import { assertTerminalWatchManualInteractionSummary,
|
|
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
|