@scotthuang/agent-knock-knock 0.12.13 → 0.12.14
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 +18 -0
- package/README.md +5 -5
- package/dist/src/callback-cli-adapter.d.ts +9 -6
- package/dist/src/callback-cli-adapter.js +119 -30
- package/dist/src/callback-cli-adapter.js.map +1 -1
- package/dist/src/callback-outbox-policy.d.ts +39 -0
- package/dist/src/callback-outbox-policy.js +163 -6
- package/dist/src/callback-outbox-policy.js.map +1 -1
- package/dist/src/callback-outbox-service.d.ts +28 -1
- package/dist/src/callback-outbox-service.js +413 -82
- package/dist/src/callback-outbox-service.js.map +1 -1
- package/dist/src/callback-outbox-settlement.d.ts +14 -1
- package/dist/src/callback-outbox-settlement.js +151 -30
- package/dist/src/callback-outbox-settlement.js.map +1 -1
- package/dist/src/callback-route-authority.d.ts +59 -0
- package/dist/src/callback-route-authority.js +123 -0
- package/dist/src/callback-route-authority.js.map +1 -0
- package/dist/src/callback-transport.d.ts +134 -0
- package/dist/src/callback-transport.js +256 -0
- package/dist/src/callback-transport.js.map +1 -0
- package/dist/src/cli-core.js +17 -4
- package/dist/src/cli-core.js.map +1 -1
- package/dist/src/codex-lifecycle-compatibility.js +2 -1
- package/dist/src/codex-lifecycle-compatibility.js.map +1 -1
- package/dist/src/deferred-foreground-application-service.d.ts +18 -0
- package/dist/src/deferred-foreground-application-service.js +72 -0
- package/dist/src/deferred-foreground-application-service.js.map +1 -1
- package/dist/src/deferred-foreground-authority-cli-adapter.js +2 -1
- package/dist/src/deferred-foreground-authority-cli-adapter.js.map +1 -1
- package/dist/src/deferred-foreground-recovery-cli-adapter.d.ts +1 -1
- package/dist/src/deferred-foreground-recovery-cli-adapter.js +108 -37
- package/dist/src/deferred-foreground-recovery-cli-adapter.js.map +1 -1
- package/dist/src/deferred-foreground-recovery-service.d.ts +1 -1
- package/dist/src/deferred-foreground-recovery-service.js +10 -3
- package/dist/src/deferred-foreground-recovery-service.js.map +1 -1
- package/dist/src/deferred-foreground-transfer-policy.d.ts +1 -1
- package/dist/src/deferred-foreground-transfer-policy.js +3 -2
- package/dist/src/deferred-foreground-transfer-policy.js.map +1 -1
- package/dist/src/deferred-foreground-transfer.d.ts +17 -1
- package/dist/src/deferred-foreground-transfer.js +244 -35
- package/dist/src/deferred-foreground-transfer.js.map +1 -1
- package/dist/src/deferred-foreground-user-close.d.ts +18 -0
- package/dist/src/deferred-foreground-user-close.js +180 -0
- package/dist/src/deferred-foreground-user-close.js.map +1 -0
- package/dist/src/openclaw-callback-transport.d.ts +4 -1
- package/dist/src/openclaw-callback-transport.js +403 -10
- package/dist/src/openclaw-callback-transport.js.map +1 -1
- package/dist/src/openclaw-managed-callback-cli-adapter.d.ts +23 -0
- package/dist/src/openclaw-managed-callback-cli-adapter.js +77 -0
- package/dist/src/openclaw-managed-callback-cli-adapter.js.map +1 -0
- package/dist/src/openclaw-plugin-command-adapter.js +28 -31
- package/dist/src/openclaw-plugin-command-adapter.js.map +1 -1
- package/dist/src/openclaw-plugin-helpers.js +8 -0
- package/dist/src/openclaw-plugin-helpers.js.map +1 -1
- package/dist/src/openclaw-plugin-schemas.d.ts +20 -1
- package/dist/src/openclaw-plugin-schemas.js +26 -2
- package/dist/src/openclaw-plugin-schemas.js.map +1 -1
- package/dist/src/protocol.d.ts +3 -0
- package/dist/src/protocol.js +4 -0
- package/dist/src/protocol.js.map +1 -1
- package/dist/src/store.d.ts +9 -0
- package/dist/src/store.js +79 -1
- package/dist/src/store.js.map +1 -1
- package/dist/src/terminal-acceptance-cli-adapter.d.ts +2 -0
- package/dist/src/terminal-acceptance-cli-adapter.js +62 -12
- package/dist/src/terminal-acceptance-cli-adapter.js.map +1 -1
- package/dist/src/terminal-action-projection.d.ts +1 -2
- package/dist/src/terminal-action-projection.js +4 -5
- package/dist/src/terminal-action-projection.js.map +1 -1
- package/dist/src/terminal-agent-bridge.d.ts +82 -0
- package/dist/src/terminal-agent-bridge.js +539 -111
- package/dist/src/terminal-agent-bridge.js.map +1 -1
- package/dist/src/terminal-command-cli-adapter.d.ts +9 -0
- package/dist/src/terminal-command-cli-adapter.js +2130 -5
- package/dist/src/terminal-command-cli-adapter.js.map +1 -1
- package/dist/src/terminal-dispatch-application.d.ts +4 -1
- package/dist/src/terminal-dispatch-application.js +11 -2
- package/dist/src/terminal-dispatch-application.js.map +1 -1
- package/dist/src/terminal-dispatch-execution.js +3 -1
- package/dist/src/terminal-dispatch-execution.js.map +1 -1
- package/dist/src/terminal-dispatch-ledger-codec.d.ts +3 -0
- package/dist/src/terminal-dispatch-ledger-codec.js +46 -19
- package/dist/src/terminal-dispatch-ledger-codec.js.map +1 -1
- package/dist/src/terminal-dispatch-presenter.js +5 -3
- package/dist/src/terminal-dispatch-presenter.js.map +1 -1
- package/dist/src/terminal-dispatch-receipt.d.ts +3 -0
- package/dist/src/terminal-dispatch-receipt.js +15 -1
- package/dist/src/terminal-dispatch-receipt.js.map +1 -1
- package/dist/src/terminal-dispatch-recovery-cli-adapter.js +68 -7
- package/dist/src/terminal-dispatch-recovery-cli-adapter.js.map +1 -1
- package/dist/src/terminal-dispatch-recovery-service.d.ts +2 -0
- package/dist/src/terminal-dispatch-recovery-service.js +9 -0
- package/dist/src/terminal-dispatch-recovery-service.js.map +1 -1
- package/dist/src/terminal-identity-authority-cli-adapter.d.ts +2 -0
- package/dist/src/terminal-identity-authority-cli-adapter.js +30 -27
- package/dist/src/terminal-identity-authority-cli-adapter.js.map +1 -1
- package/dist/src/terminal-list-cli-adapter.d.ts +1 -0
- package/dist/src/terminal-list-cli-adapter.js +50 -31
- package/dist/src/terminal-list-cli-adapter.js.map +1 -1
- package/dist/src/terminal-list-renderer.d.ts +9 -1
- package/dist/src/terminal-list-renderer.js +219 -98
- package/dist/src/terminal-list-renderer.js.map +1 -1
- package/dist/src/terminal-maintenance-cli-adapter.js +253 -326
- package/dist/src/terminal-maintenance-cli-adapter.js.map +1 -1
- package/dist/src/terminal-monitor-application-service.d.ts +5 -0
- package/dist/src/terminal-monitor-application-service.js +5 -0
- package/dist/src/terminal-monitor-application-service.js.map +1 -1
- package/dist/src/terminal-monitor-cli-adapter.d.ts +25 -1
- package/dist/src/terminal-monitor-cli-adapter.js +170 -27
- package/dist/src/terminal-monitor-cli-adapter.js.map +1 -1
- package/dist/src/terminal-monitor-reconciliation-eligibility.d.ts +1 -1
- package/dist/src/terminal-monitor-reconciliation-eligibility.js +220 -32
- package/dist/src/terminal-monitor-reconciliation-eligibility.js.map +1 -1
- package/dist/src/terminal-monitor-state-cli-adapter.d.ts +1 -1
- package/dist/src/terminal-monitor-state-cli-adapter.js +808 -147
- package/dist/src/terminal-monitor-state-cli-adapter.js.map +1 -1
- package/dist/src/terminal-monitor-state-reconciliation-service.d.ts +2 -1
- package/dist/src/terminal-monitor-state-reconciliation-service.js +12 -0
- package/dist/src/terminal-monitor-state-reconciliation-service.js.map +1 -1
- package/dist/src/terminal-monitor-supervision-cli-adapter.d.ts +2 -0
- package/dist/src/terminal-monitor-supervision-cli-adapter.js +31 -11
- package/dist/src/terminal-monitor-supervision-cli-adapter.js.map +1 -1
- package/dist/src/terminal-status-cli-adapter.d.ts +1 -0
- package/dist/src/terminal-status-cli-adapter.js +30 -28
- package/dist/src/terminal-status-cli-adapter.js.map +1 -1
- package/dist/src/terminal-submission-acceptance.js +78 -25
- package/dist/src/terminal-submission-acceptance.js.map +1 -1
- package/dist/src/terminal-submission-retry-service.d.ts +114 -0
- package/dist/src/terminal-submission-retry-service.js +589 -0
- package/dist/src/terminal-submission-retry-service.js.map +1 -0
- package/dist/src/terminal-watch-callback-cli-adapter.d.ts +10 -2
- package/dist/src/terminal-watch-callback-cli-adapter.js +76 -37
- package/dist/src/terminal-watch-callback-cli-adapter.js.map +1 -1
- package/dist/src/terminal-watch-cli-adapter.js +41 -17
- package/dist/src/terminal-watch-cli-adapter.js.map +1 -1
- package/dist/src/terminal-watch-service.d.ts +11 -6
- package/dist/src/terminal-watch-service.js +210 -41
- package/dist/src/terminal-watch-service.js.map +1 -1
- package/dist/src/terminal-watch-store.d.ts +23 -0
- package/dist/src/terminal-watch-store.js +162 -4
- package/dist/src/terminal-watch-store.js.map +1 -1
- package/docs/quickstart-tmux.md +1 -1
- package/package.json +1 -1
- package/templates/openclaw-skills/agent-knock-knock/SKILL.md +8 -5
|
@@ -10,6 +10,39 @@ export const DEFERRED_FOREGROUND_TRANSFER_SCHEMA = "agent-knock-knock/deferred-f
|
|
|
10
10
|
export const DEFERRED_FOREGROUND_TRANSFER_VERSION = 2;
|
|
11
11
|
export const DEFERRED_FOREGROUND_TRANSFER_LEGACY_VERSION = 1;
|
|
12
12
|
const TRANSFER_STATE_FILE = "state.json";
|
|
13
|
+
export function deferredForegroundActiveMessageId(transfer) {
|
|
14
|
+
return transfer.submission_retry_message_id ?? transfer.message_id;
|
|
15
|
+
}
|
|
16
|
+
export function deferredForegroundActivePreparedAt(transfer) {
|
|
17
|
+
return transfer.submission_retry_prepared_at ?? transfer.prepared_at;
|
|
18
|
+
}
|
|
19
|
+
export function isDeferredForegroundSubmissionRetryPending(transfer) {
|
|
20
|
+
const common = transfer.status === "uncertain" &&
|
|
21
|
+
transfer.input_stage === "enter_dispatched" &&
|
|
22
|
+
Boolean(transfer.submission_retry_attempt_id) &&
|
|
23
|
+
Boolean(transfer.submission_retry_mode) &&
|
|
24
|
+
Boolean(transfer.submission_retry_message_id) &&
|
|
25
|
+
Boolean(transfer.submission_retry_prepared_at) &&
|
|
26
|
+
Boolean(transfer.submission_retry_enter_reserved_at) &&
|
|
27
|
+
Boolean(transfer.submission_retry_enter_dispatched_at) &&
|
|
28
|
+
deferredForegroundActiveMessageId(transfer) ===
|
|
29
|
+
transfer.submission_retry_message_id;
|
|
30
|
+
return common && (transfer.submission_retry_mode === "exact_draft_enter"
|
|
31
|
+
? transfer.submission_retry_message_id === transfer.message_id &&
|
|
32
|
+
transfer.submission_retry_text_reserved_at === undefined &&
|
|
33
|
+
transfer.submission_retry_text_injected_at === undefined &&
|
|
34
|
+
Boolean(transfer.text_injected_at)
|
|
35
|
+
: Boolean(transfer.submission_retry_text_reserved_at) &&
|
|
36
|
+
Boolean(transfer.submission_retry_text_injected_at));
|
|
37
|
+
}
|
|
38
|
+
export function deferredForegroundActiveTextInjectedAt(transfer) {
|
|
39
|
+
return transfer.submission_retry_text_injected_at ??
|
|
40
|
+
transfer.text_injected_at;
|
|
41
|
+
}
|
|
42
|
+
export function deferredForegroundActiveEnterDispatchedAt(transfer) {
|
|
43
|
+
return transfer.submission_retry_enter_dispatched_at ??
|
|
44
|
+
transfer.enter_dispatched_at;
|
|
45
|
+
}
|
|
13
46
|
export class DeferredForegroundTransferConflictError extends Error {
|
|
14
47
|
code = "AKK_DEFERRED_FOREGROUND_TRANSFER_CONFLICT";
|
|
15
48
|
transferId;
|
|
@@ -54,6 +87,12 @@ export function assertDeferredForegroundTransfer(value, expectedTransferId, opti
|
|
|
54
87
|
assertDeferredForegroundTransferResolution(transfer);
|
|
55
88
|
assertDeferredForegroundTransferAbort(transfer, provedTerminalInputNotStarted);
|
|
56
89
|
assertDeferredForegroundTransferFailure(transfer);
|
|
90
|
+
assertDeferredForegroundUserAbandonment(transfer);
|
|
91
|
+
}
|
|
92
|
+
function deferredForegroundEvidenceStatus(value) {
|
|
93
|
+
return value.status === "user_abandoned"
|
|
94
|
+
? value.origin_status ?? value.status
|
|
95
|
+
: value.status;
|
|
57
96
|
}
|
|
58
97
|
function assertDeferredForegroundTransferHeader(value, expectedTransferId, options) {
|
|
59
98
|
assertOnlyKeys(value, [
|
|
@@ -97,6 +136,14 @@ function assertDeferredForegroundTransferHeader(value, expectedTransferId, optio
|
|
|
97
136
|
"enter_dispatched_at",
|
|
98
137
|
"agent_accepted_at",
|
|
99
138
|
"message_id",
|
|
139
|
+
"submission_retry_attempt_id",
|
|
140
|
+
"submission_retry_mode",
|
|
141
|
+
"submission_retry_message_id",
|
|
142
|
+
"submission_retry_prepared_at",
|
|
143
|
+
"submission_retry_text_reserved_at",
|
|
144
|
+
"submission_retry_text_injected_at",
|
|
145
|
+
"submission_retry_enter_reserved_at",
|
|
146
|
+
"submission_retry_enter_dispatched_at",
|
|
100
147
|
"turn_id",
|
|
101
148
|
"state_path",
|
|
102
149
|
"target_native_thread_id",
|
|
@@ -132,7 +179,9 @@ function assertDeferredForegroundTransferHeader(value, expectedTransferId, optio
|
|
|
132
179
|
"uncertain_at",
|
|
133
180
|
"recovered_at",
|
|
134
181
|
"error",
|
|
135
|
-
"do_not_retry"
|
|
182
|
+
"do_not_retry",
|
|
183
|
+
"origin_status",
|
|
184
|
+
"user_abandoned_at"
|
|
136
185
|
], "deferred foreground transfer");
|
|
137
186
|
if (value.schema !== DEFERRED_FOREGROUND_TRANSFER_SCHEMA ||
|
|
138
187
|
![
|
|
@@ -347,6 +396,21 @@ function assertDeferredForegroundTransferTarget(value) {
|
|
|
347
396
|
assertOptionalTimestamp(value[field], `deferred transfer ${field}`);
|
|
348
397
|
}
|
|
349
398
|
assertOptionalNonEmptyString(value.message_id, "deferred transfer message_id");
|
|
399
|
+
assertOptionalNonEmptyString(value.submission_retry_attempt_id, "deferred transfer submission retry attempt id");
|
|
400
|
+
if (value.submission_retry_mode !== undefined &&
|
|
401
|
+
!["exact_draft_enter", "replacement_send"].includes(value.submission_retry_mode)) {
|
|
402
|
+
throw new Error("deferred transfer submission retry mode is invalid");
|
|
403
|
+
}
|
|
404
|
+
assertOptionalNonEmptyString(value.submission_retry_message_id, "deferred transfer submission retry message id");
|
|
405
|
+
for (const field of [
|
|
406
|
+
"submission_retry_prepared_at",
|
|
407
|
+
"submission_retry_text_reserved_at",
|
|
408
|
+
"submission_retry_text_injected_at",
|
|
409
|
+
"submission_retry_enter_reserved_at",
|
|
410
|
+
"submission_retry_enter_dispatched_at"
|
|
411
|
+
]) {
|
|
412
|
+
assertOptionalTimestamp(value[field], `deferred transfer ${field}`);
|
|
413
|
+
}
|
|
350
414
|
assertOptionalNonEmptyString(value.turn_id, "deferred transfer turn_id");
|
|
351
415
|
if (value.state_path !== undefined) {
|
|
352
416
|
assertAbsolutePath(value.state_path, "deferred transfer state_path");
|
|
@@ -357,13 +421,14 @@ function assertDeferredForegroundTransferTarget(value) {
|
|
|
357
421
|
}
|
|
358
422
|
}
|
|
359
423
|
function assertDeferredForegroundTransferPreparation(value) {
|
|
424
|
+
const evidenceStatus = deferredForegroundEvidenceStatus(value);
|
|
360
425
|
const requiresTargetPrepared = [
|
|
361
426
|
"target_prepared",
|
|
362
427
|
"dispatch_started",
|
|
363
428
|
"committed",
|
|
364
429
|
"resolved",
|
|
365
430
|
"uncertain"
|
|
366
|
-
].includes(String(
|
|
431
|
+
].includes(String(evidenceStatus));
|
|
367
432
|
const targetPreparedFields = [
|
|
368
433
|
value.target_prepared_at,
|
|
369
434
|
value.target_prepared_revision,
|
|
@@ -379,7 +444,7 @@ function assertDeferredForegroundTransferPreparation(value) {
|
|
|
379
444
|
const hasAllTargetPreparedFields = targetPreparedFields.every((field) => field !== undefined);
|
|
380
445
|
if (hasAnyTargetPreparedField !== hasAllTargetPreparedFields ||
|
|
381
446
|
(requiresTargetPrepared && !hasAllTargetPreparedFields)) {
|
|
382
|
-
throw new Error(`${String(
|
|
447
|
+
throw new Error(`${String(evidenceStatus)} deferred transfer requires its target/Turn identity`);
|
|
383
448
|
}
|
|
384
449
|
if (hasAllTargetPreparedFields) {
|
|
385
450
|
if (!value.source_reserved_at ||
|
|
@@ -397,45 +462,89 @@ function assertDeferredForegroundTransferPreparation(value) {
|
|
|
397
462
|
"resolved",
|
|
398
463
|
"uncertain"
|
|
399
464
|
]
|
|
400
|
-
.includes(String(
|
|
465
|
+
.includes(String(evidenceStatus)) &&
|
|
401
466
|
!value.source_reserved_at) {
|
|
402
|
-
throw new Error(`${String(
|
|
467
|
+
throw new Error(`${String(evidenceStatus)} deferred transfer requires source_reserved_at`);
|
|
403
468
|
}
|
|
404
|
-
if (
|
|
469
|
+
if (evidenceStatus === "prepared" &&
|
|
405
470
|
(value.source_reserved_at !== undefined || hasAnyTargetPreparedField)) {
|
|
406
471
|
throw new Error("prepared deferred transfer cannot carry reservation evidence");
|
|
407
472
|
}
|
|
408
|
-
if (
|
|
473
|
+
if (evidenceStatus === "source_reserved" && hasAnyTargetPreparedField) {
|
|
409
474
|
throw new Error("source_reserved deferred transfer cannot carry target evidence");
|
|
410
475
|
}
|
|
411
476
|
}
|
|
412
477
|
function assertDeferredForegroundTransferDispatch(value) {
|
|
413
|
-
|
|
478
|
+
const evidenceStatus = deferredForegroundEvidenceStatus(value);
|
|
479
|
+
if (["dispatch_started", "committed", "resolved", "uncertain"].includes(String(evidenceStatus)) &&
|
|
414
480
|
(!value.dispatch_started_at || value.input_stage === "none")) {
|
|
415
|
-
throw new Error(`${String(
|
|
481
|
+
throw new Error(`${String(evidenceStatus)} deferred transfer requires dispatch-start evidence`);
|
|
416
482
|
}
|
|
417
483
|
assertInputStageEvidence(value);
|
|
418
484
|
if ([
|
|
419
485
|
"prepared",
|
|
420
486
|
"source_reserved",
|
|
421
487
|
"target_prepared"
|
|
422
|
-
].includes(String(
|
|
488
|
+
].includes(String(evidenceStatus)) &&
|
|
423
489
|
value.input_stage !== "none") {
|
|
424
|
-
throw new Error(`${String(
|
|
490
|
+
throw new Error(`${String(evidenceStatus)} deferred transfer cannot carry input evidence`);
|
|
425
491
|
}
|
|
426
|
-
const provedTerminalInputNotStarted = ["aborted", "abort_resolved"].includes(String(
|
|
492
|
+
const provedTerminalInputNotStarted = ["aborted", "abort_resolved"].includes(String(evidenceStatus)) &&
|
|
427
493
|
value.input_stage === "dispatch_started" &&
|
|
428
494
|
value.terminal_input_not_started_at !== undefined;
|
|
429
|
-
if (["aborted", "abort_resolved"].includes(String(
|
|
495
|
+
if (["aborted", "abort_resolved"].includes(String(evidenceStatus)) &&
|
|
430
496
|
value.input_stage !== "none" &&
|
|
431
497
|
!provedTerminalInputNotStarted) {
|
|
432
|
-
throw new Error(`${String(
|
|
498
|
+
throw new Error(`${String(evidenceStatus)} deferred transfer requires exact no-input proof`);
|
|
433
499
|
}
|
|
500
|
+
assertDeferredForegroundSubmissionRetryEvidence(value);
|
|
434
501
|
assertTimestampOrder(value);
|
|
435
502
|
return provedTerminalInputNotStarted;
|
|
436
503
|
}
|
|
504
|
+
function assertDeferredForegroundSubmissionRetryEvidence(value) {
|
|
505
|
+
const retryFields = [
|
|
506
|
+
value.submission_retry_attempt_id,
|
|
507
|
+
value.submission_retry_mode,
|
|
508
|
+
value.submission_retry_message_id,
|
|
509
|
+
value.submission_retry_prepared_at
|
|
510
|
+
];
|
|
511
|
+
const hasAny = retryFields.some((field) => field !== undefined) || [
|
|
512
|
+
value.submission_retry_text_reserved_at,
|
|
513
|
+
value.submission_retry_text_injected_at,
|
|
514
|
+
value.submission_retry_enter_reserved_at,
|
|
515
|
+
value.submission_retry_enter_dispatched_at
|
|
516
|
+
].some((field) => field !== undefined);
|
|
517
|
+
if (!hasAny)
|
|
518
|
+
return;
|
|
519
|
+
const hasCommon = retryFields.every((field) => field !== undefined);
|
|
520
|
+
const replacement = value.submission_retry_mode === "replacement_send";
|
|
521
|
+
const exactDraft = value.submission_retry_mode === "exact_draft_enter";
|
|
522
|
+
const replacementPrefixIsContinuous = value.submission_retry_text_injected_at === undefined ||
|
|
523
|
+
value.submission_retry_text_reserved_at !== undefined;
|
|
524
|
+
const enterPrefixIsContinuous = value.submission_retry_enter_reserved_at === undefined ||
|
|
525
|
+
exactDraft || value.submission_retry_text_injected_at !== undefined;
|
|
526
|
+
const dispatchPrefixIsContinuous = value.submission_retry_enter_dispatched_at === undefined ||
|
|
527
|
+
value.submission_retry_enter_reserved_at !== undefined;
|
|
528
|
+
if (!hasCommon ||
|
|
529
|
+
!value.uncertain_at ||
|
|
530
|
+
(!replacement && !exactDraft) ||
|
|
531
|
+
value.submission_retry_message_id !== value.message_id ||
|
|
532
|
+
value.submission_retry_prepared_at !== value.prepared_at ||
|
|
533
|
+
(exactDraft && (value.submission_retry_text_reserved_at !== undefined ||
|
|
534
|
+
value.submission_retry_text_injected_at !== undefined)) ||
|
|
535
|
+
!replacementPrefixIsContinuous ||
|
|
536
|
+
!enterPrefixIsContinuous ||
|
|
537
|
+
!dispatchPrefixIsContinuous ||
|
|
538
|
+
(value.input_stage === "enter_dispatched" &&
|
|
539
|
+
value.submission_retry_enter_dispatched_at === undefined) ||
|
|
540
|
+
(value.submission_retry_enter_dispatched_at !== undefined &&
|
|
541
|
+
!["enter_dispatched", "agent_accepted"].includes(value.input_stage))) {
|
|
542
|
+
throw new Error("deferred foreground submission retry stage evidence is inconsistent");
|
|
543
|
+
}
|
|
544
|
+
}
|
|
437
545
|
function assertDeferredForegroundTransferCommit(value) {
|
|
438
|
-
const
|
|
546
|
+
const evidenceStatus = deferredForegroundEvidenceStatus(value);
|
|
547
|
+
const committed = ["committed", "resolved"].includes(String(evidenceStatus));
|
|
439
548
|
const committedFields = [
|
|
440
549
|
value.target_native_thread_id,
|
|
441
550
|
value.target_accepted_revision,
|
|
@@ -510,6 +619,7 @@ function assertDeferredForegroundTransferCommit(value) {
|
|
|
510
619
|
}
|
|
511
620
|
}
|
|
512
621
|
function assertDeferredForegroundTransferResolution(value) {
|
|
622
|
+
const evidenceStatus = deferredForegroundEvidenceStatus(value);
|
|
513
623
|
const resolvedFields = [
|
|
514
624
|
value.target_after_revision,
|
|
515
625
|
value.target_after_status,
|
|
@@ -522,12 +632,12 @@ function assertDeferredForegroundTransferResolution(value) {
|
|
|
522
632
|
];
|
|
523
633
|
const hasAnyResolvedField = resolvedFields.some((field) => field !== undefined);
|
|
524
634
|
const hasAllResolvedFields = resolvedFields.every((field) => field !== undefined);
|
|
525
|
-
if (
|
|
635
|
+
if (evidenceStatus === "resolved"
|
|
526
636
|
? !hasAllResolvedFields
|
|
527
637
|
: hasAnyResolvedField) {
|
|
528
638
|
throw new Error("resolved deferred transfer requires exact final Session evidence");
|
|
529
639
|
}
|
|
530
|
-
if (
|
|
640
|
+
if (evidenceStatus === "resolved") {
|
|
531
641
|
if (!isPositiveSafeInteger(value.target_after_revision) ||
|
|
532
642
|
value.target_after_revision !==
|
|
533
643
|
Number(value.target_accepted_revision) + 1 ||
|
|
@@ -558,7 +668,8 @@ function assertDeferredForegroundTransferResolution(value) {
|
|
|
558
668
|
}
|
|
559
669
|
}
|
|
560
670
|
function assertDeferredForegroundTransferAbort(value, provedTerminalInputNotStarted) {
|
|
561
|
-
|
|
671
|
+
const evidenceStatus = deferredForegroundEvidenceStatus(value);
|
|
672
|
+
if (["aborted", "abort_resolved"].includes(String(evidenceStatus))
|
|
562
673
|
? !value.aborted_at || !(value.input_stage === "none" || provedTerminalInputNotStarted)
|
|
563
674
|
: value.aborted_at !== undefined) {
|
|
564
675
|
throw new Error("only a proven zero-input deferred transfer can carry an aborted receipt");
|
|
@@ -569,7 +680,7 @@ function assertDeferredForegroundTransferAbort(value, provedTerminalInputNotStar
|
|
|
569
680
|
value.enter_dispatched_at !== undefined ||
|
|
570
681
|
value.agent_accepted_at !== undefined
|
|
571
682
|
: value.input_stage === "dispatch_started" &&
|
|
572
|
-
["aborted", "abort_resolved"].includes(String(
|
|
683
|
+
["aborted", "abort_resolved"].includes(String(evidenceStatus))) {
|
|
573
684
|
throw new Error("terminal-input-not-started proof must fence only a dispatch intent");
|
|
574
685
|
}
|
|
575
686
|
const abortCleanupSourceFields = [
|
|
@@ -589,7 +700,7 @@ function assertDeferredForegroundTransferAbort(value, provedTerminalInputNotStar
|
|
|
589
700
|
...abortCleanupSourceFields,
|
|
590
701
|
...abortCleanupTargetFields
|
|
591
702
|
].some((field) => field !== undefined);
|
|
592
|
-
if (
|
|
703
|
+
if (evidenceStatus === "abort_resolved") {
|
|
593
704
|
if (abortCleanupSourceFields.some((field) => field === undefined)) {
|
|
594
705
|
throw new Error("resolved deferred abort requires exact source cleanup evidence");
|
|
595
706
|
}
|
|
@@ -649,10 +760,11 @@ function assertDeferredForegroundTransferAbort(value, provedTerminalInputNotStar
|
|
|
649
760
|
}
|
|
650
761
|
}
|
|
651
762
|
function assertDeferredForegroundTransferFailure(value) {
|
|
652
|
-
|
|
763
|
+
const evidenceStatus = deferredForegroundEvidenceStatus(value);
|
|
764
|
+
if (evidenceStatus === "uncertain"
|
|
653
765
|
? !value.uncertain_at || value.do_not_retry !== true ||
|
|
654
766
|
value.input_stage === "none"
|
|
655
|
-
: ["committed", "resolved"].includes(String(
|
|
767
|
+
: ["committed", "resolved"].includes(String(evidenceStatus)) &&
|
|
656
768
|
value.uncertain_at !== undefined
|
|
657
769
|
? !value.recovered_at || value.do_not_retry !== true
|
|
658
770
|
: value.uncertain_at !== undefined ||
|
|
@@ -666,10 +778,25 @@ function assertDeferredForegroundTransferFailure(value) {
|
|
|
666
778
|
"abort_resolved",
|
|
667
779
|
"uncertain",
|
|
668
780
|
...(value.recovered_at ? ["committed", "resolved"] : [])
|
|
669
|
-
].includes(String(
|
|
781
|
+
].includes(String(evidenceStatus))) {
|
|
670
782
|
throw new Error("deferred transfer error is allowed only on failure/recovery receipts");
|
|
671
783
|
}
|
|
672
784
|
}
|
|
785
|
+
function assertDeferredForegroundUserAbandonment(value) {
|
|
786
|
+
const origin = value.origin_status;
|
|
787
|
+
const abandonedAt = value.user_abandoned_at;
|
|
788
|
+
if (value.status !== "user_abandoned") {
|
|
789
|
+
if (origin !== undefined || abandonedAt !== undefined) {
|
|
790
|
+
throw new Error("user abandonment evidence is allowed only on a user_abandoned receipt");
|
|
791
|
+
}
|
|
792
|
+
return;
|
|
793
|
+
}
|
|
794
|
+
if (!USER_ABANDONMENT_ORIGIN_STATUSES.has(origin) ||
|
|
795
|
+
abandonedAt === undefined) {
|
|
796
|
+
throw new Error("user_abandoned deferred transfer requires its origin status and timestamp");
|
|
797
|
+
}
|
|
798
|
+
assertTimestamp(abandonedAt, "deferred transfer user_abandoned_at");
|
|
799
|
+
}
|
|
673
800
|
export function saveDeferredForegroundTransfer(storeDir, value, options) {
|
|
674
801
|
assertDeferredForegroundTransfer(value, undefined, {
|
|
675
802
|
allowMissingRevision: true
|
|
@@ -773,20 +900,24 @@ function assertTransferAdvance(current, candidate) {
|
|
|
773
900
|
`immutable ${changed}`);
|
|
774
901
|
}
|
|
775
902
|
const allowed = {
|
|
776
|
-
prepared: ["source_reserved", "aborted"],
|
|
777
|
-
source_reserved: ["target_prepared", "aborted"],
|
|
778
|
-
target_prepared: [
|
|
903
|
+
prepared: ["source_reserved", "aborted", "user_abandoned"],
|
|
904
|
+
source_reserved: ["target_prepared", "aborted", "user_abandoned"],
|
|
905
|
+
target_prepared: [
|
|
906
|
+
"dispatch_started", "uncertain", "aborted", "user_abandoned"
|
|
907
|
+
],
|
|
779
908
|
dispatch_started: [
|
|
780
909
|
"dispatch_started",
|
|
781
910
|
"committed",
|
|
782
911
|
"uncertain",
|
|
783
|
-
"aborted"
|
|
912
|
+
"aborted",
|
|
913
|
+
"user_abandoned"
|
|
784
914
|
],
|
|
785
|
-
committed: ["resolved"],
|
|
915
|
+
committed: ["resolved", "user_abandoned"],
|
|
786
916
|
resolved: [],
|
|
787
|
-
aborted: ["abort_resolved"],
|
|
917
|
+
aborted: ["abort_resolved", "user_abandoned"],
|
|
788
918
|
abort_resolved: [],
|
|
789
|
-
uncertain: ["committed"]
|
|
919
|
+
uncertain: ["uncertain", "committed", "user_abandoned"],
|
|
920
|
+
user_abandoned: []
|
|
790
921
|
};
|
|
791
922
|
if (!allowed[current.status].includes(candidate.status)) {
|
|
792
923
|
throw new Error(`deferred foreground transfer cannot move from ${current.status} to ` +
|
|
@@ -811,6 +942,21 @@ function assertTransferAdvance(current, candidate) {
|
|
|
811
942
|
throw new Error(`deferred foreground transfer ${current.transfer_id} cannot change ${field}`);
|
|
812
943
|
}
|
|
813
944
|
}
|
|
945
|
+
for (const field of [
|
|
946
|
+
"submission_retry_attempt_id",
|
|
947
|
+
"submission_retry_mode",
|
|
948
|
+
"submission_retry_message_id",
|
|
949
|
+
"submission_retry_prepared_at",
|
|
950
|
+
"submission_retry_text_reserved_at",
|
|
951
|
+
"submission_retry_text_injected_at",
|
|
952
|
+
"submission_retry_enter_reserved_at",
|
|
953
|
+
"submission_retry_enter_dispatched_at"
|
|
954
|
+
]) {
|
|
955
|
+
if (current[field] !== undefined &&
|
|
956
|
+
JSON.stringify(current[field]) !== JSON.stringify(candidate[field])) {
|
|
957
|
+
throw new Error(`deferred foreground transfer ${current.transfer_id} cannot change ${field}`);
|
|
958
|
+
}
|
|
959
|
+
}
|
|
814
960
|
for (const field of [
|
|
815
961
|
"source_reserved_at",
|
|
816
962
|
"target_prepared_at",
|
|
@@ -851,13 +997,19 @@ function assertTransferAdvance(current, candidate) {
|
|
|
851
997
|
"uncertain_at",
|
|
852
998
|
"recovered_at",
|
|
853
999
|
"error",
|
|
854
|
-
"do_not_retry"
|
|
1000
|
+
"do_not_retry",
|
|
1001
|
+
"origin_status",
|
|
1002
|
+
"user_abandoned_at"
|
|
855
1003
|
]) {
|
|
856
1004
|
if (current[field] !== undefined &&
|
|
857
1005
|
JSON.stringify(current[field]) !== JSON.stringify(candidate[field])) {
|
|
858
1006
|
throw new Error(`deferred foreground transfer ${current.transfer_id} cannot change ${field}`);
|
|
859
1007
|
}
|
|
860
1008
|
}
|
|
1009
|
+
if (candidate.status === "user_abandoned" &&
|
|
1010
|
+
candidate.origin_status !== current.status) {
|
|
1011
|
+
throw new Error("deferred foreground user abandonment must freeze its origin status");
|
|
1012
|
+
}
|
|
861
1013
|
if (candidate.status === "aborted" &&
|
|
862
1014
|
current.input_stage !== "none" &&
|
|
863
1015
|
!(current.input_stage === "dispatch_started" &&
|
|
@@ -865,6 +1017,37 @@ function assertTransferAdvance(current, candidate) {
|
|
|
865
1017
|
candidate.terminal_input_not_started_at !== undefined)) {
|
|
866
1018
|
throw new Error("deferred foreground transfer cannot abort after input may start");
|
|
867
1019
|
}
|
|
1020
|
+
if (current.submission_retry_attempt_id === undefined &&
|
|
1021
|
+
candidate.submission_retry_attempt_id !== undefined &&
|
|
1022
|
+
(current.status !== "uncertain" ||
|
|
1023
|
+
candidate.status !== "uncertain" ||
|
|
1024
|
+
current.input_stage !== "text_injected" ||
|
|
1025
|
+
!candidate.submission_retry_mode ||
|
|
1026
|
+
!candidate.submission_retry_message_id ||
|
|
1027
|
+
!candidate.submission_retry_prepared_at)) {
|
|
1028
|
+
throw new Error("deferred foreground submission retry requires uncertain/text_injected authority");
|
|
1029
|
+
}
|
|
1030
|
+
if (candidate.submission_retry_attempt_id !== undefined &&
|
|
1031
|
+
(!candidate.submission_retry_message_id ||
|
|
1032
|
+
!candidate.submission_retry_prepared_at ||
|
|
1033
|
+
!candidate.submission_retry_mode ||
|
|
1034
|
+
!candidate.uncertain_at ||
|
|
1035
|
+
candidate.submission_retry_message_id !== candidate.message_id ||
|
|
1036
|
+
candidate.submission_retry_prepared_at !== candidate.prepared_at ||
|
|
1037
|
+
(candidate.submission_retry_mode === "replacement_send"
|
|
1038
|
+
? (candidate.submission_retry_text_injected_at !== undefined &&
|
|
1039
|
+
!candidate.submission_retry_text_reserved_at) ||
|
|
1040
|
+
(candidate.submission_retry_enter_reserved_at !== undefined &&
|
|
1041
|
+
!candidate.submission_retry_text_injected_at)
|
|
1042
|
+
: candidate.submission_retry_message_id !== candidate.message_id ||
|
|
1043
|
+
candidate.submission_retry_text_reserved_at !== undefined ||
|
|
1044
|
+
candidate.submission_retry_text_injected_at !== undefined) ||
|
|
1045
|
+
(candidate.submission_retry_enter_dispatched_at !== undefined &&
|
|
1046
|
+
(!candidate.submission_retry_enter_reserved_at ||
|
|
1047
|
+
(candidate.input_stage !== "enter_dispatched" &&
|
|
1048
|
+
candidate.input_stage !== "agent_accepted"))))) {
|
|
1049
|
+
throw new Error("deferred foreground submission retry stage evidence is inconsistent");
|
|
1050
|
+
}
|
|
868
1051
|
}
|
|
869
1052
|
function bindingIsMonotonicTargetRefinement(before, after) {
|
|
870
1053
|
return before.binding_id === after.binding_id &&
|
|
@@ -921,6 +1104,15 @@ function assertTimestampOrder(value) {
|
|
|
921
1104
|
value.agent_accepted_at
|
|
922
1105
|
].filter((candidate) => candidate !== undefined);
|
|
923
1106
|
assertMonotonicTimestamps(inputTimeline);
|
|
1107
|
+
if (value.submission_retry_attempt_id) {
|
|
1108
|
+
assertMonotonicTimestamps([
|
|
1109
|
+
value.uncertain_at,
|
|
1110
|
+
value.submission_retry_text_reserved_at,
|
|
1111
|
+
value.submission_retry_text_injected_at,
|
|
1112
|
+
value.submission_retry_enter_reserved_at,
|
|
1113
|
+
value.submission_retry_enter_dispatched_at
|
|
1114
|
+
].filter((candidate) => candidate !== undefined));
|
|
1115
|
+
}
|
|
924
1116
|
const latestInputEvidence = inputTimeline[inputTimeline.length - 1];
|
|
925
1117
|
if (value.aborted_at) {
|
|
926
1118
|
assertTimestampNotBefore(value.aborted_at, latestInputEvidence);
|
|
@@ -936,11 +1128,13 @@ function assertTimestampOrder(value) {
|
|
|
936
1128
|
// Recovery may prove acceptance after uncertainty was recorded, so the
|
|
937
1129
|
// uncertainty timestamp is ordered after the evidence known at its own
|
|
938
1130
|
// stage, not unconditionally after a later agent_accepted_at.
|
|
939
|
-
const latestPossibleDispatch = value.
|
|
940
|
-
value.text_injected_at ?? value.dispatch_started_at
|
|
941
|
-
value.
|
|
1131
|
+
const latestPossibleDispatch = value.submission_retry_attempt_id
|
|
1132
|
+
? value.text_injected_at ?? value.dispatch_started_at
|
|
1133
|
+
: value.enter_dispatched_at ?? value.text_injected_at ?? value.dispatch_started_at ??
|
|
1134
|
+
value.target_prepared_at ?? value.source_reserved_at ?? value.prepared_at;
|
|
942
1135
|
assertTimestampNotBefore(value.uncertain_at, latestPossibleDispatch);
|
|
943
|
-
if (value
|
|
1136
|
+
if (deferredForegroundEvidenceStatus(value) === "uncertain" &&
|
|
1137
|
+
value.input_stage === "agent_accepted") {
|
|
944
1138
|
assertTimestampNotBefore(value.uncertain_at, value.agent_accepted_at);
|
|
945
1139
|
}
|
|
946
1140
|
}
|
|
@@ -956,6 +1150,11 @@ function assertTimestampOrder(value) {
|
|
|
956
1150
|
assertTimestampNotBefore(value.resolved_at, value.committed_at);
|
|
957
1151
|
assertTimestampNotBefore(value.resolved_at, value.recovered_at);
|
|
958
1152
|
}
|
|
1153
|
+
if (value.user_abandoned_at) {
|
|
1154
|
+
assertTimestampNotBefore(value.user_abandoned_at, value.abort_cleanup_completed_at ?? value.resolved_at ??
|
|
1155
|
+
value.recovered_at ?? value.committed_at ?? value.aborted_at ??
|
|
1156
|
+
value.uncertain_at ?? latestInputEvidence);
|
|
1157
|
+
}
|
|
959
1158
|
}
|
|
960
1159
|
function assertMonotonicTimestamps(values) {
|
|
961
1160
|
for (let index = 1; index < values.length; index += 1) {
|
|
@@ -1086,6 +1285,16 @@ const TRANSFER_STATUSES = new Set([
|
|
|
1086
1285
|
"resolved",
|
|
1087
1286
|
"aborted",
|
|
1088
1287
|
"abort_resolved",
|
|
1288
|
+
"uncertain",
|
|
1289
|
+
"user_abandoned"
|
|
1290
|
+
]);
|
|
1291
|
+
const USER_ABANDONMENT_ORIGIN_STATUSES = new Set([
|
|
1292
|
+
"prepared",
|
|
1293
|
+
"source_reserved",
|
|
1294
|
+
"target_prepared",
|
|
1295
|
+
"dispatch_started",
|
|
1296
|
+
"committed",
|
|
1297
|
+
"aborted",
|
|
1089
1298
|
"uncertain"
|
|
1090
1299
|
]);
|
|
1091
1300
|
const INPUT_STAGES = new Set([
|