@scotthuang/agent-knock-knock 0.13.16 → 0.13.18
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 +15 -0
- package/README.md +4 -4
- package/dist/src/claude-terminal-agent-adapter.js +1 -1
- package/dist/src/claude-terminal-agent-adapter.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-async-question-adapter.js +14 -4
- package/dist/src/codex-async-question-adapter.js.map +1 -1
- package/dist/src/codex-lifecycle-compatibility.d.ts +1 -1
- package/dist/src/codex-lifecycle-compatibility.js +9 -3
- package/dist/src/codex-lifecycle-compatibility.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 +1 -1
- package/dist/src/codex-permission-surface.js +1 -1
- package/dist/src/codex-permission-surface.js.map +1 -1
- 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 +4 -2
- package/dist/src/codex-terminal-agent-adapter.js.map +1 -1
- package/dist/src/native-thread-lifecycle-cli-adapter.d.ts +4 -134
- package/dist/src/native-thread-lifecycle-cli-adapter.js +19 -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.js +33 -6
- package/dist/src/semantic-permission-control.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-runtime.js +45 -37
- package/dist/src/semantic-tool-runtime.js.map +1 -1
- package/dist/src/semantic-tool-schemas.js +134 -33
- 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-composer-classifier.d.ts +1 -1
- package/dist/src/terminal-composer-classifier.js +2 -2
- 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-renderer.d.ts +1 -2
- package/dist/src/terminal-list-renderer.js +8 -424
- 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 +5 -1
- package/dist/src/terminal-model-control-profile.js +21 -7
- 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-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-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 +189 -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-bridge.js +1 -1
- package/dist/src/terminal-permission-control-bridge.js.map +1 -1
- package/dist/src/terminal-permission-control.js +5 -1
- package/dist/src/terminal-permission-control.js.map +1 -1
- package/dist/src/terminal-questionnaire-adapter.js +5 -2
- package/dist/src/terminal-questionnaire-adapter.js.map +1 -1
- 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-0.160.0-compatibility.md +126 -0
- package/docs/codex-permissions.md +12 -1
- package/package.json +3 -2
- package/templates/openclaw-skills/agent-knock-knock/SKILL.md +3 -3
|
@@ -1,683 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { assertPrivateRegularFile, fileEndsWithNewline, openExactRollout, readCodexRolloutHeader, sameRolloutFileIdentity, sameRolloutIdentity, sameStableFile, } from "./codex-rollout-file.js";
|
|
2
|
+
import { codexRolloutInodeKey, validateCodexOpenRootInventoryForAcceptance, validateCodexRecoveryCandidateForAcceptance, } from "./codex-rollout-inventory.js";
|
|
3
|
+
import { assertCodexRolloutLegacyHistory, exactCodexUserResponseText, fingerprintText, isRecord, optionalString, } from "./codex-rollout-records.js";
|
|
4
|
+
import { codexThreadUsesLegacyRollout, } from "./codex-session-provider.js";
|
|
5
|
+
import { exactNativeThreadId, fingerprint, normalizedRolloutIdentity, requiredString, sha256Value, validateCodexRolloutAcceptanceAnchor, validateTerminalSubmissionAcceptanceEvidence, validTimestamp, } from "./terminal-submission-facts.js";
|
|
2
6
|
import fs from "node:fs";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import { codexThreadUsesLegacyRollout } from "./codex-session-provider.js";
|
|
5
|
-
import { redactString } from "./runtime-log.js";
|
|
6
|
-
import { exactNativeThreadId, fingerprint, normalizedRolloutIdentity, parsedInteger, requiredString, sha256Value, validTimestamp, validateCodexRolloutAcceptanceAnchor, validateTerminalSubmissionAcceptanceEvidence } from "./terminal-submission-facts.js";
|
|
7
7
|
export { validateCodexRolloutAcceptanceAnchor, validateTerminalSubmissionAcceptanceEvidence };
|
|
8
|
+
export { captureCodexHumanStartedActiveTaskAnchor, observeCodexHumanStartedActiveTask, validateCodexHumanStartedActiveTaskAnchor, } from "./codex-rollout-human-task.js";
|
|
9
|
+
export { detectCodexBoundQuestionnaireAttribution, readCodexAsyncQuestionDurableEvidence, } from "./codex-rollout-interaction-evidence.js";
|
|
10
|
+
export { detectCodexBoundRolloutCompletion, } from "./codex-rollout-bound-completion.js";
|
|
8
11
|
const CODEX_ACCEPTANCE_MAX_BYTES = 16 * 1024 * 1024;
|
|
9
|
-
const CODEX_COMPLETION_MAX_BYTES = 256 * 1024 * 1024;
|
|
10
|
-
const CODEX_COMPLETION_MAX_TEXT_LENGTH = 4000;
|
|
11
|
-
const CODEX_ACTIVE_TASK_SCAN_CHUNK_BYTES = 64 * 1024;
|
|
12
|
-
const CODEX_QUESTIONNAIRE_TAIL_MAX_BYTES = 1024 * 1024;
|
|
13
|
-
const CODEX_QUESTIONNAIRE_MAX_TEXT_LENGTH = 16 * 1024;
|
|
14
|
-
const CODEX_QUESTIONNAIRE_MAX_OPTIONS = 8;
|
|
15
|
-
const CODEX_QUESTIONNAIRE_MAX_QUESTIONS = 3;
|
|
16
|
-
const CODEX_QUESTIONNAIRE_OTHER_LABEL = "None of the above";
|
|
17
|
-
const CODEX_QUESTIONNAIRE_OTHER_DESCRIPTION = "Optionally, add details in notes (tab).";
|
|
18
|
-
const CODEX_QUESTIONNAIRE_CUSTOM_LABEL = "Type something.";
|
|
19
|
-
const CODEX_QUESTIONNAIRE_CUSTOM_DESCRIPTION = "Enter a free-form answer through Codex Notes.";
|
|
20
|
-
const CODEX_ASYNC_QUESTION_TAIL_MAX_BYTES = 4 * 1024 * 1024;
|
|
21
|
-
const CODEX_ASYNC_QUESTION_MAX_ITEMS = 16;
|
|
22
|
-
const CODEX_ASYNC_QUESTION_MAX_QUESTIONS = 16;
|
|
23
|
-
const CODEX_ASYNC_QUESTION_MAX_OPTIONS = 32;
|
|
24
|
-
const CODEX_ASYNC_QUESTION_MAX_TEXT_LENGTH = 4_096;
|
|
25
|
-
const CODEX_ACTIVE_TASK_MAX_RECORD_BYTES = 16 * 1024 * 1024;
|
|
26
|
-
const CODEX_ROLLOUT_HEADER_MAX_BYTES = 1024 * 1024;
|
|
27
|
-
const CODEX_VERSION_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/u;
|
|
28
|
-
const NO_FOLLOW_FLAG = typeof fs.constants.O_NOFOLLOW === "number"
|
|
29
|
-
? fs.constants.O_NOFOLLOW
|
|
30
|
-
: 0;
|
|
31
|
-
/**
|
|
32
|
-
* Capture the latest active native task in an exact open Codex rollout.
|
|
33
|
-
* This is intentionally separate from the pre-send acceptance anchor: all
|
|
34
|
-
* task-start and root-user evidence already exists when this function runs.
|
|
35
|
-
*/
|
|
36
|
-
export function captureCodexHumanStartedActiveTaskAnchor(options) {
|
|
37
|
-
const current = options.currentIdentity;
|
|
38
|
-
const nativeThreadId = exactNativeThreadId(current.sessionId);
|
|
39
|
-
const processUuid = requiredString(current.processUuid, "Codex process UUID");
|
|
40
|
-
const processBirth = requiredString(current.processBirth, "Codex process birth");
|
|
41
|
-
if (!current.rollout) {
|
|
42
|
-
throw new Error("the active Codex task has no exact rollout identity");
|
|
43
|
-
}
|
|
44
|
-
const rollout = normalizedRolloutIdentity(current.rollout);
|
|
45
|
-
const maxBytes = positiveByteLimit(options.maxBytes, CODEX_COMPLETION_MAX_BYTES, "Codex active-task capture");
|
|
46
|
-
const opened = openExactRollout(rollout);
|
|
47
|
-
try {
|
|
48
|
-
const before = opened.stat;
|
|
49
|
-
assertPrivateRegularFile(before);
|
|
50
|
-
if (before.size === 0) {
|
|
51
|
-
return undefined;
|
|
52
|
-
}
|
|
53
|
-
if (!fileEndsWithNewline(opened.fd, before.size)) {
|
|
54
|
-
throw new Error("Codex active-task rollout has an incomplete JSONL tail; retry");
|
|
55
|
-
}
|
|
56
|
-
const header = readCodexRolloutHeader(opened.fd, before.size);
|
|
57
|
-
const codexVersion = assertExistingCodexRolloutHeader(header, nativeThreadId);
|
|
58
|
-
const active = latestCodexHumanStartedActiveTask(opened.fd, before.size, maxBytes);
|
|
59
|
-
const after = fs.fstatSync(opened.fd);
|
|
60
|
-
if (!sameStableFile(before, after)) {
|
|
61
|
-
throw new Error("Codex active-task rollout changed while it was captured; retry");
|
|
62
|
-
}
|
|
63
|
-
if (!active) {
|
|
64
|
-
return undefined;
|
|
65
|
-
}
|
|
66
|
-
const base = {
|
|
67
|
-
schema: "agent-knock-knock/codex-human-started-active-task-anchor",
|
|
68
|
-
version: 1,
|
|
69
|
-
native_thread_id: nativeThreadId,
|
|
70
|
-
process_uuid: processUuid,
|
|
71
|
-
process_birth: processBirth,
|
|
72
|
-
captured_at: (options.now ?? new Date()).toISOString(),
|
|
73
|
-
rollout,
|
|
74
|
-
turn_id: active.turnId,
|
|
75
|
-
request_hash: active.requestHash,
|
|
76
|
-
codex_version: codexVersion,
|
|
77
|
-
task_started_offset_bytes: active.taskStartedOffsetBytes,
|
|
78
|
-
user_message_offset_bytes: active.userMessageOffsetBytes,
|
|
79
|
-
observed_end_offset_bytes: before.size
|
|
80
|
-
};
|
|
81
|
-
return {
|
|
82
|
-
...base,
|
|
83
|
-
anchor_fingerprint: fingerprint(base)
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
finally {
|
|
87
|
-
fs.closeSync(opened.fd);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
/** Observe only the exact human-started task named by the persisted anchor. */
|
|
91
|
-
export function observeCodexHumanStartedActiveTask(options) {
|
|
92
|
-
try {
|
|
93
|
-
const anchor = validateCodexHumanStartedActiveTaskAnchor(options.anchor);
|
|
94
|
-
const maxBytes = positiveByteLimit(options.maxBytes, CODEX_COMPLETION_MAX_BYTES, "Codex active-task observation");
|
|
95
|
-
const resumeOffsetBytes = codexActiveTaskResumeOffset(anchor, options.resumeOffsetBytes);
|
|
96
|
-
const opened = openExactRollout(anchor.rollout);
|
|
97
|
-
try {
|
|
98
|
-
const before = opened.stat;
|
|
99
|
-
assertPrivateRegularFile(before);
|
|
100
|
-
if (before.size < anchor.observed_end_offset_bytes) {
|
|
101
|
-
throw new Error("Codex active-task rollout was truncated after capture");
|
|
102
|
-
}
|
|
103
|
-
if (before.size < resumeOffsetBytes) {
|
|
104
|
-
throw new Error("Codex active-task rollout was truncated after observation");
|
|
105
|
-
}
|
|
106
|
-
assertCodexRolloutLegacyHistory(readCodexRolloutHeader(opened.fd, before.size));
|
|
107
|
-
assertCodexActiveTaskResumeBoundary(opened.fd, resumeOffsetBytes);
|
|
108
|
-
const availableBytes = before.size - resumeOffsetBytes;
|
|
109
|
-
const bytesToRead = Math.min(availableBytes, maxBytes);
|
|
110
|
-
if (bytesToRead === 0) {
|
|
111
|
-
assertCodexHumanStartedIdentity(anchor, options.currentIdentity);
|
|
112
|
-
return {
|
|
113
|
-
status: "pending",
|
|
114
|
-
observedEndOffsetBytes: before.size,
|
|
115
|
-
safeResumeOffsetBytes: resumeOffsetBytes
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
const buffer = readExactBytes(opened.fd, resumeOffsetBytes, bytesToRead);
|
|
119
|
-
const completeLength = completeJsonlPrefixLength(buffer);
|
|
120
|
-
if (completeLength === 0 && availableBytes > maxBytes) {
|
|
121
|
-
throw new Error("Codex active-task JSONL record exceeded the bounded read limit");
|
|
122
|
-
}
|
|
123
|
-
const completeBuffer = buffer.subarray(0, completeLength);
|
|
124
|
-
const safeResumeOffsetBytes = resumeOffsetBytes + completeLength;
|
|
125
|
-
const after = fs.fstatSync(opened.fd);
|
|
126
|
-
if (!sameStableFile(before, after)) {
|
|
127
|
-
assertCodexHumanStartedIdentity(anchor, options.currentIdentity);
|
|
128
|
-
return {
|
|
129
|
-
status: "pending",
|
|
130
|
-
observedEndOffsetBytes: after.size,
|
|
131
|
-
safeResumeOffsetBytes: resumeOffsetBytes
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
const suffixRecords = parseCodexJsonlRecords(completeBuffer, "active-task suffix");
|
|
135
|
-
const laterTaskOffset = suffixRecords.find(({ value }) => isLaterCodexHumanTaskStartedRecord(value))?.offsetBytes;
|
|
136
|
-
const exactCompletionOffset = suffixRecords.find(({ value }) => isExactCodexTaskCompleteRecord(value, anchor.turn_id))?.offsetBytes;
|
|
137
|
-
const exactAbortRecords = suffixRecords.filter(({ value }) => isExactCodexTurnAbortedRecord(value, anchor.turn_id));
|
|
138
|
-
if (exactAbortRecords.length > 1) {
|
|
139
|
-
throw new Error("the exact Codex active task has duplicate turn_aborted records");
|
|
140
|
-
}
|
|
141
|
-
const exactAbort = exactAbortRecords[0];
|
|
142
|
-
const exactSettlementOffset = [
|
|
143
|
-
exactCompletionOffset,
|
|
144
|
-
exactAbort?.offsetBytes
|
|
145
|
-
].filter((offset) => offset !== undefined)
|
|
146
|
-
.sort((left, right) => left - right)[0];
|
|
147
|
-
if (laterTaskOffset !== undefined &&
|
|
148
|
-
(exactSettlementOffset === undefined ||
|
|
149
|
-
laterTaskOffset < exactSettlementOffset)) {
|
|
150
|
-
throw new Error("a later Codex human task appeared after the active-task anchor");
|
|
151
|
-
}
|
|
152
|
-
if (exactAbort &&
|
|
153
|
-
(exactCompletionOffset === undefined ||
|
|
154
|
-
exactAbort.offsetBytes < exactCompletionOffset)) {
|
|
155
|
-
return codexHumanStartedTaskAbortCompletion(anchor, exactAbort.value, before.size);
|
|
156
|
-
}
|
|
157
|
-
const completion = scanExactCodexTaskComplete(completeBuffer.toString("utf8"), anchor.turn_id);
|
|
158
|
-
if (completion.status === "failure") {
|
|
159
|
-
throw new Error(completion.detail);
|
|
160
|
-
}
|
|
161
|
-
if (completion.status === "pending") {
|
|
162
|
-
assertCodexHumanStartedIdentity(anchor, options.currentIdentity);
|
|
163
|
-
return {
|
|
164
|
-
status: "pending",
|
|
165
|
-
observedEndOffsetBytes: before.size,
|
|
166
|
-
safeResumeOffsetBytes
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
|
-
if (completion.status === "aborted") {
|
|
170
|
-
throw new Error("Codex active-task abort evidence escaped its exact lifecycle fence");
|
|
171
|
-
}
|
|
172
|
-
return {
|
|
173
|
-
status: "completed",
|
|
174
|
-
completion: {
|
|
175
|
-
source: "durable",
|
|
176
|
-
outcome: "success",
|
|
177
|
-
text: truncateCompletionText(redactString(completion.text)),
|
|
178
|
-
...(completion.timestamp ? { timestamp: completion.timestamp } : {}),
|
|
179
|
-
id: anchor.turn_id,
|
|
180
|
-
confidence: "high",
|
|
181
|
-
metadata: {
|
|
182
|
-
match: "human_started_bound_rollout_task_complete",
|
|
183
|
-
turn_id: anchor.turn_id,
|
|
184
|
-
native_thread_id: anchor.native_thread_id,
|
|
185
|
-
anchor_fingerprint: anchor.anchor_fingerprint,
|
|
186
|
-
observed_end_offset_bytes: before.size
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
};
|
|
190
|
-
}
|
|
191
|
-
finally {
|
|
192
|
-
fs.closeSync(opened.fd);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
catch (error) {
|
|
196
|
-
if (isRetryableProviderIoError(error)) {
|
|
197
|
-
return {
|
|
198
|
-
status: "unavailable",
|
|
199
|
-
reason: error instanceof Error ? error.message : String(error),
|
|
200
|
-
retryable: true
|
|
201
|
-
};
|
|
202
|
-
}
|
|
203
|
-
return {
|
|
204
|
-
status: "invalidated",
|
|
205
|
-
reason: error instanceof Error ? error.message : String(error)
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
function codexActiveTaskResumeOffset(anchor, value) {
|
|
210
|
-
const offset = value === undefined
|
|
211
|
-
? anchor.observed_end_offset_bytes
|
|
212
|
-
: safeByteOffset(value, "Codex active-task resume offset");
|
|
213
|
-
if (offset < anchor.observed_end_offset_bytes) {
|
|
214
|
-
throw new Error("Codex active-task resume offset predates its anchor");
|
|
215
|
-
}
|
|
216
|
-
return offset;
|
|
217
|
-
}
|
|
218
|
-
function assertCodexActiveTaskResumeBoundary(fd, offset) {
|
|
219
|
-
if (offset > 0 && byteAtOffset(fd, offset - 1) !== 0x0a) {
|
|
220
|
-
throw new Error("Codex active-task resume offset is not a JSONL boundary");
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
function completeJsonlPrefixLength(buffer) {
|
|
224
|
-
if (buffer.length === 0) {
|
|
225
|
-
return 0;
|
|
226
|
-
}
|
|
227
|
-
const newline = buffer.lastIndexOf(0x0a);
|
|
228
|
-
return newline < 0 ? 0 : newline + 1;
|
|
229
|
-
}
|
|
230
|
-
export function validateCodexHumanStartedActiveTaskAnchor(value) {
|
|
231
|
-
if (!isRecord(value) ||
|
|
232
|
-
value.schema !==
|
|
233
|
-
"agent-knock-knock/codex-human-started-active-task-anchor" ||
|
|
234
|
-
value.version !== 1) {
|
|
235
|
-
throw new Error("Codex human-started active-task anchor is invalid");
|
|
236
|
-
}
|
|
237
|
-
const allowedKeys = new Set([
|
|
238
|
-
"schema", "version", "native_thread_id", "process_uuid", "process_birth",
|
|
239
|
-
"captured_at", "rollout", "turn_id", "request_hash", "codex_version",
|
|
240
|
-
"task_started_offset_bytes", "user_message_offset_bytes",
|
|
241
|
-
"observed_end_offset_bytes", "anchor_fingerprint"
|
|
242
|
-
]);
|
|
243
|
-
if (Object.keys(value).some((key) => !allowedKeys.has(key))) {
|
|
244
|
-
throw new Error("Codex human-started active-task anchor has unsupported fields");
|
|
245
|
-
}
|
|
246
|
-
const nativeThreadId = exactNativeThreadId(value.native_thread_id);
|
|
247
|
-
const turnId = exactNativeThreadId(value.turn_id);
|
|
248
|
-
const capturedAt = value.captured_at;
|
|
249
|
-
if (nativeThreadId !== value.native_thread_id ||
|
|
250
|
-
turnId !== value.turn_id ||
|
|
251
|
-
!validTimestamp(capturedAt) ||
|
|
252
|
-
typeof capturedAt !== "string" ||
|
|
253
|
-
new Date(capturedAt).toISOString() !== capturedAt) {
|
|
254
|
-
throw new Error("Codex human-started active-task anchor identity is invalid");
|
|
255
|
-
}
|
|
256
|
-
for (const [field, label] of [
|
|
257
|
-
[value.process_uuid, "Codex active-task process UUID"],
|
|
258
|
-
[value.process_birth, "Codex active-task process birth"]
|
|
259
|
-
]) {
|
|
260
|
-
if (requiredString(field, label) !== field ||
|
|
261
|
-
field.includes("\0")) {
|
|
262
|
-
throw new Error(`${label} is not canonical`);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
if (sha256Value(value.request_hash, "Codex active-task request hash") !==
|
|
266
|
-
value.request_hash) {
|
|
267
|
-
throw new Error("Codex active-task request hash is not canonical");
|
|
268
|
-
}
|
|
269
|
-
if (typeof value.codex_version !== "string" ||
|
|
270
|
-
!CODEX_VERSION_PATTERN.test(value.codex_version)) {
|
|
271
|
-
throw new Error("Codex active-task version is invalid");
|
|
272
|
-
}
|
|
273
|
-
if (sha256Value(value.anchor_fingerprint, "Codex active-task anchor fingerprint") !== value.anchor_fingerprint) {
|
|
274
|
-
throw new Error("Codex active-task anchor fingerprint is not canonical");
|
|
275
|
-
}
|
|
276
|
-
if (!isRecord(value.rollout)) {
|
|
277
|
-
throw new Error("Codex human-started active-task rollout identity is invalid");
|
|
278
|
-
}
|
|
279
|
-
const rolloutKeys = new Set(["fd", "device", "inode", "path"]);
|
|
280
|
-
if (Object.keys(value.rollout).some((key) => !rolloutKeys.has(key))) {
|
|
281
|
-
throw new Error("Codex human-started active-task rollout has unsupported fields");
|
|
282
|
-
}
|
|
283
|
-
const rollout = normalizedRolloutIdentity(value.rollout);
|
|
284
|
-
if (Object.entries(rollout).some(([key, normalized]) => normalized !== value.rollout[key] || normalized.includes("\0")) ||
|
|
285
|
-
!path.isAbsolute(rollout.path)) {
|
|
286
|
-
throw new Error("Codex human-started active-task rollout path is not absolute");
|
|
287
|
-
}
|
|
288
|
-
const taskStartedOffset = safeByteOffset(value.task_started_offset_bytes, "Codex active-task task_started offset");
|
|
289
|
-
const userMessageOffset = safeByteOffset(value.user_message_offset_bytes, "Codex active-task user-message offset");
|
|
290
|
-
const observedEndOffset = safeByteOffset(value.observed_end_offset_bytes, "Codex active-task observed-end offset");
|
|
291
|
-
if (userMessageOffset <= taskStartedOffset ||
|
|
292
|
-
observedEndOffset <= userMessageOffset) {
|
|
293
|
-
throw new Error("Codex human-started active-task byte boundaries are inconsistent");
|
|
294
|
-
}
|
|
295
|
-
const { anchor_fingerprint: _anchorFingerprint, ...base } = value;
|
|
296
|
-
if (fingerprint(base) !== value.anchor_fingerprint) {
|
|
297
|
-
throw new Error("Codex human-started active-task anchor fingerprint does not match");
|
|
298
|
-
}
|
|
299
|
-
return value;
|
|
300
|
-
}
|
|
301
|
-
function assertCodexHumanStartedIdentity(anchor, current) {
|
|
302
|
-
if (exactNativeThreadId(current.sessionId) !== anchor.native_thread_id ||
|
|
303
|
-
requiredString(current.processUuid, "Codex process UUID") !==
|
|
304
|
-
anchor.process_uuid ||
|
|
305
|
-
requiredString(current.processBirth, "Codex process birth") !==
|
|
306
|
-
anchor.process_birth ||
|
|
307
|
-
!current.rollout ||
|
|
308
|
-
!sameRolloutIdentity(normalizedRolloutIdentity(current.rollout), anchor.rollout)) {
|
|
309
|
-
throw new Error("Codex process, thread, or rollout identity changed after capture");
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
function latestCodexHumanStartedActiveTask(fd, endOffset, maxBytes) {
|
|
313
|
-
const lowerBound = Math.max(0, endOffset - maxBytes);
|
|
314
|
-
const terminalTurns = new Map();
|
|
315
|
-
const userRecords = [];
|
|
316
|
-
let userMessageRecordCount = 0;
|
|
317
|
-
let cursor = endOffset;
|
|
318
|
-
let leadingRecord = Buffer.alloc(0);
|
|
319
|
-
let followingRecord;
|
|
320
|
-
while (cursor > lowerBound) {
|
|
321
|
-
const readStart = Math.max(lowerBound, cursor - CODEX_ACTIVE_TASK_SCAN_CHUNK_BYTES);
|
|
322
|
-
const chunk = readExactBytes(fd, readStart, cursor - readStart);
|
|
323
|
-
const combined = leadingRecord.length > 0
|
|
324
|
-
? Buffer.concat([chunk, leadingRecord])
|
|
325
|
-
: chunk;
|
|
326
|
-
let recordsBuffer = combined;
|
|
327
|
-
let recordsOffset = readStart;
|
|
328
|
-
if (readStart > 0) {
|
|
329
|
-
const firstNewline = combined.indexOf(0x0a);
|
|
330
|
-
if (firstNewline < 0) {
|
|
331
|
-
assertBoundedCodexActiveTaskRecord(combined.length);
|
|
332
|
-
leadingRecord = Buffer.from(combined);
|
|
333
|
-
cursor = readStart;
|
|
334
|
-
continue;
|
|
335
|
-
}
|
|
336
|
-
leadingRecord = Buffer.from(combined.subarray(0, firstNewline + 1));
|
|
337
|
-
assertBoundedCodexActiveTaskRecord(leadingRecord.length);
|
|
338
|
-
recordsBuffer = combined.subarray(firstNewline + 1);
|
|
339
|
-
recordsOffset += firstNewline + 1;
|
|
340
|
-
}
|
|
341
|
-
else {
|
|
342
|
-
leadingRecord = Buffer.alloc(0);
|
|
343
|
-
}
|
|
344
|
-
const records = parseCodexJsonlRecords(recordsBuffer, "active-task rollout tail");
|
|
345
|
-
for (let index = records.length - 1; index >= 0; index -= 1) {
|
|
346
|
-
const record = records[index];
|
|
347
|
-
const absoluteRecord = {
|
|
348
|
-
value: record.value,
|
|
349
|
-
offsetBytes: recordsOffset + record.offsetBytes
|
|
350
|
-
};
|
|
351
|
-
if (isCodexUserMessageRecord(absoluteRecord.value)) {
|
|
352
|
-
userMessageRecordCount += 1;
|
|
353
|
-
}
|
|
354
|
-
const decision = inspectCodexActiveTaskRecordInReverse(absoluteRecord, terminalTurns, userRecords, userMessageRecordCount, followingRecord);
|
|
355
|
-
if (decision.status === "resolved") {
|
|
356
|
-
return decision.active;
|
|
357
|
-
}
|
|
358
|
-
followingRecord = absoluteRecord;
|
|
359
|
-
}
|
|
360
|
-
cursor = readStart;
|
|
361
|
-
}
|
|
362
|
-
if (lowerBound > 0) {
|
|
363
|
-
throw new Error("Codex active-task boundary exceeded the bounded reverse scan limit");
|
|
364
|
-
}
|
|
365
|
-
return undefined;
|
|
366
|
-
}
|
|
367
|
-
function inspectCodexActiveTaskRecordInReverse(record, terminalTurns, userRecords, userMessageRecordCount, followingRecord) {
|
|
368
|
-
const { value, offsetBytes } = record;
|
|
369
|
-
const payload = isRecord(value.payload) ? value.payload : undefined;
|
|
370
|
-
if (!payload) {
|
|
371
|
-
return { status: "continue" };
|
|
372
|
-
}
|
|
373
|
-
if (value.type === "event_msg" &&
|
|
374
|
-
(payload.type === "task_complete" || payload.type === "turn_aborted")) {
|
|
375
|
-
const turnId = exactNativeThreadId(payload.turn_id);
|
|
376
|
-
const kind = payload.type === "task_complete" ? "completed" : "aborted";
|
|
377
|
-
const previous = terminalTurns.get(turnId);
|
|
378
|
-
if (previous) {
|
|
379
|
-
throw new Error(previous === kind
|
|
380
|
-
? `Codex rollout contains duplicate ${String(payload.type)} evidence`
|
|
381
|
-
: "Codex rollout contains conflicting terminal task evidence");
|
|
382
|
-
}
|
|
383
|
-
terminalTurns.set(turnId, kind);
|
|
384
|
-
return { status: "continue" };
|
|
385
|
-
}
|
|
386
|
-
if (isCodexUserResponseRecord(value)) {
|
|
387
|
-
const userRecord = pairedCodexActiveTaskUserRecord(record, followingRecord);
|
|
388
|
-
if (userRecord) {
|
|
389
|
-
userRecords.push(userRecord);
|
|
390
|
-
}
|
|
391
|
-
return { status: "continue" };
|
|
392
|
-
}
|
|
393
|
-
if (value.type !== "event_msg" || payload.type !== "task_started") {
|
|
394
|
-
return { status: "continue" };
|
|
395
|
-
}
|
|
396
|
-
const turnId = exactNativeThreadId(payload.turn_id);
|
|
397
|
-
if (terminalTurns.has(turnId)) {
|
|
398
|
-
return { status: "resolved" };
|
|
399
|
-
}
|
|
400
|
-
if (userMessageRecordCount === 0) {
|
|
401
|
-
return { status: "resolved" };
|
|
402
|
-
}
|
|
403
|
-
if (userMessageRecordCount !== 1) {
|
|
404
|
-
throw new Error("Codex active task has multiple user_message events");
|
|
405
|
-
}
|
|
406
|
-
const matchingUsers = userRecords.filter((user) => user.turnId === turnId);
|
|
407
|
-
if (matchingUsers.length === 0) {
|
|
408
|
-
throw new Error("Codex active task has no exact adjacent root user row for its user_message event");
|
|
409
|
-
}
|
|
410
|
-
if (matchingUsers.length !== 1) {
|
|
411
|
-
throw new Error("Codex active task has multiple same-turn root user rows");
|
|
412
|
-
}
|
|
413
|
-
const [user] = matchingUsers;
|
|
414
|
-
if (user.pairingIssue === "hash_mismatch") {
|
|
415
|
-
throw new Error("Codex active-task root user row does not match its user_message event");
|
|
416
|
-
}
|
|
417
|
-
if (user.pairingIssue === "turn_mismatch") {
|
|
418
|
-
throw new Error("Codex active-task root user row does not match its adjacent UserMessage turn");
|
|
419
|
-
}
|
|
420
|
-
if (user.requestHash === undefined) {
|
|
421
|
-
throw new Error("Codex active-task root user row has unsupported prompt content");
|
|
422
|
-
}
|
|
423
|
-
if (user.offsetBytes <= offsetBytes) {
|
|
424
|
-
throw new Error("Codex active-task root user row precedes task_started");
|
|
425
|
-
}
|
|
426
|
-
if (userRecords.some((candidate) => candidate.offsetBytes > user.offsetBytes && candidate.turnId !== turnId)) {
|
|
427
|
-
throw new Error("Codex active task is not the latest native human task");
|
|
428
|
-
}
|
|
429
|
-
return {
|
|
430
|
-
status: "resolved",
|
|
431
|
-
active: {
|
|
432
|
-
turnId,
|
|
433
|
-
requestHash: user.requestHash,
|
|
434
|
-
taskStartedOffsetBytes: offsetBytes,
|
|
435
|
-
userMessageOffsetBytes: user.offsetBytes
|
|
436
|
-
}
|
|
437
|
-
};
|
|
438
|
-
}
|
|
439
|
-
function assertBoundedCodexActiveTaskRecord(length) {
|
|
440
|
-
if (length > CODEX_ACTIVE_TASK_MAX_RECORD_BYTES) {
|
|
441
|
-
throw new Error("Codex active-task JSONL record exceeded the safe read limit");
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
function pairedCodexActiveTaskUserRecord(record, followingRecord) {
|
|
445
|
-
const payload = isRecord(record.value.payload)
|
|
446
|
-
? record.value.payload
|
|
447
|
-
: undefined;
|
|
448
|
-
const metadata = payload &&
|
|
449
|
-
isRecord(payload.internal_chat_message_metadata_passthrough)
|
|
450
|
-
? payload.internal_chat_message_metadata_passthrough
|
|
451
|
-
: undefined;
|
|
452
|
-
const rawTurnId = optionalString(metadata?.turn_id);
|
|
453
|
-
// Codex writes one provider-owned user-message event immediately after the
|
|
454
|
-
// human root response row. Older rollouts use event_msg.user_message; newer
|
|
455
|
-
// Codex surfaces use event_msg.item_completed with item.type=UserMessage.
|
|
456
|
-
// Synthetic user-role context has no such adjacent proof.
|
|
457
|
-
if (!payload ||
|
|
458
|
-
!rawTurnId ||
|
|
459
|
-
!isCodexUserMessageRecord(followingRecord?.value)) {
|
|
460
|
-
return undefined;
|
|
461
|
-
}
|
|
462
|
-
const text = exactCodexUserResponseText(payload.content);
|
|
463
|
-
const requestHash = text === undefined ? undefined : fingerprintText(text);
|
|
464
|
-
const userMessageHash = codexUserMessageHash(followingRecord?.value);
|
|
465
|
-
const adjacentTurnId = codexUserMessageTurnId(followingRecord?.value);
|
|
466
|
-
const rootTurnId = exactNativeThreadId(rawTurnId);
|
|
467
|
-
const pairingIssue = adjacentTurnId !== undefined &&
|
|
468
|
-
exactNativeThreadId(adjacentTurnId) !== rootTurnId
|
|
469
|
-
? "turn_mismatch"
|
|
470
|
-
: requestHash === undefined || userMessageHash === undefined
|
|
471
|
-
? "unsupported"
|
|
472
|
-
: requestHash !== userMessageHash
|
|
473
|
-
? "hash_mismatch"
|
|
474
|
-
: undefined;
|
|
475
|
-
return {
|
|
476
|
-
offsetBytes: record.offsetBytes,
|
|
477
|
-
turnId: rootTurnId,
|
|
478
|
-
...(pairingIssue ? { pairingIssue } : { requestHash })
|
|
479
|
-
};
|
|
480
|
-
}
|
|
481
|
-
function isCodexUserResponseRecord(value) {
|
|
482
|
-
const payload = isRecord(value.payload) ? value.payload : undefined;
|
|
483
|
-
return value.type === "response_item" &&
|
|
484
|
-
payload?.type === "message" &&
|
|
485
|
-
payload.role === "user";
|
|
486
|
-
}
|
|
487
|
-
function codexUserMessageHash(value) {
|
|
488
|
-
const payload = value && isRecord(value.payload) ? value.payload : undefined;
|
|
489
|
-
if (value?.type === "event_msg" &&
|
|
490
|
-
payload?.type === "user_message" &&
|
|
491
|
-
typeof payload.message === "string") {
|
|
492
|
-
return fingerprintText(payload.message);
|
|
493
|
-
}
|
|
494
|
-
const item = payload?.type === "item_completed" && isRecord(payload.item)
|
|
495
|
-
? payload.item
|
|
496
|
-
: undefined;
|
|
497
|
-
const content = item?.type === "UserMessage" && Array.isArray(item.content)
|
|
498
|
-
? item.content
|
|
499
|
-
: undefined;
|
|
500
|
-
if (!content || content.length !== 1)
|
|
501
|
-
return undefined;
|
|
502
|
-
const text = content[0];
|
|
503
|
-
return isRecord(text) && text.type === "text" &&
|
|
504
|
-
typeof text.text === "string"
|
|
505
|
-
? fingerprintText(text.text)
|
|
506
|
-
: undefined;
|
|
507
|
-
}
|
|
508
|
-
function codexUserMessageTurnId(value) {
|
|
509
|
-
const payload = value && isRecord(value.payload) ? value.payload : undefined;
|
|
510
|
-
return value?.type === "event_msg" &&
|
|
511
|
-
payload?.type === "item_completed" &&
|
|
512
|
-
isRecord(payload.item) &&
|
|
513
|
-
payload.item.type === "UserMessage"
|
|
514
|
-
? optionalString(payload.turn_id)
|
|
515
|
-
: undefined;
|
|
516
|
-
}
|
|
517
|
-
function isCodexUserMessageRecord(value) {
|
|
518
|
-
const payload = value && isRecord(value.payload) ? value.payload : undefined;
|
|
519
|
-
return value?.type === "event_msg" &&
|
|
520
|
-
(payload?.type === "user_message" ||
|
|
521
|
-
(payload?.type === "item_completed" &&
|
|
522
|
-
isRecord(payload.item) &&
|
|
523
|
-
payload.item.type === "UserMessage"));
|
|
524
|
-
}
|
|
525
|
-
function isLaterCodexHumanTaskStartedRecord(value) {
|
|
526
|
-
const payload = isRecord(value.payload) ? value.payload : undefined;
|
|
527
|
-
// User-role response rows can be same-turn synthetic context. Only a fresh
|
|
528
|
-
// task_started record is authoritative evidence that the anchor was passed.
|
|
529
|
-
return value.type === "event_msg" &&
|
|
530
|
-
payload?.type === "task_started";
|
|
531
|
-
}
|
|
532
|
-
function isExactCodexTaskCompleteRecord(value, expectedTurnId) {
|
|
533
|
-
const payload = isRecord(value.payload) ? value.payload : undefined;
|
|
534
|
-
return value.type === "event_msg" &&
|
|
535
|
-
payload?.type === "task_complete" &&
|
|
536
|
-
optionalString(payload.turn_id)?.toLowerCase() === expectedTurnId;
|
|
537
|
-
}
|
|
538
|
-
function isExactCodexTurnAbortedRecord(value, expectedTurnId) {
|
|
539
|
-
const payload = isRecord(value.payload) ? value.payload : undefined;
|
|
540
|
-
return value.type === "event_msg" &&
|
|
541
|
-
payload?.type === "turn_aborted" &&
|
|
542
|
-
optionalString(payload.turn_id)?.toLowerCase() === expectedTurnId;
|
|
543
|
-
}
|
|
544
|
-
function codexHumanStartedTaskAbortCompletion(anchor, value, observedEndOffsetBytes) {
|
|
545
|
-
const payload = isRecord(value.payload) ? value.payload : undefined;
|
|
546
|
-
if (!payload || payload.type !== "turn_aborted") {
|
|
547
|
-
throw new Error("Codex active-task abort evidence is invalid");
|
|
548
|
-
}
|
|
549
|
-
const reason = optionalString(payload.reason) ?? "interrupted";
|
|
550
|
-
if (value.timestamp !== undefined && !validTimestamp(value.timestamp)) {
|
|
551
|
-
throw new Error("Codex active-task turn_aborted record has an invalid timestamp");
|
|
552
|
-
}
|
|
553
|
-
const redactedReason = truncateCompletionText(redactString(reason));
|
|
554
|
-
return {
|
|
555
|
-
status: "completed",
|
|
556
|
-
completion: {
|
|
557
|
-
source: "durable",
|
|
558
|
-
outcome: "failure",
|
|
559
|
-
text: truncateCompletionText(redactString(`Codex task stopped: ${redactedReason}`)),
|
|
560
|
-
...(value.timestamp !== undefined
|
|
561
|
-
? { timestamp: String(value.timestamp) }
|
|
562
|
-
: {}),
|
|
563
|
-
id: anchor.turn_id,
|
|
564
|
-
confidence: "high",
|
|
565
|
-
metadata: {
|
|
566
|
-
match: "human_started_bound_rollout_turn_aborted",
|
|
567
|
-
turn_id: anchor.turn_id,
|
|
568
|
-
native_thread_id: anchor.native_thread_id,
|
|
569
|
-
anchor_fingerprint: anchor.anchor_fingerprint,
|
|
570
|
-
abort_reason: redactedReason,
|
|
571
|
-
observed_end_offset_bytes: observedEndOffsetBytes
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
};
|
|
575
|
-
}
|
|
576
|
-
function parseCodexJsonlRecords(buffer, label) {
|
|
577
|
-
if (buffer.length > 0 && buffer[buffer.length - 1] !== 0x0a) {
|
|
578
|
-
throw new Error(`Codex ${label} ends with an incomplete JSONL record`);
|
|
579
|
-
}
|
|
580
|
-
const records = [];
|
|
581
|
-
let offset = 0;
|
|
582
|
-
while (offset < buffer.length) {
|
|
583
|
-
const newline = buffer.indexOf(0x0a, offset);
|
|
584
|
-
if (newline < 0) {
|
|
585
|
-
break;
|
|
586
|
-
}
|
|
587
|
-
const recordOffset = offset;
|
|
588
|
-
let lineBuffer = buffer.subarray(offset, newline);
|
|
589
|
-
if (lineBuffer.at(-1) === 0x0d) {
|
|
590
|
-
lineBuffer = lineBuffer.subarray(0, lineBuffer.length - 1);
|
|
591
|
-
}
|
|
592
|
-
offset = newline + 1;
|
|
593
|
-
const line = lineBuffer.toString("utf8");
|
|
594
|
-
if (!line.trim()) {
|
|
595
|
-
continue;
|
|
596
|
-
}
|
|
597
|
-
let parsed;
|
|
598
|
-
try {
|
|
599
|
-
parsed = JSON.parse(line);
|
|
600
|
-
}
|
|
601
|
-
catch {
|
|
602
|
-
throw new Error(`Codex ${label} contains invalid JSONL`);
|
|
603
|
-
}
|
|
604
|
-
if (!isRecord(parsed)) {
|
|
605
|
-
throw new Error(`Codex ${label} contains a non-object JSONL record`);
|
|
606
|
-
}
|
|
607
|
-
records.push({ value: parsed, offsetBytes: recordOffset });
|
|
608
|
-
}
|
|
609
|
-
return records;
|
|
610
|
-
}
|
|
611
|
-
function assertExistingCodexRolloutHeader(buffer, nativeThreadId) {
|
|
612
|
-
const first = parseCodexJsonlRecords(buffer, "active-task rollout")[0]?.value;
|
|
613
|
-
const payload = first?.type === "session_meta" && isRecord(first.payload)
|
|
614
|
-
? first.payload
|
|
615
|
-
: undefined;
|
|
616
|
-
if (!payload ||
|
|
617
|
-
exactNativeThreadId(payload.id) !== nativeThreadId ||
|
|
618
|
-
payload.originator !== "codex-tui" ||
|
|
619
|
-
payload.source !== "cli") {
|
|
620
|
-
throw new Error("Codex active-task rollout header does not identify the exact CLI thread");
|
|
621
|
-
}
|
|
622
|
-
if (!codexThreadUsesLegacyRollout(payload)) {
|
|
623
|
-
throw new Error("Codex rollout does not use legacy history");
|
|
624
|
-
}
|
|
625
|
-
const codexVersion = optionalString(payload.cli_version);
|
|
626
|
-
if (!codexVersion || !CODEX_VERSION_PATTERN.test(codexVersion)) {
|
|
627
|
-
throw new Error("Codex active-task rollout header has no exact CLI version");
|
|
628
|
-
}
|
|
629
|
-
return codexVersion;
|
|
630
|
-
}
|
|
631
|
-
/**
|
|
632
|
-
* Persisted anchors must check the history contract without widening their
|
|
633
|
-
* existing thread/version requirements. Older legacy headers can be minimal.
|
|
634
|
-
*/
|
|
635
|
-
function assertCodexRolloutLegacyHistory(buffer) {
|
|
636
|
-
const first = parseCodexJsonlRecords(buffer, "rollout header")[0]?.value;
|
|
637
|
-
const payload = first?.type === "session_meta" && isRecord(first.payload)
|
|
638
|
-
? first.payload
|
|
639
|
-
: undefined;
|
|
640
|
-
if (payload && !codexThreadUsesLegacyRollout(payload)) {
|
|
641
|
-
throw new Error("Codex rollout does not use legacy history");
|
|
642
|
-
}
|
|
643
|
-
}
|
|
644
|
-
function readCodexRolloutHeader(fd, size) {
|
|
645
|
-
const bytesToRead = Math.min(size, CODEX_ROLLOUT_HEADER_MAX_BYTES);
|
|
646
|
-
const buffer = readExactBytes(fd, 0, bytesToRead);
|
|
647
|
-
const newline = buffer.indexOf(0x0a);
|
|
648
|
-
if (newline < 0) {
|
|
649
|
-
throw new Error("Codex active-task rollout header exceeded the safe read limit");
|
|
650
|
-
}
|
|
651
|
-
return buffer.subarray(0, newline + 1);
|
|
652
|
-
}
|
|
653
|
-
function readExactBytes(fd, offset, length) {
|
|
654
|
-
const buffer = Buffer.allocUnsafe(length);
|
|
655
|
-
let total = 0;
|
|
656
|
-
while (total < length) {
|
|
657
|
-
const read = fs.readSync(fd, buffer, total, length - total, offset + total);
|
|
658
|
-
if (read === 0) {
|
|
659
|
-
break;
|
|
660
|
-
}
|
|
661
|
-
total += read;
|
|
662
|
-
}
|
|
663
|
-
if (total !== length) {
|
|
664
|
-
throw new Error("Codex rollout changed while it was being read");
|
|
665
|
-
}
|
|
666
|
-
return buffer;
|
|
667
|
-
}
|
|
668
|
-
function positiveByteLimit(value, fallback, label) {
|
|
669
|
-
const result = value ?? fallback;
|
|
670
|
-
if (!Number.isSafeInteger(result) || result <= 0) {
|
|
671
|
-
throw new Error(`${label} byte limit is invalid`);
|
|
672
|
-
}
|
|
673
|
-
return result;
|
|
674
|
-
}
|
|
675
|
-
function safeByteOffset(value, label) {
|
|
676
|
-
if (!Number.isSafeInteger(value) || Number(value) < 0) {
|
|
677
|
-
throw new Error(`${label} is invalid`);
|
|
678
|
-
}
|
|
679
|
-
return Number(value);
|
|
680
|
-
}
|
|
681
12
|
export function captureCodexRolloutAcceptanceAnchor(options) {
|
|
682
13
|
const processUuid = requiredString(options.processUuid, "Codex process UUID");
|
|
683
14
|
const processBirth = requiredString(options.processBirth, "Codex process birth");
|
|
@@ -1002,94 +333,6 @@ export function detectCodexCandidateSetRolloutAcceptance({ anchor: anchorValue,
|
|
|
1002
333
|
evidence: matches[0].evidence
|
|
1003
334
|
};
|
|
1004
335
|
}
|
|
1005
|
-
/**
|
|
1006
|
-
* Attributes a rendered questionnaire to one exact accepted Codex turn while
|
|
1007
|
-
* the process has multiple open root rollouts. No prompt or tool arguments are
|
|
1008
|
-
* returned: callers receive only a bounded fingerprint or a typed failure.
|
|
1009
|
-
*/
|
|
1010
|
-
export function detectCodexBoundQuestionnaireAttribution(options) {
|
|
1011
|
-
let inventory;
|
|
1012
|
-
let acceptedThreadId;
|
|
1013
|
-
let acceptedRollout;
|
|
1014
|
-
let acceptanceId;
|
|
1015
|
-
try {
|
|
1016
|
-
inventory = validateCodexOpenRootInventoryForAcceptance(options.currentInventory);
|
|
1017
|
-
acceptedThreadId = exactNativeThreadId(options.acceptedIdentity.sessionId);
|
|
1018
|
-
acceptanceId = exactNativeThreadId(options.acceptanceId);
|
|
1019
|
-
if (!options.acceptedIdentity.rollout) {
|
|
1020
|
-
throw new Error("accepted Codex rollout identity is unavailable");
|
|
1021
|
-
}
|
|
1022
|
-
acceptedRollout = normalizedRolloutIdentity(options.acceptedIdentity.rollout);
|
|
1023
|
-
if (requiredString(options.acceptedIdentity.processUuid, "accepted Codex process UUID") !== inventory.processUuid ||
|
|
1024
|
-
requiredString(options.acceptedIdentity.processBirth, "accepted Codex process birth") !== inventory.processBirth) {
|
|
1025
|
-
return { status: "not_matched", code: "accepted_root_missing" };
|
|
1026
|
-
}
|
|
1027
|
-
}
|
|
1028
|
-
catch {
|
|
1029
|
-
return { status: "unavailable", code: "invalid_inventory" };
|
|
1030
|
-
}
|
|
1031
|
-
let normalizedScreen;
|
|
1032
|
-
try {
|
|
1033
|
-
normalizedScreen = normalizedCodexQuestionnaireScreen(options.screen);
|
|
1034
|
-
}
|
|
1035
|
-
catch {
|
|
1036
|
-
return { status: "unavailable", code: "invalid_screen" };
|
|
1037
|
-
}
|
|
1038
|
-
const acceptedRoots = inventory.roots.filter((root) => root.sessionId.toLowerCase() === acceptedThreadId &&
|
|
1039
|
-
sameRolloutFileIdentity(root.rollout, acceptedRollout));
|
|
1040
|
-
if (acceptedRoots.length !== 1) {
|
|
1041
|
-
return { status: "not_matched", code: "accepted_root_missing" };
|
|
1042
|
-
}
|
|
1043
|
-
let maxBytes;
|
|
1044
|
-
try {
|
|
1045
|
-
maxBytes = positiveByteLimit(options.maxBytesPerRollout, CODEX_QUESTIONNAIRE_TAIL_MAX_BYTES, "Codex questionnaire rollout scan limit");
|
|
1046
|
-
}
|
|
1047
|
-
catch {
|
|
1048
|
-
return { status: "unavailable", code: "rollout_scan_unavailable" };
|
|
1049
|
-
}
|
|
1050
|
-
const pending = [];
|
|
1051
|
-
try {
|
|
1052
|
-
for (const root of inventory.roots) {
|
|
1053
|
-
pending.push(...readPendingCodexRequestUserInput({
|
|
1054
|
-
rollout: root.rollout,
|
|
1055
|
-
nativeThreadId: root.sessionId,
|
|
1056
|
-
maxBytes
|
|
1057
|
-
}));
|
|
1058
|
-
}
|
|
1059
|
-
}
|
|
1060
|
-
catch {
|
|
1061
|
-
return { status: "unavailable", code: "rollout_scan_unavailable" };
|
|
1062
|
-
}
|
|
1063
|
-
const acceptedCalls = pending.filter((call) => call.nativeThreadId === acceptedThreadId &&
|
|
1064
|
-
call.turnId === acceptanceId &&
|
|
1065
|
-
sameRolloutFileIdentity(call.rollout, acceptedRollout));
|
|
1066
|
-
if (acceptedCalls.length !== 1) {
|
|
1067
|
-
return { status: "not_matched", code: "accepted_call_not_unique" };
|
|
1068
|
-
}
|
|
1069
|
-
const matching = options.screen.exactShape &&
|
|
1070
|
-
normalizedScreen.responseKind !== "confirm" &&
|
|
1071
|
-
normalizedScreen.responseKind !== "multi_select"
|
|
1072
|
-
? pending.filter((call) => codexPendingQuestionnaireMatchesScreen(call, normalizedScreen))
|
|
1073
|
-
: pending;
|
|
1074
|
-
if (matching.length !== 1 || matching[0] !== acceptedCalls[0]) {
|
|
1075
|
-
return { status: "not_matched", code: "screen_signature_not_unique" };
|
|
1076
|
-
}
|
|
1077
|
-
return {
|
|
1078
|
-
status: "matched",
|
|
1079
|
-
evidenceFingerprint: fingerprint({
|
|
1080
|
-
schema: "agent-knock-knock/codex-bound-questionnaire-attribution",
|
|
1081
|
-
version: 1,
|
|
1082
|
-
native_thread_id: acceptedThreadId,
|
|
1083
|
-
turn_id: acceptanceId,
|
|
1084
|
-
rollout: {
|
|
1085
|
-
device: acceptedRollout.device,
|
|
1086
|
-
inode: acceptedRollout.inode,
|
|
1087
|
-
path: acceptedRollout.path
|
|
1088
|
-
},
|
|
1089
|
-
screen: normalizedScreen
|
|
1090
|
-
})
|
|
1091
|
-
};
|
|
1092
|
-
}
|
|
1093
336
|
export function detectCodexRolloutAcceptance(options) {
|
|
1094
337
|
const anchor = validateCodexRolloutAcceptanceAnchor(options.anchor);
|
|
1095
338
|
if (anchor.version === 3) {
|
|
@@ -1127,504 +370,22 @@ export function detectCodexRolloutAcceptance(options) {
|
|
|
1127
370
|
});
|
|
1128
371
|
return scan.status === "accepted" ? scan.evidence : undefined;
|
|
1129
372
|
}
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
* recover completion even after later native turns have been appended.
|
|
1137
|
-
*/
|
|
1138
|
-
export function detectCodexBoundRolloutCompletion(options) {
|
|
1139
|
-
let anchor;
|
|
1140
|
-
try {
|
|
1141
|
-
anchor = validateCodexRolloutAcceptanceAnchor(options.anchor);
|
|
1142
|
-
}
|
|
1143
|
-
catch (error) {
|
|
1144
|
-
return codexCompletionFailure("invalid_anchor", error);
|
|
1145
|
-
}
|
|
1146
|
-
const initialDiagnostics = {
|
|
1147
|
-
detector: "codex_exact_bound_rollout",
|
|
1148
|
-
anchor_fingerprint: anchor.anchor_fingerprint,
|
|
1149
|
-
scan_start_offset_bytes: anchor.offset_bytes
|
|
1150
|
-
};
|
|
1151
|
-
let requestHash;
|
|
1152
|
-
let acceptance;
|
|
1153
|
-
let expectedNativeThreadId;
|
|
1154
|
-
try {
|
|
1155
|
-
requestHash = sha256Value(options.requestHash, "terminal request hash");
|
|
1156
|
-
expectedNativeThreadId = anchor.version === 1
|
|
1157
|
-
? anchor.native_thread_id
|
|
1158
|
-
: exactNativeThreadId(options.acceptanceEvidence.nativeThreadId);
|
|
1159
|
-
acceptance = validateTerminalSubmissionAcceptanceEvidence(options.acceptanceEvidence, {
|
|
1160
|
-
source: "codex_rollout",
|
|
1161
|
-
nativeThreadId: expectedNativeThreadId,
|
|
1162
|
-
requestHash
|
|
1163
|
-
});
|
|
1164
|
-
}
|
|
1165
|
-
catch (error) {
|
|
1166
|
-
return codexCompletionFailure("invalid_acceptance_evidence", error, initialDiagnostics);
|
|
1167
|
-
}
|
|
1168
|
-
const acceptedCandidate = anchor.version === 3
|
|
1169
|
-
? anchor.candidate_rollouts.find((candidate) => candidate.native_thread_id === expectedNativeThreadId)
|
|
1170
|
-
: undefined;
|
|
1171
|
-
const scanStartOffset = acceptedCandidate?.offset_bytes ??
|
|
1172
|
-
anchor.offset_bytes;
|
|
1173
|
-
const baseDiagnostics = {
|
|
1174
|
-
...initialDiagnostics,
|
|
1175
|
-
scan_start_offset_bytes: scanStartOffset,
|
|
1176
|
-
native_thread_id: expectedNativeThreadId
|
|
1177
|
-
};
|
|
1178
|
-
let acceptanceId;
|
|
1179
|
-
try {
|
|
1180
|
-
acceptanceId = exactNativeThreadId(acceptance.acceptanceId);
|
|
1181
|
-
}
|
|
1182
|
-
catch (error) {
|
|
1183
|
-
return codexCompletionFailure("acceptance_turn_mismatch", error, baseDiagnostics);
|
|
1184
|
-
}
|
|
1185
|
-
const diagnostics = {
|
|
1186
|
-
...baseDiagnostics,
|
|
1187
|
-
acceptance_id: acceptanceId
|
|
1188
|
-
};
|
|
1189
|
-
if (acceptance.anchorFingerprint !== anchor.anchor_fingerprint) {
|
|
1190
|
-
return codexCompletionFailure("acceptance_anchor_mismatch", new Error("Codex acceptance evidence belongs to a different byte anchor"), diagnostics);
|
|
1191
|
-
}
|
|
1192
|
-
const evidenceTurnId = optionalString(acceptance.metadata?.turn_id);
|
|
1193
|
-
if (evidenceTurnId !== undefined) {
|
|
1194
|
-
let normalizedEvidenceTurnId;
|
|
1195
|
-
try {
|
|
1196
|
-
normalizedEvidenceTurnId = exactNativeThreadId(evidenceTurnId);
|
|
373
|
+
function acceptedCodexTurnFromSuffix(text, requestHash) {
|
|
374
|
+
const startedTurns = new Map();
|
|
375
|
+
const matches = [];
|
|
376
|
+
for (const line of text.split(/\r?\n/u)) {
|
|
377
|
+
if (!line) {
|
|
378
|
+
continue;
|
|
1197
379
|
}
|
|
1198
|
-
|
|
1199
|
-
|
|
380
|
+
let value;
|
|
381
|
+
try {
|
|
382
|
+
value = JSON.parse(line);
|
|
1200
383
|
}
|
|
1201
|
-
|
|
1202
|
-
|
|
384
|
+
catch {
|
|
385
|
+
throw new Error("Codex rollout acceptance suffix contains invalid JSONL");
|
|
1203
386
|
}
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
try {
|
|
1207
|
-
evidenceAnchorOffset = optionalSafeOffset(acceptance.metadata?.anchor_offset_bytes);
|
|
1208
|
-
}
|
|
1209
|
-
catch (error) {
|
|
1210
|
-
return codexCompletionFailure("invalid_acceptance_evidence", error, diagnostics);
|
|
1211
|
-
}
|
|
1212
|
-
if (evidenceAnchorOffset !== undefined &&
|
|
1213
|
-
evidenceAnchorOffset !== scanStartOffset) {
|
|
1214
|
-
return codexCompletionFailure("acceptance_anchor_mismatch", new Error("Codex acceptance evidence has a different byte offset"), diagnostics);
|
|
1215
|
-
}
|
|
1216
|
-
const current = options.currentIdentity;
|
|
1217
|
-
try {
|
|
1218
|
-
if (exactNativeThreadId(current.sessionId) !== expectedNativeThreadId ||
|
|
1219
|
-
requiredString(current.processUuid, "Codex process UUID") !==
|
|
1220
|
-
anchor.process_uuid ||
|
|
1221
|
-
requiredString(current.processBirth, "Codex process birth") !==
|
|
1222
|
-
anchor.process_birth) {
|
|
1223
|
-
throw new Error("Codex process or native thread identity changed during completion polling");
|
|
1224
|
-
}
|
|
1225
|
-
}
|
|
1226
|
-
catch (error) {
|
|
1227
|
-
return codexCompletionFailure("binding_identity_mismatch", error, diagnostics);
|
|
1228
|
-
}
|
|
1229
|
-
if (!current.rollout) {
|
|
1230
|
-
return codexCompletionFailure("rollout_identity_mismatch", new Error("the accepted Codex Turn lost its persisted exact rollout identity"), diagnostics);
|
|
1231
|
-
}
|
|
1232
|
-
let rollout;
|
|
1233
|
-
try {
|
|
1234
|
-
rollout = normalizedRolloutIdentity(current.rollout);
|
|
1235
|
-
}
|
|
1236
|
-
catch (error) {
|
|
1237
|
-
return codexCompletionFailure("rollout_identity_mismatch", error, diagnostics);
|
|
1238
|
-
}
|
|
1239
|
-
const rolloutDiagnostics = {
|
|
1240
|
-
...diagnostics,
|
|
1241
|
-
rollout_identity_fingerprint: fingerprint(rollout)
|
|
1242
|
-
};
|
|
1243
|
-
const anchoredRollout = anchor.version === 3
|
|
1244
|
-
? acceptedCandidate?.rollout
|
|
1245
|
-
: anchor.rollout;
|
|
1246
|
-
const anchoredRolloutMatches = !anchoredRollout || (anchor.version === 3
|
|
1247
|
-
? sameRolloutFileIdentity(anchoredRollout, rollout)
|
|
1248
|
-
: sameRolloutIdentity(anchoredRollout, rollout));
|
|
1249
|
-
if (!anchoredRolloutMatches) {
|
|
1250
|
-
return codexCompletionFailure("rollout_identity_mismatch", new Error("Codex rollout identity changed after terminal acceptance"), rolloutDiagnostics);
|
|
1251
|
-
}
|
|
1252
|
-
let opened;
|
|
1253
|
-
try {
|
|
1254
|
-
opened = openExactRollout(rollout);
|
|
1255
|
-
}
|
|
1256
|
-
catch (error) {
|
|
1257
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1258
|
-
return codexCompletionFailure(/identity does not match/u.test(message)
|
|
1259
|
-
? "rollout_identity_mismatch"
|
|
1260
|
-
: "rollout_unreadable", error, rolloutDiagnostics);
|
|
1261
|
-
}
|
|
1262
|
-
try {
|
|
1263
|
-
const before = opened.stat;
|
|
1264
|
-
try {
|
|
1265
|
-
assertPrivateRegularFile(before);
|
|
1266
|
-
}
|
|
1267
|
-
catch (error) {
|
|
1268
|
-
return codexCompletionFailure("rollout_unreadable", error, rolloutDiagnostics);
|
|
1269
|
-
}
|
|
1270
|
-
let evidenceEndOffset;
|
|
1271
|
-
try {
|
|
1272
|
-
evidenceEndOffset = optionalSafeOffset(acceptance.metadata?.observed_end_offset_bytes);
|
|
1273
|
-
}
|
|
1274
|
-
catch (error) {
|
|
1275
|
-
return codexCompletionFailure("invalid_acceptance_evidence", error, rolloutDiagnostics);
|
|
1276
|
-
}
|
|
1277
|
-
if (before.size < scanStartOffset ||
|
|
1278
|
-
(evidenceEndOffset !== undefined && before.size < evidenceEndOffset)) {
|
|
1279
|
-
return codexCompletionFailure("rollout_truncated", new Error("Codex rollout was truncated after native acceptance"), {
|
|
1280
|
-
...rolloutDiagnostics,
|
|
1281
|
-
observed_end_offset_bytes: before.size
|
|
1282
|
-
});
|
|
1283
|
-
}
|
|
1284
|
-
const bytesToRead = before.size - scanStartOffset;
|
|
1285
|
-
const observedDiagnostics = {
|
|
1286
|
-
...rolloutDiagnostics,
|
|
1287
|
-
observed_end_offset_bytes: before.size
|
|
1288
|
-
};
|
|
1289
|
-
if (bytesToRead > CODEX_COMPLETION_MAX_BYTES) {
|
|
1290
|
-
return codexCompletionFailure("scan_limit_exceeded", new Error("Codex bound completion suffix exceeded the safe scan limit"), observedDiagnostics);
|
|
1291
|
-
}
|
|
1292
|
-
try {
|
|
1293
|
-
if (before.size > 0) {
|
|
1294
|
-
assertCodexRolloutLegacyHistory(readCodexRolloutHeader(opened.fd, before.size));
|
|
1295
|
-
}
|
|
1296
|
-
}
|
|
1297
|
-
catch (error) {
|
|
1298
|
-
return codexCompletionFailure("rollout_unreadable", error, observedDiagnostics);
|
|
1299
|
-
}
|
|
1300
|
-
if (bytesToRead === 0) {
|
|
1301
|
-
return codexCompletionPending("exact_turn_not_complete", observedDiagnostics, "no post-anchor Codex rollout records are available yet");
|
|
1302
|
-
}
|
|
1303
|
-
if (!fileEndsWithNewline(opened.fd, before.size)) {
|
|
1304
|
-
return codexCompletionPending("partial_rollout_record", observedDiagnostics, "the exact Codex rollout ends with a partial JSONL record");
|
|
1305
|
-
}
|
|
1306
|
-
const buffer = Buffer.allocUnsafe(bytesToRead);
|
|
1307
|
-
const bytesRead = fs.readSync(opened.fd, buffer, 0, bytesToRead, scanStartOffset);
|
|
1308
|
-
const after = fs.fstatSync(opened.fd);
|
|
1309
|
-
if (bytesRead !== bytesToRead || !sameStableFile(before, after)) {
|
|
1310
|
-
return codexCompletionPending("rollout_changed_during_scan", observedDiagnostics, "the exact Codex rollout changed while completion was scanned");
|
|
1311
|
-
}
|
|
1312
|
-
const scan = scanExactCodexTaskComplete(buffer.toString("utf8"), acceptanceId);
|
|
1313
|
-
const scanDiagnostics = {
|
|
1314
|
-
...observedDiagnostics,
|
|
1315
|
-
scanned_records: scan.scannedRecords,
|
|
1316
|
-
observed_task_complete_records: scan.observedTaskCompleteRecords,
|
|
1317
|
-
observed_turn_aborted_records: scan.observedTurnAbortedRecords
|
|
1318
|
-
};
|
|
1319
|
-
if (scan.status === "failure") {
|
|
1320
|
-
return codexCompletionFailure(scan.code, scan.detail, scanDiagnostics);
|
|
1321
|
-
}
|
|
1322
|
-
if (scan.status === "pending") {
|
|
1323
|
-
return codexCompletionPending("exact_turn_not_complete", scanDiagnostics, "the accepted Codex native turn has no durable task_complete record yet");
|
|
1324
|
-
}
|
|
1325
|
-
if (scan.status === "aborted") {
|
|
1326
|
-
const reason = truncateCompletionText(redactString(scan.reason ?? "interrupted"));
|
|
1327
|
-
const completion = {
|
|
1328
|
-
source: "durable",
|
|
1329
|
-
outcome: "failure",
|
|
1330
|
-
text: truncateCompletionText(redactString(`Codex task stopped: ${reason}`)),
|
|
1331
|
-
...(scan.timestamp ? { timestamp: scan.timestamp } : {}),
|
|
1332
|
-
id: acceptanceId,
|
|
1333
|
-
confidence: "high",
|
|
1334
|
-
metadata: {
|
|
1335
|
-
match: "bound_rollout_turn_aborted",
|
|
1336
|
-
turn_id: acceptanceId,
|
|
1337
|
-
native_thread_id: expectedNativeThreadId,
|
|
1338
|
-
anchor_fingerprint: anchor.anchor_fingerprint,
|
|
1339
|
-
rollout_identity_fingerprint: rolloutDiagnostics.rollout_identity_fingerprint,
|
|
1340
|
-
abort_reason: reason,
|
|
1341
|
-
scan_start_offset_bytes: scanStartOffset,
|
|
1342
|
-
observed_end_offset_bytes: before.size,
|
|
1343
|
-
scanned_records: scan.scannedRecords,
|
|
1344
|
-
observed_task_complete_records: scan.observedTaskCompleteRecords,
|
|
1345
|
-
observed_turn_aborted_records: scan.observedTurnAbortedRecords
|
|
1346
|
-
}
|
|
1347
|
-
};
|
|
1348
|
-
return {
|
|
1349
|
-
status: "completed",
|
|
1350
|
-
completion,
|
|
1351
|
-
diagnostics: {
|
|
1352
|
-
...scanDiagnostics,
|
|
1353
|
-
code: "abort_found"
|
|
1354
|
-
}
|
|
1355
|
-
};
|
|
1356
|
-
}
|
|
1357
|
-
const completion = {
|
|
1358
|
-
source: "durable",
|
|
1359
|
-
outcome: "success",
|
|
1360
|
-
text: truncateCompletionText(redactString(scan.text)),
|
|
1361
|
-
...(scan.timestamp ? { timestamp: scan.timestamp } : {}),
|
|
1362
|
-
id: acceptanceId,
|
|
1363
|
-
confidence: "high",
|
|
1364
|
-
metadata: {
|
|
1365
|
-
match: "bound_rollout_task_complete",
|
|
1366
|
-
turn_id: acceptanceId,
|
|
1367
|
-
native_thread_id: expectedNativeThreadId,
|
|
1368
|
-
anchor_fingerprint: anchor.anchor_fingerprint,
|
|
1369
|
-
rollout_identity_fingerprint: rolloutDiagnostics.rollout_identity_fingerprint,
|
|
1370
|
-
scan_start_offset_bytes: scanStartOffset,
|
|
1371
|
-
observed_end_offset_bytes: before.size,
|
|
1372
|
-
scanned_records: scan.scannedRecords,
|
|
1373
|
-
observed_task_complete_records: scan.observedTaskCompleteRecords,
|
|
1374
|
-
observed_turn_aborted_records: scan.observedTurnAbortedRecords
|
|
1375
|
-
}
|
|
1376
|
-
};
|
|
1377
|
-
return {
|
|
1378
|
-
status: "completed",
|
|
1379
|
-
completion,
|
|
1380
|
-
diagnostics: {
|
|
1381
|
-
...scanDiagnostics,
|
|
1382
|
-
code: "completion_found"
|
|
1383
|
-
}
|
|
1384
|
-
};
|
|
1385
|
-
}
|
|
1386
|
-
catch (error) {
|
|
1387
|
-
return codexCompletionFailure("rollout_unreadable", error, rolloutDiagnostics);
|
|
1388
|
-
}
|
|
1389
|
-
finally {
|
|
1390
|
-
try {
|
|
1391
|
-
fs.closeSync(opened.fd);
|
|
1392
|
-
}
|
|
1393
|
-
catch {
|
|
1394
|
-
// The scan result is already fenced by the descriptor identity and both
|
|
1395
|
-
// file snapshots. A close failure cannot make it safe to retry a native
|
|
1396
|
-
// turn, so do not replace that deterministic result with an exception.
|
|
1397
|
-
}
|
|
1398
|
-
}
|
|
1399
|
-
}
|
|
1400
|
-
function scanExactCodexTaskComplete(text, acceptanceId) {
|
|
1401
|
-
let scannedRecords = 0;
|
|
1402
|
-
let observedTaskCompleteRecords = 0;
|
|
1403
|
-
let observedTurnAbortedRecords = 0;
|
|
1404
|
-
let exactTaskStartedIndex;
|
|
1405
|
-
let laterTaskStartedIndex;
|
|
1406
|
-
const exactMatches = [];
|
|
1407
|
-
const exactAborts = [];
|
|
1408
|
-
for (const rawLine of text.split("\n")) {
|
|
1409
|
-
const line = rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine;
|
|
1410
|
-
if (!line) {
|
|
1411
|
-
continue;
|
|
1412
|
-
}
|
|
1413
|
-
let value;
|
|
1414
|
-
try {
|
|
1415
|
-
value = JSON.parse(line);
|
|
1416
|
-
}
|
|
1417
|
-
catch {
|
|
1418
|
-
return {
|
|
1419
|
-
status: "failure",
|
|
1420
|
-
code: "invalid_rollout_jsonl",
|
|
1421
|
-
detail: `Codex bound completion suffix contains invalid JSONL at record ${scannedRecords + 1}`,
|
|
1422
|
-
scannedRecords,
|
|
1423
|
-
observedTaskCompleteRecords,
|
|
1424
|
-
observedTurnAbortedRecords
|
|
1425
|
-
};
|
|
1426
|
-
}
|
|
1427
|
-
const recordIndex = scannedRecords;
|
|
1428
|
-
scannedRecords += 1;
|
|
1429
|
-
if (isRecord(value) &&
|
|
1430
|
-
value.type === "event_msg" &&
|
|
1431
|
-
isRecord(value.payload) &&
|
|
1432
|
-
value.payload.type === "task_started") {
|
|
1433
|
-
const turnId = optionalString(value.payload.turn_id)?.toLowerCase();
|
|
1434
|
-
if (turnId === acceptanceId) {
|
|
1435
|
-
exactTaskStartedIndex ??= recordIndex;
|
|
1436
|
-
}
|
|
1437
|
-
else if (exactTaskStartedIndex !== undefined &&
|
|
1438
|
-
laterTaskStartedIndex === undefined) {
|
|
1439
|
-
laterTaskStartedIndex = recordIndex;
|
|
1440
|
-
}
|
|
1441
|
-
continue;
|
|
1442
|
-
}
|
|
1443
|
-
if (isRecord(value) &&
|
|
1444
|
-
value.type === "event_msg" &&
|
|
1445
|
-
isRecord(value.payload) &&
|
|
1446
|
-
value.payload.type === "turn_aborted") {
|
|
1447
|
-
observedTurnAbortedRecords += 1;
|
|
1448
|
-
const turnId = optionalString(value.payload.turn_id)?.toLowerCase();
|
|
1449
|
-
if (turnId === acceptanceId) {
|
|
1450
|
-
exactAborts.push({ value, index: recordIndex });
|
|
1451
|
-
}
|
|
1452
|
-
continue;
|
|
1453
|
-
}
|
|
1454
|
-
if (!isRecord(value) ||
|
|
1455
|
-
value.type !== "event_msg" ||
|
|
1456
|
-
!isRecord(value.payload) ||
|
|
1457
|
-
value.payload.type !== "task_complete") {
|
|
1458
|
-
continue;
|
|
1459
|
-
}
|
|
1460
|
-
observedTaskCompleteRecords += 1;
|
|
1461
|
-
const turnId = optionalString(value.payload.turn_id)?.toLowerCase();
|
|
1462
|
-
if (turnId === acceptanceId) {
|
|
1463
|
-
exactMatches.push({ value, index: recordIndex });
|
|
1464
|
-
}
|
|
1465
|
-
}
|
|
1466
|
-
if (exactMatches.length > 1) {
|
|
1467
|
-
return {
|
|
1468
|
-
status: "failure",
|
|
1469
|
-
code: "duplicate_exact_completion",
|
|
1470
|
-
detail: "the exact accepted Codex turn has duplicate task_complete records",
|
|
1471
|
-
scannedRecords,
|
|
1472
|
-
observedTaskCompleteRecords,
|
|
1473
|
-
observedTurnAbortedRecords
|
|
1474
|
-
};
|
|
1475
|
-
}
|
|
1476
|
-
if (exactAborts.length > 1) {
|
|
1477
|
-
return {
|
|
1478
|
-
status: "failure",
|
|
1479
|
-
code: "duplicate_exact_abort",
|
|
1480
|
-
detail: "the exact accepted Codex turn has duplicate turn_aborted records",
|
|
1481
|
-
scannedRecords,
|
|
1482
|
-
observedTaskCompleteRecords,
|
|
1483
|
-
observedTurnAbortedRecords
|
|
1484
|
-
};
|
|
1485
|
-
}
|
|
1486
|
-
if (exactMatches.length > 0 && exactAborts.length > 0) {
|
|
1487
|
-
return {
|
|
1488
|
-
status: "failure",
|
|
1489
|
-
code: "conflicting_exact_settlement",
|
|
1490
|
-
detail: "the exact accepted Codex turn has conflicting completion and abort records",
|
|
1491
|
-
scannedRecords,
|
|
1492
|
-
observedTaskCompleteRecords,
|
|
1493
|
-
observedTurnAbortedRecords
|
|
1494
|
-
};
|
|
1495
|
-
}
|
|
1496
|
-
const exactSettlementIndex = exactMatches[0]?.index ?? exactAborts[0]?.index;
|
|
1497
|
-
if (laterTaskStartedIndex !== undefined &&
|
|
1498
|
-
(exactSettlementIndex === undefined ||
|
|
1499
|
-
laterTaskStartedIndex < exactSettlementIndex)) {
|
|
1500
|
-
return {
|
|
1501
|
-
status: "failure",
|
|
1502
|
-
code: "later_turn_started",
|
|
1503
|
-
detail: "a later Codex native turn started before the exact accepted turn settled",
|
|
1504
|
-
scannedRecords,
|
|
1505
|
-
observedTaskCompleteRecords,
|
|
1506
|
-
observedTurnAbortedRecords
|
|
1507
|
-
};
|
|
1508
|
-
}
|
|
1509
|
-
if (exactAborts.length === 1) {
|
|
1510
|
-
const match = exactAborts[0].value;
|
|
1511
|
-
if (match.timestamp !== undefined && !validTimestamp(match.timestamp)) {
|
|
1512
|
-
return {
|
|
1513
|
-
status: "failure",
|
|
1514
|
-
code: "invalid_exact_abort",
|
|
1515
|
-
detail: "the exact Codex turn_aborted record has an invalid timestamp",
|
|
1516
|
-
scannedRecords,
|
|
1517
|
-
observedTaskCompleteRecords,
|
|
1518
|
-
observedTurnAbortedRecords
|
|
1519
|
-
};
|
|
1520
|
-
}
|
|
1521
|
-
const payload = match.payload;
|
|
1522
|
-
return {
|
|
1523
|
-
status: "aborted",
|
|
1524
|
-
reason: optionalString(payload.reason),
|
|
1525
|
-
...(match.timestamp !== undefined
|
|
1526
|
-
? { timestamp: String(match.timestamp) }
|
|
1527
|
-
: {}),
|
|
1528
|
-
scannedRecords,
|
|
1529
|
-
observedTaskCompleteRecords,
|
|
1530
|
-
observedTurnAbortedRecords
|
|
1531
|
-
};
|
|
1532
|
-
}
|
|
1533
|
-
if (exactMatches.length === 0) {
|
|
1534
|
-
return {
|
|
1535
|
-
status: "pending",
|
|
1536
|
-
scannedRecords,
|
|
1537
|
-
observedTaskCompleteRecords,
|
|
1538
|
-
observedTurnAbortedRecords
|
|
1539
|
-
};
|
|
1540
|
-
}
|
|
1541
|
-
const match = exactMatches[0].value;
|
|
1542
|
-
const payload = match.payload;
|
|
1543
|
-
const textValue = optionalString(payload.last_agent_message);
|
|
1544
|
-
if (!textValue) {
|
|
1545
|
-
return {
|
|
1546
|
-
status: "failure",
|
|
1547
|
-
code: "invalid_exact_completion",
|
|
1548
|
-
detail: "the exact Codex task_complete record has no final agent message",
|
|
1549
|
-
scannedRecords,
|
|
1550
|
-
observedTaskCompleteRecords,
|
|
1551
|
-
observedTurnAbortedRecords
|
|
1552
|
-
};
|
|
1553
|
-
}
|
|
1554
|
-
if (match.timestamp !== undefined && !validTimestamp(match.timestamp)) {
|
|
1555
|
-
return {
|
|
1556
|
-
status: "failure",
|
|
1557
|
-
code: "invalid_exact_completion",
|
|
1558
|
-
detail: "the exact Codex task_complete record has an invalid timestamp",
|
|
1559
|
-
scannedRecords,
|
|
1560
|
-
observedTaskCompleteRecords,
|
|
1561
|
-
observedTurnAbortedRecords
|
|
1562
|
-
};
|
|
1563
|
-
}
|
|
1564
|
-
return {
|
|
1565
|
-
status: "completed",
|
|
1566
|
-
text: textValue,
|
|
1567
|
-
...(match.timestamp !== undefined
|
|
1568
|
-
? { timestamp: String(match.timestamp) }
|
|
1569
|
-
: {}),
|
|
1570
|
-
scannedRecords,
|
|
1571
|
-
observedTaskCompleteRecords,
|
|
1572
|
-
observedTurnAbortedRecords
|
|
1573
|
-
};
|
|
1574
|
-
}
|
|
1575
|
-
function codexCompletionPending(code, diagnostics, detail) {
|
|
1576
|
-
return {
|
|
1577
|
-
status: "pending",
|
|
1578
|
-
diagnostics: {
|
|
1579
|
-
...diagnostics,
|
|
1580
|
-
code,
|
|
1581
|
-
detail
|
|
1582
|
-
}
|
|
1583
|
-
};
|
|
1584
|
-
}
|
|
1585
|
-
function codexCompletionFailure(code, error, diagnostics = {
|
|
1586
|
-
detector: "codex_exact_bound_rollout"
|
|
1587
|
-
}) {
|
|
1588
|
-
const detail = error instanceof Error ? error.message : String(error);
|
|
1589
|
-
return {
|
|
1590
|
-
status: "failure",
|
|
1591
|
-
diagnostics: {
|
|
1592
|
-
...diagnostics,
|
|
1593
|
-
code,
|
|
1594
|
-
detail: truncateCompletionText(redactString(detail))
|
|
1595
|
-
}
|
|
1596
|
-
};
|
|
1597
|
-
}
|
|
1598
|
-
function optionalSafeOffset(value) {
|
|
1599
|
-
if (value === undefined) {
|
|
1600
|
-
return undefined;
|
|
1601
|
-
}
|
|
1602
|
-
if (!Number.isSafeInteger(value) || Number(value) < 0) {
|
|
1603
|
-
throw new Error("Codex acceptance evidence byte offset is invalid");
|
|
1604
|
-
}
|
|
1605
|
-
return Number(value);
|
|
1606
|
-
}
|
|
1607
|
-
function truncateCompletionText(value) {
|
|
1608
|
-
return value.length <= CODEX_COMPLETION_MAX_TEXT_LENGTH
|
|
1609
|
-
? value
|
|
1610
|
-
: `${value.slice(0, CODEX_COMPLETION_MAX_TEXT_LENGTH - 1)}…`;
|
|
1611
|
-
}
|
|
1612
|
-
function acceptedCodexTurnFromSuffix(text, requestHash) {
|
|
1613
|
-
const startedTurns = new Map();
|
|
1614
|
-
const matches = [];
|
|
1615
|
-
for (const line of text.split(/\r?\n/u)) {
|
|
1616
|
-
if (!line) {
|
|
1617
|
-
continue;
|
|
1618
|
-
}
|
|
1619
|
-
let value;
|
|
1620
|
-
try {
|
|
1621
|
-
value = JSON.parse(line);
|
|
1622
|
-
}
|
|
1623
|
-
catch {
|
|
1624
|
-
throw new Error("Codex rollout acceptance suffix contains invalid JSONL");
|
|
1625
|
-
}
|
|
1626
|
-
if (!isRecord(value) || !isRecord(value.payload)) {
|
|
1627
|
-
continue;
|
|
387
|
+
if (!isRecord(value) || !isRecord(value.payload)) {
|
|
388
|
+
continue;
|
|
1628
389
|
}
|
|
1629
390
|
const payload = value.payload;
|
|
1630
391
|
if (value.type === "event_msg" && payload.type === "task_started") {
|
|
@@ -1756,16 +517,6 @@ function scanCodexRolloutAcceptance({ rollout: rolloutValue, nativeThreadId: nat
|
|
|
1756
517
|
fs.closeSync(opened.fd);
|
|
1757
518
|
}
|
|
1758
519
|
}
|
|
1759
|
-
function exactCodexUserResponseText(content) {
|
|
1760
|
-
if (!Array.isArray(content) || content.length !== 1) {
|
|
1761
|
-
return undefined;
|
|
1762
|
-
}
|
|
1763
|
-
const item = content[0];
|
|
1764
|
-
return isRecord(item) && item.type === "input_text" &&
|
|
1765
|
-
typeof item.text === "string"
|
|
1766
|
-
? item.text
|
|
1767
|
-
: undefined;
|
|
1768
|
-
}
|
|
1769
520
|
function assertVirginRolloutHeader(options) {
|
|
1770
521
|
const firstLine = options.text.split("\n").find((line) => line.trim() !== "");
|
|
1771
522
|
if (!firstLine) {
|
|
@@ -1797,579 +548,4 @@ function assertVirginRolloutHeader(options) {
|
|
|
1797
548
|
throw new Error("virgin Codex rollout predates its terminal submission anchor");
|
|
1798
549
|
}
|
|
1799
550
|
}
|
|
1800
|
-
function validateCodexOpenRootInventoryForAcceptance(value) {
|
|
1801
|
-
if (!isRecord(value) ||
|
|
1802
|
-
value.schema !==
|
|
1803
|
-
"agent-knock-knock/codex-open-root-rollout-inventory" ||
|
|
1804
|
-
value.version !== 1 ||
|
|
1805
|
-
!["verified_absent", "resolved", "unbound"].includes(String(value.status)) ||
|
|
1806
|
-
!Number.isSafeInteger(value.pid) ||
|
|
1807
|
-
value.pid <= 1 ||
|
|
1808
|
-
!Array.isArray(value.roots) ||
|
|
1809
|
-
value.roots.length > 128 ||
|
|
1810
|
-
!/^[0-9a-f]{64}$/u.test(String(value.inventoryFingerprint))) {
|
|
1811
|
-
throw new Error("Codex open-root rollout inventory is invalid");
|
|
1812
|
-
}
|
|
1813
|
-
requiredString(value.processUuid, "Codex process UUID");
|
|
1814
|
-
requiredString(value.processBirth, "Codex process birth");
|
|
1815
|
-
if (value.processUuid !==
|
|
1816
|
-
`codex-pid:${value.pid}:birth:${value.processBirth}`) {
|
|
1817
|
-
throw new Error("Codex open-root rollout inventory process UUID is inconsistent");
|
|
1818
|
-
}
|
|
1819
|
-
if (value.cwd !== undefined && !path.isAbsolute(value.cwd)) {
|
|
1820
|
-
throw new Error("Codex open-root rollout inventory cwd is not absolute");
|
|
1821
|
-
}
|
|
1822
|
-
if ((value.status === "verified_absent" && value.roots.length !== 0) ||
|
|
1823
|
-
(value.status === "resolved" && value.roots.length !== 1) ||
|
|
1824
|
-
(value.status === "unbound" &&
|
|
1825
|
-
(value.roots.length < 2 ||
|
|
1826
|
-
value.reason !== "multiple_open_root_rollouts"))) {
|
|
1827
|
-
throw new Error("Codex open-root rollout inventory status is inconsistent");
|
|
1828
|
-
}
|
|
1829
|
-
const seenThreads = new Set();
|
|
1830
|
-
const seenFiles = new Set();
|
|
1831
|
-
for (const identity of value.roots) {
|
|
1832
|
-
if (!isRecord(identity)) {
|
|
1833
|
-
throw new Error("Codex open-root rollout inventory identity is invalid");
|
|
1834
|
-
}
|
|
1835
|
-
const nativeThreadId = exactNativeThreadId(identity.sessionId);
|
|
1836
|
-
if (identity.processUuid !== value.processUuid ||
|
|
1837
|
-
identity.processBirth !== value.processBirth ||
|
|
1838
|
-
identity.evidence !== "codex_open_root_rollout") {
|
|
1839
|
-
throw new Error("Codex open-root rollout inventory has mixed process authority");
|
|
1840
|
-
}
|
|
1841
|
-
const rollout = normalizedRolloutIdentity(identity.rollout);
|
|
1842
|
-
if (seenThreads.has(nativeThreadId) ||
|
|
1843
|
-
seenFiles.has(`${rollout.device}:${rollout.inode}`)) {
|
|
1844
|
-
throw new Error("Codex open-root rollout inventory is ambiguous");
|
|
1845
|
-
}
|
|
1846
|
-
seenThreads.add(nativeThreadId);
|
|
1847
|
-
seenFiles.add(`${rollout.device}:${rollout.inode}`);
|
|
1848
|
-
}
|
|
1849
|
-
const { status: _status, inventoryFingerprint, reason: _reason, ...authority } = value;
|
|
1850
|
-
if (fingerprint(authority) !== inventoryFingerprint) {
|
|
1851
|
-
throw new Error("Codex open-root rollout inventory fingerprint does not match");
|
|
1852
|
-
}
|
|
1853
|
-
return value;
|
|
1854
|
-
}
|
|
1855
|
-
/**
|
|
1856
|
-
* Read only the bounded, complete tail of one exact rollout and return async
|
|
1857
|
-
* questions belonging to its single currently active native turn. A missing
|
|
1858
|
-
* task boundary, a partial record, file drift, or an unsupported payload is a
|
|
1859
|
-
* hard absence rather than permission to infer from screen text alone.
|
|
1860
|
-
*/
|
|
1861
|
-
export function readCodexAsyncQuestionDurableEvidence(input) {
|
|
1862
|
-
const rollout = normalizedRolloutIdentity(input.rollout);
|
|
1863
|
-
const nativeThreadId = exactNativeThreadId(input.nativeThreadId);
|
|
1864
|
-
const maxBytes = positiveByteLimit(input.maxBytes, CODEX_ASYNC_QUESTION_TAIL_MAX_BYTES, "Codex async-question rollout scan limit");
|
|
1865
|
-
const opened = openExactRollout(rollout);
|
|
1866
|
-
try {
|
|
1867
|
-
const before = opened.stat;
|
|
1868
|
-
assertPrivateRegularFile(before);
|
|
1869
|
-
const header = readCodexRolloutHeader(opened.fd, before.size);
|
|
1870
|
-
assertExistingCodexRolloutHeader(header, nativeThreadId);
|
|
1871
|
-
if (!fileEndsWithNewline(opened.fd, before.size)) {
|
|
1872
|
-
throw new Error("Codex async-question rollout has a partial JSONL record");
|
|
1873
|
-
}
|
|
1874
|
-
const readStart = Math.max(0, before.size - maxBytes);
|
|
1875
|
-
const length = before.size - readStart;
|
|
1876
|
-
const buffer = Buffer.allocUnsafe(length);
|
|
1877
|
-
if (fs.readSync(opened.fd, buffer, 0, length, readStart) !== length) {
|
|
1878
|
-
throw new Error("Codex async-question rollout changed while it was read");
|
|
1879
|
-
}
|
|
1880
|
-
const after = fs.fstatSync(opened.fd);
|
|
1881
|
-
if (!sameStableFile(before, after)) {
|
|
1882
|
-
throw new Error("Codex async-question rollout changed while it was scanned");
|
|
1883
|
-
}
|
|
1884
|
-
const atBoundary = readStart === 0 ||
|
|
1885
|
-
byteAtOffset(opened.fd, readStart - 1) === 0x0a;
|
|
1886
|
-
const firstComplete = atBoundary ? 0 : buffer.indexOf(0x0a) + 1;
|
|
1887
|
-
if (firstComplete <= 0 && !atBoundary) {
|
|
1888
|
-
throw new Error("Codex async-question rollout record exceeded its bounded tail scan");
|
|
1889
|
-
}
|
|
1890
|
-
const records = parseCodexJsonlRecords(buffer.subarray(firstComplete), "async-question tail");
|
|
1891
|
-
return codexAsyncQuestionEvidenceFromRecords({
|
|
1892
|
-
records,
|
|
1893
|
-
prefixTruncated: readStart > 0
|
|
1894
|
-
});
|
|
1895
|
-
}
|
|
1896
|
-
finally {
|
|
1897
|
-
fs.closeSync(opened.fd);
|
|
1898
|
-
}
|
|
1899
|
-
}
|
|
1900
|
-
function codexAsyncQuestionEvidenceFromRecords(input) {
|
|
1901
|
-
const active = activeCodexAsyncQuestionTask(input);
|
|
1902
|
-
if (!active)
|
|
1903
|
-
return undefined;
|
|
1904
|
-
const evidence = [];
|
|
1905
|
-
let totalQuestions = 0;
|
|
1906
|
-
for (const { value } of input.records.slice(active.startIndex + 1)) {
|
|
1907
|
-
const payload = isRecord(value.payload) ? value.payload : undefined;
|
|
1908
|
-
if (value.type !== "event_msg" ||
|
|
1909
|
-
payload?.type !== "item_completed" ||
|
|
1910
|
-
exactNativeThreadId(payload.turn_id) !== active.turnId ||
|
|
1911
|
-
!isRecord(payload.item) ||
|
|
1912
|
-
payload.item.type !== "AgentMessage" ||
|
|
1913
|
-
payload.item.delivery !== "async") {
|
|
1914
|
-
continue;
|
|
1915
|
-
}
|
|
1916
|
-
if (evidence.length >= CODEX_ASYNC_QUESTION_MAX_ITEMS) {
|
|
1917
|
-
throw new Error("Codex async-question item count exceeds the safe limit");
|
|
1918
|
-
}
|
|
1919
|
-
const item = codexAsyncQuestionItemEvidence(payload.item, active.turnId);
|
|
1920
|
-
totalQuestions += item.questions.length;
|
|
1921
|
-
if (totalQuestions > CODEX_ASYNC_QUESTION_MAX_QUESTIONS) {
|
|
1922
|
-
throw new Error("Codex active async-question total exceeds the safe limit");
|
|
1923
|
-
}
|
|
1924
|
-
evidence.push(item);
|
|
1925
|
-
}
|
|
1926
|
-
return evidence.length === 0 ? undefined : evidence;
|
|
1927
|
-
}
|
|
1928
|
-
function activeCodexAsyncQuestionTask(input) {
|
|
1929
|
-
let activeTurnId;
|
|
1930
|
-
let activeStartIndex = -1;
|
|
1931
|
-
let activeClosed = false;
|
|
1932
|
-
input.records.forEach(({ value }, index) => {
|
|
1933
|
-
const payload = isRecord(value.payload) ? value.payload : undefined;
|
|
1934
|
-
if (!payload || value.type !== "event_msg")
|
|
1935
|
-
return;
|
|
1936
|
-
if (payload.type === "task_started") {
|
|
1937
|
-
activeTurnId = exactNativeThreadId(payload.turn_id);
|
|
1938
|
-
activeStartIndex = index;
|
|
1939
|
-
activeClosed = false;
|
|
1940
|
-
return;
|
|
1941
|
-
}
|
|
1942
|
-
if (activeTurnId &&
|
|
1943
|
-
(payload.type === "task_complete" || payload.type === "turn_aborted") &&
|
|
1944
|
-
(payload.turn_id === undefined ||
|
|
1945
|
-
exactNativeThreadId(payload.turn_id) === activeTurnId)) {
|
|
1946
|
-
activeClosed = true;
|
|
1947
|
-
}
|
|
1948
|
-
});
|
|
1949
|
-
if (!activeTurnId || activeStartIndex < 0 || activeClosed) {
|
|
1950
|
-
if (input.prefixTruncated && activeStartIndex < 0) {
|
|
1951
|
-
throw new Error("Codex async-question tail omitted the active task boundary");
|
|
1952
|
-
}
|
|
1953
|
-
return undefined;
|
|
1954
|
-
}
|
|
1955
|
-
return { turnId: activeTurnId, startIndex: activeStartIndex };
|
|
1956
|
-
}
|
|
1957
|
-
function codexAsyncQuestionItemEvidence(item, turnId) {
|
|
1958
|
-
const itemId = requiredString(item.id, "Codex async-question item id");
|
|
1959
|
-
const rawQuestions = item.questions;
|
|
1960
|
-
if (!Array.isArray(rawQuestions) ||
|
|
1961
|
-
rawQuestions.length < 1 ||
|
|
1962
|
-
rawQuestions.length > CODEX_ASYNC_QUESTION_MAX_QUESTIONS) {
|
|
1963
|
-
throw new Error("Codex async-question count is invalid");
|
|
1964
|
-
}
|
|
1965
|
-
const questions = rawQuestions.map((question) => {
|
|
1966
|
-
if (!isRecord(question)) {
|
|
1967
|
-
throw new Error("Codex async-question payload is invalid");
|
|
1968
|
-
}
|
|
1969
|
-
const title = normalizedCodexQuestionnaireText(question.title, "Codex async-question title");
|
|
1970
|
-
if (title.length > CODEX_ASYNC_QUESTION_MAX_TEXT_LENGTH) {
|
|
1971
|
-
throw new Error("Codex async-question title exceeds the safe limit");
|
|
1972
|
-
}
|
|
1973
|
-
const rawOptions = question.options;
|
|
1974
|
-
if (rawOptions !== undefined && rawOptions !== null &&
|
|
1975
|
-
(!Array.isArray(rawOptions) ||
|
|
1976
|
-
rawOptions.length < 1 ||
|
|
1977
|
-
rawOptions.length > CODEX_ASYNC_QUESTION_MAX_OPTIONS)) {
|
|
1978
|
-
throw new Error("Codex async-question options are invalid");
|
|
1979
|
-
}
|
|
1980
|
-
const options = rawOptions == null
|
|
1981
|
-
? undefined
|
|
1982
|
-
: rawOptions.map((option) => {
|
|
1983
|
-
const label = normalizedCodexQuestionnaireText(option, "Codex async-question option");
|
|
1984
|
-
if (label.length > CODEX_ASYNC_QUESTION_MAX_TEXT_LENGTH) {
|
|
1985
|
-
throw new Error("Codex async-question option exceeds the safe limit");
|
|
1986
|
-
}
|
|
1987
|
-
return label;
|
|
1988
|
-
});
|
|
1989
|
-
return {
|
|
1990
|
-
title,
|
|
1991
|
-
...(options === undefined ? {} : { options })
|
|
1992
|
-
};
|
|
1993
|
-
});
|
|
1994
|
-
return {
|
|
1995
|
-
itemId,
|
|
1996
|
-
turnId,
|
|
1997
|
-
questions,
|
|
1998
|
-
currentIndex: 0,
|
|
1999
|
-
remainingCount: questions.length
|
|
2000
|
-
};
|
|
2001
|
-
}
|
|
2002
|
-
function readPendingCodexRequestUserInput(input) {
|
|
2003
|
-
const rollout = normalizedRolloutIdentity(input.rollout);
|
|
2004
|
-
const nativeThreadId = exactNativeThreadId(input.nativeThreadId);
|
|
2005
|
-
const opened = openExactRollout(rollout);
|
|
2006
|
-
try {
|
|
2007
|
-
const before = opened.stat;
|
|
2008
|
-
assertPrivateRegularFile(before);
|
|
2009
|
-
const header = readCodexRolloutHeader(opened.fd, before.size);
|
|
2010
|
-
assertExistingCodexRolloutHeader(header, nativeThreadId);
|
|
2011
|
-
if (!fileEndsWithNewline(opened.fd, before.size)) {
|
|
2012
|
-
throw new Error("Codex questionnaire rollout has a partial JSONL record");
|
|
2013
|
-
}
|
|
2014
|
-
const readStart = Math.max(0, before.size - input.maxBytes);
|
|
2015
|
-
const length = before.size - readStart;
|
|
2016
|
-
const buffer = Buffer.allocUnsafe(length);
|
|
2017
|
-
if (fs.readSync(opened.fd, buffer, 0, length, readStart) !== length) {
|
|
2018
|
-
throw new Error("Codex questionnaire rollout changed while it was read");
|
|
2019
|
-
}
|
|
2020
|
-
const after = fs.fstatSync(opened.fd);
|
|
2021
|
-
if (!sameStableFile(before, after)) {
|
|
2022
|
-
throw new Error("Codex questionnaire rollout changed while it was scanned");
|
|
2023
|
-
}
|
|
2024
|
-
const atBoundary = readStart === 0 ||
|
|
2025
|
-
byteAtOffset(opened.fd, readStart - 1) === 0x0a;
|
|
2026
|
-
const firstComplete = atBoundary ? 0 : buffer.indexOf(0x0a) + 1;
|
|
2027
|
-
if (firstComplete <= 0 && !atBoundary) {
|
|
2028
|
-
throw new Error("Codex questionnaire rollout record exceeded its bounded tail scan");
|
|
2029
|
-
}
|
|
2030
|
-
const records = parseCodexJsonlRecords(buffer.subarray(firstComplete), "questionnaire tail");
|
|
2031
|
-
return pendingCodexRequestUserInputFromRecords({
|
|
2032
|
-
records,
|
|
2033
|
-
nativeThreadId,
|
|
2034
|
-
rollout,
|
|
2035
|
-
prefixTruncated: readStart > 0
|
|
2036
|
-
});
|
|
2037
|
-
}
|
|
2038
|
-
finally {
|
|
2039
|
-
fs.closeSync(opened.fd);
|
|
2040
|
-
}
|
|
2041
|
-
}
|
|
2042
|
-
function pendingCodexRequestUserInputFromRecords(input) {
|
|
2043
|
-
const calls = [];
|
|
2044
|
-
const outputs = new Map();
|
|
2045
|
-
const terminalTurns = new Map();
|
|
2046
|
-
const unscopedTerminalIndexes = [];
|
|
2047
|
-
const startedTurns = [];
|
|
2048
|
-
let sawLifecycle = false;
|
|
2049
|
-
input.records.forEach(({ value }, index) => {
|
|
2050
|
-
const payload = isRecord(value.payload) ? value.payload : undefined;
|
|
2051
|
-
if (!payload)
|
|
2052
|
-
return;
|
|
2053
|
-
if (value.type === "event_msg" &&
|
|
2054
|
-
payload.type === "task_started") {
|
|
2055
|
-
startedTurns.push({
|
|
2056
|
-
index,
|
|
2057
|
-
turnId: exactNativeThreadId(payload.turn_id)
|
|
2058
|
-
});
|
|
2059
|
-
sawLifecycle = true;
|
|
2060
|
-
return;
|
|
2061
|
-
}
|
|
2062
|
-
if (value.type === "event_msg" &&
|
|
2063
|
-
(payload.type === "task_complete" || payload.type === "turn_aborted")) {
|
|
2064
|
-
const rawTurnId = optionalString(payload.turn_id);
|
|
2065
|
-
if (!rawTurnId) {
|
|
2066
|
-
unscopedTerminalIndexes.push(index);
|
|
2067
|
-
sawLifecycle = true;
|
|
2068
|
-
return;
|
|
2069
|
-
}
|
|
2070
|
-
const turnId = exactNativeThreadId(rawTurnId);
|
|
2071
|
-
const indexes = terminalTurns.get(turnId) ?? [];
|
|
2072
|
-
indexes.push(index);
|
|
2073
|
-
terminalTurns.set(turnId, indexes);
|
|
2074
|
-
sawLifecycle = true;
|
|
2075
|
-
return;
|
|
2076
|
-
}
|
|
2077
|
-
if (value.type !== "response_item" ||
|
|
2078
|
-
payload.type !== "function_call" ||
|
|
2079
|
-
payload.name !== "request_user_input") {
|
|
2080
|
-
if (value.type === "response_item" &&
|
|
2081
|
-
payload.type === "function_call_output") {
|
|
2082
|
-
const callId = requiredString(payload.call_id, "Codex questionnaire output call id");
|
|
2083
|
-
const indexes = outputs.get(callId) ?? [];
|
|
2084
|
-
indexes.push(index);
|
|
2085
|
-
outputs.set(callId, indexes);
|
|
2086
|
-
}
|
|
2087
|
-
return;
|
|
2088
|
-
}
|
|
2089
|
-
const metadata = isRecord(payload.internal_chat_message_metadata_passthrough)
|
|
2090
|
-
? payload.internal_chat_message_metadata_passthrough
|
|
2091
|
-
: undefined;
|
|
2092
|
-
const callId = requiredString(payload.call_id, "Codex questionnaire call id");
|
|
2093
|
-
if (calls.some((call) => call.callId === callId)) {
|
|
2094
|
-
throw new Error("Codex questionnaire call id is duplicated");
|
|
2095
|
-
}
|
|
2096
|
-
calls.push({
|
|
2097
|
-
index,
|
|
2098
|
-
callId,
|
|
2099
|
-
turnId: exactNativeThreadId(metadata?.turn_id),
|
|
2100
|
-
questions: parseCodexRequestUserInputArguments(payload.arguments)
|
|
2101
|
-
});
|
|
2102
|
-
});
|
|
2103
|
-
const pending = calls.filter((call) => {
|
|
2104
|
-
if (outputs.get(call.callId)?.some((index) => index > call.index)) {
|
|
2105
|
-
return false;
|
|
2106
|
-
}
|
|
2107
|
-
if (terminalTurns.get(call.turnId)?.some((index) => index > call.index)) {
|
|
2108
|
-
return false;
|
|
2109
|
-
}
|
|
2110
|
-
if (unscopedTerminalIndexes.some((index) => index > call.index)) {
|
|
2111
|
-
return false;
|
|
2112
|
-
}
|
|
2113
|
-
const precedingStart = latestCodexTaskStartBefore(startedTurns, call.index);
|
|
2114
|
-
if (precedingStart && precedingStart.turnId !== call.turnId) {
|
|
2115
|
-
return false;
|
|
2116
|
-
}
|
|
2117
|
-
return !startedTurns.some((started) => started.index > call.index);
|
|
2118
|
-
});
|
|
2119
|
-
if (input.prefixTruncated &&
|
|
2120
|
-
pending.length === 0 &&
|
|
2121
|
-
!sawLifecycle) {
|
|
2122
|
-
throw new Error("Codex questionnaire tail cannot exclude an omitted pending request");
|
|
2123
|
-
}
|
|
2124
|
-
return pending.map((call) => ({
|
|
2125
|
-
nativeThreadId: input.nativeThreadId,
|
|
2126
|
-
rollout: input.rollout,
|
|
2127
|
-
turnId: call.turnId,
|
|
2128
|
-
questions: call.questions
|
|
2129
|
-
}));
|
|
2130
|
-
}
|
|
2131
|
-
function latestCodexTaskStartBefore(starts, recordIndex) {
|
|
2132
|
-
let latest;
|
|
2133
|
-
for (const start of starts) {
|
|
2134
|
-
if (start.index >= recordIndex)
|
|
2135
|
-
break;
|
|
2136
|
-
latest = start;
|
|
2137
|
-
}
|
|
2138
|
-
return latest;
|
|
2139
|
-
}
|
|
2140
|
-
function parseCodexRequestUserInputArguments(value) {
|
|
2141
|
-
if (typeof value !== "string" ||
|
|
2142
|
-
value.length === 0 ||
|
|
2143
|
-
value.length > CODEX_QUESTIONNAIRE_TAIL_MAX_BYTES) {
|
|
2144
|
-
throw new Error("Codex questionnaire arguments are invalid");
|
|
2145
|
-
}
|
|
2146
|
-
let parsed;
|
|
2147
|
-
try {
|
|
2148
|
-
parsed = JSON.parse(value);
|
|
2149
|
-
}
|
|
2150
|
-
catch {
|
|
2151
|
-
throw new Error("Codex questionnaire arguments are not valid JSON");
|
|
2152
|
-
}
|
|
2153
|
-
const questions = isRecord(parsed) ? parsed.questions : undefined;
|
|
2154
|
-
if (!Array.isArray(questions) ||
|
|
2155
|
-
questions.length === 0 ||
|
|
2156
|
-
questions.length > CODEX_QUESTIONNAIRE_MAX_QUESTIONS) {
|
|
2157
|
-
throw new Error("Codex questionnaire question count is invalid");
|
|
2158
|
-
}
|
|
2159
|
-
return questions.map((question) => {
|
|
2160
|
-
if (!isRecord(question)) {
|
|
2161
|
-
throw new Error("Codex questionnaire question is invalid");
|
|
2162
|
-
}
|
|
2163
|
-
const rawOptions = question.options;
|
|
2164
|
-
if (rawOptions !== undefined &&
|
|
2165
|
-
(!Array.isArray(rawOptions) ||
|
|
2166
|
-
rawOptions.length > CODEX_QUESTIONNAIRE_MAX_OPTIONS)) {
|
|
2167
|
-
throw new Error("Codex questionnaire option count is invalid");
|
|
2168
|
-
}
|
|
2169
|
-
return {
|
|
2170
|
-
prompt: normalizedCodexQuestionnaireText(question.question, "Codex questionnaire prompt"),
|
|
2171
|
-
options: (rawOptions ?? []).map((option) => {
|
|
2172
|
-
if (!isRecord(option)) {
|
|
2173
|
-
throw new Error("Codex questionnaire option is invalid");
|
|
2174
|
-
}
|
|
2175
|
-
return {
|
|
2176
|
-
label: normalizedCodexQuestionnaireText(option.label, "Codex questionnaire option label"),
|
|
2177
|
-
...(option.description === undefined
|
|
2178
|
-
? {}
|
|
2179
|
-
: {
|
|
2180
|
-
description: normalizedCodexQuestionnaireText(option.description, "Codex questionnaire option description")
|
|
2181
|
-
})
|
|
2182
|
-
};
|
|
2183
|
-
})
|
|
2184
|
-
};
|
|
2185
|
-
});
|
|
2186
|
-
}
|
|
2187
|
-
function codexPendingQuestionnaireMatchesScreen(call, screen) {
|
|
2188
|
-
if (screen.currentStep < 1 ||
|
|
2189
|
-
screen.currentStep > screen.totalSteps ||
|
|
2190
|
-
call.questions.length !== screen.totalSteps) {
|
|
2191
|
-
return false;
|
|
2192
|
-
}
|
|
2193
|
-
const question = call.questions[screen.currentStep - 1];
|
|
2194
|
-
if (!question)
|
|
2195
|
-
return false;
|
|
2196
|
-
if (question.prompt !== screen.prompt)
|
|
2197
|
-
return false;
|
|
2198
|
-
if (screen.responseKind === "free_text")
|
|
2199
|
-
return true;
|
|
2200
|
-
if (screen.responseKind !== "single_select")
|
|
2201
|
-
return false;
|
|
2202
|
-
return codexRenderedOptionsMatch(question.options, screen.options ?? []);
|
|
2203
|
-
}
|
|
2204
|
-
function normalizedCodexQuestionnaireScreen(screen) {
|
|
2205
|
-
if (!Number.isSafeInteger(screen.currentStep) ||
|
|
2206
|
-
!Number.isSafeInteger(screen.totalSteps) ||
|
|
2207
|
-
screen.currentStep < 1 ||
|
|
2208
|
-
screen.totalSteps < 1 ||
|
|
2209
|
-
screen.currentStep > screen.totalSteps ||
|
|
2210
|
-
screen.totalSteps > CODEX_QUESTIONNAIRE_MAX_QUESTIONS ||
|
|
2211
|
-
!["single_select", "multi_select", "free_text", "confirm"].includes(screen.responseKind) ||
|
|
2212
|
-
(screen.options?.length ?? 0) > CODEX_QUESTIONNAIRE_MAX_OPTIONS) {
|
|
2213
|
-
throw new Error("Codex questionnaire screen evidence is invalid");
|
|
2214
|
-
}
|
|
2215
|
-
return {
|
|
2216
|
-
currentStep: screen.currentStep,
|
|
2217
|
-
totalSteps: screen.totalSteps,
|
|
2218
|
-
prompt: normalizedCodexQuestionnaireText(screen.prompt, "Codex questionnaire screen prompt"),
|
|
2219
|
-
responseKind: screen.responseKind,
|
|
2220
|
-
...(screen.options === undefined
|
|
2221
|
-
? {}
|
|
2222
|
-
: {
|
|
2223
|
-
options: screen.options.map((option) => ({
|
|
2224
|
-
label: normalizedCodexQuestionnaireText(option.label, "Codex questionnaire screen option label"),
|
|
2225
|
-
...(option.description === undefined
|
|
2226
|
-
? {}
|
|
2227
|
-
: {
|
|
2228
|
-
description: normalizedCodexQuestionnaireText(option.description, "Codex questionnaire screen option description")
|
|
2229
|
-
})
|
|
2230
|
-
}))
|
|
2231
|
-
})
|
|
2232
|
-
};
|
|
2233
|
-
}
|
|
2234
|
-
function normalizedCodexQuestionnaireText(value, label) {
|
|
2235
|
-
if (typeof value !== "string" ||
|
|
2236
|
-
value.length === 0 ||
|
|
2237
|
-
value.length > CODEX_QUESTIONNAIRE_MAX_TEXT_LENGTH ||
|
|
2238
|
-
value.includes("\0")) {
|
|
2239
|
-
throw new Error(`${label} is invalid`);
|
|
2240
|
-
}
|
|
2241
|
-
const normalized = value.replace(/\s+/gu, " ").trim();
|
|
2242
|
-
if (!normalized)
|
|
2243
|
-
throw new Error(`${label} is empty`);
|
|
2244
|
-
return normalized;
|
|
2245
|
-
}
|
|
2246
|
-
function codexRenderedOptionsMatch(durable, rendered) {
|
|
2247
|
-
if (sameCodexQuestionnaireOptions(durable, rendered))
|
|
2248
|
-
return true;
|
|
2249
|
-
const canonicalOther = {
|
|
2250
|
-
label: CODEX_QUESTIONNAIRE_OTHER_LABEL,
|
|
2251
|
-
description: CODEX_QUESTIONNAIRE_OTHER_DESCRIPTION
|
|
2252
|
-
};
|
|
2253
|
-
if (sameCodexQuestionnaireOptions([...durable, canonicalOther], rendered)) {
|
|
2254
|
-
return true;
|
|
2255
|
-
}
|
|
2256
|
-
return !durable.some((option) => option.label === CODEX_QUESTIONNAIRE_CUSTOM_LABEL) && sameCodexQuestionnaireOptions([
|
|
2257
|
-
...durable,
|
|
2258
|
-
{
|
|
2259
|
-
label: CODEX_QUESTIONNAIRE_CUSTOM_LABEL,
|
|
2260
|
-
description: CODEX_QUESTIONNAIRE_CUSTOM_DESCRIPTION
|
|
2261
|
-
},
|
|
2262
|
-
canonicalOther
|
|
2263
|
-
], rendered);
|
|
2264
|
-
}
|
|
2265
|
-
function sameCodexQuestionnaireOptions(left, right) {
|
|
2266
|
-
return left.length === right.length && left.every((option, index) => option.label === right[index]?.label &&
|
|
2267
|
-
option.description === right[index]?.description);
|
|
2268
|
-
}
|
|
2269
|
-
function validateCodexRecoveryCandidateForAcceptance(value, anchor) {
|
|
2270
|
-
if (!isRecord(value)) {
|
|
2271
|
-
throw new Error("persisted Codex recovery candidate is invalid");
|
|
2272
|
-
}
|
|
2273
|
-
const sessionId = exactNativeThreadId(value.sessionId);
|
|
2274
|
-
const rollout = normalizedRolloutIdentity(value.rollout);
|
|
2275
|
-
if (value.processUuid !== anchor.process_uuid ||
|
|
2276
|
-
value.processBirth !== anchor.process_birth ||
|
|
2277
|
-
value.evidence !== "codex_open_root_rollout") {
|
|
2278
|
-
throw new Error("persisted Codex recovery candidate has different process authority");
|
|
2279
|
-
}
|
|
2280
|
-
return {
|
|
2281
|
-
sessionId,
|
|
2282
|
-
processUuid: anchor.process_uuid,
|
|
2283
|
-
processBirth: anchor.process_birth,
|
|
2284
|
-
rollout,
|
|
2285
|
-
evidence: "codex_open_root_rollout"
|
|
2286
|
-
};
|
|
2287
|
-
}
|
|
2288
|
-
function openExactRollout(rollout) {
|
|
2289
|
-
if (!path.isAbsolute(rollout.path)) {
|
|
2290
|
-
throw new Error("Codex rollout path is not absolute");
|
|
2291
|
-
}
|
|
2292
|
-
const before = fs.lstatSync(rollout.path);
|
|
2293
|
-
if (!before.isFile() || before.isSymbolicLink()) {
|
|
2294
|
-
throw new Error("Codex rollout path is not a regular file");
|
|
2295
|
-
}
|
|
2296
|
-
const fd = fs.openSync(rollout.path, fs.constants.O_RDONLY | NO_FOLLOW_FLAG);
|
|
2297
|
-
try {
|
|
2298
|
-
const stat = fs.fstatSync(fd);
|
|
2299
|
-
if (BigInt(stat.dev) !== parsedInteger(rollout.device) ||
|
|
2300
|
-
BigInt(stat.ino) !== parsedInteger(rollout.inode)) {
|
|
2301
|
-
throw new Error("Codex rollout descriptor identity does not match its open file");
|
|
2302
|
-
}
|
|
2303
|
-
return { fd, stat };
|
|
2304
|
-
}
|
|
2305
|
-
catch (error) {
|
|
2306
|
-
fs.closeSync(fd);
|
|
2307
|
-
throw error;
|
|
2308
|
-
}
|
|
2309
|
-
}
|
|
2310
|
-
function assertPrivateRegularFile(stat) {
|
|
2311
|
-
if (!stat.isFile()) {
|
|
2312
|
-
throw new Error("Codex rollout is not a regular file");
|
|
2313
|
-
}
|
|
2314
|
-
if (process.platform !== "win32" &&
|
|
2315
|
-
typeof process.getuid === "function" &&
|
|
2316
|
-
stat.uid !== process.getuid()) {
|
|
2317
|
-
throw new Error("Codex rollout is not owned by the current user");
|
|
2318
|
-
}
|
|
2319
|
-
if (process.platform !== "win32" && (stat.mode & 0o022) !== 0) {
|
|
2320
|
-
throw new Error("Codex rollout is writable by another user");
|
|
2321
|
-
}
|
|
2322
|
-
}
|
|
2323
|
-
function fileEndsWithNewline(fd, size) {
|
|
2324
|
-
if (size <= 0) {
|
|
2325
|
-
return true;
|
|
2326
|
-
}
|
|
2327
|
-
const last = Buffer.allocUnsafe(1);
|
|
2328
|
-
return fs.readSync(fd, last, 0, 1, size - 1) === 1 && last[0] === 0x0a;
|
|
2329
|
-
}
|
|
2330
|
-
function byteAtOffset(fd, offset) {
|
|
2331
|
-
const buffer = Buffer.allocUnsafe(1);
|
|
2332
|
-
return fs.readSync(fd, buffer, 0, 1, offset) === 1
|
|
2333
|
-
? buffer[0]
|
|
2334
|
-
: undefined;
|
|
2335
|
-
}
|
|
2336
|
-
function isRetryableProviderIoError(error) {
|
|
2337
|
-
if (!isRecord(error) || typeof error.code !== "string") {
|
|
2338
|
-
return false;
|
|
2339
|
-
}
|
|
2340
|
-
return new Set([
|
|
2341
|
-
"EACCES", "EAGAIN", "EBUSY", "EIO", "EMFILE", "ENFILE", "ENOENT",
|
|
2342
|
-
"EPERM", "ESTALE", "ETIMEDOUT"
|
|
2343
|
-
]).has(error.code);
|
|
2344
|
-
}
|
|
2345
|
-
function sameStableFile(left, right) {
|
|
2346
|
-
return left.dev === right.dev &&
|
|
2347
|
-
left.ino === right.ino &&
|
|
2348
|
-
left.size === right.size &&
|
|
2349
|
-
left.mtimeMs === right.mtimeMs &&
|
|
2350
|
-
left.ctimeMs === right.ctimeMs;
|
|
2351
|
-
}
|
|
2352
|
-
function sameRolloutIdentity(left, right) {
|
|
2353
|
-
return left.fd === right.fd &&
|
|
2354
|
-
sameRolloutFileIdentity(left, right);
|
|
2355
|
-
}
|
|
2356
|
-
function sameRolloutFileIdentity(left, right) {
|
|
2357
|
-
return left.device === right.device &&
|
|
2358
|
-
left.inode === right.inode &&
|
|
2359
|
-
left.path === right.path;
|
|
2360
|
-
}
|
|
2361
|
-
function codexRolloutInodeKey(rollout) {
|
|
2362
|
-
return `${rollout.device}:${rollout.inode}`;
|
|
2363
|
-
}
|
|
2364
|
-
function optionalString(value) {
|
|
2365
|
-
return typeof value === "string" && value.trim().length > 0
|
|
2366
|
-
? value.trim()
|
|
2367
|
-
: undefined;
|
|
2368
|
-
}
|
|
2369
|
-
function fingerprintText(value) {
|
|
2370
|
-
return createHash("sha256").update(value).digest("hex");
|
|
2371
|
-
}
|
|
2372
|
-
function isRecord(value) {
|
|
2373
|
-
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
2374
|
-
}
|
|
2375
551
|
//# sourceMappingURL=terminal-submission-acceptance.js.map
|