@scotthuang/agent-knock-knock 0.12.5 → 0.12.7
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 +38 -0
- package/README.md +14 -10
- package/dist/src/agent-session-provider.d.ts +34 -0
- package/dist/src/cli-core.js +3154 -821
- package/dist/src/cli-core.js.map +1 -1
- package/dist/src/codex-local-session-provider.d.ts +3 -1
- package/dist/src/codex-local-session-provider.js +6 -0
- package/dist/src/codex-local-session-provider.js.map +1 -1
- package/dist/src/codex-store-adapter.d.ts +18 -1
- package/dist/src/codex-store-adapter.js +135 -16
- package/dist/src/codex-store-adapter.js.map +1 -1
- package/dist/src/deferred-foreground-transfer.d.ts +22 -4
- package/dist/src/deferred-foreground-transfer.js +106 -5
- package/dist/src/deferred-foreground-transfer.js.map +1 -1
- package/dist/src/openclaw-plugin.js +22 -4
- package/dist/src/openclaw-plugin.js.map +1 -1
- package/dist/src/store.d.ts +1 -1
- package/dist/src/store.js +96 -6
- package/dist/src/store.js.map +1 -1
- package/dist/src/terminal-submission-acceptance.d.ts +51 -1
- package/dist/src/terminal-submission-acceptance.js +361 -71
- package/dist/src/terminal-submission-acceptance.js.map +1 -1
- package/package.json +1 -1
- package/templates/openclaw-skills/agent-knock-knock/SKILL.md +8 -8
|
@@ -180,8 +180,174 @@ export function captureCodexRolloutAcceptanceAnchor(options) {
|
|
|
180
180
|
fs.closeSync(opened.fd);
|
|
181
181
|
}
|
|
182
182
|
}
|
|
183
|
+
export function captureCodexCandidateSetRolloutAcceptanceAnchor({ inventory: inventoryValue, now = new Date() }) {
|
|
184
|
+
const inventory = validateCodexOpenRootInventoryForAcceptance(inventoryValue);
|
|
185
|
+
const candidateRollouts = inventory.roots.map((identity) => {
|
|
186
|
+
const rollout = normalizedRolloutIdentity(identity.rollout);
|
|
187
|
+
const opened = openExactRollout(rollout);
|
|
188
|
+
try {
|
|
189
|
+
const before = opened.stat;
|
|
190
|
+
assertPrivateRegularFile(before);
|
|
191
|
+
if (before.size > 0 && !fileEndsWithNewline(opened.fd, before.size)) {
|
|
192
|
+
throw new Error("Codex candidate rollout did not end at a complete JSONL record before terminal submission");
|
|
193
|
+
}
|
|
194
|
+
const after = fs.fstatSync(opened.fd);
|
|
195
|
+
if (!sameStableFile(before, after)) {
|
|
196
|
+
throw new Error("Codex candidate rollout changed while its terminal submission anchor was captured");
|
|
197
|
+
}
|
|
198
|
+
return {
|
|
199
|
+
native_thread_id: exactNativeThreadId(identity.sessionId),
|
|
200
|
+
rollout,
|
|
201
|
+
offset_bytes: before.size
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
finally {
|
|
205
|
+
fs.closeSync(opened.fd);
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
const base = {
|
|
209
|
+
schema: "agent-knock-knock/codex-rollout-acceptance-anchor",
|
|
210
|
+
version: 3,
|
|
211
|
+
process_uuid: inventory.processUuid,
|
|
212
|
+
process_birth: inventory.processBirth,
|
|
213
|
+
captured_at: now.toISOString(),
|
|
214
|
+
mode: "candidate_set",
|
|
215
|
+
native_thread_binding: "post_submission",
|
|
216
|
+
file_existed: false,
|
|
217
|
+
offset_bytes: 0,
|
|
218
|
+
zero_file_baseline: candidateRollouts.length === 0,
|
|
219
|
+
inventory_pid: inventory.pid,
|
|
220
|
+
...(inventory.cwd ? { inventory_cwd: inventory.cwd } : {}),
|
|
221
|
+
inventory_fingerprint: inventory.inventoryFingerprint,
|
|
222
|
+
candidate_rollouts: candidateRollouts
|
|
223
|
+
};
|
|
224
|
+
return {
|
|
225
|
+
...base,
|
|
226
|
+
anchor_fingerprint: fingerprint(base)
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
export function detectCodexCandidateSetRolloutAcceptance({ anchor: anchorValue, currentInventory: inventoryValue, requestHash: requestHashValue }) {
|
|
230
|
+
const anchor = validateCodexRolloutAcceptanceAnchor(anchorValue);
|
|
231
|
+
if (anchor.version !== 3) {
|
|
232
|
+
throw new Error("Codex candidate-set acceptance requires a version 3 anchor");
|
|
233
|
+
}
|
|
234
|
+
const inventory = validateCodexOpenRootInventoryForAcceptance(inventoryValue);
|
|
235
|
+
const requestHash = sha256Value(requestHashValue, "terminal request hash");
|
|
236
|
+
if (inventory.processUuid !== anchor.process_uuid ||
|
|
237
|
+
inventory.processBirth !== anchor.process_birth ||
|
|
238
|
+
inventory.pid !== anchor.inventory_pid ||
|
|
239
|
+
inventory.cwd !== anchor.inventory_cwd) {
|
|
240
|
+
return {
|
|
241
|
+
status: "uncertain",
|
|
242
|
+
code: "candidate_inventory_changed",
|
|
243
|
+
reason: "Codex process incarnation changed during candidate-set acceptance polling",
|
|
244
|
+
inspected_candidates: 0,
|
|
245
|
+
exact_matches: 0
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
const anchoredByThread = new Map(anchor.candidate_rollouts.map((candidate) => [
|
|
249
|
+
candidate.native_thread_id,
|
|
250
|
+
candidate
|
|
251
|
+
]));
|
|
252
|
+
const currentByThread = new Map(inventory.roots.map((identity) => [identity.sessionId, identity]));
|
|
253
|
+
for (const identity of inventory.roots) {
|
|
254
|
+
const anchored = anchoredByThread.get(identity.sessionId);
|
|
255
|
+
if (anchored &&
|
|
256
|
+
!sameRolloutIdentity(anchored.rollout, identity.rollout)) {
|
|
257
|
+
return {
|
|
258
|
+
status: "uncertain",
|
|
259
|
+
code: "candidate_inventory_changed",
|
|
260
|
+
reason: `Codex candidate ${identity.sessionId} changed rollout identity after capture`,
|
|
261
|
+
inspected_candidates: 0,
|
|
262
|
+
exact_matches: 0
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
for (const anchored of anchor.candidate_rollouts) {
|
|
267
|
+
if (!currentByThread.has(anchored.native_thread_id)) {
|
|
268
|
+
return {
|
|
269
|
+
status: "uncertain",
|
|
270
|
+
code: "candidate_inventory_changed",
|
|
271
|
+
reason: `Codex candidate ${anchored.native_thread_id} is no longer open in the exact process inventory`,
|
|
272
|
+
inspected_candidates: 0,
|
|
273
|
+
exact_matches: 0
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
const candidates = inventory.roots.map((identity) => {
|
|
278
|
+
const anchored = anchoredByThread.get(identity.sessionId);
|
|
279
|
+
return {
|
|
280
|
+
identity,
|
|
281
|
+
offsetBytes: anchored?.offset_bytes ?? 0,
|
|
282
|
+
requireFreshHeader: anchored === undefined
|
|
283
|
+
};
|
|
284
|
+
});
|
|
285
|
+
const matches = [];
|
|
286
|
+
let incompleteCandidates = 0;
|
|
287
|
+
for (const candidate of candidates) {
|
|
288
|
+
let scan;
|
|
289
|
+
try {
|
|
290
|
+
scan = scanCodexRolloutAcceptance({
|
|
291
|
+
rollout: candidate.identity.rollout,
|
|
292
|
+
nativeThreadId: candidate.identity.sessionId,
|
|
293
|
+
processUuid: anchor.process_uuid,
|
|
294
|
+
processBirth: anchor.process_birth,
|
|
295
|
+
requestHash,
|
|
296
|
+
anchorFingerprint: anchor.anchor_fingerprint,
|
|
297
|
+
offsetBytes: candidate.offsetBytes,
|
|
298
|
+
requireFreshHeader: candidate.requireFreshHeader,
|
|
299
|
+
capturedAt: anchor.captured_at
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
catch (error) {
|
|
303
|
+
return {
|
|
304
|
+
status: "uncertain",
|
|
305
|
+
code: "candidate_scan_invalid",
|
|
306
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
307
|
+
inspected_candidates: candidates.indexOf(candidate) + 1,
|
|
308
|
+
exact_matches: matches.length
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
if (scan.status === "incomplete") {
|
|
312
|
+
incompleteCandidates += 1;
|
|
313
|
+
}
|
|
314
|
+
else if (scan.status === "accepted") {
|
|
315
|
+
matches.push({
|
|
316
|
+
identity: candidate.identity,
|
|
317
|
+
evidence: scan.evidence
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
if (matches.length > 1) {
|
|
322
|
+
return {
|
|
323
|
+
status: "uncertain",
|
|
324
|
+
code: "multiple_exact_request_acceptances",
|
|
325
|
+
reason: "multiple Codex root rollouts durably accepted the exact terminal request",
|
|
326
|
+
inspected_candidates: candidates.length,
|
|
327
|
+
exact_matches: matches.length
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
if (matches.length === 0 || incompleteCandidates > 0) {
|
|
331
|
+
return {
|
|
332
|
+
status: "pending",
|
|
333
|
+
inspected_candidates: candidates.length,
|
|
334
|
+
exact_matches: matches.length,
|
|
335
|
+
...(incompleteCandidates > 0
|
|
336
|
+
? { incomplete_candidates: incompleteCandidates }
|
|
337
|
+
: {})
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
return {
|
|
341
|
+
status: "accepted",
|
|
342
|
+
identity: matches[0].identity,
|
|
343
|
+
evidence: matches[0].evidence
|
|
344
|
+
};
|
|
345
|
+
}
|
|
183
346
|
export function detectCodexRolloutAcceptance(options) {
|
|
184
347
|
const anchor = validateCodexRolloutAcceptanceAnchor(options.anchor);
|
|
348
|
+
if (anchor.version === 3) {
|
|
349
|
+
throw new Error("Codex candidate-set acceptance requires the inventory-aware detector");
|
|
350
|
+
}
|
|
185
351
|
const requestHash = sha256Value(options.requestHash, "terminal request hash");
|
|
186
352
|
const current = options.currentIdentity;
|
|
187
353
|
const currentNativeThreadId = exactNativeThreadId(current.sessionId);
|
|
@@ -201,66 +367,18 @@ export function detectCodexRolloutAcceptance(options) {
|
|
|
201
367
|
!sameRolloutIdentity(anchor.rollout, rollout)) {
|
|
202
368
|
throw new Error("Codex rollout identity changed during acceptance polling");
|
|
203
369
|
}
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
throw new Error("Codex rollout acceptance suffix exceeded the bounded read limit");
|
|
217
|
-
}
|
|
218
|
-
if (!fileEndsWithNewline(opened.fd, before.size)) {
|
|
219
|
-
return undefined;
|
|
220
|
-
}
|
|
221
|
-
const buffer = Buffer.allocUnsafe(bytesToRead);
|
|
222
|
-
const bytesRead = fs.readSync(opened.fd, buffer, 0, bytesToRead, anchor.offset_bytes);
|
|
223
|
-
if (bytesRead !== bytesToRead) {
|
|
224
|
-
return undefined;
|
|
225
|
-
}
|
|
226
|
-
const after = fs.fstatSync(opened.fd);
|
|
227
|
-
if (!sameStableFile(before, after)) {
|
|
228
|
-
return undefined;
|
|
229
|
-
}
|
|
230
|
-
const suffix = buffer.toString("utf8");
|
|
231
|
-
if (anchor.version === 2) {
|
|
232
|
-
assertVirginRolloutHeader({
|
|
233
|
-
text: suffix,
|
|
234
|
-
nativeThreadId: expectedNativeThreadId,
|
|
235
|
-
capturedAt: anchor.captured_at
|
|
236
|
-
});
|
|
237
|
-
}
|
|
238
|
-
const accepted = acceptedCodexTurnFromSuffix(suffix, requestHash);
|
|
239
|
-
if (!accepted) {
|
|
240
|
-
return undefined;
|
|
241
|
-
}
|
|
242
|
-
const evidenceBase = {
|
|
243
|
-
source: "codex_rollout",
|
|
244
|
-
kind: "native_user_turn",
|
|
245
|
-
nativeThreadId: expectedNativeThreadId,
|
|
246
|
-
requestHash,
|
|
247
|
-
acceptanceId: accepted.turnId,
|
|
248
|
-
acceptedAt: accepted.userTimestamp ?? accepted.startedAt,
|
|
249
|
-
anchorFingerprint: anchor.anchor_fingerprint,
|
|
250
|
-
metadata: {
|
|
251
|
-
turn_id: accepted.turnId,
|
|
252
|
-
anchor_offset_bytes: anchor.offset_bytes,
|
|
253
|
-
observed_end_offset_bytes: before.size
|
|
254
|
-
}
|
|
255
|
-
};
|
|
256
|
-
return {
|
|
257
|
-
...evidenceBase,
|
|
258
|
-
evidenceFingerprint: fingerprint(evidenceBase)
|
|
259
|
-
};
|
|
260
|
-
}
|
|
261
|
-
finally {
|
|
262
|
-
fs.closeSync(opened.fd);
|
|
263
|
-
}
|
|
370
|
+
const scan = scanCodexRolloutAcceptance({
|
|
371
|
+
rollout,
|
|
372
|
+
nativeThreadId: expectedNativeThreadId,
|
|
373
|
+
processUuid: anchor.process_uuid,
|
|
374
|
+
processBirth: anchor.process_birth,
|
|
375
|
+
requestHash,
|
|
376
|
+
anchorFingerprint: anchor.anchor_fingerprint,
|
|
377
|
+
offsetBytes: anchor.offset_bytes,
|
|
378
|
+
requireFreshHeader: anchor.version === 2,
|
|
379
|
+
capturedAt: anchor.captured_at
|
|
380
|
+
});
|
|
381
|
+
return scan.status === "accepted" ? scan.evidence : undefined;
|
|
264
382
|
}
|
|
265
383
|
/**
|
|
266
384
|
* Scans the exact rollout and native turn proven by terminal acceptance.
|
|
@@ -300,8 +418,14 @@ export function detectCodexBoundRolloutCompletion(options) {
|
|
|
300
418
|
catch (error) {
|
|
301
419
|
return codexCompletionFailure("invalid_acceptance_evidence", error, initialDiagnostics);
|
|
302
420
|
}
|
|
421
|
+
const acceptedCandidate = anchor.version === 3
|
|
422
|
+
? anchor.candidate_rollouts.find((candidate) => candidate.native_thread_id === expectedNativeThreadId)
|
|
423
|
+
: undefined;
|
|
424
|
+
const scanStartOffset = acceptedCandidate?.offset_bytes ??
|
|
425
|
+
anchor.offset_bytes;
|
|
303
426
|
const baseDiagnostics = {
|
|
304
427
|
...initialDiagnostics,
|
|
428
|
+
scan_start_offset_bytes: scanStartOffset,
|
|
305
429
|
native_thread_id: expectedNativeThreadId
|
|
306
430
|
};
|
|
307
431
|
let acceptanceId;
|
|
@@ -339,7 +463,7 @@ export function detectCodexBoundRolloutCompletion(options) {
|
|
|
339
463
|
return codexCompletionFailure("invalid_acceptance_evidence", error, diagnostics);
|
|
340
464
|
}
|
|
341
465
|
if (evidenceAnchorOffset !== undefined &&
|
|
342
|
-
evidenceAnchorOffset !==
|
|
466
|
+
evidenceAnchorOffset !== scanStartOffset) {
|
|
343
467
|
return codexCompletionFailure("acceptance_anchor_mismatch", new Error("Codex acceptance evidence has a different byte offset"), diagnostics);
|
|
344
468
|
}
|
|
345
469
|
const current = options.currentIdentity;
|
|
@@ -369,7 +493,10 @@ export function detectCodexBoundRolloutCompletion(options) {
|
|
|
369
493
|
...diagnostics,
|
|
370
494
|
rollout_identity_fingerprint: fingerprint(rollout)
|
|
371
495
|
};
|
|
372
|
-
|
|
496
|
+
const anchoredRollout = anchor.version === 3
|
|
497
|
+
? acceptedCandidate?.rollout
|
|
498
|
+
: anchor.rollout;
|
|
499
|
+
if (anchoredRollout && !sameRolloutIdentity(anchoredRollout, rollout)) {
|
|
373
500
|
return codexCompletionFailure("rollout_identity_mismatch", new Error("Codex rollout identity changed after terminal acceptance"), rolloutDiagnostics);
|
|
374
501
|
}
|
|
375
502
|
let opened;
|
|
@@ -397,14 +524,14 @@ export function detectCodexBoundRolloutCompletion(options) {
|
|
|
397
524
|
catch (error) {
|
|
398
525
|
return codexCompletionFailure("invalid_acceptance_evidence", error, rolloutDiagnostics);
|
|
399
526
|
}
|
|
400
|
-
if (before.size <
|
|
527
|
+
if (before.size < scanStartOffset ||
|
|
401
528
|
(evidenceEndOffset !== undefined && before.size < evidenceEndOffset)) {
|
|
402
529
|
return codexCompletionFailure("rollout_truncated", new Error("Codex rollout was truncated after native acceptance"), {
|
|
403
530
|
...rolloutDiagnostics,
|
|
404
531
|
observed_end_offset_bytes: before.size
|
|
405
532
|
});
|
|
406
533
|
}
|
|
407
|
-
const bytesToRead = before.size -
|
|
534
|
+
const bytesToRead = before.size - scanStartOffset;
|
|
408
535
|
const observedDiagnostics = {
|
|
409
536
|
...rolloutDiagnostics,
|
|
410
537
|
observed_end_offset_bytes: before.size
|
|
@@ -419,7 +546,7 @@ export function detectCodexBoundRolloutCompletion(options) {
|
|
|
419
546
|
return codexCompletionPending("partial_rollout_record", observedDiagnostics, "the exact Codex rollout ends with a partial JSONL record");
|
|
420
547
|
}
|
|
421
548
|
const buffer = Buffer.allocUnsafe(bytesToRead);
|
|
422
|
-
const bytesRead = fs.readSync(opened.fd, buffer, 0, bytesToRead,
|
|
549
|
+
const bytesRead = fs.readSync(opened.fd, buffer, 0, bytesToRead, scanStartOffset);
|
|
423
550
|
const after = fs.fstatSync(opened.fd);
|
|
424
551
|
if (bytesRead !== bytesToRead || !sameStableFile(before, after)) {
|
|
425
552
|
return codexCompletionPending("rollout_changed_during_scan", observedDiagnostics, "the exact Codex rollout changed while completion was scanned");
|
|
@@ -449,7 +576,7 @@ export function detectCodexBoundRolloutCompletion(options) {
|
|
|
449
576
|
native_thread_id: expectedNativeThreadId,
|
|
450
577
|
anchor_fingerprint: anchor.anchor_fingerprint,
|
|
451
578
|
rollout_identity_fingerprint: rolloutDiagnostics.rollout_identity_fingerprint,
|
|
452
|
-
scan_start_offset_bytes:
|
|
579
|
+
scan_start_offset_bytes: scanStartOffset,
|
|
453
580
|
observed_end_offset_bytes: before.size,
|
|
454
581
|
scanned_records: scan.scannedRecords,
|
|
455
582
|
observed_task_complete_records: scan.observedTaskCompleteRecords
|
|
@@ -661,6 +788,78 @@ function acceptedCodexTurnFromSuffix(text, requestHash) {
|
|
|
661
788
|
}
|
|
662
789
|
return matches[0];
|
|
663
790
|
}
|
|
791
|
+
function scanCodexRolloutAcceptance({ rollout: rolloutValue, nativeThreadId: nativeThreadIdValue, processUuid: processUuidValue, processBirth: processBirthValue, requestHash, anchorFingerprint, offsetBytes, requireFreshHeader, capturedAt }) {
|
|
792
|
+
const rollout = normalizedRolloutIdentity(rolloutValue);
|
|
793
|
+
const nativeThreadId = exactNativeThreadId(nativeThreadIdValue);
|
|
794
|
+
requiredString(processUuidValue, "Codex process UUID");
|
|
795
|
+
requiredString(processBirthValue, "Codex process birth");
|
|
796
|
+
if (!Number.isSafeInteger(offsetBytes) || offsetBytes < 0) {
|
|
797
|
+
throw new Error("Codex rollout acceptance byte offset is invalid");
|
|
798
|
+
}
|
|
799
|
+
const opened = openExactRollout(rollout);
|
|
800
|
+
try {
|
|
801
|
+
const before = opened.stat;
|
|
802
|
+
assertPrivateRegularFile(before);
|
|
803
|
+
if (before.size < offsetBytes) {
|
|
804
|
+
throw new Error("Codex rollout was truncated after terminal submission");
|
|
805
|
+
}
|
|
806
|
+
const bytesToRead = before.size - offsetBytes;
|
|
807
|
+
if (bytesToRead === 0) {
|
|
808
|
+
return { status: "pending" };
|
|
809
|
+
}
|
|
810
|
+
if (bytesToRead > CODEX_ACCEPTANCE_MAX_BYTES) {
|
|
811
|
+
throw new Error("Codex rollout acceptance suffix exceeded the bounded read limit");
|
|
812
|
+
}
|
|
813
|
+
if (!fileEndsWithNewline(opened.fd, before.size)) {
|
|
814
|
+
return { status: "incomplete" };
|
|
815
|
+
}
|
|
816
|
+
const buffer = Buffer.allocUnsafe(bytesToRead);
|
|
817
|
+
const bytesRead = fs.readSync(opened.fd, buffer, 0, bytesToRead, offsetBytes);
|
|
818
|
+
if (bytesRead !== bytesToRead) {
|
|
819
|
+
return { status: "incomplete" };
|
|
820
|
+
}
|
|
821
|
+
const after = fs.fstatSync(opened.fd);
|
|
822
|
+
if (!sameStableFile(before, after)) {
|
|
823
|
+
return { status: "incomplete" };
|
|
824
|
+
}
|
|
825
|
+
const suffix = buffer.toString("utf8");
|
|
826
|
+
if (requireFreshHeader) {
|
|
827
|
+
assertVirginRolloutHeader({
|
|
828
|
+
text: suffix,
|
|
829
|
+
nativeThreadId,
|
|
830
|
+
capturedAt
|
|
831
|
+
});
|
|
832
|
+
}
|
|
833
|
+
const accepted = acceptedCodexTurnFromSuffix(suffix, requestHash);
|
|
834
|
+
if (!accepted) {
|
|
835
|
+
return { status: "pending" };
|
|
836
|
+
}
|
|
837
|
+
const evidenceBase = {
|
|
838
|
+
source: "codex_rollout",
|
|
839
|
+
kind: "native_user_turn",
|
|
840
|
+
nativeThreadId,
|
|
841
|
+
requestHash,
|
|
842
|
+
acceptanceId: accepted.turnId,
|
|
843
|
+
acceptedAt: accepted.userTimestamp ?? accepted.startedAt,
|
|
844
|
+
anchorFingerprint,
|
|
845
|
+
metadata: {
|
|
846
|
+
turn_id: accepted.turnId,
|
|
847
|
+
anchor_offset_bytes: offsetBytes,
|
|
848
|
+
observed_end_offset_bytes: before.size
|
|
849
|
+
}
|
|
850
|
+
};
|
|
851
|
+
return {
|
|
852
|
+
status: "accepted",
|
|
853
|
+
evidence: {
|
|
854
|
+
...evidenceBase,
|
|
855
|
+
evidenceFingerprint: fingerprint(evidenceBase)
|
|
856
|
+
}
|
|
857
|
+
};
|
|
858
|
+
}
|
|
859
|
+
finally {
|
|
860
|
+
fs.closeSync(opened.fd);
|
|
861
|
+
}
|
|
862
|
+
}
|
|
664
863
|
function exactCodexUserResponseText(content) {
|
|
665
864
|
if (!Array.isArray(content) || content.length !== 1) {
|
|
666
865
|
return undefined;
|
|
@@ -699,11 +898,11 @@ function assertVirginRolloutHeader(options) {
|
|
|
699
898
|
throw new Error("virgin Codex rollout predates its terminal submission anchor");
|
|
700
899
|
}
|
|
701
900
|
}
|
|
702
|
-
function validateCodexRolloutAcceptanceAnchor(value) {
|
|
901
|
+
export function validateCodexRolloutAcceptanceAnchor(value) {
|
|
703
902
|
if (!isRecord(value) ||
|
|
704
903
|
value.schema !== "agent-knock-knock/codex-rollout-acceptance-anchor" ||
|
|
705
|
-
(value.version !== 1 && value.version !== 2) ||
|
|
706
|
-
!["existing", "pre_materialization"].includes(String(value.mode)) ||
|
|
904
|
+
(value.version !== 1 && value.version !== 2 && value.version !== 3) ||
|
|
905
|
+
!["existing", "pre_materialization", "candidate_set"].includes(String(value.mode)) ||
|
|
707
906
|
!Number.isSafeInteger(value.offset_bytes) ||
|
|
708
907
|
value.offset_bytes < 0 ||
|
|
709
908
|
typeof value.file_existed !== "boolean") {
|
|
@@ -715,11 +914,27 @@ function validateCodexRolloutAcceptanceAnchor(value) {
|
|
|
715
914
|
throw new Error("bound Codex acceptance anchor has deferred binding state");
|
|
716
915
|
}
|
|
717
916
|
}
|
|
718
|
-
else if (value.mode !== "pre_materialization" ||
|
|
917
|
+
else if (value.version === 2 && (value.mode !== "pre_materialization" ||
|
|
719
918
|
value.native_thread_binding !== "post_submission" ||
|
|
720
|
-
"native_thread_id" in value) {
|
|
919
|
+
"native_thread_id" in value)) {
|
|
721
920
|
throw new Error("virgin Codex acceptance anchor is inconsistent");
|
|
722
921
|
}
|
|
922
|
+
else if (value.version === 3 && (value.mode !== "candidate_set" ||
|
|
923
|
+
value.native_thread_binding !== "post_submission" ||
|
|
924
|
+
"native_thread_id" in value ||
|
|
925
|
+
value.file_existed !== false ||
|
|
926
|
+
value.offset_bytes !== 0 ||
|
|
927
|
+
typeof value.zero_file_baseline !== "boolean" ||
|
|
928
|
+
!Number.isSafeInteger(value.inventory_pid) ||
|
|
929
|
+
value.inventory_pid <= 1 ||
|
|
930
|
+
(value.inventory_cwd !== undefined &&
|
|
931
|
+
!path.isAbsolute(value.inventory_cwd)) ||
|
|
932
|
+
!/^[0-9a-f]{64}$/u.test(String(value.inventory_fingerprint)) ||
|
|
933
|
+
!Array.isArray(value.candidate_rollouts) ||
|
|
934
|
+
value.candidate_rollouts.length > 128 ||
|
|
935
|
+
value.zero_file_baseline !== (value.candidate_rollouts.length === 0))) {
|
|
936
|
+
throw new Error("candidate-set Codex acceptance anchor is inconsistent");
|
|
937
|
+
}
|
|
723
938
|
requiredString(value.process_uuid, "Codex process UUID");
|
|
724
939
|
requiredString(value.process_birth, "Codex process birth");
|
|
725
940
|
if (!validTimestamp(value.captured_at)) {
|
|
@@ -730,6 +945,26 @@ function validateCodexRolloutAcceptanceAnchor(value) {
|
|
|
730
945
|
if (fingerprint(base) !== value.anchor_fingerprint) {
|
|
731
946
|
throw new Error("Codex rollout acceptance anchor fingerprint does not match");
|
|
732
947
|
}
|
|
948
|
+
if (value.version === 3) {
|
|
949
|
+
const seenThreads = new Set();
|
|
950
|
+
const seenFiles = new Set();
|
|
951
|
+
for (const candidate of value.candidate_rollouts) {
|
|
952
|
+
if (!isRecord(candidate)) {
|
|
953
|
+
throw new Error("Codex candidate-set rollout entry is invalid");
|
|
954
|
+
}
|
|
955
|
+
const nativeThreadId = exactNativeThreadId(candidate.native_thread_id);
|
|
956
|
+
const rollout = normalizedRolloutIdentity(candidate.rollout);
|
|
957
|
+
if (!Number.isSafeInteger(candidate.offset_bytes) ||
|
|
958
|
+
candidate.offset_bytes < 0 ||
|
|
959
|
+
seenThreads.has(nativeThreadId) ||
|
|
960
|
+
seenFiles.has(`${rollout.device}:${rollout.inode}`)) {
|
|
961
|
+
throw new Error("Codex candidate-set rollout entries are ambiguous");
|
|
962
|
+
}
|
|
963
|
+
seenThreads.add(nativeThreadId);
|
|
964
|
+
seenFiles.add(`${rollout.device}:${rollout.inode}`);
|
|
965
|
+
}
|
|
966
|
+
return value;
|
|
967
|
+
}
|
|
733
968
|
const rollout = "rollout" in value ? value.rollout : undefined;
|
|
734
969
|
if (value.file_existed !== (value.mode === "existing") ||
|
|
735
970
|
value.file_existed !== Boolean(rollout)) {
|
|
@@ -748,6 +983,61 @@ function validateCodexRolloutAcceptanceAnchor(value) {
|
|
|
748
983
|
}
|
|
749
984
|
return value;
|
|
750
985
|
}
|
|
986
|
+
function validateCodexOpenRootInventoryForAcceptance(value) {
|
|
987
|
+
if (!isRecord(value) ||
|
|
988
|
+
value.schema !==
|
|
989
|
+
"agent-knock-knock/codex-open-root-rollout-inventory" ||
|
|
990
|
+
value.version !== 1 ||
|
|
991
|
+
!["verified_absent", "resolved", "unbound"].includes(String(value.status)) ||
|
|
992
|
+
!Number.isSafeInteger(value.pid) ||
|
|
993
|
+
value.pid <= 1 ||
|
|
994
|
+
!Array.isArray(value.roots) ||
|
|
995
|
+
value.roots.length > 128 ||
|
|
996
|
+
!/^[0-9a-f]{64}$/u.test(String(value.inventoryFingerprint))) {
|
|
997
|
+
throw new Error("Codex open-root rollout inventory is invalid");
|
|
998
|
+
}
|
|
999
|
+
requiredString(value.processUuid, "Codex process UUID");
|
|
1000
|
+
requiredString(value.processBirth, "Codex process birth");
|
|
1001
|
+
if (value.processUuid !==
|
|
1002
|
+
`codex-pid:${value.pid}:birth:${value.processBirth}`) {
|
|
1003
|
+
throw new Error("Codex open-root rollout inventory process UUID is inconsistent");
|
|
1004
|
+
}
|
|
1005
|
+
if (value.cwd !== undefined && !path.isAbsolute(value.cwd)) {
|
|
1006
|
+
throw new Error("Codex open-root rollout inventory cwd is not absolute");
|
|
1007
|
+
}
|
|
1008
|
+
if ((value.status === "verified_absent" && value.roots.length !== 0) ||
|
|
1009
|
+
(value.status === "resolved" && value.roots.length !== 1) ||
|
|
1010
|
+
(value.status === "unbound" &&
|
|
1011
|
+
(value.roots.length < 2 ||
|
|
1012
|
+
value.reason !== "multiple_open_root_rollouts"))) {
|
|
1013
|
+
throw new Error("Codex open-root rollout inventory status is inconsistent");
|
|
1014
|
+
}
|
|
1015
|
+
const seenThreads = new Set();
|
|
1016
|
+
const seenFiles = new Set();
|
|
1017
|
+
for (const identity of value.roots) {
|
|
1018
|
+
if (!isRecord(identity)) {
|
|
1019
|
+
throw new Error("Codex open-root rollout inventory identity is invalid");
|
|
1020
|
+
}
|
|
1021
|
+
const nativeThreadId = exactNativeThreadId(identity.sessionId);
|
|
1022
|
+
if (identity.processUuid !== value.processUuid ||
|
|
1023
|
+
identity.processBirth !== value.processBirth ||
|
|
1024
|
+
identity.evidence !== "codex_open_root_rollout") {
|
|
1025
|
+
throw new Error("Codex open-root rollout inventory has mixed process authority");
|
|
1026
|
+
}
|
|
1027
|
+
const rollout = normalizedRolloutIdentity(identity.rollout);
|
|
1028
|
+
if (seenThreads.has(nativeThreadId) ||
|
|
1029
|
+
seenFiles.has(`${rollout.device}:${rollout.inode}`)) {
|
|
1030
|
+
throw new Error("Codex open-root rollout inventory is ambiguous");
|
|
1031
|
+
}
|
|
1032
|
+
seenThreads.add(nativeThreadId);
|
|
1033
|
+
seenFiles.add(`${rollout.device}:${rollout.inode}`);
|
|
1034
|
+
}
|
|
1035
|
+
const { status: _status, inventoryFingerprint, reason: _reason, ...authority } = value;
|
|
1036
|
+
if (fingerprint(authority) !== inventoryFingerprint) {
|
|
1037
|
+
throw new Error("Codex open-root rollout inventory fingerprint does not match");
|
|
1038
|
+
}
|
|
1039
|
+
return value;
|
|
1040
|
+
}
|
|
751
1041
|
function openExactRollout(rollout) {
|
|
752
1042
|
if (!path.isAbsolute(rollout.path)) {
|
|
753
1043
|
throw new Error("Codex rollout path is not absolute");
|