@scotthuang/agent-knock-knock 0.12.11 → 0.12.13
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 +40 -0
- package/README.md +43 -30
- package/dist/src/approval-policy.d.ts +11 -2
- package/dist/src/approval-policy.js +33 -7
- package/dist/src/approval-policy.js.map +1 -1
- package/dist/src/callback-cli-adapter.d.ts +47 -0
- package/dist/src/callback-cli-adapter.js +156 -0
- package/dist/src/callback-cli-adapter.js.map +1 -0
- package/dist/src/callback-outbox-policy.d.ts +95 -0
- package/dist/src/callback-outbox-policy.js +149 -0
- package/dist/src/callback-outbox-policy.js.map +1 -0
- package/dist/src/callback-outbox-service.d.ts +247 -0
- package/dist/src/callback-outbox-service.js +1053 -0
- package/dist/src/callback-outbox-service.js.map +1 -0
- package/dist/src/callback-outbox-settlement.d.ts +54 -0
- package/dist/src/callback-outbox-settlement.js +241 -0
- package/dist/src/callback-outbox-settlement.js.map +1 -0
- package/dist/src/canonical-json.d.ts +1 -0
- package/dist/src/canonical-json.js +14 -0
- package/dist/src/canonical-json.js.map +1 -0
- package/dist/src/claude-lifecycle-compatibility.js +15 -0
- package/dist/src/claude-lifecycle-compatibility.js.map +1 -1
- package/dist/src/claude-local-transcript-provider.d.ts +82 -1
- package/dist/src/claude-local-transcript-provider.js +873 -23
- package/dist/src/claude-local-transcript-provider.js.map +1 -1
- package/dist/src/cli-command-runtime.d.ts +14 -0
- package/dist/src/cli-command-runtime.js +164 -0
- package/dist/src/cli-command-runtime.js.map +1 -0
- package/dist/src/cli-core.d.ts +4 -42
- package/dist/src/cli-core.js +958 -28684
- package/dist/src/cli-core.js.map +1 -1
- package/dist/src/cli-runtime-context.d.ts +57 -0
- package/dist/src/cli-runtime-context.js +118 -0
- package/dist/src/cli-runtime-context.js.map +1 -0
- package/dist/src/codex-lifecycle-compatibility.js +2 -1
- package/dist/src/codex-lifecycle-compatibility.js.map +1 -1
- package/dist/src/codex-local-session-provider.d.ts +18 -0
- package/dist/src/codex-local-session-provider.js +67 -0
- package/dist/src/codex-local-session-provider.js.map +1 -1
- package/dist/src/codex-session-provider.js +1 -3
- package/dist/src/codex-session-provider.js.map +1 -1
- package/dist/src/conversation-trace.d.ts +30 -0
- package/dist/src/conversation-trace.js +185 -0
- package/dist/src/conversation-trace.js.map +1 -0
- package/dist/src/deferred-foreground-application-service.d.ts +84 -0
- package/dist/src/deferred-foreground-application-service.js +742 -0
- package/dist/src/deferred-foreground-application-service.js.map +1 -0
- package/dist/src/deferred-foreground-authority-cli-adapter.d.ts +78 -0
- package/dist/src/deferred-foreground-authority-cli-adapter.js +795 -0
- package/dist/src/deferred-foreground-authority-cli-adapter.js.map +1 -0
- package/dist/src/deferred-foreground-boundary.d.ts +61 -0
- package/dist/src/deferred-foreground-boundary.js +2 -0
- package/dist/src/deferred-foreground-boundary.js.map +1 -0
- package/dist/src/deferred-foreground-capability.d.ts +4 -0
- package/dist/src/deferred-foreground-capability.js +184 -0
- package/dist/src/deferred-foreground-capability.js.map +1 -0
- package/dist/src/deferred-foreground-preparation-cli-adapter.d.ts +53 -0
- package/dist/src/deferred-foreground-preparation-cli-adapter.js +210 -0
- package/dist/src/deferred-foreground-preparation-cli-adapter.js.map +1 -0
- package/dist/src/deferred-foreground-preparation-service.d.ts +72 -0
- package/dist/src/deferred-foreground-preparation-service.js +179 -0
- package/dist/src/deferred-foreground-preparation-service.js.map +1 -0
- package/dist/src/deferred-foreground-recovery-cli-adapter.d.ts +139 -0
- package/dist/src/deferred-foreground-recovery-cli-adapter.js +1125 -0
- package/dist/src/deferred-foreground-recovery-cli-adapter.js.map +1 -0
- package/dist/src/deferred-foreground-recovery-service.d.ts +48 -0
- package/dist/src/deferred-foreground-recovery-service.js +91 -0
- package/dist/src/deferred-foreground-recovery-service.js.map +1 -0
- package/dist/src/deferred-foreground-transfer-policy.d.ts +2 -0
- package/dist/src/deferred-foreground-transfer-policy.js +10 -0
- package/dist/src/deferred-foreground-transfer-policy.js.map +1 -0
- package/dist/src/deferred-foreground-transfer.js +46 -105
- package/dist/src/deferred-foreground-transfer.js.map +1 -1
- package/dist/src/doctor-capabilities.d.ts +9 -0
- package/dist/src/doctor-capabilities.js +22 -0
- package/dist/src/doctor-capabilities.js.map +1 -1
- package/dist/src/durable-json-file.d.ts +22 -0
- package/dist/src/durable-json-file.js +127 -0
- package/dist/src/durable-json-file.js.map +1 -0
- package/dist/src/file-lock-cli-adapter.d.ts +43 -0
- package/dist/src/file-lock-cli-adapter.js +150 -0
- package/dist/src/file-lock-cli-adapter.js.map +1 -0
- package/dist/src/herdr-terminal-control-provider.js +1 -3
- package/dist/src/herdr-terminal-control-provider.js.map +1 -1
- package/dist/src/install-doctor-command-adapter.d.ts +17 -0
- package/dist/src/install-doctor-command-adapter.js +225 -0
- package/dist/src/install-doctor-command-adapter.js.map +1 -0
- package/dist/src/live-lifecycle-evidence.js +76 -46
- package/dist/src/live-lifecycle-evidence.js.map +1 -1
- package/dist/src/live-lifecycle-smoke.js +74 -50
- package/dist/src/live-lifecycle-smoke.js.map +1 -1
- package/dist/src/managed-session.d.ts +2 -0
- package/dist/src/managed-session.js +24 -0
- package/dist/src/managed-session.js.map +1 -1
- package/dist/src/managed-turn-recovery-service.d.ts +86 -0
- package/dist/src/managed-turn-recovery-service.js +145 -0
- package/dist/src/managed-turn-recovery-service.js.map +1 -0
- package/dist/src/mutation-transaction.d.ts +48 -0
- package/dist/src/mutation-transaction.js +73 -0
- package/dist/src/mutation-transaction.js.map +1 -0
- package/dist/src/native-thread-lifecycle-cli-adapter.d.ts +154 -0
- package/dist/src/native-thread-lifecycle-cli-adapter.js +786 -0
- package/dist/src/native-thread-lifecycle-cli-adapter.js.map +1 -0
- package/dist/src/native-thread-lifecycle-ledger-cli-adapter.d.ts +25 -0
- package/dist/src/native-thread-lifecycle-ledger-cli-adapter.js +50 -0
- package/dist/src/native-thread-lifecycle-ledger-cli-adapter.js.map +1 -0
- package/dist/src/native-thread-lifecycle-query-service.d.ts +75 -0
- package/dist/src/native-thread-lifecycle-query-service.js +297 -0
- package/dist/src/native-thread-lifecycle-query-service.js.map +1 -0
- package/dist/src/native-thread-lifecycle-recovery-adapter.d.ts +45 -0
- package/dist/src/native-thread-lifecycle-recovery-adapter.js +154 -0
- package/dist/src/native-thread-lifecycle-recovery-adapter.js.map +1 -0
- package/dist/src/native-thread-lifecycle-recovery-service.d.ts +211 -0
- package/dist/src/native-thread-lifecycle-recovery-service.js +1093 -0
- package/dist/src/native-thread-lifecycle-recovery-service.js.map +1 -0
- package/dist/src/native-thread-resume-snapshot-policy.d.ts +73 -0
- package/dist/src/native-thread-resume-snapshot-policy.js +218 -0
- package/dist/src/native-thread-resume-snapshot-policy.js.map +1 -0
- package/dist/src/native-thread-resume-snapshot.d.ts +3 -61
- package/dist/src/native-thread-resume-snapshot.js +2 -143
- package/dist/src/native-thread-resume-snapshot.js.map +1 -1
- package/dist/src/native-thread-transition-application.d.ts +147 -0
- package/dist/src/native-thread-transition-application.js +974 -0
- package/dist/src/native-thread-transition-application.js.map +1 -0
- package/dist/src/native-thread-transition-policy.d.ts +127 -0
- package/dist/src/native-thread-transition-policy.js +154 -0
- package/dist/src/native-thread-transition-policy.js.map +1 -0
- package/dist/src/native-thread-transition-resource-adapter.d.ts +12 -0
- package/dist/src/native-thread-transition-resource-adapter.js +44 -0
- package/dist/src/native-thread-transition-resource-adapter.js.map +1 -0
- package/dist/src/native-thread-transition-settlement-service.d.ts +94 -0
- package/dist/src/native-thread-transition-settlement-service.js +134 -0
- package/dist/src/native-thread-transition-settlement-service.js.map +1 -0
- package/dist/src/native-thread-transition-verification-adapter.d.ts +62 -0
- package/dist/src/native-thread-transition-verification-adapter.js +451 -0
- package/dist/src/native-thread-transition-verification-adapter.js.map +1 -0
- package/dist/src/openclaw-callback-transport.d.ts +69 -0
- package/dist/src/openclaw-callback-transport.js +583 -0
- package/dist/src/openclaw-callback-transport.js.map +1 -0
- package/dist/src/openclaw-plugin-callback-adapter.d.ts +1 -0
- package/dist/src/openclaw-plugin-callback-adapter.js +423 -0
- package/dist/src/openclaw-plugin-callback-adapter.js.map +1 -0
- package/dist/src/openclaw-plugin-command-adapter.d.ts +16 -0
- package/dist/src/openclaw-plugin-command-adapter.js +1868 -0
- package/dist/src/openclaw-plugin-command-adapter.js.map +1 -0
- package/dist/src/openclaw-plugin-helpers.d.ts +27 -4
- package/dist/src/openclaw-plugin-helpers.js +356 -81
- package/dist/src/openclaw-plugin-helpers.js.map +1 -1
- package/dist/src/openclaw-plugin-schemas.d.ts +358 -0
- package/dist/src/openclaw-plugin-schemas.js +366 -0
- package/dist/src/openclaw-plugin-schemas.js.map +1 -0
- package/dist/src/openclaw-plugin-supervisor.d.ts +6 -0
- package/dist/src/openclaw-plugin-supervisor.js +123 -0
- package/dist/src/openclaw-plugin-supervisor.js.map +1 -0
- package/dist/src/openclaw-plugin.js +7 -2212
- package/dist/src/openclaw-plugin.js.map +1 -1
- package/dist/src/openclaw-private-authority-offers.d.ts +22 -0
- package/dist/src/openclaw-private-authority-offers.js +177 -0
- package/dist/src/openclaw-private-authority-offers.js.map +1 -0
- package/dist/src/protocol.d.ts +5 -0
- package/dist/src/protocol.js +59 -3
- package/dist/src/protocol.js.map +1 -1
- package/dist/src/session-store.d.ts +1 -0
- package/dist/src/session-store.js +84 -106
- package/dist/src/session-store.js.map +1 -1
- package/dist/src/store.d.ts +1 -0
- package/dist/src/store.js +5 -2
- package/dist/src/store.js.map +1 -1
- package/dist/src/terminal-acceptance-application-service.d.ts +65 -0
- package/dist/src/terminal-acceptance-application-service.js +63 -0
- package/dist/src/terminal-acceptance-application-service.js.map +1 -0
- package/dist/src/terminal-acceptance-cli-adapter.d.ts +210 -0
- package/dist/src/terminal-acceptance-cli-adapter.js +957 -0
- package/dist/src/terminal-acceptance-cli-adapter.js.map +1 -0
- package/dist/src/terminal-action-projection.d.ts +309 -0
- package/dist/src/terminal-action-projection.js +376 -0
- package/dist/src/terminal-action-projection.js.map +1 -0
- package/dist/src/terminal-agent-bridge.d.ts +3 -0
- package/dist/src/terminal-agent-bridge.js +29 -8
- package/dist/src/terminal-agent-bridge.js.map +1 -1
- package/dist/src/terminal-authority-policy.d.ts +141 -0
- package/dist/src/terminal-authority-policy.js +367 -0
- package/dist/src/terminal-authority-policy.js.map +1 -0
- package/dist/src/terminal-binding-authority.d.ts +118 -0
- package/dist/src/terminal-binding-authority.js +245 -0
- package/dist/src/terminal-binding-authority.js.map +1 -0
- package/dist/src/terminal-binding-reconciliation-service.d.ts +75 -0
- package/dist/src/terminal-binding-reconciliation-service.js +73 -0
- package/dist/src/terminal-binding-reconciliation-service.js.map +1 -0
- package/dist/src/terminal-command-cli-adapter.d.ts +380 -0
- package/dist/src/terminal-command-cli-adapter.js +3288 -0
- package/dist/src/terminal-command-cli-adapter.js.map +1 -0
- package/dist/src/terminal-control-ref.d.ts +4 -0
- package/dist/src/terminal-control-ref.js +11 -8
- package/dist/src/terminal-control-ref.js.map +1 -1
- package/dist/src/terminal-delegate-cli-adapter.d.ts +41 -0
- package/dist/src/terminal-delegate-cli-adapter.js +242 -0
- package/dist/src/terminal-delegate-cli-adapter.js.map +1 -0
- package/dist/src/terminal-dispatch-abort.d.ts +22 -0
- package/dist/src/terminal-dispatch-abort.js +30 -0
- package/dist/src/terminal-dispatch-abort.js.map +1 -0
- package/dist/src/terminal-dispatch-application.d.ts +115 -0
- package/dist/src/terminal-dispatch-application.js +478 -0
- package/dist/src/terminal-dispatch-application.js.map +1 -0
- package/dist/src/terminal-dispatch-capability.d.ts +63 -0
- package/dist/src/terminal-dispatch-capability.js +133 -0
- package/dist/src/terminal-dispatch-capability.js.map +1 -0
- package/dist/src/terminal-dispatch-completion-cli-adapter.d.ts +22 -0
- package/dist/src/terminal-dispatch-completion-cli-adapter.js +96 -0
- package/dist/src/terminal-dispatch-completion-cli-adapter.js.map +1 -0
- package/dist/src/terminal-dispatch-composition.d.ts +79 -0
- package/dist/src/terminal-dispatch-composition.js +2 -0
- package/dist/src/terminal-dispatch-composition.js.map +1 -0
- package/dist/src/terminal-dispatch-execution.d.ts +290 -0
- package/dist/src/terminal-dispatch-execution.js +916 -0
- package/dist/src/terminal-dispatch-execution.js.map +1 -0
- package/dist/src/terminal-dispatch-ledger-codec.d.ts +110 -0
- package/dist/src/terminal-dispatch-ledger-codec.js +399 -0
- package/dist/src/terminal-dispatch-ledger-codec.js.map +1 -0
- package/dist/src/terminal-dispatch-policy.d.ts +93 -0
- package/dist/src/terminal-dispatch-policy.js +122 -0
- package/dist/src/terminal-dispatch-policy.js.map +1 -0
- package/dist/src/terminal-dispatch-presenter.d.ts +65 -0
- package/dist/src/terminal-dispatch-presenter.js +258 -0
- package/dist/src/terminal-dispatch-presenter.js.map +1 -0
- package/dist/src/terminal-dispatch-receipt.d.ts +79 -0
- package/dist/src/terminal-dispatch-receipt.js +314 -0
- package/dist/src/terminal-dispatch-receipt.js.map +1 -0
- package/dist/src/terminal-dispatch-recovery-cli-adapter.d.ts +132 -0
- package/dist/src/terminal-dispatch-recovery-cli-adapter.js +1330 -0
- package/dist/src/terminal-dispatch-recovery-cli-adapter.js.map +1 -0
- package/dist/src/terminal-dispatch-recovery-service.d.ts +271 -0
- package/dist/src/terminal-dispatch-recovery-service.js +374 -0
- package/dist/src/terminal-dispatch-recovery-service.js.map +1 -0
- package/dist/src/terminal-dispatch-repository-cli-adapter.d.ts +35 -0
- package/dist/src/terminal-dispatch-repository-cli-adapter.js +483 -0
- package/dist/src/terminal-dispatch-repository-cli-adapter.js.map +1 -0
- package/dist/src/terminal-handoff-application-service.d.ts +7 -0
- package/dist/src/terminal-handoff-application-service.js +18 -0
- package/dist/src/terminal-handoff-application-service.js.map +1 -0
- package/dist/src/terminal-handoff-cli-adapter.d.ts +229 -0
- package/dist/src/terminal-handoff-cli-adapter.js +1389 -0
- package/dist/src/terminal-handoff-cli-adapter.js.map +1 -0
- package/dist/src/terminal-handoff-facts.d.ts +12 -0
- package/dist/src/terminal-handoff-facts.js +16 -0
- package/dist/src/terminal-handoff-facts.js.map +1 -0
- package/dist/src/terminal-identity-authority-cli-adapter.d.ts +318 -0
- package/dist/src/terminal-identity-authority-cli-adapter.js +1072 -0
- package/dist/src/terminal-identity-authority-cli-adapter.js.map +1 -0
- package/dist/src/terminal-identity-authority-service.d.ts +48 -0
- package/dist/src/terminal-identity-authority-service.js +45 -0
- package/dist/src/terminal-identity-authority-service.js.map +1 -0
- package/dist/src/terminal-list-cli-adapter.d.ts +274 -0
- package/dist/src/terminal-list-cli-adapter.js +2444 -0
- package/dist/src/terminal-list-cli-adapter.js.map +1 -0
- package/dist/src/terminal-list-renderer.d.ts +34 -0
- package/dist/src/terminal-list-renderer.js +643 -0
- package/dist/src/terminal-list-renderer.js.map +1 -0
- package/dist/src/terminal-maintenance-cli-adapter.d.ts +102 -0
- package/dist/src/terminal-maintenance-cli-adapter.js +1059 -0
- package/dist/src/terminal-maintenance-cli-adapter.js.map +1 -0
- package/dist/src/terminal-monitor-application-service.d.ts +275 -0
- package/dist/src/terminal-monitor-application-service.js +982 -0
- package/dist/src/terminal-monitor-application-service.js.map +1 -0
- package/dist/src/terminal-monitor-cli-adapter.d.ts +130 -0
- package/dist/src/terminal-monitor-cli-adapter.js +442 -0
- package/dist/src/terminal-monitor-cli-adapter.js.map +1 -0
- package/dist/src/terminal-monitor-decision-policy.d.ts +98 -0
- package/dist/src/terminal-monitor-decision-policy.js +246 -0
- package/dist/src/terminal-monitor-decision-policy.js.map +1 -0
- package/dist/src/terminal-monitor-launch-plan.d.ts +45 -0
- package/dist/src/terminal-monitor-launch-plan.js +94 -0
- package/dist/src/terminal-monitor-launch-plan.js.map +1 -0
- package/dist/src/terminal-monitor-ownership-policy.d.ts +31 -0
- package/dist/src/terminal-monitor-ownership-policy.js +64 -0
- package/dist/src/terminal-monitor-ownership-policy.js.map +1 -0
- package/dist/src/terminal-monitor-poll-policy.d.ts +48 -0
- package/dist/src/terminal-monitor-poll-policy.js +67 -0
- package/dist/src/terminal-monitor-poll-policy.js.map +1 -0
- package/dist/src/terminal-monitor-reconciliation-eligibility.d.ts +53 -0
- package/dist/src/terminal-monitor-reconciliation-eligibility.js +114 -0
- package/dist/src/terminal-monitor-reconciliation-eligibility.js.map +1 -0
- package/dist/src/terminal-monitor-state-cli-adapter.d.ts +106 -0
- package/dist/src/terminal-monitor-state-cli-adapter.js +1492 -0
- package/dist/src/terminal-monitor-state-cli-adapter.js.map +1 -0
- package/dist/src/terminal-monitor-state-reconciliation-service.d.ts +87 -0
- package/dist/src/terminal-monitor-state-reconciliation-service.js +106 -0
- package/dist/src/terminal-monitor-state-reconciliation-service.js.map +1 -0
- package/dist/src/terminal-monitor-supervision-cli-adapter.d.ts +122 -0
- package/dist/src/terminal-monitor-supervision-cli-adapter.js +685 -0
- package/dist/src/terminal-monitor-supervision-cli-adapter.js.map +1 -0
- package/dist/src/terminal-process-facts.d.ts +2 -0
- package/dist/src/terminal-process-facts.js +19 -0
- package/dist/src/terminal-process-facts.js.map +1 -0
- package/dist/src/terminal-process-source.d.ts +6 -4
- package/dist/src/terminal-process-source.js +25 -20
- package/dist/src/terminal-process-source.js.map +1 -1
- package/dist/src/terminal-runtime-cli-adapter.d.ts +80 -0
- package/dist/src/terminal-runtime-cli-adapter.js +488 -0
- package/dist/src/terminal-runtime-cli-adapter.js.map +1 -0
- package/dist/src/terminal-scoped-approval-authority.d.ts +62 -0
- package/dist/src/terminal-scoped-approval-authority.js +281 -0
- package/dist/src/terminal-scoped-approval-authority.js.map +1 -0
- package/dist/src/terminal-selector-projection-service.d.ts +26 -0
- package/dist/src/terminal-selector-projection-service.js +81 -0
- package/dist/src/terminal-selector-projection-service.js.map +1 -0
- package/dist/src/terminal-status-cli-adapter.d.ts +126 -0
- package/dist/src/terminal-status-cli-adapter.js +577 -0
- package/dist/src/terminal-status-cli-adapter.js.map +1 -0
- package/dist/src/terminal-status-facts.d.ts +56 -0
- package/dist/src/terminal-status-facts.js +199 -0
- package/dist/src/terminal-status-facts.js.map +1 -0
- package/dist/src/terminal-submission-acceptance.d.ts +62 -145
- package/dist/src/terminal-submission-acceptance.js +555 -222
- package/dist/src/terminal-submission-acceptance.js.map +1 -1
- package/dist/src/terminal-submission-facts.d.ts +123 -0
- package/dist/src/terminal-submission-facts.js +205 -0
- package/dist/src/terminal-submission-facts.js.map +1 -0
- package/dist/src/terminal-turn-binding-authority-cli-adapter.d.ts +14 -0
- package/dist/src/terminal-turn-binding-authority-cli-adapter.js +90 -0
- package/dist/src/terminal-turn-binding-authority-cli-adapter.js.map +1 -0
- package/dist/src/terminal-watch-callback-cli-adapter.d.ts +20 -0
- package/dist/src/terminal-watch-callback-cli-adapter.js +55 -0
- package/dist/src/terminal-watch-callback-cli-adapter.js.map +1 -0
- package/dist/src/terminal-watch-cli-adapter.d.ts +64 -0
- package/dist/src/terminal-watch-cli-adapter.js +508 -0
- package/dist/src/terminal-watch-cli-adapter.js.map +1 -0
- package/dist/src/terminal-watch-service.d.ts +97 -0
- package/dist/src/terminal-watch-service.js +714 -0
- package/dist/src/terminal-watch-service.js.map +1 -0
- package/dist/src/terminal-watch-store.d.ts +149 -0
- package/dist/src/terminal-watch-store.js +861 -0
- package/dist/src/terminal-watch-store.js.map +1 -0
- package/dist/src/value-guards.d.ts +4 -0
- package/dist/src/value-guards.js +12 -0
- package/dist/src/value-guards.js.map +1 -0
- package/dist/src/verified-dead-agent-policy.d.ts +192 -0
- package/dist/src/verified-dead-agent-policy.js +262 -0
- package/dist/src/verified-dead-agent-policy.js.map +1 -0
- package/docs/quickstart-herdr.md +8 -0
- package/docs/quickstart-tmux.md +33 -6
- package/openclaw.plugin.json +11 -3
- package/package.json +3 -1
- package/templates/openclaw-skills/agent-knock-knock/SKILL.md +58 -40
|
@@ -2,111 +2,559 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { redactString } from "./runtime-log.js";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
import { exactNativeThreadId, fingerprint, normalizedRolloutIdentity, parsedInteger, requiredString, sha256Value, validTimestamp, validateCodexRolloutAcceptanceAnchor, validateTerminalSubmissionAcceptanceEvidence } from "./terminal-submission-facts.js";
|
|
6
|
+
export { validateCodexRolloutAcceptanceAnchor, validateTerminalSubmissionAcceptanceEvidence };
|
|
7
|
+
const CODEX_ACCEPTANCE_MAX_BYTES = 16 * 1024 * 1024;
|
|
8
|
+
const CODEX_COMPLETION_MAX_BYTES = 256 * 1024 * 1024;
|
|
9
|
+
const CODEX_COMPLETION_MAX_TEXT_LENGTH = 4000;
|
|
10
|
+
const CODEX_ACTIVE_TASK_SCAN_CHUNK_BYTES = 64 * 1024;
|
|
11
|
+
const CODEX_ACTIVE_TASK_MAX_RECORD_BYTES = 16 * 1024 * 1024;
|
|
12
|
+
const CODEX_ROLLOUT_HEADER_MAX_BYTES = 1024 * 1024;
|
|
13
|
+
const CODEX_VERSION_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/u;
|
|
14
|
+
const NO_FOLLOW_FLAG = typeof fs.constants.O_NOFOLLOW === "number"
|
|
15
|
+
? fs.constants.O_NOFOLLOW
|
|
16
|
+
: 0;
|
|
17
|
+
/**
|
|
18
|
+
* Capture the latest active native task in an exact open Codex rollout.
|
|
19
|
+
* This is intentionally separate from the pre-send acceptance anchor: all
|
|
20
|
+
* task-start and root-user evidence already exists when this function runs.
|
|
21
|
+
*/
|
|
22
|
+
export function captureCodexHumanStartedActiveTaskAnchor(options) {
|
|
23
|
+
const current = options.currentIdentity;
|
|
24
|
+
const nativeThreadId = exactNativeThreadId(current.sessionId);
|
|
25
|
+
const processUuid = requiredString(current.processUuid, "Codex process UUID");
|
|
26
|
+
const processBirth = requiredString(current.processBirth, "Codex process birth");
|
|
27
|
+
if (!current.rollout) {
|
|
28
|
+
throw new Error("the active Codex task has no exact rollout identity");
|
|
29
|
+
}
|
|
30
|
+
const rollout = normalizedRolloutIdentity(current.rollout);
|
|
31
|
+
const maxBytes = positiveByteLimit(options.maxBytes, CODEX_COMPLETION_MAX_BYTES, "Codex active-task capture");
|
|
32
|
+
const opened = openExactRollout(rollout);
|
|
33
|
+
try {
|
|
34
|
+
const before = opened.stat;
|
|
35
|
+
assertPrivateRegularFile(before);
|
|
36
|
+
if (before.size === 0) {
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
if (!fileEndsWithNewline(opened.fd, before.size)) {
|
|
40
|
+
throw new Error("Codex active-task rollout has an incomplete JSONL tail; retry");
|
|
41
|
+
}
|
|
42
|
+
const header = readCodexRolloutHeader(opened.fd, before.size);
|
|
43
|
+
const codexVersion = assertExistingCodexRolloutHeader(header, nativeThreadId);
|
|
44
|
+
const active = latestCodexHumanStartedActiveTask(opened.fd, before.size, maxBytes);
|
|
45
|
+
const after = fs.fstatSync(opened.fd);
|
|
46
|
+
if (!sameStableFile(before, after)) {
|
|
47
|
+
throw new Error("Codex active-task rollout changed while it was captured; retry");
|
|
48
|
+
}
|
|
49
|
+
if (!active) {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
const base = {
|
|
53
|
+
schema: "agent-knock-knock/codex-human-started-active-task-anchor",
|
|
54
|
+
version: 1,
|
|
55
|
+
native_thread_id: nativeThreadId,
|
|
56
|
+
process_uuid: processUuid,
|
|
57
|
+
process_birth: processBirth,
|
|
58
|
+
captured_at: (options.now ?? new Date()).toISOString(),
|
|
59
|
+
rollout,
|
|
60
|
+
turn_id: active.turnId,
|
|
61
|
+
request_hash: active.requestHash,
|
|
62
|
+
codex_version: codexVersion,
|
|
63
|
+
task_started_offset_bytes: active.taskStartedOffsetBytes,
|
|
64
|
+
user_message_offset_bytes: active.userMessageOffsetBytes,
|
|
65
|
+
observed_end_offset_bytes: before.size
|
|
66
|
+
};
|
|
67
|
+
return {
|
|
68
|
+
...base,
|
|
69
|
+
anchor_fingerprint: fingerprint(base)
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
finally {
|
|
73
|
+
fs.closeSync(opened.fd);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/** Observe only the exact human-started task named by the persisted anchor. */
|
|
77
|
+
export function observeCodexHumanStartedActiveTask(options) {
|
|
78
|
+
try {
|
|
79
|
+
const anchor = validateCodexHumanStartedActiveTaskAnchor(options.anchor);
|
|
80
|
+
const maxBytes = positiveByteLimit(options.maxBytes, CODEX_COMPLETION_MAX_BYTES, "Codex active-task observation");
|
|
81
|
+
const resumeOffsetBytes = codexActiveTaskResumeOffset(anchor, options.resumeOffsetBytes);
|
|
82
|
+
const opened = openExactRollout(anchor.rollout);
|
|
83
|
+
try {
|
|
84
|
+
const before = opened.stat;
|
|
85
|
+
assertPrivateRegularFile(before);
|
|
86
|
+
if (before.size < anchor.observed_end_offset_bytes) {
|
|
87
|
+
throw new Error("Codex active-task rollout was truncated after capture");
|
|
88
|
+
}
|
|
89
|
+
if (before.size < resumeOffsetBytes) {
|
|
90
|
+
throw new Error("Codex active-task rollout was truncated after observation");
|
|
91
|
+
}
|
|
92
|
+
assertCodexActiveTaskResumeBoundary(opened.fd, resumeOffsetBytes);
|
|
93
|
+
const availableBytes = before.size - resumeOffsetBytes;
|
|
94
|
+
const bytesToRead = Math.min(availableBytes, maxBytes);
|
|
95
|
+
if (bytesToRead === 0) {
|
|
96
|
+
assertCodexHumanStartedIdentity(anchor, options.currentIdentity);
|
|
97
|
+
return {
|
|
98
|
+
status: "pending",
|
|
99
|
+
observedEndOffsetBytes: before.size,
|
|
100
|
+
safeResumeOffsetBytes: resumeOffsetBytes
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
const buffer = readExactBytes(opened.fd, resumeOffsetBytes, bytesToRead);
|
|
104
|
+
const completeLength = completeJsonlPrefixLength(buffer);
|
|
105
|
+
if (completeLength === 0 && availableBytes > maxBytes) {
|
|
106
|
+
throw new Error("Codex active-task JSONL record exceeded the bounded read limit");
|
|
107
|
+
}
|
|
108
|
+
const completeBuffer = buffer.subarray(0, completeLength);
|
|
109
|
+
const safeResumeOffsetBytes = resumeOffsetBytes + completeLength;
|
|
110
|
+
const after = fs.fstatSync(opened.fd);
|
|
111
|
+
if (!sameStableFile(before, after)) {
|
|
112
|
+
assertCodexHumanStartedIdentity(anchor, options.currentIdentity);
|
|
113
|
+
return {
|
|
114
|
+
status: "pending",
|
|
115
|
+
observedEndOffsetBytes: after.size,
|
|
116
|
+
safeResumeOffsetBytes: resumeOffsetBytes
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
const suffixRecords = parseCodexJsonlRecords(completeBuffer, "active-task suffix");
|
|
120
|
+
const laterTaskOffset = suffixRecords.find(({ value }) => isLaterCodexHumanTaskRecord(value))?.offsetBytes;
|
|
121
|
+
const exactCompletionOffset = suffixRecords.find(({ value }) => isExactCodexTaskCompleteRecord(value, anchor.turn_id))?.offsetBytes;
|
|
122
|
+
const exactAbortRecords = suffixRecords.filter(({ value }) => isExactCodexTurnAbortedRecord(value, anchor.turn_id));
|
|
123
|
+
if (exactAbortRecords.length > 1) {
|
|
124
|
+
throw new Error("the exact Codex active task has duplicate turn_aborted records");
|
|
125
|
+
}
|
|
126
|
+
const exactAbort = exactAbortRecords[0];
|
|
127
|
+
const exactSettlementOffset = [
|
|
128
|
+
exactCompletionOffset,
|
|
129
|
+
exactAbort?.offsetBytes
|
|
130
|
+
].filter((offset) => offset !== undefined)
|
|
131
|
+
.sort((left, right) => left - right)[0];
|
|
132
|
+
if (laterTaskOffset !== undefined &&
|
|
133
|
+
(exactSettlementOffset === undefined ||
|
|
134
|
+
laterTaskOffset < exactSettlementOffset)) {
|
|
135
|
+
throw new Error("a later Codex human task appeared after the active-task anchor");
|
|
136
|
+
}
|
|
137
|
+
if (exactAbort &&
|
|
138
|
+
(exactCompletionOffset === undefined ||
|
|
139
|
+
exactAbort.offsetBytes < exactCompletionOffset)) {
|
|
140
|
+
return codexHumanStartedTaskAbortCompletion(anchor, exactAbort.value, before.size);
|
|
141
|
+
}
|
|
142
|
+
const completion = scanExactCodexTaskComplete(completeBuffer.toString("utf8"), anchor.turn_id);
|
|
143
|
+
if (completion.status === "failure") {
|
|
144
|
+
throw new Error(completion.detail);
|
|
145
|
+
}
|
|
146
|
+
if (completion.status === "pending") {
|
|
147
|
+
assertCodexHumanStartedIdentity(anchor, options.currentIdentity);
|
|
148
|
+
return {
|
|
149
|
+
status: "pending",
|
|
150
|
+
observedEndOffsetBytes: before.size,
|
|
151
|
+
safeResumeOffsetBytes
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
return {
|
|
155
|
+
status: "completed",
|
|
156
|
+
completion: {
|
|
157
|
+
source: "durable",
|
|
158
|
+
outcome: "success",
|
|
159
|
+
text: truncateCompletionText(redactString(completion.text)),
|
|
160
|
+
...(completion.timestamp ? { timestamp: completion.timestamp } : {}),
|
|
161
|
+
id: anchor.turn_id,
|
|
162
|
+
confidence: "high",
|
|
163
|
+
metadata: {
|
|
164
|
+
match: "human_started_bound_rollout_task_complete",
|
|
165
|
+
turn_id: anchor.turn_id,
|
|
166
|
+
native_thread_id: anchor.native_thread_id,
|
|
167
|
+
anchor_fingerprint: anchor.anchor_fingerprint,
|
|
168
|
+
observed_end_offset_bytes: before.size
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
finally {
|
|
174
|
+
fs.closeSync(opened.fd);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
if (isRetryableProviderIoError(error)) {
|
|
179
|
+
return {
|
|
180
|
+
status: "unavailable",
|
|
181
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
182
|
+
retryable: true
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
return {
|
|
186
|
+
status: "invalidated",
|
|
187
|
+
reason: error instanceof Error ? error.message : String(error)
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
function codexActiveTaskResumeOffset(anchor, value) {
|
|
192
|
+
const offset = value === undefined
|
|
193
|
+
? anchor.observed_end_offset_bytes
|
|
194
|
+
: safeByteOffset(value, "Codex active-task resume offset");
|
|
195
|
+
if (offset < anchor.observed_end_offset_bytes) {
|
|
196
|
+
throw new Error("Codex active-task resume offset predates its anchor");
|
|
197
|
+
}
|
|
198
|
+
return offset;
|
|
199
|
+
}
|
|
200
|
+
function assertCodexActiveTaskResumeBoundary(fd, offset) {
|
|
201
|
+
if (offset > 0 && byteAtOffset(fd, offset - 1) !== 0x0a) {
|
|
202
|
+
throw new Error("Codex active-task resume offset is not a JSONL boundary");
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
function completeJsonlPrefixLength(buffer) {
|
|
206
|
+
if (buffer.length === 0) {
|
|
207
|
+
return 0;
|
|
208
|
+
}
|
|
209
|
+
const newline = buffer.lastIndexOf(0x0a);
|
|
210
|
+
return newline < 0 ? 0 : newline + 1;
|
|
211
|
+
}
|
|
212
|
+
export function validateCodexHumanStartedActiveTaskAnchor(value) {
|
|
213
|
+
if (!isRecord(value) ||
|
|
214
|
+
value.schema !==
|
|
215
|
+
"agent-knock-knock/codex-human-started-active-task-anchor" ||
|
|
216
|
+
value.version !== 1) {
|
|
217
|
+
throw new Error("Codex human-started active-task anchor is invalid");
|
|
8
218
|
}
|
|
9
219
|
const allowedKeys = new Set([
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"acceptanceId",
|
|
15
|
-
"acceptedAt",
|
|
16
|
-
"anchorFingerprint",
|
|
17
|
-
"evidenceFingerprint",
|
|
18
|
-
"metadata"
|
|
220
|
+
"schema", "version", "native_thread_id", "process_uuid", "process_birth",
|
|
221
|
+
"captured_at", "rollout", "turn_id", "request_hash", "codex_version",
|
|
222
|
+
"task_started_offset_bytes", "user_message_offset_bytes",
|
|
223
|
+
"observed_end_offset_bytes", "anchor_fingerprint"
|
|
19
224
|
]);
|
|
20
225
|
if (Object.keys(value).some((key) => !allowedKeys.has(key))) {
|
|
21
|
-
throw new Error("
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
if (
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
throw new Error("native acceptance evidence metadata is invalid");
|
|
41
|
-
}
|
|
42
|
-
const allowedMetadata = value.source === "codex_rollout"
|
|
43
|
-
? new Set([
|
|
44
|
-
"turn_id",
|
|
45
|
-
"anchor_offset_bytes",
|
|
46
|
-
"observed_end_offset_bytes"
|
|
47
|
-
])
|
|
48
|
-
: new Set([
|
|
49
|
-
"prompt_uuid",
|
|
50
|
-
"claude_version",
|
|
51
|
-
"transcript_file_id",
|
|
52
|
-
"anchor_offset_bytes",
|
|
53
|
-
"observed_end_offset_bytes",
|
|
54
|
-
"agent_started_at_ms"
|
|
55
|
-
]);
|
|
56
|
-
for (const [key, item] of Object.entries(value.metadata)) {
|
|
57
|
-
if (!allowedMetadata.has(key) ||
|
|
58
|
-
!(typeof item === "string" ||
|
|
59
|
-
(typeof item === "number" && Number.isFinite(item)))) {
|
|
60
|
-
throw new Error("native acceptance evidence metadata is not allowlisted");
|
|
61
|
-
}
|
|
226
|
+
throw new Error("Codex human-started active-task anchor has unsupported fields");
|
|
227
|
+
}
|
|
228
|
+
const nativeThreadId = exactNativeThreadId(value.native_thread_id);
|
|
229
|
+
const turnId = exactNativeThreadId(value.turn_id);
|
|
230
|
+
const capturedAt = value.captured_at;
|
|
231
|
+
if (nativeThreadId !== value.native_thread_id ||
|
|
232
|
+
turnId !== value.turn_id ||
|
|
233
|
+
!validTimestamp(capturedAt) ||
|
|
234
|
+
typeof capturedAt !== "string" ||
|
|
235
|
+
new Date(capturedAt).toISOString() !== capturedAt) {
|
|
236
|
+
throw new Error("Codex human-started active-task anchor identity is invalid");
|
|
237
|
+
}
|
|
238
|
+
for (const [field, label] of [
|
|
239
|
+
[value.process_uuid, "Codex active-task process UUID"],
|
|
240
|
+
[value.process_birth, "Codex active-task process birth"]
|
|
241
|
+
]) {
|
|
242
|
+
if (requiredString(field, label) !== field ||
|
|
243
|
+
field.includes("\0")) {
|
|
244
|
+
throw new Error(`${label} is not canonical`);
|
|
62
245
|
}
|
|
63
246
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
throw new Error("
|
|
247
|
+
if (sha256Value(value.request_hash, "Codex active-task request hash") !==
|
|
248
|
+
value.request_hash) {
|
|
249
|
+
throw new Error("Codex active-task request hash is not canonical");
|
|
250
|
+
}
|
|
251
|
+
if (typeof value.codex_version !== "string" ||
|
|
252
|
+
!CODEX_VERSION_PATTERN.test(value.codex_version)) {
|
|
253
|
+
throw new Error("Codex active-task version is invalid");
|
|
254
|
+
}
|
|
255
|
+
if (sha256Value(value.anchor_fingerprint, "Codex active-task anchor fingerprint") !== value.anchor_fingerprint) {
|
|
256
|
+
throw new Error("Codex active-task anchor fingerprint is not canonical");
|
|
257
|
+
}
|
|
258
|
+
if (!isRecord(value.rollout)) {
|
|
259
|
+
throw new Error("Codex human-started active-task rollout identity is invalid");
|
|
260
|
+
}
|
|
261
|
+
const rolloutKeys = new Set(["fd", "device", "inode", "path"]);
|
|
262
|
+
if (Object.keys(value.rollout).some((key) => !rolloutKeys.has(key))) {
|
|
263
|
+
throw new Error("Codex human-started active-task rollout has unsupported fields");
|
|
264
|
+
}
|
|
265
|
+
const rollout = normalizedRolloutIdentity(value.rollout);
|
|
266
|
+
if (Object.entries(rollout).some(([key, normalized]) => normalized !== value.rollout[key] || normalized.includes("\0")) ||
|
|
267
|
+
!path.isAbsolute(rollout.path)) {
|
|
268
|
+
throw new Error("Codex human-started active-task rollout path is not absolute");
|
|
269
|
+
}
|
|
270
|
+
const taskStartedOffset = safeByteOffset(value.task_started_offset_bytes, "Codex active-task task_started offset");
|
|
271
|
+
const userMessageOffset = safeByteOffset(value.user_message_offset_bytes, "Codex active-task user-message offset");
|
|
272
|
+
const observedEndOffset = safeByteOffset(value.observed_end_offset_bytes, "Codex active-task observed-end offset");
|
|
273
|
+
if (userMessageOffset <= taskStartedOffset ||
|
|
274
|
+
observedEndOffset <= userMessageOffset) {
|
|
275
|
+
throw new Error("Codex human-started active-task byte boundaries are inconsistent");
|
|
276
|
+
}
|
|
277
|
+
const { anchor_fingerprint: _anchorFingerprint, ...base } = value;
|
|
278
|
+
if (fingerprint(base) !== value.anchor_fingerprint) {
|
|
279
|
+
throw new Error("Codex human-started active-task anchor fingerprint does not match");
|
|
67
280
|
}
|
|
68
281
|
return value;
|
|
69
282
|
}
|
|
70
|
-
|
|
71
|
-
if (
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
};
|
|
283
|
+
function assertCodexHumanStartedIdentity(anchor, current) {
|
|
284
|
+
if (exactNativeThreadId(current.sessionId) !== anchor.native_thread_id ||
|
|
285
|
+
requiredString(current.processUuid, "Codex process UUID") !==
|
|
286
|
+
anchor.process_uuid ||
|
|
287
|
+
requiredString(current.processBirth, "Codex process birth") !==
|
|
288
|
+
anchor.process_birth ||
|
|
289
|
+
!current.rollout ||
|
|
290
|
+
!sameRolloutIdentity(normalizedRolloutIdentity(current.rollout), anchor.rollout)) {
|
|
291
|
+
throw new Error("Codex process, thread, or rollout identity changed after capture");
|
|
80
292
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
293
|
+
}
|
|
294
|
+
function latestCodexHumanStartedActiveTask(fd, endOffset, maxBytes) {
|
|
295
|
+
const lowerBound = Math.max(0, endOffset - maxBytes);
|
|
296
|
+
const terminalTurns = new Map();
|
|
297
|
+
const userRecords = [];
|
|
298
|
+
let cursor = endOffset;
|
|
299
|
+
let leadingRecord = Buffer.alloc(0);
|
|
300
|
+
while (cursor > lowerBound) {
|
|
301
|
+
const readStart = Math.max(lowerBound, cursor - CODEX_ACTIVE_TASK_SCAN_CHUNK_BYTES);
|
|
302
|
+
const chunk = readExactBytes(fd, readStart, cursor - readStart);
|
|
303
|
+
const combined = leadingRecord.length > 0
|
|
304
|
+
? Buffer.concat([chunk, leadingRecord])
|
|
305
|
+
: chunk;
|
|
306
|
+
let recordsBuffer = combined;
|
|
307
|
+
let recordsOffset = readStart;
|
|
308
|
+
if (readStart > 0) {
|
|
309
|
+
const firstNewline = combined.indexOf(0x0a);
|
|
310
|
+
if (firstNewline < 0) {
|
|
311
|
+
assertBoundedCodexActiveTaskRecord(combined.length);
|
|
312
|
+
leadingRecord = Buffer.from(combined);
|
|
313
|
+
cursor = readStart;
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
leadingRecord = Buffer.from(combined.subarray(0, firstNewline + 1));
|
|
317
|
+
assertBoundedCodexActiveTaskRecord(leadingRecord.length);
|
|
318
|
+
recordsBuffer = combined.subarray(firstNewline + 1);
|
|
319
|
+
recordsOffset += firstNewline + 1;
|
|
320
|
+
}
|
|
321
|
+
else {
|
|
322
|
+
leadingRecord = Buffer.alloc(0);
|
|
323
|
+
}
|
|
324
|
+
const records = parseCodexJsonlRecords(recordsBuffer, "active-task rollout tail");
|
|
325
|
+
for (let index = records.length - 1; index >= 0; index -= 1) {
|
|
326
|
+
const record = records[index];
|
|
327
|
+
const decision = inspectCodexActiveTaskRecordInReverse({
|
|
328
|
+
value: record.value,
|
|
329
|
+
offsetBytes: recordsOffset + record.offsetBytes
|
|
330
|
+
}, terminalTurns, userRecords);
|
|
331
|
+
if (decision.status === "resolved") {
|
|
332
|
+
return decision.active;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
cursor = readStart;
|
|
90
336
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
replayed: true,
|
|
94
|
-
delivered: false,
|
|
95
|
-
status: "submission_uncertain",
|
|
96
|
-
submission_outcome: "uncertain",
|
|
97
|
-
delivery_receipt: "enter_dispatched",
|
|
98
|
-
do_not_retry: true,
|
|
99
|
-
evidence_error: error instanceof Error ? error.message : String(error)
|
|
100
|
-
};
|
|
337
|
+
if (lowerBound > 0) {
|
|
338
|
+
throw new Error("Codex active-task boundary exceeded the bounded reverse scan limit");
|
|
101
339
|
}
|
|
340
|
+
return undefined;
|
|
341
|
+
}
|
|
342
|
+
function inspectCodexActiveTaskRecordInReverse(record, terminalTurns, userRecords) {
|
|
343
|
+
const { value, offsetBytes } = record;
|
|
344
|
+
const payload = isRecord(value.payload) ? value.payload : undefined;
|
|
345
|
+
if (!payload) {
|
|
346
|
+
return { status: "continue" };
|
|
347
|
+
}
|
|
348
|
+
if (value.type === "event_msg" &&
|
|
349
|
+
(payload.type === "task_complete" || payload.type === "turn_aborted")) {
|
|
350
|
+
const turnId = exactNativeThreadId(payload.turn_id);
|
|
351
|
+
const kind = payload.type === "task_complete" ? "completed" : "aborted";
|
|
352
|
+
const previous = terminalTurns.get(turnId);
|
|
353
|
+
if (previous) {
|
|
354
|
+
throw new Error(previous === kind
|
|
355
|
+
? `Codex rollout contains duplicate ${String(payload.type)} evidence`
|
|
356
|
+
: "Codex rollout contains conflicting terminal task evidence");
|
|
357
|
+
}
|
|
358
|
+
terminalTurns.set(turnId, kind);
|
|
359
|
+
return { status: "continue" };
|
|
360
|
+
}
|
|
361
|
+
if (value.type === "response_item" &&
|
|
362
|
+
payload.type === "message" &&
|
|
363
|
+
payload.role === "user") {
|
|
364
|
+
const metadata = isRecord(payload.internal_chat_message_metadata_passthrough)
|
|
365
|
+
? payload.internal_chat_message_metadata_passthrough
|
|
366
|
+
: undefined;
|
|
367
|
+
const rawTurnId = optionalString(metadata?.turn_id);
|
|
368
|
+
if (rawTurnId) {
|
|
369
|
+
const text = exactCodexUserResponseText(payload.content);
|
|
370
|
+
userRecords.push({
|
|
371
|
+
offsetBytes,
|
|
372
|
+
turnId: rawTurnId.toLowerCase(),
|
|
373
|
+
...(text === undefined ? {} : { requestHash: fingerprintText(text) })
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
return { status: "continue" };
|
|
377
|
+
}
|
|
378
|
+
if (value.type !== "event_msg" || payload.type !== "task_started") {
|
|
379
|
+
return { status: "continue" };
|
|
380
|
+
}
|
|
381
|
+
const turnId = exactNativeThreadId(payload.turn_id);
|
|
382
|
+
if (terminalTurns.has(turnId)) {
|
|
383
|
+
return { status: "resolved" };
|
|
384
|
+
}
|
|
385
|
+
const matchingUsers = userRecords.filter((user) => user.turnId === turnId);
|
|
386
|
+
if (matchingUsers.length === 0) {
|
|
387
|
+
return { status: "resolved" };
|
|
388
|
+
}
|
|
389
|
+
if (matchingUsers.length !== 1) {
|
|
390
|
+
throw new Error("Codex active task has multiple same-turn root user rows");
|
|
391
|
+
}
|
|
392
|
+
const [user] = matchingUsers;
|
|
393
|
+
if (user.requestHash === undefined) {
|
|
394
|
+
throw new Error("Codex active-task root user row has unsupported prompt content");
|
|
395
|
+
}
|
|
396
|
+
if (user.offsetBytes <= offsetBytes) {
|
|
397
|
+
throw new Error("Codex active-task root user row precedes task_started");
|
|
398
|
+
}
|
|
399
|
+
if (userRecords.some((candidate) => candidate.offsetBytes > user.offsetBytes && candidate.turnId !== turnId)) {
|
|
400
|
+
throw new Error("Codex active task is not the latest native human task");
|
|
401
|
+
}
|
|
402
|
+
return {
|
|
403
|
+
status: "resolved",
|
|
404
|
+
active: {
|
|
405
|
+
turnId,
|
|
406
|
+
requestHash: user.requestHash,
|
|
407
|
+
taskStartedOffsetBytes: offsetBytes,
|
|
408
|
+
userMessageOffsetBytes: user.offsetBytes
|
|
409
|
+
}
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
function assertBoundedCodexActiveTaskRecord(length) {
|
|
413
|
+
if (length > CODEX_ACTIVE_TASK_MAX_RECORD_BYTES) {
|
|
414
|
+
throw new Error("Codex active-task JSONL record exceeded the safe read limit");
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
function isLaterCodexHumanTaskRecord(value) {
|
|
418
|
+
const payload = isRecord(value.payload) ? value.payload : undefined;
|
|
419
|
+
return Boolean(payload &&
|
|
420
|
+
((value.type === "event_msg" && payload.type === "task_started") ||
|
|
421
|
+
(value.type === "response_item" &&
|
|
422
|
+
payload.type === "message" &&
|
|
423
|
+
payload.role === "user")));
|
|
424
|
+
}
|
|
425
|
+
function isExactCodexTaskCompleteRecord(value, expectedTurnId) {
|
|
426
|
+
const payload = isRecord(value.payload) ? value.payload : undefined;
|
|
427
|
+
return value.type === "event_msg" &&
|
|
428
|
+
payload?.type === "task_complete" &&
|
|
429
|
+
optionalString(payload.turn_id)?.toLowerCase() === expectedTurnId;
|
|
430
|
+
}
|
|
431
|
+
function isExactCodexTurnAbortedRecord(value, expectedTurnId) {
|
|
432
|
+
const payload = isRecord(value.payload) ? value.payload : undefined;
|
|
433
|
+
return value.type === "event_msg" &&
|
|
434
|
+
payload?.type === "turn_aborted" &&
|
|
435
|
+
optionalString(payload.turn_id)?.toLowerCase() === expectedTurnId;
|
|
436
|
+
}
|
|
437
|
+
function codexHumanStartedTaskAbortCompletion(anchor, value, observedEndOffsetBytes) {
|
|
438
|
+
const payload = isRecord(value.payload) ? value.payload : undefined;
|
|
439
|
+
if (!payload || payload.type !== "turn_aborted") {
|
|
440
|
+
throw new Error("Codex active-task abort evidence is invalid");
|
|
441
|
+
}
|
|
442
|
+
const reason = optionalString(payload.reason) ?? "interrupted";
|
|
443
|
+
if (value.timestamp !== undefined && !validTimestamp(value.timestamp)) {
|
|
444
|
+
throw new Error("Codex active-task turn_aborted record has an invalid timestamp");
|
|
445
|
+
}
|
|
446
|
+
const redactedReason = truncateCompletionText(redactString(reason));
|
|
447
|
+
return {
|
|
448
|
+
status: "completed",
|
|
449
|
+
completion: {
|
|
450
|
+
source: "durable",
|
|
451
|
+
outcome: "failure",
|
|
452
|
+
text: truncateCompletionText(redactString(`Codex task stopped: ${redactedReason}`)),
|
|
453
|
+
...(value.timestamp !== undefined
|
|
454
|
+
? { timestamp: String(value.timestamp) }
|
|
455
|
+
: {}),
|
|
456
|
+
id: anchor.turn_id,
|
|
457
|
+
confidence: "high",
|
|
458
|
+
metadata: {
|
|
459
|
+
match: "human_started_bound_rollout_turn_aborted",
|
|
460
|
+
turn_id: anchor.turn_id,
|
|
461
|
+
native_thread_id: anchor.native_thread_id,
|
|
462
|
+
anchor_fingerprint: anchor.anchor_fingerprint,
|
|
463
|
+
abort_reason: redactedReason,
|
|
464
|
+
observed_end_offset_bytes: observedEndOffsetBytes
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
function parseCodexJsonlRecords(buffer, label) {
|
|
470
|
+
if (buffer.length > 0 && buffer[buffer.length - 1] !== 0x0a) {
|
|
471
|
+
throw new Error(`Codex ${label} ends with an incomplete JSONL record`);
|
|
472
|
+
}
|
|
473
|
+
const records = [];
|
|
474
|
+
let offset = 0;
|
|
475
|
+
while (offset < buffer.length) {
|
|
476
|
+
const newline = buffer.indexOf(0x0a, offset);
|
|
477
|
+
if (newline < 0) {
|
|
478
|
+
break;
|
|
479
|
+
}
|
|
480
|
+
const recordOffset = offset;
|
|
481
|
+
let lineBuffer = buffer.subarray(offset, newline);
|
|
482
|
+
if (lineBuffer.at(-1) === 0x0d) {
|
|
483
|
+
lineBuffer = lineBuffer.subarray(0, lineBuffer.length - 1);
|
|
484
|
+
}
|
|
485
|
+
offset = newline + 1;
|
|
486
|
+
const line = lineBuffer.toString("utf8");
|
|
487
|
+
if (!line.trim()) {
|
|
488
|
+
continue;
|
|
489
|
+
}
|
|
490
|
+
let parsed;
|
|
491
|
+
try {
|
|
492
|
+
parsed = JSON.parse(line);
|
|
493
|
+
}
|
|
494
|
+
catch {
|
|
495
|
+
throw new Error(`Codex ${label} contains invalid JSONL`);
|
|
496
|
+
}
|
|
497
|
+
if (!isRecord(parsed)) {
|
|
498
|
+
throw new Error(`Codex ${label} contains a non-object JSONL record`);
|
|
499
|
+
}
|
|
500
|
+
records.push({ value: parsed, offsetBytes: recordOffset });
|
|
501
|
+
}
|
|
502
|
+
return records;
|
|
503
|
+
}
|
|
504
|
+
function assertExistingCodexRolloutHeader(buffer, nativeThreadId) {
|
|
505
|
+
const first = parseCodexJsonlRecords(buffer, "active-task rollout")[0]?.value;
|
|
506
|
+
const payload = first?.type === "session_meta" && isRecord(first.payload)
|
|
507
|
+
? first.payload
|
|
508
|
+
: undefined;
|
|
509
|
+
if (!payload ||
|
|
510
|
+
exactNativeThreadId(payload.id) !== nativeThreadId ||
|
|
511
|
+
payload.originator !== "codex-tui" ||
|
|
512
|
+
payload.source !== "cli") {
|
|
513
|
+
throw new Error("Codex active-task rollout header does not identify the exact CLI thread");
|
|
514
|
+
}
|
|
515
|
+
const codexVersion = optionalString(payload.cli_version);
|
|
516
|
+
if (!codexVersion || !CODEX_VERSION_PATTERN.test(codexVersion)) {
|
|
517
|
+
throw new Error("Codex active-task rollout header has no exact CLI version");
|
|
518
|
+
}
|
|
519
|
+
return codexVersion;
|
|
520
|
+
}
|
|
521
|
+
function readCodexRolloutHeader(fd, size) {
|
|
522
|
+
const bytesToRead = Math.min(size, CODEX_ROLLOUT_HEADER_MAX_BYTES);
|
|
523
|
+
const buffer = readExactBytes(fd, 0, bytesToRead);
|
|
524
|
+
const newline = buffer.indexOf(0x0a);
|
|
525
|
+
if (newline < 0) {
|
|
526
|
+
throw new Error("Codex active-task rollout header exceeded the safe read limit");
|
|
527
|
+
}
|
|
528
|
+
return buffer.subarray(0, newline + 1);
|
|
529
|
+
}
|
|
530
|
+
function readExactBytes(fd, offset, length) {
|
|
531
|
+
const buffer = Buffer.allocUnsafe(length);
|
|
532
|
+
let total = 0;
|
|
533
|
+
while (total < length) {
|
|
534
|
+
const read = fs.readSync(fd, buffer, total, length - total, offset + total);
|
|
535
|
+
if (read === 0) {
|
|
536
|
+
break;
|
|
537
|
+
}
|
|
538
|
+
total += read;
|
|
539
|
+
}
|
|
540
|
+
if (total !== length) {
|
|
541
|
+
throw new Error("Codex rollout changed while it was being read");
|
|
542
|
+
}
|
|
543
|
+
return buffer;
|
|
544
|
+
}
|
|
545
|
+
function positiveByteLimit(value, fallback, label) {
|
|
546
|
+
const result = value ?? fallback;
|
|
547
|
+
if (!Number.isSafeInteger(result) || result <= 0) {
|
|
548
|
+
throw new Error(`${label} byte limit is invalid`);
|
|
549
|
+
}
|
|
550
|
+
return result;
|
|
551
|
+
}
|
|
552
|
+
function safeByteOffset(value, label) {
|
|
553
|
+
if (!Number.isSafeInteger(value) || Number(value) < 0) {
|
|
554
|
+
throw new Error(`${label} is invalid`);
|
|
555
|
+
}
|
|
556
|
+
return Number(value);
|
|
102
557
|
}
|
|
103
|
-
const CODEX_ACCEPTANCE_MAX_BYTES = 16 * 1024 * 1024;
|
|
104
|
-
const CODEX_COMPLETION_MAX_BYTES = 256 * 1024 * 1024;
|
|
105
|
-
const CODEX_COMPLETION_MAX_TEXT_LENGTH = 4000;
|
|
106
|
-
const NATIVE_THREAD_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/iu;
|
|
107
|
-
const NO_FOLLOW_FLAG = typeof fs.constants.O_NOFOLLOW === "number"
|
|
108
|
-
? fs.constants.O_NOFOLLOW
|
|
109
|
-
: 0;
|
|
110
558
|
export function captureCodexRolloutAcceptanceAnchor(options) {
|
|
111
559
|
const processUuid = requiredString(options.processUuid, "Codex process UUID");
|
|
112
560
|
const processBirth = requiredString(options.processBirth, "Codex process birth");
|
|
@@ -898,91 +1346,6 @@ function assertVirginRolloutHeader(options) {
|
|
|
898
1346
|
throw new Error("virgin Codex rollout predates its terminal submission anchor");
|
|
899
1347
|
}
|
|
900
1348
|
}
|
|
901
|
-
export function validateCodexRolloutAcceptanceAnchor(value) {
|
|
902
|
-
if (!isRecord(value) ||
|
|
903
|
-
value.schema !== "agent-knock-knock/codex-rollout-acceptance-anchor" ||
|
|
904
|
-
(value.version !== 1 && value.version !== 2 && value.version !== 3) ||
|
|
905
|
-
!["existing", "pre_materialization", "candidate_set"].includes(String(value.mode)) ||
|
|
906
|
-
!Number.isSafeInteger(value.offset_bytes) ||
|
|
907
|
-
value.offset_bytes < 0 ||
|
|
908
|
-
typeof value.file_existed !== "boolean") {
|
|
909
|
-
throw new Error("Codex rollout acceptance anchor is invalid");
|
|
910
|
-
}
|
|
911
|
-
if (value.version === 1) {
|
|
912
|
-
exactNativeThreadId(value.native_thread_id);
|
|
913
|
-
if ("native_thread_binding" in value) {
|
|
914
|
-
throw new Error("bound Codex acceptance anchor has deferred binding state");
|
|
915
|
-
}
|
|
916
|
-
}
|
|
917
|
-
else if (value.version === 2 && (value.mode !== "pre_materialization" ||
|
|
918
|
-
value.native_thread_binding !== "post_submission" ||
|
|
919
|
-
"native_thread_id" in value)) {
|
|
920
|
-
throw new Error("virgin Codex acceptance anchor is inconsistent");
|
|
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
|
-
}
|
|
938
|
-
requiredString(value.process_uuid, "Codex process UUID");
|
|
939
|
-
requiredString(value.process_birth, "Codex process birth");
|
|
940
|
-
if (!validTimestamp(value.captured_at)) {
|
|
941
|
-
throw new Error("Codex acceptance capture timestamp is invalid");
|
|
942
|
-
}
|
|
943
|
-
sha256Value(value.anchor_fingerprint, "Codex acceptance anchor fingerprint");
|
|
944
|
-
const { anchor_fingerprint: _fingerprint, ...base } = value;
|
|
945
|
-
if (fingerprint(base) !== value.anchor_fingerprint) {
|
|
946
|
-
throw new Error("Codex rollout acceptance anchor fingerprint does not match");
|
|
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
|
-
}
|
|
968
|
-
const rollout = "rollout" in value ? value.rollout : undefined;
|
|
969
|
-
if (value.file_existed !== (value.mode === "existing") ||
|
|
970
|
-
value.file_existed !== Boolean(rollout)) {
|
|
971
|
-
throw new Error("Codex rollout acceptance anchor file state is inconsistent");
|
|
972
|
-
}
|
|
973
|
-
if (value.mode === "existing" && rollout) {
|
|
974
|
-
normalizedRolloutIdentity(rollout);
|
|
975
|
-
if (value.expected_empty_native_session !== undefined) {
|
|
976
|
-
throw new Error("existing Codex acceptance anchor has pre-materialization state");
|
|
977
|
-
}
|
|
978
|
-
}
|
|
979
|
-
else if (value.offset_bytes !== 0 ||
|
|
980
|
-
rollout !== undefined ||
|
|
981
|
-
value.expected_empty_native_session !== true) {
|
|
982
|
-
throw new Error("Codex pre-materialization acceptance anchor is inconsistent");
|
|
983
|
-
}
|
|
984
|
-
return value;
|
|
985
|
-
}
|
|
986
1349
|
function validateCodexOpenRootInventoryForAcceptance(value) {
|
|
987
1350
|
if (!isRecord(value) ||
|
|
988
1351
|
value.schema !==
|
|
@@ -1080,6 +1443,21 @@ function fileEndsWithNewline(fd, size) {
|
|
|
1080
1443
|
const last = Buffer.allocUnsafe(1);
|
|
1081
1444
|
return fs.readSync(fd, last, 0, 1, size - 1) === 1 && last[0] === 0x0a;
|
|
1082
1445
|
}
|
|
1446
|
+
function byteAtOffset(fd, offset) {
|
|
1447
|
+
const buffer = Buffer.allocUnsafe(1);
|
|
1448
|
+
return fs.readSync(fd, buffer, 0, 1, offset) === 1
|
|
1449
|
+
? buffer[0]
|
|
1450
|
+
: undefined;
|
|
1451
|
+
}
|
|
1452
|
+
function isRetryableProviderIoError(error) {
|
|
1453
|
+
if (!isRecord(error) || typeof error.code !== "string") {
|
|
1454
|
+
return false;
|
|
1455
|
+
}
|
|
1456
|
+
return new Set([
|
|
1457
|
+
"EACCES", "EAGAIN", "EBUSY", "EIO", "EMFILE", "ENFILE", "ENOENT",
|
|
1458
|
+
"EPERM", "ESTALE", "ETIMEDOUT"
|
|
1459
|
+
]).has(error.code);
|
|
1460
|
+
}
|
|
1083
1461
|
function sameStableFile(left, right) {
|
|
1084
1462
|
return left.dev === right.dev &&
|
|
1085
1463
|
left.ino === right.ino &&
|
|
@@ -1087,65 +1465,20 @@ function sameStableFile(left, right) {
|
|
|
1087
1465
|
left.mtimeMs === right.mtimeMs &&
|
|
1088
1466
|
left.ctimeMs === right.ctimeMs;
|
|
1089
1467
|
}
|
|
1090
|
-
function normalizedRolloutIdentity(value) {
|
|
1091
|
-
const normalized = {
|
|
1092
|
-
fd: requiredString(value.fd, "Codex rollout descriptor"),
|
|
1093
|
-
device: requiredString(value.device, "Codex rollout device"),
|
|
1094
|
-
inode: requiredString(value.inode, "Codex rollout inode"),
|
|
1095
|
-
path: requiredString(value.path, "Codex rollout path")
|
|
1096
|
-
};
|
|
1097
|
-
parsedInteger(normalized.device);
|
|
1098
|
-
parsedInteger(normalized.inode);
|
|
1099
|
-
return normalized;
|
|
1100
|
-
}
|
|
1101
1468
|
function sameRolloutIdentity(left, right) {
|
|
1102
1469
|
return left.fd === right.fd &&
|
|
1103
1470
|
left.device === right.device &&
|
|
1104
1471
|
left.inode === right.inode &&
|
|
1105
1472
|
left.path === right.path;
|
|
1106
1473
|
}
|
|
1107
|
-
function exactNativeThreadId(value) {
|
|
1108
|
-
const text = requiredString(value, "Codex native thread ID").toLowerCase();
|
|
1109
|
-
if (!NATIVE_THREAD_ID_PATTERN.test(text)) {
|
|
1110
|
-
throw new Error("Codex native thread ID is not an exact UUID");
|
|
1111
|
-
}
|
|
1112
|
-
return text;
|
|
1113
|
-
}
|
|
1114
|
-
function parsedInteger(value) {
|
|
1115
|
-
try {
|
|
1116
|
-
return BigInt(value);
|
|
1117
|
-
}
|
|
1118
|
-
catch {
|
|
1119
|
-
throw new Error("Codex rollout file identity is not numeric");
|
|
1120
|
-
}
|
|
1121
|
-
}
|
|
1122
|
-
function sha256Value(value, label) {
|
|
1123
|
-
const text = requiredString(value, label).toLowerCase();
|
|
1124
|
-
if (!/^[0-9a-f]{64}$/u.test(text)) {
|
|
1125
|
-
throw new Error(`${label} is not a SHA-256 value`);
|
|
1126
|
-
}
|
|
1127
|
-
return text;
|
|
1128
|
-
}
|
|
1129
|
-
function requiredString(value, label) {
|
|
1130
|
-
if (typeof value !== "string" || value.trim().length === 0) {
|
|
1131
|
-
throw new Error(`${label} is unavailable`);
|
|
1132
|
-
}
|
|
1133
|
-
return value.trim();
|
|
1134
|
-
}
|
|
1135
1474
|
function optionalString(value) {
|
|
1136
1475
|
return typeof value === "string" && value.trim().length > 0
|
|
1137
1476
|
? value.trim()
|
|
1138
1477
|
: undefined;
|
|
1139
1478
|
}
|
|
1140
|
-
function validTimestamp(value) {
|
|
1141
|
-
return typeof value === "string" && Number.isFinite(Date.parse(value));
|
|
1142
|
-
}
|
|
1143
1479
|
function fingerprintText(value) {
|
|
1144
1480
|
return createHash("sha256").update(value).digest("hex");
|
|
1145
1481
|
}
|
|
1146
|
-
function fingerprint(value) {
|
|
1147
|
-
return createHash("sha256").update(JSON.stringify(value)).digest("hex");
|
|
1148
|
-
}
|
|
1149
1482
|
function isRecord(value) {
|
|
1150
1483
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
1151
1484
|
}
|