@ornexus/neocortex 4.60.68 → 4.60.69

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.
Files changed (232) hide show
  1. package/dist/release-source.json +1 -1
  2. package/dist/sbom.cdx.json +1194 -1194
  3. package/install.ps1 +1 -1
  4. package/install.sh +1 -1
  5. package/package.json +2 -2
  6. package/packages/client/dist/adapters/adapter-registry.js +1 -108
  7. package/packages/client/dist/adapters/antigravity-adapter.js +2 -77
  8. package/packages/client/dist/adapters/claude-code-adapter.js +3 -79
  9. package/packages/client/dist/adapters/codex-adapter.js +2 -80
  10. package/packages/client/dist/adapters/cursor-adapter.js +4 -115
  11. package/packages/client/dist/adapters/gemini-adapter.js +2 -71
  12. package/packages/client/dist/adapters/grok-adapter.js +2 -42
  13. package/packages/client/dist/adapters/index.js +1 -22
  14. package/packages/client/dist/adapters/platform-detector.js +1 -212
  15. package/packages/client/dist/adapters/target-adapter.js +0 -12
  16. package/packages/client/dist/adapters/vscode-adapter.js +2 -72
  17. package/packages/client/dist/agent/refresh-stubs.js +2 -417
  18. package/packages/client/dist/agent/update-agent-yaml.js +1 -102
  19. package/packages/client/dist/agent/update-description.js +1 -251
  20. package/packages/client/dist/cache/crypto-utils.js +1 -78
  21. package/packages/client/dist/cache/encrypted-cache.js +1 -108
  22. package/packages/client/dist/cache/in-memory-asset-cache.js +1 -73
  23. package/packages/client/dist/cache/index.js +1 -13
  24. package/packages/client/dist/cache/protected-pi-boundary.js +1 -44
  25. package/packages/client/dist/checkpoint/checkpoint-client-reader.js +2 -127
  26. package/packages/client/dist/checkpoint/index.js +1 -11
  27. package/packages/client/dist/checkpoint/shared-checkpoint-types.js +1 -32
  28. package/packages/client/dist/cli.js +3 -290
  29. package/packages/client/dist/commands/activate.js +8 -394
  30. package/packages/client/dist/commands/architecture-answers.js +7 -274
  31. package/packages/client/dist/commands/architecture-memory-terminal.js +4 -253
  32. package/packages/client/dist/commands/architecture-non-ui-continuation.js +1 -100
  33. package/packages/client/dist/commands/architecture-root-binding.js +1 -51
  34. package/packages/client/dist/commands/cache-status.js +2 -112
  35. package/packages/client/dist/commands/config.js +1 -88
  36. package/packages/client/dist/commands/doctor.js +2 -635
  37. package/packages/client/dist/commands/invoke.d.ts +6 -0
  38. package/packages/client/dist/commands/invoke.js +142 -7140
  39. package/packages/client/dist/commands/legacy-epic-terminal.js +1 -71
  40. package/packages/client/dist/commands/legacy-story-branch.js +1 -96
  41. package/packages/client/dist/commands/legacy-story-pr.js +1 -276
  42. package/packages/client/dist/commands/mutation-qa-checkpoint.js +5 -285
  43. package/packages/client/dist/commands/mutation-qa-gate.js +1 -54
  44. package/packages/client/dist/commands/mutation-qa-git.js +1 -41
  45. package/packages/client/dist/commands/mutation-qa-policy.js +1 -102
  46. package/packages/client/dist/commands/mutation-qa-run.js +3 -219
  47. package/packages/client/dist/commands/mutation-qa-verdict.js +1 -149
  48. package/packages/client/dist/commands/mutation-qa.js +1 -220
  49. package/packages/client/dist/commands/native-agent.js +2 -136
  50. package/packages/client/dist/commands/planning-diagnosis.js +1 -43
  51. package/packages/client/dist/commands/planning-generation.js +1 -53
  52. package/packages/client/dist/commands/planning-memory.js +1 -40
  53. package/packages/client/dist/commands/planning-research.js +1 -42
  54. package/packages/client/dist/commands/private-session.js +1 -75
  55. package/packages/client/dist/commands/property-qa.js +1 -139
  56. package/packages/client/dist/commands/qa-control.js +1 -16
  57. package/packages/client/dist/commands/qa-scan-browser-launch.js +1 -55
  58. package/packages/client/dist/commands/qa-scan-browser-route.js +1 -28
  59. package/packages/client/dist/commands/qa-scan-capture.js +5 -663
  60. package/packages/client/dist/commands/qa-scan-discovery.js +3 -376
  61. package/packages/client/dist/commands/qa-scan-review.js +5 -826
  62. package/packages/client/dist/commands/qa-synthetic-backend-capture.js +1 -266
  63. package/packages/client/dist/commands/qa-synthetic-backend-review.js +1 -297
  64. package/packages/client/dist/commands/qa-synthetic-backend.js +2 -340
  65. package/packages/client/dist/commands/qa-synthetic-broker-port.js +1 -85
  66. package/packages/client/dist/commands/qa-synthetic-browser.js +1 -39
  67. package/packages/client/dist/commands/refactor-characterization.js +21 -671
  68. package/packages/client/dist/commands/refactor-commit.js +13 -399
  69. package/packages/client/dist/commands/refactor-control.js +3 -142
  70. package/packages/client/dist/commands/refactor-cpp-assertions.js +3 -93
  71. package/packages/client/dist/commands/refactor-review.js +5 -283
  72. package/packages/client/dist/commands/refactor-root.js +7 -588
  73. package/packages/client/dist/commands/refresh-memory.js +3 -156
  74. package/packages/client/dist/commands/shared-architecture-non-ui.js +1 -24
  75. package/packages/client/dist/commands/story-checkpoint.js +1 -171
  76. package/packages/client/dist/config/profile-directory.js +1 -13
  77. package/packages/client/dist/config/resolver-selection.js +1 -278
  78. package/packages/client/dist/config/secure-config.js +12 -281
  79. package/packages/client/dist/config.js +2 -223
  80. package/packages/client/dist/constants.js +1 -25
  81. package/packages/client/dist/context/context-collector.js +2 -222
  82. package/packages/client/dist/context/context-sanitizer.js +1 -145
  83. package/packages/client/dist/continuity/autonomous-governance.js +1 -285
  84. package/packages/client/dist/continuity/continuity-client-state-store.js +1 -1587
  85. package/packages/client/dist/continuity/invoke-hooks.js +1 -129
  86. package/packages/client/dist/continuity/migrations/001-initial-schema.js +2 -12
  87. package/packages/client/dist/continuity/migrations/002-processing-output-summary-indexes.js +2 -12
  88. package/packages/client/dist/continuity/migrations/003-headless-execution-attempts.js +2 -12
  89. package/packages/client/dist/continuity/migrations/004-outbox-delivery-state.js +2 -12
  90. package/packages/client/dist/continuity/migrations/005-bounded-headless-queue.js +2 -12
  91. package/packages/client/dist/continuity/migrations/006-native-agent-dispatches.js +2 -10
  92. package/packages/client/dist/continuity/native-agent-store.js +1 -69
  93. package/packages/client/dist/continuity/native-agent-types.js +1 -90
  94. package/packages/client/dist/continuity/outbox.js +1 -117
  95. package/packages/client/dist/continuity/runner-bootstrap-policy.js +1 -172
  96. package/packages/client/dist/continuity/sqlite-store.js +81 -2536
  97. package/packages/client/dist/effects/invoke-effect-gateways.js +1 -62
  98. package/packages/client/dist/errors/error-messages.js +2 -228
  99. package/packages/client/dist/errors/frontmatter-diagnostics.js +1 -125
  100. package/packages/client/dist/evidence/collector.js +1 -347
  101. package/packages/client/dist/evidence/index.js +1 -1
  102. package/packages/client/dist/evidence/types.js +0 -2
  103. package/packages/client/dist/fs/atomic-state-replace.js +1 -152
  104. package/packages/client/dist/generated/command-registry.generated.js +1 -4076
  105. package/packages/client/dist/generated/public-projections.generated.js +1 -149
  106. package/packages/client/dist/git/direct-yolo-c01-retry.js +4 -138
  107. package/packages/client/dist/git/direct-yolo-checkpoint.js +4 -1234
  108. package/packages/client/dist/git/git-lifecycle-step-ids.js +1 -14
  109. package/packages/client/dist/git/git-lifecycle-store.js +9 -1938
  110. package/packages/client/dist/git/legacy-story-branch-checkpoint.js +9 -259
  111. package/packages/client/dist/git/legacy-story-branch-setup.js +4 -348
  112. package/packages/client/dist/git/legacy-story-pr.js +70 -3538
  113. package/packages/client/dist/git/secure-filesystem-boundary.js +5 -182
  114. package/packages/client/dist/graph-retrieval/pre-command-hook.js +1 -284
  115. package/packages/client/dist/graph-retrieval/shared-graph-retrieval-contract.js +1 -237
  116. package/packages/client/dist/i18n/first-run.js +2 -60
  117. package/packages/client/dist/index.js +1 -47
  118. package/packages/client/dist/invocation/invoke-input.js +1 -24
  119. package/packages/client/dist/license/index.js +1 -5
  120. package/packages/client/dist/license/license-client.js +1 -304
  121. package/packages/client/dist/machine/fingerprint.js +2 -163
  122. package/packages/client/dist/machine/index.js +1 -5
  123. package/packages/client/dist/memory/project-memory-path-scope.js +1 -62
  124. package/packages/client/dist/memory/project-memory-writer.js +40 -1007
  125. package/packages/client/dist/memory/shared-project-memory-types.js +2 -253
  126. package/packages/client/dist/migrations/project-identity-capability.js +1 -50
  127. package/packages/client/dist/migrations/project-identity.js +7 -493
  128. package/packages/client/dist/migrations/state-json.js +4 -344
  129. package/packages/client/dist/policy/architecture-policy.js +2 -184
  130. package/packages/client/dist/policy/index.js +1 -7
  131. package/packages/client/dist/policy/shared-policy-types.js +0 -38
  132. package/packages/client/dist/policy/subagent-approval-policy.js +1 -55
  133. package/packages/client/dist/private-execution/clean-baseline.js +2 -279
  134. package/packages/client/dist/private-execution/codex-executor.js +4 -574
  135. package/packages/client/dist/private-execution/codex-host-runtime.js +1 -137
  136. package/packages/client/dist/private-execution/codex-lifecycle-guard.js +1 -97
  137. package/packages/client/dist/private-execution/codex-scoped-permissions.js +1 -128
  138. package/packages/client/dist/private-execution/delivery.js +13 -410
  139. package/packages/client/dist/private-execution/diagnosis-root-review.js +2 -294
  140. package/packages/client/dist/private-execution/frozen-handoff.js +6 -19
  141. package/packages/client/dist/private-execution/implementation-validation.js +2 -106
  142. package/packages/client/dist/private-execution/isolated-admission.js +23 -1432
  143. package/packages/client/dist/private-execution/isolated-session.js +5 -725
  144. package/packages/client/dist/private-execution/isolated-workspace.js +1 -122
  145. package/packages/client/dist/private-execution/pact-consumer-runtime.js +3 -409
  146. package/packages/client/dist/private-execution/pact-test-source.js +1 -258
  147. package/packages/client/dist/private-execution/pact-toolchain.js +3 -290
  148. package/packages/client/dist/private-execution/pact-write-set.js +1 -172
  149. package/packages/client/dist/private-execution/planning-diagnosis-checkpoint.js +5 -406
  150. package/packages/client/dist/private-execution/planning-generation-checkpoint.js +5 -462
  151. package/packages/client/dist/private-execution/planning-memory-checkpoint.js +3 -139
  152. package/packages/client/dist/private-execution/planning-research-checkpoint.js +4 -325
  153. package/packages/client/dist/private-execution/private-report-store.js +1 -42
  154. package/packages/client/dist/private-execution/property-test-candidate.js +1 -103
  155. package/packages/client/dist/private-execution/property-test-choices.js +3 -147
  156. package/packages/client/dist/private-execution/property-test-plan.js +7 -271
  157. package/packages/client/dist/private-execution/property-test-review.js +4 -258
  158. package/packages/client/dist/private-execution/property-test-runtime.js +9 -372
  159. package/packages/client/dist/private-execution/property-test-source.js +1 -139
  160. package/packages/client/dist/private-execution/root-checkpoint.js +1 -80
  161. package/packages/client/dist/private-execution/root-memory-git-evidence.js +3 -135
  162. package/packages/client/dist/private-execution/root-memory-recovery.js +1 -93
  163. package/packages/client/dist/private-execution/root-memory-request.js +1 -29
  164. package/packages/client/dist/private-execution/root-memory-scope.js +1 -178
  165. package/packages/client/dist/private-execution/root-memory-writer.js +1 -102
  166. package/packages/client/dist/private-execution/session-lease.js +1 -63
  167. package/packages/client/dist/private-execution/shared-private-admission-types.js +1 -50
  168. package/packages/client/dist/private-execution/shared-project-validation.js +1 -66
  169. package/packages/client/dist/private-execution/shared-root-memory-types.js +1 -61
  170. package/packages/client/dist/private-execution/ux-source-validation.js +1 -372
  171. package/packages/client/dist/private-execution/ux-write-set.js +2 -191
  172. package/packages/client/dist/resilience/circuit-breaker.js +1 -170
  173. package/packages/client/dist/resilience/degradation-manager.js +1 -164
  174. package/packages/client/dist/resilience/freshness-indicator.js +1 -100
  175. package/packages/client/dist/resilience/index.js +1 -8
  176. package/packages/client/dist/resilience/recovery-detector.js +1 -74
  177. package/packages/client/dist/resolvers/asset-resolver.js +0 -13
  178. package/packages/client/dist/resolvers/local-resolver.js +8 -255
  179. package/packages/client/dist/resolvers/remote-resolver.js +1 -233
  180. package/packages/client/dist/runner/binary.js +1 -404
  181. package/packages/client/dist/runner/cli.js +17 -1571
  182. package/packages/client/dist/runner/headless/adapters.js +1 -49
  183. package/packages/client/dist/runner/headless/claude-code-adapter.js +1 -79
  184. package/packages/client/dist/runner/headless/continuous-story-workspace.js +11 -1628
  185. package/packages/client/dist/runner/headless/epic-git-reconciliation-inventory.js +1 -69
  186. package/packages/client/dist/runner/headless/epic-merge-candidate-qa.js +2 -110
  187. package/packages/client/dist/runner/headless/epic-terminal-git-cli-adapter.js +8 -1236
  188. package/packages/client/dist/runner/headless/epic-terminal-git-runner.js +2 -318
  189. package/packages/client/dist/runner/headless/fake-adapter.js +1 -62
  190. package/packages/client/dist/runner/headless/managed-profile.js +1 -132
  191. package/packages/client/dist/runner/headless/opencode-adapter.js +2 -145
  192. package/packages/client/dist/runner/headless/prepared-story-bundle.js +1 -43
  193. package/packages/client/dist/runner/headless/resolver.js +1 -231
  194. package/packages/client/dist/runner/headless/run-yolo.js +1 -885
  195. package/packages/client/dist/runner/headless/run-yoloop.js +2 -2574
  196. package/packages/client/dist/runner/headless/types.js +2 -574
  197. package/packages/client/dist/runner/local-console.js +13 -396
  198. package/packages/client/dist/runner/portal-sync-worker.js +1 -1487
  199. package/packages/client/dist/runner/project-identity.js +1 -106
  200. package/packages/client/dist/runner/scheduler.js +7 -995
  201. package/packages/client/dist/runner-cli.js +2 -18
  202. package/packages/client/dist/state/invocation-shape.js +1 -13
  203. package/packages/client/dist/state/project-state-snapshot.js +1 -1677
  204. package/packages/client/dist/state/state-json-repair.js +3 -785
  205. package/packages/client/dist/state/structural-dag-projection.js +1 -118
  206. package/packages/client/dist/telemetry/index.js +1 -5
  207. package/packages/client/dist/telemetry/offline-queue.js +1 -131
  208. package/packages/client/dist/tier/index.js +1 -5
  209. package/packages/client/dist/tier/tier-aware-client.js +1 -405
  210. package/packages/client/dist/types/index.js +1 -38
  211. package/packages/client/dist/version.js +1 -27
  212. package/packages/client/dist/yolo/change-evidence-collector.js +1 -126
  213. package/packages/client/dist/yolo/invoke-envelope-capability.js +1 -62
  214. package/packages/client/dist/yolo/spec-envelope-collector.js +5 -185
  215. package/packages/client/dist/yoloop/discovery-hook.js +2 -300
  216. package/packages/client/dist/yoloop/index.js +1 -8
  217. package/packages/client/dist/yoloop/invoke-hooks.js +12 -669
  218. package/packages/client/dist/yoloop/shared-discover-epics.js +1 -1007
  219. package/packages/client/dist/yoloop/shared-yoloop-types.js +1 -33
  220. package/packages/client/dist/yoloop/target-normalization.js +1 -180
  221. package/packages/client/dist/yoloop/yoloop-client-state-store.js +1 -1723
  222. package/targets-stubs/antigravity/gemini.md +1 -1
  223. package/targets-stubs/antigravity/skill/SKILL.md +1 -1
  224. package/targets-stubs/claude-code/neocortex-root.agent.yaml +1 -1
  225. package/targets-stubs/claude-code/neocortex-root.md +2 -2
  226. package/targets-stubs/claude-code/neocortex.agent.yaml +1 -1
  227. package/targets-stubs/claude-code/neocortex.md +2 -2
  228. package/targets-stubs/codex/AGENTS.md +1 -1
  229. package/targets-stubs/cursor/agent.md +2 -2
  230. package/targets-stubs/gemini-cli/agent.md +2 -2
  231. package/targets-stubs/opencode/neocortex-root.md +1 -1
  232. package/targets-stubs/vscode/neocortex.agent.md +2 -2
@@ -1,1186 +1,31 @@
1
- /**
2
- * @license FSL-1.1
3
- * Copyright (c) 2026 OrNexus AI
4
- *
5
- * P180.04 -- Local SQLite continuity store, migrations and integrity checks.
6
- */
7
- import { Buffer } from 'node:buffer';
8
- import { createHash } from 'node:crypto';
9
- import { chmodSync, closeSync, existsSync, mkdirSync, openSync, readSync } from 'node:fs';
10
- import { dirname, join } from 'node:path';
11
- import { INITIAL_CONTINUITY_SQLITE_MIGRATION } from './migrations/001-initial-schema.js';
12
- import { PROCESSING_OUTPUT_SUMMARY_SQLITE_MIGRATION } from './migrations/002-processing-output-summary-indexes.js';
13
- import { HEADLESS_EXECUTION_ATTEMPTS_SQLITE_MIGRATION } from './migrations/003-headless-execution-attempts.js';
14
- import { OUTBOX_DELIVERY_STATE_SQLITE_MIGRATION } from './migrations/004-outbox-delivery-state.js';
15
- import { BOUNDED_HEADLESS_QUEUE_SQLITE_MIGRATION } from './migrations/005-bounded-headless-queue.js';
16
- import { NATIVE_AGENT_DISPATCHES_SQLITE_MIGRATION } from './migrations/006-native-agent-dispatches.js';
17
- import { NATIVE_AGENT_TERMINAL_STATES, isNativeAgentDispatchReceiptV1, isNativeAgentStateTransitionAllowed, isNativeDispatchBindingV1, isNativeHostAttestationV1, } from './native-agent-types.js';
18
- import { CONTINUITY_QUEUE_REASON_CODES, CONTINUITY_RECEIPT_COMPACTION_EVENT_TYPE, CONTINUITY_RECEIPT_RETENTION_REASON_CODES, DEFAULT_CONTINUITY_QUEUE_POLICY, computeContinuityQueueRetryAt, computeContinuityOutboxFailureDecision, normalizeContinuityQueuePolicy, normalizeContinuityQueuePriority, normalizeContinuityQueueScope, normalizeContinuityRetentionPolicy, } from './outbox.js';
19
- export const CONTINUITY_SQLITE_RELATIVE_DIR = '.neocortex/continuity';
20
- export const CONTINUITY_SQLITE_FILENAME = 'continuity.sqlite';
21
- export const CONTINUITY_SQLITE_SCHEMA_VERSION = 6;
22
- export const DEFAULT_CONTINUITY_SQLITE_BUSY_TIMEOUT_MS = 5_000;
23
- export const CONTINUITY_PROCESSING_OUTPUT_SUMMARY_EVENT_TYPE = 'processing_output_summary';
24
- export const CONTINUITY_PROCESSING_OUTPUT_SUMMARY_MAX_CHARS = 2_000;
25
- export const DEFAULT_CONTINUITY_PROCESSING_SUMMARY_LIMIT = 20;
26
- export const MAX_CONTINUITY_PROCESSING_SUMMARY_LIMIT = 100;
27
- export const SQLITE_ADAPTER_INSTALL_GUIDANCE = 'Install the optional Enterprise continuity SQLite adapter with `npm install --workspace packages/client better-sqlite3 --include=optional` or reinstall Neocortex with optional dependencies enabled.';
28
- /**
29
- * P195.04: Client-local mirror of the shared continuity process-stage contract
30
- * (SSoT: `packages/shared/src/continuity/runner-state.ts` ->
31
- * `CONTINUITY_PUBLIC_STAGES` / `CONTINUITY_STAGE_HISTORY_MAX`). The client keeps a
32
- * local copy so continuity SQLite persistence has no runtime `@neocortex/shared`
33
- * import; a drift test in `continuity-sqlite.test.ts` asserts parity with the
34
- * shared SSoT. Values are an allow-list only -- unknown/arbitrary strings never
35
- * persist or project as a stage.
36
- */
37
- export const CONTINUITY_PUBLIC_STAGES = Object.freeze([
38
- // Goal tick decision kinds.
39
- 'discover_more',
40
- 'create_epic',
41
- 'run_yoloop',
42
- 'run_yolo',
43
- 'run_verifier',
44
- 'run_review_gate',
45
- 'compact_context',
46
- 'wait_external',
47
- 'backoff',
48
- 'waiting_human',
49
- 'complete',
50
- 'abort',
51
- // Loop lifecycle states.
52
- 'scheduled',
53
- 'running',
54
- 'overlap_skipped',
55
- 'missed_run_catchup',
56
- 'completed',
57
- 'aborted',
58
- 'cancelled',
59
- 'blocked',
60
- // Pre-tick states (goal draft/planned before the first tick decision).
61
- 'draft',
62
- 'planned',
63
- ]);
64
- /** P195.04: Maximum retained `stageHistory` entries (newest kept). ADR-P195-03. */
65
- export const CONTINUITY_STAGE_HISTORY_MAX = 20;
66
- const CONTINUITY_PUBLIC_STAGE_SET = new Set(CONTINUITY_PUBLIC_STAGES);
67
- /** P195.04: Bounded ISO-8601 date-time prefix accepted for `stageHistory.at`. */
68
- const CONTINUITY_STAGE_TIMESTAMP_PATTERN = /^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}(?::\d{2}(?:\.\d{1,6})?)?(?:Z|[+-]\d{2}:?\d{2})?$/;
69
- export function isContinuityPublicStage(value) {
70
- return typeof value === 'string' && CONTINUITY_PUBLIC_STAGE_SET.has(value);
71
- }
72
- export function sanitizeContinuityStageTimestamp(value) {
73
- if (typeof value !== 'string')
74
- return undefined;
75
- const trimmed = value.trim();
76
- if (trimmed.length === 0 || trimmed.length > 40)
77
- return undefined;
78
- if (!CONTINUITY_STAGE_TIMESTAMP_PATTERN.test(trimmed))
79
- return undefined;
80
- return trimmed;
81
- }
82
- /**
83
- * P195.04: Validate an unknown value into a bounded, allow-listed stage history.
84
- * Unknown stages and unsafe timestamps are dropped, and the newest
85
- * `CONTINUITY_STAGE_HISTORY_MAX` entries are kept.
86
- */
87
- export function sanitizeContinuityStageHistory(value) {
88
- if (!Array.isArray(value))
89
- return [];
90
- const entries = [];
91
- for (const item of value) {
92
- if (!isRecord(item) || !isContinuityPublicStage(item.stage))
93
- continue;
94
- const at = sanitizeContinuityStageTimestamp(item.at);
95
- if (!at)
96
- continue;
97
- entries.push({ stage: item.stage, at });
98
- }
99
- return entries.slice(-CONTINUITY_STAGE_HISTORY_MAX);
100
- }
101
- export function normalizeContinuityStageIteration(value) {
102
- if (typeof value !== 'number' || !Number.isFinite(value) || value < 0)
103
- return undefined;
104
- return Math.floor(value);
105
- }
106
- export class ContinuitySqliteStoreError extends Error {
107
- code;
108
- reasonCode;
109
- guidance;
110
- mutationBlocked;
111
- cause;
112
- constructor(code, message, options = {}) {
113
- super(message);
114
- this.name = 'ContinuitySqliteStoreError';
115
- this.code = code;
116
- this.reasonCode = code;
117
- this.guidance = options.guidance;
118
- this.cause = options.cause;
119
- this.mutationBlocked = options.mutationBlocked ?? code === 'sqlite-integrity-failed';
120
- }
121
- }
122
- function toNativeAgentReceipt(row) {
123
- const receipt = Object.freeze({
124
- schemaVersion: 1,
125
- storyId: row.story_id,
126
- graphRevision: row.graph_revision,
127
- dispatchKey: row.dispatch_key,
128
- profile: row.profile,
129
- parentHandle: row.parent_handle,
130
- handle: row.handle,
131
- topology: row.topology,
132
- state: row.state,
133
- requestedAt: row.requested_at,
134
- observedAt: row.observed_at,
135
- terminalAt: row.terminal_at,
136
- freshness: row.freshness,
137
- observationSource: row.observation_source,
138
- reasonCode: row.reason_code,
139
- revision: row.revision,
140
- publicSafe: true,
141
- });
142
- if (!isNativeAgentDispatchReceiptV1(receipt)) {
143
- throw new ContinuitySqliteStoreError('sqlite-integrity-failed', 'Native-agent receipt failed its closed public contract.', { mutationBlocked: true });
144
- }
145
- return receipt;
146
- }
147
- const CONTINUITY_MIGRATIONS = [
148
- INITIAL_CONTINUITY_SQLITE_MIGRATION,
149
- PROCESSING_OUTPUT_SUMMARY_SQLITE_MIGRATION,
150
- HEADLESS_EXECUTION_ATTEMPTS_SQLITE_MIGRATION,
151
- OUTBOX_DELIVERY_STATE_SQLITE_MIGRATION,
152
- BOUNDED_HEADLESS_QUEUE_SQLITE_MIGRATION,
153
- NATIVE_AGENT_DISPATCHES_SQLITE_MIGRATION,
154
- ];
155
- const CONTINUITY_TABLES = Object.freeze([
156
- 'continuity_migrations',
157
- 'jobs',
158
- 'job_leases',
159
- 'job_events',
160
- 'job_receipts',
161
- 'goal_campaigns',
162
- 'goal_ledger_items',
163
- 'goal_verifiers',
164
- 'goal_review_gates',
165
- 'loop_jobs',
166
- 'artifacts',
167
- 'state_snapshots',
168
- 'headless_execution_attempts',
169
- 'runner_config',
170
- 'outbox',
171
- 'native_agent_dispatches',
172
- 'native_agent_observations',
173
- ]);
174
- const UNSAFE_METADATA_KEY_PATTERN = /(raw[_-]?(?:logs?|payloads?)|std(?:out|err)|stack(?:[_-]?trace)?|transcript|objective(?:[_-]?(?:text|body|prompt))|prompt(?:[_-]?(?:body|text))?|private[_-]?(?:urls?|prompts?)|secret|token|password|protected(?:[_-]?(?:field|body|bodies|internals))?|workflow(?:[_-]?body)?|step(?:[_-]?body)?|customer(?:[_-]?(?:pii|data))?|\bpii\b|authorization|api[_-]?key|license[_-]?key|argv|args|executable(?:[_-]?path)?|binary(?:[_-]?path)?|shell|cwd|command[_-]?path|vendor(?:[_-]?(?:payload|corpus|response|output)))/i;
175
- const UNSAFE_METADATA_VALUE_PATTERN = /(https?:\/\/\S+|(?:^|\s)(?:\/(?:home|Users|media|mnt|tmp|var|etc|private)\/\S+|[A-Za-z]:\\\S+)|ghp_[A-Za-z0-9_]+|sk-[A-Za-z0-9_-]+|-----BEGIN [A-Z ]*PRIVATE KEY-----|authorization\s*:|password\s*=|token\s*=|api[_-]?key\s*=|license[_-]?key\s*=|raw[_ -]?(?:logs?|outputs?)\s*[:=]|transcript|stack\s*trace|stderr:|stdout:|system prompt|prompt[_ -]?(?:body|text)|workflow body|step body|\bargv\b|(?:bash|sh|zsh|powershell|pwsh)\s+-c\b|(?:\.{0,2}\/|[A-Za-z]:\\|\/)[^\s]*(?:\.exe|\.bat|\.cmd|\.ps1|\.sh|\/bin\/[^\s]+)|\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b|PROTECTED_|PRIVATE_INTERNALS|CUSTOMER_PII|\bPII\b|VENDOR_CORPUS|P180[_-]?SYNTHETIC[_-]?CONTINUITY|P185[_-]?SYNTHETIC[_-]?CONTINUITY|P187[_-]?SYNTHETIC[_-]?(?:CONTINUITY|RUNNER|WATCH)|P190[_-]?SYNTHETIC[_-]?(?:HEADLESS|CONTINUITY)|P192[_-]?SYNTHETIC[_-]?(?:PORTAL|RUNNER|SYNC))/i;
176
- const PUBLIC_REDACTION_REASON_VALUES = new Set([
177
- 'unsafe_key',
178
- 'raw_log',
179
- 'prompt_body',
180
- 'private_url',
181
- 'local_absolute_path',
182
- 'secret',
183
- 'protected_field',
184
- 'unbounded_text',
185
- 'unbounded_number',
186
- 'unknown_required_operation',
187
- 'missing',
188
- ]);
189
- const SQLITE_CORRUPT_PATTERN = /(corrupt|malformed|not a database|file is not a database|database disk image is malformed)/i;
190
- const SQLITE_FOREIGN_KEY_PATTERN = /(foreign key constraint failed|SQLITE_CONSTRAINT_FOREIGNKEY)/i;
191
- const PROCESSING_SUMMARY_CURSOR_PREFIX = 'p185:v1:';
192
- const CONTINUITY_PUBLIC_METADATA_ALLOWED_KEYS = new Set([
193
- 'schemaVersion',
194
- 'contractVersion',
195
- 'eventType',
196
- 'executionId',
197
- 'jobId',
198
- 'operationId',
199
- 'idempotencyKey',
200
- 'goalId',
201
- 'loopId',
202
- 'campaignId',
203
- 'itemId',
204
- 'ledgerItemIds',
205
- 'verifierId',
206
- 'verifierIds',
207
- 'gateId',
208
- 'reviewGateIds',
209
- 'runnerId',
210
- 'leaseId',
211
- 'status',
212
- 'reasonCode',
213
- 'reasonCodes',
214
- 'runtimeId',
215
- 'modelId',
216
- 'runtimeSource',
217
- 'headlessRuntime',
218
- 'headlessModel',
219
- 'headlessRuntimeReasonCode',
220
- 'originRuntime',
221
- 'originModel',
222
- 'targetRef',
223
- 'attemptId',
224
- 'attemptStatus',
225
- 'terminalClass',
226
- 'releaseBlocked',
227
- 'directiveHash',
228
- 'timeoutMs',
229
- 'leaseExpiresAt',
230
- 'heartbeatAt',
231
- 'notBeforeAt',
232
- 'maxAttempts',
233
- 'counts',
234
- 'hashes',
235
- 'relativePaths',
236
- 'artifactRefs',
237
- 'evidenceRefs',
238
- 'validationRefs',
239
- 'registryRefs',
240
- 'storyIds',
241
- 'epicIds',
242
- 'debtIds',
243
- 'iteration',
244
- 'currentStage',
245
- 'stageHistory',
246
- 'durationMs',
247
- 'retryCount',
248
- 'attemptCount',
249
- 'queuePriority',
250
- 'queueScope',
251
- 'queueCap',
252
- 'queueDepth',
253
- 'queueAvailable',
254
- 'queueBackpressure',
255
- 'nextTickAt',
256
- 'nextAttemptAt',
257
- 'nextRetryAt',
258
- 'deadLetterReasonCode',
259
- 'deliveryCursor',
260
- 'lastAttemptAt',
261
- 'sentAt',
262
- 'ackedAt',
263
- 'lastErrorReason',
264
- 'lastErrorSummary',
265
- 'deadLetterSummary',
266
- 'deadLetteredAt',
267
- 'processingOutputSource',
268
- 'publicSummary',
269
- 'redactionEvidence',
270
- 'selectorKind',
271
- 'targetId',
272
- 'targetJobId',
273
- 'relatedJobIds',
274
- 'serverAccessToLocalhost',
275
- 'serverHeldStream',
276
- 'initialCursor',
277
- 'targetResolutionState',
278
- 'targetKind',
279
- 'relatedJobCount',
280
- 'snapshotStatus',
281
- 'snapshotGeneratedAt',
282
- 'renderedSummaryCount',
283
- 'cursorState',
284
- 'currentCursor',
285
- 'nextCursor',
286
- 'cursorAdvanced',
287
- 'eventId',
288
- 'latestEventId',
289
- 'watchStatus',
290
- 'stopReason',
291
- 'syncVersion',
292
- 'transportKind',
293
- 'envelopeId',
294
- 'workspaceId',
295
- 'workspaceLabel',
296
- 'projectId',
297
- 'projectLabel',
298
- 'snapshotId',
299
- 'syncCursor',
300
- 'previousSyncCursor',
301
- 'nextSyncCursor',
302
- 'commandId',
303
- 'receiptId',
304
- 'acknowledgedAt',
305
- 'observedAt',
306
- 'generatedAt',
307
- 'syncStatus',
308
- 'commandStatus',
309
- 'receiptStatus',
310
- 'actorType',
311
- 'actorId',
312
- 'tenantId',
313
- 'commandKind',
314
- 'auditStatus',
315
- 'redactionCount',
316
- 'autonomousGovernance',
317
- 'governanceVersion',
318
- 'governanceEvidence',
319
- 'governanceSource',
320
- 'publicSafe',
321
- ]);
322
- const CONTINUITY_PROCESSING_OUTPUT_SUMMARY_SOURCES = new Set([
323
- 'foreground_invoke',
324
- 'scheduled_tick',
325
- 'runner_tick',
326
- 'manual_tick',
327
- 'headless_execution',
328
- 'jobs_command',
329
- 'invoke_watch',
330
- 'portal_render',
331
- ]);
332
- function defaultDynamicImport(specifier) {
333
- return import(specifier);
334
- }
335
- function checksumMigration(sql) {
336
- return `sha256:${createHash('sha256').update(sql).digest('hex')}`;
337
- }
338
- function isRecord(value) {
339
- return typeof value === 'object' && value !== null && !Array.isArray(value);
340
- }
341
- function getErrorMessage(error) {
342
- return error instanceof Error ? error.message : String(error);
343
- }
344
- function getErrorCode(error) {
345
- return isRecord(error) && typeof error.code === 'string' ? error.code : undefined;
346
- }
347
- function mapSqliteError(error, fallback) {
348
- if (error instanceof ContinuitySqliteStoreError)
349
- return error;
350
- const message = getErrorMessage(error);
351
- const nativeCode = getErrorCode(error);
352
- if (nativeCode === 'SQLITE_CORRUPT' || nativeCode === 'SQLITE_NOTADB' || SQLITE_CORRUPT_PATTERN.test(message)) {
353
- return new ContinuitySqliteStoreError('sqlite-integrity-failed', 'Continuity SQLite integrity check failed; durable mutation is blocked until the store is repaired or rebuilt.', { cause: error, mutationBlocked: true });
354
- }
355
- if (nativeCode === 'SQLITE_CONSTRAINT_FOREIGNKEY' || SQLITE_FOREIGN_KEY_PATTERN.test(message)) {
356
- return new ContinuitySqliteStoreError('sqlite-foreign-key-failed', 'Continuity SQLite foreign-key validation failed; mutation was rejected.', { cause: error });
357
- }
358
- return new ContinuitySqliteStoreError(fallback, message, { cause: error });
359
- }
360
- function assertPublicSafeValue(value, path) {
361
- if (typeof value === 'string') {
362
- if (/\.(?:redactionEvidence|redactionSummary)\[\d+\]\.field$/.test(path) && /^[A-Za-z0-9_.:[\]-]{1,120}$/.test(value)) {
363
- return;
364
- }
365
- if (/\.(?:redactionEvidence|redactionSummary)\[\d+\]\.reason$/.test(path) && PUBLIC_REDACTION_REASON_VALUES.has(value)) {
366
- return;
367
- }
368
- if (UNSAFE_METADATA_VALUE_PATTERN.test(value)) {
369
- throw new ContinuitySqliteStoreError('sqlite-public-safety-rejected', `Continuity SQLite metadata rejected unsafe value at ${path}.`);
370
- }
371
- return;
372
- }
373
- if (value === null || typeof value === 'number' || typeof value === 'boolean')
374
- return;
375
- if (Array.isArray(value)) {
376
- value.forEach((item, index) => assertPublicSafeValue(item, `${path}[${index}]`));
377
- return;
378
- }
379
- if (isRecord(value)) {
380
- for (const [key, child] of Object.entries(value)) {
381
- const publicReleaseBlockedSignal = key === 'terminationToken'
382
- && path.endsWith('.releaseBlocked')
383
- && child === '[YOLOOP_RELEASE_BLOCKED]';
384
- if (UNSAFE_METADATA_KEY_PATTERN.test(key) && !publicReleaseBlockedSignal) {
385
- throw new ContinuitySqliteStoreError('sqlite-public-safety-rejected', `Continuity SQLite metadata rejected unsafe key at ${path}.${key}.`);
386
- }
387
- assertPublicSafeValue(child, `${path}.${key}`);
388
- }
389
- return;
390
- }
391
- throw new ContinuitySqliteStoreError('sqlite-public-safety-rejected', `Continuity SQLite metadata rejected unsupported value at ${path}.`);
392
- }
393
- function assertPublicSafeText(value, path) {
394
- if (value === undefined)
395
- return;
396
- if (UNSAFE_METADATA_VALUE_PATTERN.test(value)) {
397
- throw new ContinuitySqliteStoreError('sqlite-public-safety-rejected', `Continuity SQLite text rejected unsafe value at ${path}.`);
398
- }
399
- }
400
- function assertProcessingSummaryText(value, path, maxChars = 240) {
401
- if (value.trim().length === 0) {
402
- throw new ContinuitySqliteStoreError('sqlite-public-safety-rejected', `Continuity SQLite processing summary text is required at ${path}.`);
403
- }
404
- assertPublicSafeText(value, path);
405
- if (value.length > maxChars) {
406
- throw new ContinuitySqliteStoreError('sqlite-public-safety-rejected', `Continuity SQLite processing summary text exceeded the public bound at ${path}.`);
407
- }
408
- }
409
- function filterContinuityPublicMetadata(input) {
410
- const metadata = {};
411
- if (!input)
412
- return metadata;
413
- for (const [key, value] of Object.entries(input)) {
414
- if (!CONTINUITY_PUBLIC_METADATA_ALLOWED_KEYS.has(key))
415
- continue;
416
- if (UNSAFE_METADATA_KEY_PATTERN.test(key))
417
- continue;
418
- try {
419
- assertPublicSafeValue(value, `metadata.${key}`);
420
- metadata[key] = value;
421
- }
422
- catch {
423
- // Summary metadata is best-effort public context. Unsafe entries are
424
- // filtered so a safe public summary can still be persisted without raw
425
- // logs, prompts, secrets, private URLs or protected bodies.
426
- }
427
- }
428
- return metadata;
429
- }
430
- function buildProcessingOutputSummaryMetadata(input) {
431
- const metadata = {
432
- ...filterContinuityPublicMetadata(input.metadata),
433
- schemaVersion: 1,
434
- contractVersion: 'continuity-v1',
435
- eventType: CONTINUITY_PROCESSING_OUTPUT_SUMMARY_EVENT_TYPE,
436
- executionId: input.executionId,
437
- jobId: input.jobId,
438
- operationId: input.executionId,
439
- publicSummary: input.publicSummary,
440
- };
441
- if (input.idempotencyKey)
442
- metadata.idempotencyKey = input.idempotencyKey;
443
- if (input.goalId)
444
- metadata.goalId = input.goalId;
445
- if (input.loopId)
446
- metadata.loopId = input.loopId;
447
- if (input.reasonCode)
448
- metadata.reasonCode = input.reasonCode;
449
- if (input.source && CONTINUITY_PROCESSING_OUTPUT_SUMMARY_SOURCES.has(input.source)) {
450
- metadata.processingOutputSource = input.source;
451
- }
452
- return filterContinuityPublicMetadata(metadata);
453
- }
454
- function normalizeProcessingSummaryLimit(limit) {
455
- const candidate = Math.floor(limit ?? DEFAULT_CONTINUITY_PROCESSING_SUMMARY_LIMIT);
456
- if (!Number.isFinite(candidate))
457
- return DEFAULT_CONTINUITY_PROCESSING_SUMMARY_LIMIT;
458
- return Math.max(1, Math.min(MAX_CONTINUITY_PROCESSING_SUMMARY_LIMIT, candidate));
459
- }
460
- function processingSummaryEventId(input) {
461
- if (input.eventId && input.eventId.trim().length > 0)
462
- return input.eventId;
463
- const safe = input.executionId.replace(/[^a-z0-9_.:-]+/gi, '-').replace(/^-+|-+$/g, '');
464
- if (safe.length > 0)
465
- return `event-processing-summary-${safe.slice(0, 160)}`;
466
- return `event-processing-summary-${createHash('sha256').update(input.executionId).digest('hex').slice(0, 32)}`;
467
- }
468
- function encodeProcessingSummaryCursor(row) {
469
- return `${PROCESSING_SUMMARY_CURSOR_PREFIX}${Buffer.from(JSON.stringify([row.created_at, row.event_id]), 'utf8').toString('base64url')}`;
470
- }
471
- function decodeProcessingSummaryCursor(cursor) {
472
- if (!cursor?.startsWith(PROCESSING_SUMMARY_CURSOR_PREFIX))
473
- return undefined;
474
- try {
475
- const payload = cursor.slice(PROCESSING_SUMMARY_CURSOR_PREFIX.length);
476
- const decoded = JSON.parse(Buffer.from(payload, 'base64url').toString('utf8'));
477
- if (!Array.isArray(decoded) || decoded.length !== 2)
478
- return undefined;
479
- const [createdAt, eventId] = decoded;
480
- if (typeof createdAt !== 'string' || typeof eventId !== 'string')
481
- return undefined;
482
- return { createdAt, eventId };
483
- }
484
- catch {
485
- return undefined;
486
- }
487
- }
488
- function normalizeProcessingSummaryTargetId(kind, value) {
489
- if (!value)
490
- return undefined;
491
- const normalized = value.trim().replace(/^@/, '');
492
- if (kind === 'job' && /^job-J\d{3,}$/i.test(normalized))
493
- return `job-J${normalized.match(/\d+$/)?.[0] ?? ''}`;
494
- if (kind === 'goal' && /^goal-G\d{3,}$/i.test(normalized))
495
- return `goal-G${normalized.match(/\d+$/)?.[0] ?? ''}`;
496
- if (kind === 'loop' && /^loop-L\d{3,}$/i.test(normalized))
497
- return `loop-L${normalized.match(/\d+$/)?.[0] ?? ''}`;
498
- return undefined;
499
- }
500
- function normalizedRelatedJobIds(values) {
501
- if (!values)
502
- return [];
503
- return uniqueValues(values
504
- .map((value) => normalizeProcessingSummaryTargetId('job', value))
505
- .filter((value) => Boolean(value)));
506
- }
507
- function processingSummaryTargetPredicate(options) {
508
- const target = options.target;
509
- if (!target) {
510
- const jobId = normalizeProcessingSummaryTargetId('job', options.jobId);
511
- return jobId ? { sql: ' AND job_id = ?', params: [jobId] } : { sql: '', params: [] };
512
- }
513
- const selectorKind = target.selectorKind;
514
- if (selectorKind === 'job') {
515
- const targetJobId = normalizeProcessingSummaryTargetId('job', target.jobId ?? target.targetId ?? options.jobId);
516
- return targetJobId ? { sql: ' AND job_id = ?', params: [targetJobId] } : { sql: ' AND 1 = 0', params: [] };
517
- }
518
- if (selectorKind === 'goal') {
519
- const goalId = normalizeProcessingSummaryTargetId('goal', target.goalId ?? target.targetId);
520
- if (!goalId)
521
- return { sql: ' AND 1 = 0', params: [] };
522
- const relatedJobIds = normalizedRelatedJobIds(target.relatedJobIds);
523
- const relatedPredicate = relatedJobIds.length > 0
524
- ? ` OR job_id IN (${relatedJobIds.map(() => '?').join(', ')})`
525
- : '';
526
- return {
527
- sql: ` AND (job_id = ? OR json_extract(metadata_json, '$.goalId') = ?${relatedPredicate})`,
528
- params: [goalId, goalId, ...relatedJobIds],
529
- };
530
- }
531
- if (selectorKind === 'loop') {
532
- const loopId = normalizeProcessingSummaryTargetId('loop', target.loopId ?? target.targetId);
533
- if (!loopId)
534
- return { sql: ' AND 1 = 0', params: [] };
535
- const relatedJobIds = normalizedRelatedJobIds(target.relatedJobIds);
536
- const relatedPredicate = relatedJobIds.length > 0
537
- ? ` OR job_id IN (${relatedJobIds.map(() => '?').join(', ')})`
538
- : '';
539
- return {
540
- sql: ` AND (job_id = ? OR json_extract(metadata_json, '$.loopId') = ?${relatedPredicate})`,
541
- params: [loopId, loopId, ...relatedJobIds],
542
- };
543
- }
544
- return { sql: ' AND 1 = 0', params: [] };
545
- }
546
- function toProcessingOutputSummaryRow(row) {
547
- const output = {
548
- event_id: row.event_id,
549
- job_id: row.job_id,
550
- execution_id: row.operation_id,
551
- public_summary: row.summary,
552
- reason_code: row.reason_code,
553
- created_at: row.created_at,
554
- metadata_json: row.metadata_json,
555
- };
556
- return {
557
- ...output,
558
- cursor: encodeProcessingSummaryCursor(output),
559
- };
560
- }
561
- function stringifyPublicMetadata(metadata) {
562
- if (!metadata)
563
- return null;
564
- assertPublicSafeValue(metadata, 'metadata');
565
- return JSON.stringify(metadata);
566
- }
567
- function retentionHashes(jobId, receipts, outputs) {
568
- const digest = (values) => `sha256:${createHash('sha256')
569
- .update(values
570
- .map((value) => [value.id, value.status, value.reasonCode].join(':'))
571
- .sort()
572
- .join('\n'))
573
- .digest('hex')}`;
574
- const receiptHash = digest(receipts);
575
- const outputHash = digest(outputs);
576
- return {
577
- receipts: receiptHash,
578
- outputs: outputHash,
579
- aggregate: `sha256:${createHash('sha256').update(`${jobId}:${receiptHash}:${outputHash}`).digest('hex')}`,
580
- };
581
- }
582
- /**
583
- * P195.04: Parse an existing `jobs.metadata_json` value into a mutable object for
584
- * additive read-modify-write of stage progress. Corrupt/absent metadata returns a
585
- * fresh object so forward-compatible reads never throw on older DBs.
586
- */
587
- function parseJobMetadataObject(metadataJson) {
588
- if (!metadataJson)
589
- return {};
590
- try {
591
- const parsed = JSON.parse(metadataJson);
592
- return isRecord(parsed) ? { ...parsed } : {};
593
- }
594
- catch {
595
- return {};
596
- }
597
- }
598
- function assertBoundedPublicText(value, path, maxChars, required = false) {
599
- if (value === undefined) {
600
- if (required) {
601
- throw new ContinuitySqliteStoreError('sqlite-public-safety-rejected', `Continuity SQLite text is required at ${path}.`);
602
- }
603
- return;
604
- }
605
- assertPublicSafeText(value, path);
606
- if (value.trim().length === 0 || value.length > maxChars) {
607
- throw new ContinuitySqliteStoreError('sqlite-public-safety-rejected', `Continuity SQLite text exceeded the public bound at ${path}.`);
608
- }
609
- }
610
- function assertNonNegativeInteger(value, path) {
611
- if (value === undefined)
612
- return;
613
- if (!Number.isFinite(value) || value < 0 || Math.floor(value) !== value) {
614
- throw new ContinuitySqliteStoreError('sqlite-public-safety-rejected', `Continuity SQLite numeric value rejected at ${path}.`);
615
- }
616
- }
617
- function assertOutboxIdentity(input) {
618
- assertBoundedPublicText(input.outboxId, 'outbox.outboxId', 160, true);
619
- assertBoundedPublicText(input.jobId, 'outbox.jobId', 160);
620
- assertBoundedPublicText(input.operationId, 'outbox.operationId', 160);
621
- assertBoundedPublicText(input.idempotencyKey, 'outbox.idempotencyKey', 160);
622
- assertBoundedPublicText(input.type, 'outbox.type', 80);
623
- assertBoundedPublicText(input.status, 'outbox.status', 40);
624
- assertBoundedPublicText(input.deliveryCursor, 'outbox.deliveryCursor', 200);
625
- assertBoundedPublicText(input.timestamp, 'outbox.timestamp', 80);
626
- assertBoundedPublicText(input.reasonCode, 'outbox.reasonCode', 100);
627
- assertBoundedPublicText(input.summary, 'outbox.summary', 240);
628
- }
629
- function isOutboxStatus(value) {
630
- return value === 'pending' || value === 'sent' || value === 'acked' || value === 'failed' || value === 'dead_letter';
631
- }
632
- function publicOutboxStatus(value) {
633
- return typeof value === 'string' && isOutboxStatus(value) ? value : 'failed';
634
- }
635
- function optionalOutboxString(value) {
636
- return typeof value === 'string' && value.length > 0 ? value : undefined;
637
- }
638
- function parseOutboxPayload(payloadJson) {
639
- if (typeof payloadJson !== 'string' || payloadJson.trim().length === 0)
640
- return {};
641
- try {
642
- const parsed = JSON.parse(payloadJson);
643
- if (!isRecord(parsed))
644
- return {};
645
- assertPublicSafeValue(parsed, 'outbox.payload');
646
- return parsed;
647
- }
648
- catch {
649
- return {};
650
- }
651
- }
652
- function toOutboxDeliveryRow(row) {
653
- return {
654
- outboxId: String(row.outbox_id ?? ''),
655
- ...(optionalOutboxString(row.job_id) ? { jobId: optionalOutboxString(row.job_id) } : {}),
656
- operationId: String(row.operation_id ?? ''),
657
- ...(optionalOutboxString(row.idempotency_key) ? { idempotencyKey: optionalOutboxString(row.idempotency_key) } : {}),
658
- type: String(row.type ?? ''),
659
- status: publicOutboxStatus(row.status),
660
- payload: parseOutboxPayload(row.payload_json),
661
- retryCount: typeof row.retry_count === 'number' ? row.retry_count : 0,
662
- attemptCount: typeof row.attempt_count === 'number' ? row.attempt_count : 0,
663
- ...(optionalOutboxString(row.delivery_cursor) ? { deliveryCursor: optionalOutboxString(row.delivery_cursor) } : {}),
664
- ...(optionalOutboxString(row.next_attempt_at) ? { nextAttemptAt: optionalOutboxString(row.next_attempt_at) } : {}),
665
- ...(optionalOutboxString(row.last_attempt_at) ? { lastAttemptAt: optionalOutboxString(row.last_attempt_at) } : {}),
666
- ...(optionalOutboxString(row.sent_at) ? { sentAt: optionalOutboxString(row.sent_at) } : {}),
667
- ...(optionalOutboxString(row.acked_at) ? { ackedAt: optionalOutboxString(row.acked_at) } : {}),
668
- ...(optionalOutboxString(row.last_error_code) ? { lastErrorCode: optionalOutboxString(row.last_error_code) } : {}),
669
- ...(optionalOutboxString(row.last_error_reason) ? { lastErrorReason: optionalOutboxString(row.last_error_reason) } : {}),
670
- ...(optionalOutboxString(row.last_error_summary) ? { lastErrorSummary: optionalOutboxString(row.last_error_summary) } : {}),
671
- ...(optionalOutboxString(row.dead_letter_summary) ? { deadLetterSummary: optionalOutboxString(row.dead_letter_summary) } : {}),
672
- ...(optionalOutboxString(row.dead_lettered_at) ? { deadLetteredAt: optionalOutboxString(row.dead_lettered_at) } : {}),
673
- createdAt: String(row.created_at ?? ''),
674
- updatedAt: String(row.updated_at ?? ''),
675
- publicSafe: true,
676
- };
677
- }
678
- function outboxTransitionResult(input) {
679
- return { ...input, publicSafe: true };
680
- }
681
- function stringifyPublicJsonValue(value, path) {
682
- if (value === undefined)
683
- return null;
684
- assertPublicSafeValue(value, path);
685
- return JSON.stringify(value);
686
- }
687
- function normalizePublicRelativePaths(paths) {
688
- if (!paths)
689
- return undefined;
690
- const normalized = uniqueValues(paths.map((path) => path.replace(/\\/g, '/')))
691
- .filter((path) => path.length > 0)
692
- .slice(0, 50);
693
- for (const [index, path] of normalized.entries()) {
694
- assertBoundedPublicText(path, `relativePaths[${index}]`, 240, true);
695
- const segments = path.split('/').filter(Boolean);
696
- if (path.startsWith('/') || path.startsWith('~') || path.includes('://') || /^[A-Za-z]:\//.test(path) || segments.includes('..')) {
697
- throw new ContinuitySqliteStoreError('sqlite-public-safety-rejected', `Continuity SQLite rejected non-relative path at relativePaths[${index}].`);
698
- }
699
- }
700
- return normalized;
701
- }
702
- function buildHeadlessExecutionPublicMetadata(input) {
703
- const metadata = {
704
- ...filterContinuityPublicMetadata(input.metadata),
705
- schemaVersion: 1,
706
- contractVersion: 'continuity-v1',
707
- attemptId: input.attemptId,
708
- executionId: input.executionId,
709
- jobId: input.jobId,
710
- };
711
- if (input.directiveHash)
712
- metadata.directiveHash = input.directiveHash;
713
- if (input.runtimeId)
714
- metadata.runtimeId = input.runtimeId;
715
- if (input.modelId)
716
- metadata.modelId = input.modelId;
717
- if (input.runtimeSource)
718
- metadata.runtimeSource = input.runtimeSource;
719
- if (input.status)
720
- metadata.attemptStatus = input.status;
721
- if (input.reasonCode)
722
- metadata.reasonCode = input.reasonCode;
723
- if (input.durationMs !== undefined)
724
- metadata.durationMs = input.durationMs;
725
- if (input.retryCount !== undefined)
726
- metadata.retryCount = input.retryCount;
727
- if (input.attemptCount !== undefined)
728
- metadata.attemptCount = input.attemptCount;
729
- if (input.maxAttempts !== undefined)
730
- metadata.maxAttempts = input.maxAttempts;
731
- if (input.queuePriority !== undefined)
732
- metadata.queuePriority = input.queuePriority;
733
- if (input.queueScope)
734
- metadata.queueScope = input.queueScope;
735
- if (input.nextRetryAt)
736
- metadata.nextRetryAt = input.nextRetryAt;
737
- if (input.deadLetterReasonCode)
738
- metadata.deadLetterReasonCode = input.deadLetterReasonCode;
739
- if (input.counts)
740
- metadata.counts = input.counts;
741
- if (input.hashes)
742
- metadata.hashes = input.hashes;
743
- if (input.redactionSummary)
744
- metadata.redactionEvidence = input.redactionSummary;
745
- if (input.relativePaths)
746
- metadata.relativePaths = normalizePublicRelativePaths(input.relativePaths);
747
- return filterContinuityPublicMetadata(metadata);
748
- }
749
- function validateHeadlessExecutionAttemptIdentity(input) {
750
- assertBoundedPublicText(input.attemptId, 'headless_execution.attemptId', 160, true);
751
- assertBoundedPublicText(input.executionId, 'headless_execution.executionId', 160, true);
752
- assertBoundedPublicText(input.jobId, 'headless_execution.jobId', 160, true);
753
- assertBoundedPublicText(input.directiveHash, 'headless_execution.directiveHash', 160);
754
- assertBoundedPublicText(input.targetRef, 'headless_execution.targetRef', 240);
755
- assertBoundedPublicText(input.storyId, 'headless_execution.storyId', 120);
756
- assertBoundedPublicText(input.epicId, 'headless_execution.epicId', 120);
757
- assertBoundedPublicText(input.runtimeId, 'headless_execution.runtimeId', 80);
758
- assertBoundedPublicText(input.modelId, 'headless_execution.modelId', 120);
759
- assertBoundedPublicText(input.runtimeSource, 'headless_execution.runtimeSource', 40);
760
- assertBoundedPublicText(input.reasonCode, 'headless_execution.reasonCode', 100);
761
- assertBoundedPublicText(input.publicSummary, 'headless_execution.publicSummary', 240);
762
- assertBoundedPublicText(input.leaseId, 'headless_execution.leaseId', 160);
763
- assertBoundedPublicText(input.runnerId, 'headless_execution.runnerId', 160);
764
- assertBoundedPublicText(input.timestamp, 'headless_execution.timestamp', 80);
765
- }
766
- function asSinglePragmaValue(value) {
767
- if (Array.isArray(value)) {
768
- const first = value[0];
769
- if (isRecord(first))
770
- return Object.values(first)[0];
771
- return first;
772
- }
773
- if (isRecord(value))
774
- return Object.values(value)[0];
775
- return value;
776
- }
777
- function isContinuityTable(table) {
778
- return CONTINUITY_TABLES.includes(table);
779
- }
780
- function uniqueValues(values) {
781
- return Array.from(new Set(values));
782
- }
783
- function createBetterSqlite3Adapter(moduleValue) {
784
- const constructorValue = isRecord(moduleValue) && 'default' in moduleValue
785
- ? moduleValue.default
786
- : moduleValue;
787
- if (typeof constructorValue !== 'function') {
788
- throw new ContinuitySqliteStoreError('sqlite-adapter-unavailable', 'Optional better-sqlite3 adapter did not export a database constructor.', { guidance: SQLITE_ADAPTER_INSTALL_GUIDANCE });
789
- }
790
- const Database = constructorValue;
791
- return {
792
- adapterName: 'better-sqlite3',
793
- open(filePath, options) {
794
- return new Database(filePath, options);
795
- },
796
- };
797
- }
798
- export async function loadBetterSqlite3Adapter(importer = defaultDynamicImport) {
799
- try {
800
- const moduleValue = await importer('better-sqlite3');
801
- return { ok: true, adapter: createBetterSqlite3Adapter(moduleValue) };
802
- }
803
- catch (error) {
804
- if (error instanceof ContinuitySqliteStoreError)
805
- return { ok: false, error };
806
- return {
807
- ok: false,
808
- error: new ContinuitySqliteStoreError('sqlite-adapter-unavailable', 'Optional Enterprise continuity SQLite adapter better-sqlite3 is unavailable.', { cause: error, guidance: SQLITE_ADAPTER_INSTALL_GUIDANCE }),
809
- };
810
- }
811
- }
812
- export function continuitySqliteStorePath(projectRoot) {
813
- return join(projectRoot, CONTINUITY_SQLITE_RELATIVE_DIR, CONTINUITY_SQLITE_FILENAME);
814
- }
815
- function ensureContinuityDirectory(projectRoot) {
816
- const directory = join(projectRoot, CONTINUITY_SQLITE_RELATIVE_DIR);
817
- mkdirSync(directory, { recursive: true, mode: 0o700 });
818
- try {
819
- chmodSync(directory, 0o700);
820
- }
821
- catch {
822
- // Permission tightening is best-effort on non-POSIX filesystems.
823
- }
824
- return directory;
825
- }
826
- export async function openContinuitySqliteStore(options) {
827
- const adapter = options.adapter ?? await resolveAdapter(options.adapterLoader);
828
- const filePath = options.filePath ?? continuitySqliteStorePath(options.projectRoot);
829
- if (options.strictReadOnly) {
830
- assertContinuitySqliteReadOnlyHeader(filePath);
831
- }
832
- else {
833
- if (options.filePath) {
834
- mkdirSync(dirname(filePath), { recursive: true, mode: 0o700 });
835
- try {
836
- chmodSync(dirname(filePath), 0o700);
837
- }
838
- catch { /* best-effort */ }
839
- }
840
- else {
841
- ensureContinuityDirectory(options.projectRoot);
842
- }
843
- }
844
- let db;
845
- try {
846
- db = adapter.open(filePath, options.strictReadOnly
847
- ? { fileMustExist: true, readonly: true }
848
- : undefined);
849
- }
850
- catch (error) {
851
- throw mapSqliteError(error, 'sqlite-integrity-failed');
852
- }
853
- const store = new ContinuitySqliteStore({
854
- db,
855
- filePath,
856
- adapterName: adapter.adapterName,
857
- busyTimeoutMs: options.busyTimeoutMs ?? DEFAULT_CONTINUITY_SQLITE_BUSY_TIMEOUT_MS,
858
- integrityCheck: options.integrityCheck ?? true,
859
- now: options.now ?? (() => new Date().toISOString()),
860
- });
861
- try {
862
- if (options.strictReadOnly) {
863
- store.runIntegrityCheck();
864
- // Prove the expected schema is readable without attempting repair.
865
- store.getSchemaVersion();
866
- return store;
867
- }
868
- store.initialize();
869
- if (existsSync(filePath)) {
870
- try {
871
- chmodSync(filePath, 0o600);
872
- }
873
- catch {
874
- // Best-effort on non-POSIX filesystems.
875
- }
876
- }
877
- return store;
878
- }
879
- catch (error) {
880
- try {
881
- db.close();
882
- }
883
- catch {
884
- // Ignore close errors while surfacing the original hard stop.
885
- }
886
- throw error;
887
- }
888
- }
889
- function assertContinuitySqliteReadOnlyHeader(filePath) {
890
- if (!existsSync(filePath)) {
891
- throw new ContinuitySqliteStoreError('sqlite-integrity-failed', 'Continuity database is unavailable for physical read-only inspection.', { mutationBlocked: true });
892
- }
893
- let header;
894
- try {
895
- const fd = openSync(filePath, 'r');
896
- try {
897
- header = Buffer.alloc(20);
898
- if (readSync(fd, header, 0, header.length, 0) < header.length)
899
- throw new Error('short-header');
900
- }
901
- finally {
902
- closeSync(fd);
903
- }
904
- }
905
- catch (error) {
906
- throw new ContinuitySqliteStoreError('sqlite-integrity-failed', 'Continuity database header could not be inspected read-only.', { cause: error, mutationBlocked: true });
907
- }
908
- if (header.subarray(0, 16).toString('ascii') !== 'SQLite format 3\0' || header[18] === 2 || header[19] === 2) {
909
- throw new ContinuitySqliteStoreError('sqlite-integrity-failed', 'Continuity database requires an unsafe WAL read profile.', { mutationBlocked: true });
910
- }
911
- }
912
- /**
913
- * P205.05 physical inspection boundary. The database must already exist and is
914
- * opened with the adapter's OS-backed read-only flag. No directory creation,
915
- * chmod, initialization, migrations or mutative pragma is attempted.
916
- */
917
- export async function openContinuitySqliteReadOnly(options) {
918
- const filePath = continuitySqliteStorePath(options.projectRoot);
919
- // SQLite read-only opens can still create `-wal`/`-shm` companions when the
920
- // database header is in WAL mode. Deny that profile before adapter open.
921
- assertContinuitySqliteReadOnlyHeader(filePath);
922
- const adapter = options.adapter ?? await resolveAdapter(options.adapterLoader);
923
- let db;
924
- try {
925
- db = adapter.open(filePath, { fileMustExist: true, readonly: true });
926
- const integrity = asSinglePragmaValue(db.pragma('integrity_check', { simple: true }));
927
- if (String(integrity).toLowerCase() !== 'ok')
928
- throw new Error('integrity');
929
- }
930
- catch (error) {
931
- throw new ContinuitySqliteStoreError('sqlite-integrity-failed', 'Continuity database failed physical read-only inspection.', { cause: error, mutationBlocked: true });
932
- }
933
- const assertReadStatement = (source) => {
934
- const normalized = source.trim().replace(/^\(+/u, '').toLowerCase();
935
- if (!/^(select|with\s)/u.test(normalized) || /\b(insert|update|delete|replace|create|alter|drop|pragma|attach|detach|vacuum|reindex)\b/u.test(normalized)) {
936
- throw new ContinuitySqliteStoreError('sqlite-mutation-blocked', 'Statement is not in the physical read-only query profile.', { mutationBlocked: true });
937
- }
938
- };
939
- return Object.freeze({
940
- filePath,
941
- adapterName: adapter.adapterName,
942
- all(source, ...params) { assertReadStatement(source); return db.prepare(source).all(...params); },
943
- get(source, ...params) { assertReadStatement(source); return db.prepare(source).get(...params); },
944
- close() { db.close(); },
945
- });
946
- }
947
- async function resolveAdapter(loader) {
948
- const result = loader ? await loader() : await loadBetterSqlite3Adapter();
949
- if (!result.ok)
950
- throw result.error;
951
- return result.adapter;
952
- }
953
- export class ContinuitySqliteStore {
954
- filePath;
955
- adapterName;
956
- db;
957
- busyTimeoutMs;
958
- integrityCheck;
959
- now;
960
- mutationBlocked = false;
961
- constructor(options) {
962
- this.db = options.db;
963
- this.filePath = options.filePath;
964
- this.adapterName = options.adapterName;
965
- this.busyTimeoutMs = options.busyTimeoutMs;
966
- this.integrityCheck = options.integrityCheck;
967
- this.now = options.now;
968
- }
969
- initialize() {
970
- try {
971
- this.configureConnection();
972
- if (this.integrityCheck)
973
- this.runIntegrityCheck();
974
- this.ensureMigrationTable();
975
- this.applyMigrations();
976
- if (this.integrityCheck)
977
- this.runIntegrityCheck();
978
- }
979
- catch (error) {
980
- throw mapSqliteError(error, 'sqlite-migration-failed');
981
- }
982
- }
983
- close() {
984
- this.db.close();
985
- }
986
- pragmaValue(source) {
987
- return asSinglePragmaValue(this.db.pragma(source, { simple: true }));
988
- }
989
- runIntegrityCheck() {
990
- try {
991
- const result = String(asSinglePragmaValue(this.db.pragma('integrity_check', { simple: true })) ?? '');
992
- if (result.toLowerCase() !== 'ok') {
993
- this.mutationBlocked = true;
994
- throw new ContinuitySqliteStoreError('sqlite-integrity-failed', 'Continuity SQLite integrity check failed; durable mutation is blocked until the store is repaired or rebuilt.', { mutationBlocked: true });
995
- }
996
- }
997
- catch (error) {
998
- this.mutationBlocked = true;
999
- throw mapSqliteError(error, 'sqlite-integrity-failed');
1000
- }
1001
- }
1002
- getSchemaVersion() {
1003
- const row = this.db.prepare('SELECT COALESCE(MAX(version), 0) AS version FROM continuity_migrations').get();
1004
- return isRecord(row) && typeof row.version === 'number' ? row.version : 0;
1005
- }
1006
- countRows(table) {
1007
- if (!isContinuityTable(table)) {
1008
- throw new ContinuitySqliteStoreError('sqlite-public-safety-rejected', 'Unknown continuity table name.');
1009
- }
1010
- const row = this.db.prepare(`SELECT COUNT(*) AS count FROM ${table}`).get();
1011
- return isRecord(row) && typeof row.count === 'number' ? row.count : 0;
1012
- }
1013
- reserveNativeAgentDispatch(input) {
1014
- this.assertCanMutate();
1015
- if (!isNativeDispatchBindingV1(input.binding) || !isNativeHostAttestationV1(input.hostAttestation)) {
1016
- throw new ContinuitySqliteStoreError('sqlite-public-safety-rejected', 'Native-agent reservation rejected invalid binding or host evidence.');
1017
- }
1018
- const candidate = {
1019
- schemaVersion: 1,
1020
- storyId: input.binding.storyId,
1021
- graphRevision: input.binding.graphRevision.replace(/^sha256:/, '').toLowerCase(),
1022
- dispatchKey: input.binding.dispatchKey.toLowerCase(),
1023
- profile: 'neocortex',
1024
- parentHandle: null,
1025
- handle: null,
1026
- topology: input.topology ?? 'native-agent',
1027
- state: 'requested',
1028
- requestedAt: input.requestedAt,
1029
- observedAt: null,
1030
- terminalAt: null,
1031
- freshness: 'unknown',
1032
- observationSource: 'local-reservation',
1033
- reasonCode: input.reasonCode ?? 'native-agent-dispatch-reserved',
1034
- revision: 0,
1035
- publicSafe: true,
1036
- };
1037
- if (!isNativeAgentDispatchReceiptV1(candidate)) {
1038
- throw new ContinuitySqliteStoreError('sqlite-public-safety-rejected', 'Native-agent reservation rejected unsafe public fields.');
1039
- }
1040
- try {
1041
- return this.runInTransaction(() => {
1042
- const inserted = this.db.prepare(`
1
+ import{Buffer as J}from"node:buffer";import{createHash as M}from"node:crypto";import{chmodSync as z,closeSync as ge,existsSync as te,mkdirSync as ae,openSync as Ae,readSync as xe}from"node:fs";import{dirname as re,join as se}from"node:path";import{INITIAL_CONTINUITY_SQLITE_MIGRATION as Re}from"./migrations/001-initial-schema.js";import{PROCESSING_OUTPUT_SUMMARY_SQLITE_MIGRATION as Oe}from"./migrations/002-processing-output-summary-indexes.js";import{HEADLESS_EXECUTION_ATTEMPTS_SQLITE_MIGRATION as Ne}from"./migrations/003-headless-execution-attempts.js";import{OUTBOX_DELIVERY_STATE_SQLITE_MIGRATION as Te}from"./migrations/004-outbox-delivery-state.js";import{BOUNDED_HEADLESS_QUEUE_SQLITE_MIGRATION as Le}from"./migrations/005-bounded-headless-queue.js";import{NATIVE_AGENT_DISPATCHES_SQLITE_MIGRATION as ve}from"./migrations/006-native-agent-dispatches.js";import{NATIVE_AGENT_TERMINAL_STATES as oe,isNativeAgentDispatchReceiptV1 as X,isNativeAgentStateTransitionAllowed as je,isNativeDispatchBindingV1 as Me,isNativeHostAttestationV1 as ke}from"./native-agent-types.js";import{CONTINUITY_QUEUE_REASON_CODES as k,CONTINUITY_RECEIPT_COMPACTION_EVENT_TYPE as W,CONTINUITY_RECEIPT_RETENTION_REASON_CODES as F,DEFAULT_CONTINUITY_QUEUE_POLICY as De,computeContinuityQueueRetryAt as Ue,computeContinuityOutboxFailureDecision as we,normalizeContinuityQueuePolicy as B,normalizeContinuityQueuePriority as qe,normalizeContinuityQueueScope as Pe,normalizeContinuityRetentionPolicy as He}from"./outbox.js";const ne=".neocortex/continuity",We="continuity.sqlite",kt=6,Fe=5e3,O="processing_output_summary",Be=2e3,de=20,$e=100,ie="Install the optional Enterprise continuity SQLite adapter with `npm install --workspace packages/client better-sqlite3 --include=optional` or reinstall Neocortex with optional dependencies enabled.",Ye=Object.freeze(["discover_more","create_epic","run_yoloop","run_yolo","run_verifier","run_review_gate","compact_context","wait_external","backoff","waiting_human","complete","abort","scheduled","running","overlap_skipped","missed_run_catchup","completed","aborted","cancelled","blocked","draft","planned"]),ce=20,Ke=new Set(Ye),Ve=/^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}(?::\d{2}(?:\.\d{1,6})?)?(?:Z|[+-]\d{2}:?\d{2})?$/;function le(a){return typeof a=="string"&&Ke.has(a)}function ue(a){if(typeof a!="string")return;const e=a.trim();if(!(e.length===0||e.length>40)&&Ve.test(e))return e}function Ge(a){if(!Array.isArray(a))return[];const e=[];for(const t of a){if(!E(t)||!le(t.stage))continue;const r=ue(t.at);r&&e.push({stage:t.stage,at:r})}return e.slice(-ce)}function Qe(a){if(!(typeof a!="number"||!Number.isFinite(a)||a<0))return Math.floor(a)}class i extends Error{code;reasonCode;guidance;mutationBlocked;cause;constructor(e,t,r={}){super(t),this.name="ContinuitySqliteStoreError",this.code=e,this.reasonCode=e,this.guidance=r.guidance,this.cause=r.cause,this.mutationBlocked=r.mutationBlocked??e==="sqlite-integrity-failed"}}function $(a){const e=Object.freeze({schemaVersion:1,storyId:a.story_id,graphRevision:a.graph_revision,dispatchKey:a.dispatch_key,profile:a.profile,parentHandle:a.parent_handle,handle:a.handle,topology:a.topology,state:a.state,requestedAt:a.requested_at,observedAt:a.observed_at,terminalAt:a.terminal_at,freshness:a.freshness,observationSource:a.observation_source,reasonCode:a.reason_code,revision:a.revision,publicSafe:!0});if(!X(e))throw new i("sqlite-integrity-failed","Native-agent receipt failed its closed public contract.",{mutationBlocked:!0});return e}const Je=[Re,Oe,Ne,Te,Le,ve],ze=Object.freeze(["continuity_migrations","jobs","job_leases","job_events","job_receipts","goal_campaigns","goal_ledger_items","goal_verifiers","goal_review_gates","loop_jobs","artifacts","state_snapshots","headless_execution_attempts","runner_config","outbox","native_agent_dispatches","native_agent_observations"]),_e=/(raw[_-]?(?:logs?|payloads?)|std(?:out|err)|stack(?:[_-]?trace)?|transcript|objective(?:[_-]?(?:text|body|prompt))|prompt(?:[_-]?(?:body|text))?|private[_-]?(?:urls?|prompts?)|secret|token|password|protected(?:[_-]?(?:field|body|bodies|internals))?|workflow(?:[_-]?body)?|step(?:[_-]?body)?|customer(?:[_-]?(?:pii|data))?|\bpii\b|authorization|api[_-]?key|license[_-]?key|argv|args|executable(?:[_-]?path)?|binary(?:[_-]?path)?|shell|cwd|command[_-]?path|vendor(?:[_-]?(?:payload|corpus|response|output)))/i,me=/(https?:\/\/\S+|(?:^|\s)(?:\/(?:home|Users|media|mnt|tmp|var|etc|private)\/\S+|[A-Za-z]:\\\S+)|ghp_[A-Za-z0-9_]+|sk-[A-Za-z0-9_-]+|-----BEGIN [A-Z ]*PRIVATE KEY-----|authorization\s*:|password\s*=|token\s*=|api[_-]?key\s*=|license[_-]?key\s*=|raw[_ -]?(?:logs?|outputs?)\s*[:=]|transcript|stack\s*trace|stderr:|stdout:|system prompt|prompt[_ -]?(?:body|text)|workflow body|step body|\bargv\b|(?:bash|sh|zsh|powershell|pwsh)\s+-c\b|(?:\.{0,2}\/|[A-Za-z]:\\|\/)[^\s]*(?:\.exe|\.bat|\.cmd|\.ps1|\.sh|\/bin\/[^\s]+)|\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b|PROTECTED_|PRIVATE_INTERNALS|CUSTOMER_PII|\bPII\b|VENDOR_CORPUS|P180[_-]?SYNTHETIC[_-]?CONTINUITY|P185[_-]?SYNTHETIC[_-]?CONTINUITY|P187[_-]?SYNTHETIC[_-]?(?:CONTINUITY|RUNNER|WATCH)|P190[_-]?SYNTHETIC[_-]?(?:HEADLESS|CONTINUITY)|P192[_-]?SYNTHETIC[_-]?(?:PORTAL|RUNNER|SYNC))/i,Xe=new Set(["unsafe_key","raw_log","prompt_body","private_url","local_absolute_path","secret","protected_field","unbounded_text","unbounded_number","unknown_required_operation","missing"]),Ze=/(corrupt|malformed|not a database|file is not a database|database disk image is malformed)/i,et=/(foreign key constraint failed|SQLITE_CONSTRAINT_FOREIGNKEY)/i,Z="p185:v1:",tt=new Set(["schemaVersion","contractVersion","eventType","executionId","jobId","operationId","idempotencyKey","goalId","loopId","campaignId","itemId","ledgerItemIds","verifierId","verifierIds","gateId","reviewGateIds","runnerId","leaseId","status","reasonCode","reasonCodes","runtimeId","modelId","runtimeSource","headlessRuntime","headlessModel","headlessRuntimeReasonCode","originRuntime","originModel","targetRef","attemptId","attemptStatus","terminalClass","releaseBlocked","directiveHash","timeoutMs","leaseExpiresAt","heartbeatAt","notBeforeAt","maxAttempts","counts","hashes","relativePaths","artifactRefs","evidenceRefs","validationRefs","registryRefs","storyIds","epicIds","debtIds","iteration","currentStage","stageHistory","durationMs","retryCount","attemptCount","queuePriority","queueScope","queueCap","queueDepth","queueAvailable","queueBackpressure","nextTickAt","nextAttemptAt","nextRetryAt","deadLetterReasonCode","deliveryCursor","lastAttemptAt","sentAt","ackedAt","lastErrorReason","lastErrorSummary","deadLetterSummary","deadLetteredAt","processingOutputSource","publicSummary","redactionEvidence","selectorKind","targetId","targetJobId","relatedJobIds","serverAccessToLocalhost","serverHeldStream","initialCursor","targetResolutionState","targetKind","relatedJobCount","snapshotStatus","snapshotGeneratedAt","renderedSummaryCount","cursorState","currentCursor","nextCursor","cursorAdvanced","eventId","latestEventId","watchStatus","stopReason","syncVersion","transportKind","envelopeId","workspaceId","workspaceLabel","projectId","projectLabel","snapshotId","syncCursor","previousSyncCursor","nextSyncCursor","commandId","receiptId","acknowledgedAt","observedAt","generatedAt","syncStatus","commandStatus","receiptStatus","actorType","actorId","tenantId","commandKind","auditStatus","redactionCount","autonomousGovernance","governanceVersion","governanceEvidence","governanceSource","publicSafe"]),at=new Set(["foreground_invoke","scheduled_tick","runner_tick","manual_tick","headless_execution","jobs_command","invoke_watch","portal_render"]);function rt(a){return import(a)}function st(a){return`sha256:${M("sha256").update(a).digest("hex")}`}function E(a){return typeof a=="object"&&a!==null&&!Array.isArray(a)}function ot(a){return a instanceof Error?a.message:String(a)}function nt(a){return E(a)&&typeof a.code=="string"?a.code:void 0}function m(a,e){if(a instanceof i)return a;const t=ot(a),r=nt(a);return r==="SQLITE_CORRUPT"||r==="SQLITE_NOTADB"||Ze.test(t)?new i("sqlite-integrity-failed","Continuity SQLite integrity check failed; durable mutation is blocked until the store is repaired or rebuilt.",{cause:a,mutationBlocked:!0}):r==="SQLITE_CONSTRAINT_FOREIGNKEY"||et.test(t)?new i("sqlite-foreign-key-failed","Continuity SQLite foreign-key validation failed; mutation was rejected.",{cause:a}):new i(e,t,{cause:a})}function g(a,e){if(typeof a=="string"){if(/\.(?:redactionEvidence|redactionSummary)\[\d+\]\.field$/.test(e)&&/^[A-Za-z0-9_.:[\]-]{1,120}$/.test(a)||/\.(?:redactionEvidence|redactionSummary)\[\d+\]\.reason$/.test(e)&&Xe.has(a))return;if(me.test(a))throw new i("sqlite-public-safety-rejected",`Continuity SQLite metadata rejected unsafe value at ${e}.`);return}if(!(a===null||typeof a=="number"||typeof a=="boolean")){if(Array.isArray(a)){a.forEach((t,r)=>g(t,`${e}[${r}]`));return}if(E(a)){for(const[t,r]of Object.entries(a)){const s=t==="terminationToken"&&e.endsWith(".releaseBlocked")&&r==="[YOLOOP_RELEASE_BLOCKED]";if(_e.test(t)&&!s)throw new i("sqlite-public-safety-rejected",`Continuity SQLite metadata rejected unsafe key at ${e}.${t}.`);g(r,`${e}.${t}`)}return}throw new i("sqlite-public-safety-rejected",`Continuity SQLite metadata rejected unsupported value at ${e}.`)}}function b(a,e){if(a!==void 0&&me.test(a))throw new i("sqlite-public-safety-rejected",`Continuity SQLite text rejected unsafe value at ${e}.`)}function Y(a,e,t=240){if(a.trim().length===0)throw new i("sqlite-public-safety-rejected",`Continuity SQLite processing summary text is required at ${e}.`);if(b(a,e),a.length>t)throw new i("sqlite-public-safety-rejected",`Continuity SQLite processing summary text exceeded the public bound at ${e}.`)}function K(a){const e={};if(!a)return e;for(const[t,r]of Object.entries(a))if(tt.has(t)&&!_e.test(t))try{g(r,`metadata.${t}`),e[t]=r}catch{}return e}function dt(a){const e={...K(a.metadata),schemaVersion:1,contractVersion:"continuity-v1",eventType:O,executionId:a.executionId,jobId:a.jobId,operationId:a.executionId,publicSummary:a.publicSummary};return a.idempotencyKey&&(e.idempotencyKey=a.idempotencyKey),a.goalId&&(e.goalId=a.goalId),a.loopId&&(e.loopId=a.loopId),a.reasonCode&&(e.reasonCode=a.reasonCode),a.source&&at.has(a.source)&&(e.processingOutputSource=a.source),K(e)}function ye(a){const e=Math.floor(a??de);return Number.isFinite(e)?Math.max(1,Math.min($e,e)):de}function it(a){if(a.eventId&&a.eventId.trim().length>0)return a.eventId;const e=a.executionId.replace(/[^a-z0-9_.:-]+/gi,"-").replace(/^-+|-+$/g,"");return e.length>0?`event-processing-summary-${e.slice(0,160)}`:`event-processing-summary-${M("sha256").update(a.executionId).digest("hex").slice(0,32)}`}function ct(a){return`${Z}${J.from(JSON.stringify([a.created_at,a.event_id]),"utf8").toString("base64url")}`}function lt(a){if(a?.startsWith(Z))try{const e=a.slice(Z.length),t=JSON.parse(J.from(e,"base64url").toString("utf8"));if(!Array.isArray(t)||t.length!==2)return;const[r,s]=t;return typeof r!="string"||typeof s!="string"?void 0:{createdAt:r,eventId:s}}catch{return}}function D(a,e){if(!e)return;const t=e.trim().replace(/^@/,"");if(a==="job"&&/^job-J\d{3,}$/i.test(t))return`job-J${t.match(/\d+$/)?.[0]??""}`;if(a==="goal"&&/^goal-G\d{3,}$/i.test(t))return`goal-G${t.match(/\d+$/)?.[0]??""}`;if(a==="loop"&&/^loop-L\d{3,}$/i.test(t))return`loop-L${t.match(/\d+$/)?.[0]??""}`}function he(a){return a?q(a.map(e=>D("job",e)).filter(e=>!!e)):[]}function pe(a){const e=a.target;if(!e){const r=D("job",a.jobId);return r?{sql:" AND job_id = ?",params:[r]}:{sql:"",params:[]}}const t=e.selectorKind;if(t==="job"){const r=D("job",e.jobId??e.targetId??a.jobId);return r?{sql:" AND job_id = ?",params:[r]}:{sql:" AND 1 = 0",params:[]}}if(t==="goal"){const r=D("goal",e.goalId??e.targetId);if(!r)return{sql:" AND 1 = 0",params:[]};const s=he(e.relatedJobIds);return{sql:` AND (job_id = ? OR json_extract(metadata_json, '$.goalId') = ?${s.length>0?` OR job_id IN (${s.map(()=>"?").join(", ")})`:""})`,params:[r,r,...s]}}if(t==="loop"){const r=D("loop",e.loopId??e.targetId);if(!r)return{sql:" AND 1 = 0",params:[]};const s=he(e.relatedJobIds);return{sql:` AND (job_id = ? OR json_extract(metadata_json, '$.loopId') = ?${s.length>0?` OR job_id IN (${s.map(()=>"?").join(", ")})`:""})`,params:[r,r,...s]}}return{sql:" AND 1 = 0",params:[]}}function be(a){const e={event_id:a.event_id,job_id:a.job_id,execution_id:a.operation_id,public_summary:a.summary,reason_code:a.reason_code,created_at:a.created_at,metadata_json:a.metadata_json};return{...e,cursor:ct(e)}}function S(a){return a?(g(a,"metadata"),JSON.stringify(a)):null}function ut(a,e,t){const r=d=>`sha256:${M("sha256").update(d.map(o=>[o.id,o.status,o.reasonCode].join(":")).sort().join(`
2
+ `)).digest("hex")}`,s=r(e),n=r(t);return{receipts:s,outputs:n,aggregate:`sha256:${M("sha256").update(`${a}:${s}:${n}`).digest("hex")}`}}function _t(a){if(!a)return{};try{const e=JSON.parse(a);return E(e)?{...e}:{}}catch{return{}}}function c(a,e,t,r=!1){if(a===void 0){if(r)throw new i("sqlite-public-safety-rejected",`Continuity SQLite text is required at ${e}.`);return}if(b(a,e),a.trim().length===0||a.length>t)throw new i("sqlite-public-safety-rejected",`Continuity SQLite text exceeded the public bound at ${e}.`)}function N(a,e){if(a!==void 0&&(!Number.isFinite(a)||a<0||Math.floor(a)!==a))throw new i("sqlite-public-safety-rejected",`Continuity SQLite numeric value rejected at ${e}.`)}function v(a){c(a.outboxId,"outbox.outboxId",160,!0),c(a.jobId,"outbox.jobId",160),c(a.operationId,"outbox.operationId",160),c(a.idempotencyKey,"outbox.idempotencyKey",160),c(a.type,"outbox.type",80),c(a.status,"outbox.status",40),c(a.deliveryCursor,"outbox.deliveryCursor",200),c(a.timestamp,"outbox.timestamp",80),c(a.reasonCode,"outbox.reasonCode",100),c(a.summary,"outbox.summary",240)}function Ie(a){return a==="pending"||a==="sent"||a==="acked"||a==="failed"||a==="dead_letter"}function mt(a){return typeof a=="string"&&Ie(a)?a:"failed"}function h(a){return typeof a=="string"&&a.length>0?a:void 0}function yt(a){if(typeof a!="string"||a.trim().length===0)return{};try{const e=JSON.parse(a);return E(e)?(g(e,"outbox.payload"),e):{}}catch{return{}}}function ht(a){return{outboxId:String(a.outbox_id??""),...h(a.job_id)?{jobId:h(a.job_id)}:{},operationId:String(a.operation_id??""),...h(a.idempotency_key)?{idempotencyKey:h(a.idempotency_key)}:{},type:String(a.type??""),status:mt(a.status),payload:yt(a.payload_json),retryCount:typeof a.retry_count=="number"?a.retry_count:0,attemptCount:typeof a.attempt_count=="number"?a.attempt_count:0,...h(a.delivery_cursor)?{deliveryCursor:h(a.delivery_cursor)}:{},...h(a.next_attempt_at)?{nextAttemptAt:h(a.next_attempt_at)}:{},...h(a.last_attempt_at)?{lastAttemptAt:h(a.last_attempt_at)}:{},...h(a.sent_at)?{sentAt:h(a.sent_at)}:{},...h(a.acked_at)?{ackedAt:h(a.acked_at)}:{},...h(a.last_error_code)?{lastErrorCode:h(a.last_error_code)}:{},...h(a.last_error_reason)?{lastErrorReason:h(a.last_error_reason)}:{},...h(a.last_error_summary)?{lastErrorSummary:h(a.last_error_summary)}:{},...h(a.dead_letter_summary)?{deadLetterSummary:h(a.dead_letter_summary)}:{},...h(a.dead_lettered_at)?{deadLetteredAt:h(a.dead_lettered_at)}:{},createdAt:String(a.created_at??""),updatedAt:String(a.updated_at??""),publicSafe:!0}}function C(a){return{...a,publicSafe:!0}}function T(a,e){return a===void 0?null:(g(a,e),JSON.stringify(a))}function U(a){if(!a)return;const e=q(a.map(t=>t.replace(/\\/g,"/"))).filter(t=>t.length>0).slice(0,50);for(const[t,r]of e.entries()){c(r,`relativePaths[${t}]`,240,!0);const s=r.split("/").filter(Boolean);if(r.startsWith("/")||r.startsWith("~")||r.includes("://")||/^[A-Za-z]:\//.test(r)||s.includes(".."))throw new i("sqlite-public-safety-rejected",`Continuity SQLite rejected non-relative path at relativePaths[${t}].`)}return e}function V(a){const e={...K(a.metadata),schemaVersion:1,contractVersion:"continuity-v1",attemptId:a.attemptId,executionId:a.executionId,jobId:a.jobId};return a.directiveHash&&(e.directiveHash=a.directiveHash),a.runtimeId&&(e.runtimeId=a.runtimeId),a.modelId&&(e.modelId=a.modelId),a.runtimeSource&&(e.runtimeSource=a.runtimeSource),a.status&&(e.attemptStatus=a.status),a.reasonCode&&(e.reasonCode=a.reasonCode),a.durationMs!==void 0&&(e.durationMs=a.durationMs),a.retryCount!==void 0&&(e.retryCount=a.retryCount),a.attemptCount!==void 0&&(e.attemptCount=a.attemptCount),a.maxAttempts!==void 0&&(e.maxAttempts=a.maxAttempts),a.queuePriority!==void 0&&(e.queuePriority=a.queuePriority),a.queueScope&&(e.queueScope=a.queueScope),a.nextRetryAt&&(e.nextRetryAt=a.nextRetryAt),a.deadLetterReasonCode&&(e.deadLetterReasonCode=a.deadLetterReasonCode),a.counts&&(e.counts=a.counts),a.hashes&&(e.hashes=a.hashes),a.redactionSummary&&(e.redactionEvidence=a.redactionSummary),a.relativePaths&&(e.relativePaths=U(a.relativePaths)),K(e)}function L(a){c(a.attemptId,"headless_execution.attemptId",160,!0),c(a.executionId,"headless_execution.executionId",160,!0),c(a.jobId,"headless_execution.jobId",160,!0),c(a.directiveHash,"headless_execution.directiveHash",160),c(a.targetRef,"headless_execution.targetRef",240),c(a.storyId,"headless_execution.storyId",120),c(a.epicId,"headless_execution.epicId",120),c(a.runtimeId,"headless_execution.runtimeId",80),c(a.modelId,"headless_execution.modelId",120),c(a.runtimeSource,"headless_execution.runtimeSource",40),c(a.reasonCode,"headless_execution.reasonCode",100),c(a.publicSummary,"headless_execution.publicSummary",240),c(a.leaseId,"headless_execution.leaseId",160),c(a.runnerId,"headless_execution.runnerId",160),c(a.timestamp,"headless_execution.timestamp",80)}function w(a){if(Array.isArray(a)){const e=a[0];return E(e)?Object.values(e)[0]:e}return E(a)?Object.values(a)[0]:a}function pt(a){return ze.includes(a)}function q(a){return Array.from(new Set(a))}function bt(a){const e=E(a)&&"default"in a?a.default:a;if(typeof e!="function")throw new i("sqlite-adapter-unavailable","Optional better-sqlite3 adapter did not export a database constructor.",{guidance:ie});const t=e;return{adapterName:"better-sqlite3",open(r,s){return new t(r,s)}}}async function It(a=rt){try{const e=await a("better-sqlite3");return{ok:!0,adapter:bt(e)}}catch(e){return e instanceof i?{ok:!1,error:e}:{ok:!1,error:new i("sqlite-adapter-unavailable","Optional Enterprise continuity SQLite adapter better-sqlite3 is unavailable.",{cause:e,guidance:ie})}}}function Ee(a){return se(a,ne,We)}function Et(a){const e=se(a,ne);ae(e,{recursive:!0,mode:448});try{z(e,448)}catch{}return e}async function Dt(a){const e=a.adapter??await Ce(a.adapterLoader),t=a.filePath??Ee(a.projectRoot);if(a.strictReadOnly)fe(t);else if(a.filePath){ae(re(t),{recursive:!0,mode:448});try{z(re(t),448)}catch{}}else Et(a.projectRoot);let r;try{r=e.open(t,a.strictReadOnly?{fileMustExist:!0,readonly:!0}:void 0)}catch(n){throw m(n,"sqlite-integrity-failed")}const s=new ft({db:r,filePath:t,adapterName:e.adapterName,busyTimeoutMs:a.busyTimeoutMs??Fe,integrityCheck:a.integrityCheck??!0,now:a.now??(()=>new Date().toISOString())});try{if(a.strictReadOnly)return s.runIntegrityCheck(),s.getSchemaVersion(),s;if(s.initialize(),te(t))try{z(t,384)}catch{}return s}catch(n){try{r.close()}catch{}throw n}}function fe(a){if(!te(a))throw new i("sqlite-integrity-failed","Continuity database is unavailable for physical read-only inspection.",{mutationBlocked:!0});let e;try{const t=Ae(a,"r");try{if(e=J.alloc(20),xe(t,e,0,e.length,0)<e.length)throw new Error("short-header")}finally{ge(t)}}catch(t){throw new i("sqlite-integrity-failed","Continuity database header could not be inspected read-only.",{cause:t,mutationBlocked:!0})}if(e.subarray(0,16).toString("ascii")!=="SQLite format 3\0"||e[18]===2||e[19]===2)throw new i("sqlite-integrity-failed","Continuity database requires an unsafe WAL read profile.",{mutationBlocked:!0})}async function Ut(a){const e=Ee(a.projectRoot);fe(e);const t=a.adapter??await Ce(a.adapterLoader);let r;try{r=t.open(e,{fileMustExist:!0,readonly:!0});const n=w(r.pragma("integrity_check",{simple:!0}));if(String(n).toLowerCase()!=="ok")throw new Error("integrity")}catch(n){throw new i("sqlite-integrity-failed","Continuity database failed physical read-only inspection.",{cause:n,mutationBlocked:!0})}const s=n=>{const d=n.trim().replace(/^\(+/u,"").toLowerCase();if(!/^(select|with\s)/u.test(d)||/\b(insert|update|delete|replace|create|alter|drop|pragma|attach|detach|vacuum|reindex)\b/u.test(d))throw new i("sqlite-mutation-blocked","Statement is not in the physical read-only query profile.",{mutationBlocked:!0})};return Object.freeze({filePath:e,adapterName:t.adapterName,all(n,...d){return s(n),r.prepare(n).all(...d)},get(n,...d){return s(n),r.prepare(n).get(...d)},close(){r.close()}})}async function Ce(a){const e=a?await a():await It();if(!e.ok)throw e.error;return e.adapter}class ft{filePath;adapterName;db;busyTimeoutMs;integrityCheck;now;mutationBlocked=!1;constructor(e){this.db=e.db,this.filePath=e.filePath,this.adapterName=e.adapterName,this.busyTimeoutMs=e.busyTimeoutMs,this.integrityCheck=e.integrityCheck,this.now=e.now}initialize(){try{this.configureConnection(),this.integrityCheck&&this.runIntegrityCheck(),this.ensureMigrationTable(),this.applyMigrations(),this.integrityCheck&&this.runIntegrityCheck()}catch(e){throw m(e,"sqlite-migration-failed")}}close(){this.db.close()}pragmaValue(e){return w(this.db.pragma(e,{simple:!0}))}runIntegrityCheck(){try{if(String(w(this.db.pragma("integrity_check",{simple:!0}))??"").toLowerCase()!=="ok")throw this.mutationBlocked=!0,new i("sqlite-integrity-failed","Continuity SQLite integrity check failed; durable mutation is blocked until the store is repaired or rebuilt.",{mutationBlocked:!0})}catch(e){throw this.mutationBlocked=!0,m(e,"sqlite-integrity-failed")}}getSchemaVersion(){const e=this.db.prepare("SELECT COALESCE(MAX(version), 0) AS version FROM continuity_migrations").get();return E(e)&&typeof e.version=="number"?e.version:0}countRows(e){if(!pt(e))throw new i("sqlite-public-safety-rejected","Unknown continuity table name.");const t=this.db.prepare(`SELECT COUNT(*) AS count FROM ${e}`).get();return E(t)&&typeof t.count=="number"?t.count:0}reserveNativeAgentDispatch(e){if(this.assertCanMutate(),!Me(e.binding)||!ke(e.hostAttestation))throw new i("sqlite-public-safety-rejected","Native-agent reservation rejected invalid binding or host evidence.");const t={schemaVersion:1,storyId:e.binding.storyId,graphRevision:e.binding.graphRevision.replace(/^sha256:/,"").toLowerCase(),dispatchKey:e.binding.dispatchKey.toLowerCase(),profile:"neocortex",parentHandle:null,handle:null,topology:e.topology??"native-agent",state:"requested",requestedAt:e.requestedAt,observedAt:null,terminalAt:null,freshness:"unknown",observationSource:"local-reservation",reasonCode:e.reasonCode??"native-agent-dispatch-reserved",revision:0,publicSafe:!0};if(!X(t))throw new i("sqlite-public-safety-rejected","Native-agent reservation rejected unsafe public fields.");try{return this.runInTransaction(()=>{const r=this.db.prepare(`
1043
3
  INSERT OR IGNORE INTO native_agent_dispatches (
1044
4
  dispatch_key, story_id, graph_revision, profile,
1045
5
  host_kind, host_version, host_origin, host_confidence,
1046
6
  parent_handle, handle, topology, state, requested_at, observed_at,
1047
7
  terminal_at, freshness, observation_source, reason_code, revision
1048
8
  ) VALUES (?, ?, ?, 'neocortex', ?, ?, ?, ?, NULL, NULL, ?, 'requested', ?, NULL, NULL, 'unknown', 'local-reservation', ?, 0)
1049
- `).run(candidate.dispatchKey, candidate.storyId, candidate.graphRevision, input.hostAttestation.hostKind, input.hostAttestation.hostVersion, input.hostAttestation.origin, input.hostAttestation.confidence, candidate.topology, candidate.requestedAt, candidate.reasonCode);
1050
- const row = this.findNativeAgentDispatchRow(candidate.dispatchKey, candidate.storyId, candidate.graphRevision);
1051
- if (!row)
1052
- throw new Error('native-agent-reservation-readback-missing');
1053
- if (row.dispatch_key !== candidate.dispatchKey
1054
- || row.story_id !== candidate.storyId
1055
- || row.graph_revision !== candidate.graphRevision) {
1056
- throw new ContinuitySqliteStoreError('sqlite-mutation-failed', 'Native-agent reservation conflicts with an existing story graph binding.');
1057
- }
1058
- return Object.freeze({
1059
- receipt: toNativeAgentReceipt(row),
1060
- reserved: inserted.changes === 1,
1061
- replay: inserted.changes === 0,
1062
- });
1063
- });
1064
- }
1065
- catch (error) {
1066
- throw mapSqliteError(error, 'sqlite-mutation-failed');
1067
- }
1068
- }
1069
- observeNativeAgentDispatch(input) {
1070
- this.assertCanMutate();
1071
- try {
1072
- return this.runInTransaction(() => {
1073
- const currentRow = this.db.prepare('SELECT * FROM native_agent_dispatches WHERE dispatch_key = ?').get(input.dispatchKey);
1074
- if (!currentRow) {
1075
- throw new ContinuitySqliteStoreError('sqlite-mutation-failed', 'Native-agent dispatch reservation is missing.');
1076
- }
1077
- const current = toNativeAgentReceipt(currentRow);
1078
- const terminalAt = NATIVE_AGENT_TERMINAL_STATES.has(input.state)
1079
- ? input.terminalAt ?? input.observedAt
1080
- : null;
1081
- const handle = input.handle ?? current.handle;
1082
- const parentHandle = input.parentHandle === undefined ? current.parentHandle : input.parentHandle;
1083
- const candidate = {
1084
- ...current,
1085
- handle,
1086
- parentHandle,
1087
- topology: input.topology,
1088
- state: input.state,
1089
- observedAt: input.observedAt,
1090
- terminalAt,
1091
- freshness: input.freshness,
1092
- observationSource: input.observationSource,
1093
- reasonCode: input.reasonCode,
1094
- revision: current.revision + 1,
1095
- };
1096
- if (!isNativeAgentDispatchReceiptV1(candidate)) {
1097
- throw new ContinuitySqliteStoreError('sqlite-public-safety-rejected', 'Native-agent observation rejected unsafe or inconsistent fields.');
1098
- }
1099
- if (current.handle && input.handle && current.handle !== input.handle) {
1100
- throw new ContinuitySqliteStoreError('sqlite-mutation-failed', 'Native-agent observation handle conflicts with the reserved dispatch.');
1101
- }
1102
- if (!isNativeAgentStateTransitionAllowed(current.state, input.state)) {
1103
- throw new ContinuitySqliteStoreError('sqlite-mutation-failed', 'Native-agent observation attempted a terminal or non-monotonic regression.');
1104
- }
1105
- const sameObservation = current.state === candidate.state
1106
- && current.handle === candidate.handle
1107
- && current.parentHandle === candidate.parentHandle
1108
- && current.topology === candidate.topology
1109
- && current.observedAt === candidate.observedAt
1110
- && current.terminalAt === candidate.terminalAt
1111
- && current.freshness === candidate.freshness
1112
- && current.observationSource === candidate.observationSource
1113
- && current.reasonCode === candidate.reasonCode;
1114
- if (sameObservation)
1115
- return Object.freeze({ receipt: current, applied: false, replay: true });
1116
- if (input.expectedRevision !== current.revision) {
1117
- throw new ContinuitySqliteStoreError('sqlite-mutation-failed', 'Native-agent observation CAS revision conflict.');
1118
- }
1119
- if (current.observedAt && Date.parse(input.observedAt) < Date.parse(current.observedAt)) {
1120
- throw new ContinuitySqliteStoreError('sqlite-mutation-failed', 'Native-agent observation timestamp regressed.');
1121
- }
1122
- const updated = this.db.prepare(`
9
+ `).run(t.dispatchKey,t.storyId,t.graphRevision,e.hostAttestation.hostKind,e.hostAttestation.hostVersion,e.hostAttestation.origin,e.hostAttestation.confidence,t.topology,t.requestedAt,t.reasonCode),s=this.findNativeAgentDispatchRow(t.dispatchKey,t.storyId,t.graphRevision);if(!s)throw new Error("native-agent-reservation-readback-missing");if(s.dispatch_key!==t.dispatchKey||s.story_id!==t.storyId||s.graph_revision!==t.graphRevision)throw new i("sqlite-mutation-failed","Native-agent reservation conflicts with an existing story graph binding.");return Object.freeze({receipt:$(s),reserved:r.changes===1,replay:r.changes===0})})}catch(r){throw m(r,"sqlite-mutation-failed")}}observeNativeAgentDispatch(e){this.assertCanMutate();try{return this.runInTransaction(()=>{const t=this.db.prepare("SELECT * FROM native_agent_dispatches WHERE dispatch_key = ?").get(e.dispatchKey);if(!t)throw new i("sqlite-mutation-failed","Native-agent dispatch reservation is missing.");const r=$(t),s=oe.has(e.state)?e.terminalAt??e.observedAt:null,n=e.handle??r.handle,d=e.parentHandle===void 0?r.parentHandle:e.parentHandle,o={...r,handle:n,parentHandle:d,topology:e.topology,state:e.state,observedAt:e.observedAt,terminalAt:s,freshness:e.freshness,observationSource:e.observationSource,reasonCode:e.reasonCode,revision:r.revision+1};if(!X(o))throw new i("sqlite-public-safety-rejected","Native-agent observation rejected unsafe or inconsistent fields.");if(r.handle&&e.handle&&r.handle!==e.handle)throw new i("sqlite-mutation-failed","Native-agent observation handle conflicts with the reserved dispatch.");if(!je(r.state,e.state))throw new i("sqlite-mutation-failed","Native-agent observation attempted a terminal or non-monotonic regression.");if(r.state===o.state&&r.handle===o.handle&&r.parentHandle===o.parentHandle&&r.topology===o.topology&&r.observedAt===o.observedAt&&r.terminalAt===o.terminalAt&&r.freshness===o.freshness&&r.observationSource===o.observationSource&&r.reasonCode===o.reasonCode)return Object.freeze({receipt:r,applied:!1,replay:!0});if(e.expectedRevision!==r.revision)throw new i("sqlite-mutation-failed","Native-agent observation CAS revision conflict.");if(r.observedAt&&Date.parse(e.observedAt)<Date.parse(r.observedAt))throw new i("sqlite-mutation-failed","Native-agent observation timestamp regressed.");if(this.db.prepare(`
1123
10
  UPDATE native_agent_dispatches
1124
11
  SET parent_handle = ?, handle = ?, topology = ?, state = ?, observed_at = ?,
1125
12
  terminal_at = ?, freshness = ?, observation_source = ?, reason_code = ?, revision = revision + 1
1126
13
  WHERE dispatch_key = ? AND revision = ?
1127
- `).run(candidate.parentHandle, candidate.handle, candidate.topology, candidate.state, candidate.observedAt, candidate.terminalAt, candidate.freshness, candidate.observationSource, candidate.reasonCode, candidate.dispatchKey, input.expectedRevision);
1128
- if (updated.changes !== 1) {
1129
- throw new ContinuitySqliteStoreError('sqlite-mutation-failed', 'Native-agent observation CAS update was not applied.');
1130
- }
1131
- this.db.prepare(`
14
+ `).run(o.parentHandle,o.handle,o.topology,o.state,o.observedAt,o.terminalAt,o.freshness,o.observationSource,o.reasonCode,o.dispatchKey,e.expectedRevision).changes!==1)throw new i("sqlite-mutation-failed","Native-agent observation CAS update was not applied.");return this.db.prepare(`
1132
15
  INSERT INTO native_agent_observations (
1133
16
  dispatch_key, sequence, state, handle, parent_handle, topology,
1134
17
  observed_at, terminal_at, freshness, observation_source, reason_code
1135
18
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1136
- `).run(candidate.dispatchKey, candidate.revision, candidate.state, candidate.handle, candidate.parentHandle, candidate.topology, candidate.observedAt, candidate.terminalAt, candidate.freshness, candidate.observationSource, candidate.reasonCode);
1137
- return Object.freeze({ receipt: candidate, applied: true, replay: false });
1138
- });
1139
- }
1140
- catch (error) {
1141
- throw mapSqliteError(error, 'sqlite-mutation-failed');
1142
- }
1143
- }
1144
- getNativeAgentDispatchStatus(dispatchKey, options = {}) {
1145
- const row = this.db.prepare('SELECT * FROM native_agent_dispatches WHERE dispatch_key = ?').get(dispatchKey);
1146
- if (!row)
1147
- return null;
1148
- const receipt = toNativeAgentReceipt(row);
1149
- const staleAfterMs = Math.max(1_000, options.staleAfterMs ?? 30_000);
1150
- const nowMs = Date.parse(options.nowIso ?? this.now());
1151
- const observedMs = receipt.observedAt ? Date.parse(receipt.observedAt) : NaN;
1152
- if (!NATIVE_AGENT_TERMINAL_STATES.has(receipt.state)
1153
- && Number.isFinite(nowMs)
1154
- && (!Number.isFinite(observedMs) || nowMs - observedMs > staleAfterMs)) {
1155
- return Object.freeze({
1156
- ...receipt,
1157
- state: 'unknown',
1158
- freshness: receipt.observedAt ? 'stale' : 'unknown',
1159
- reasonCode: receipt.observedAt ? 'native-agent-observation-stale' : 'native-agent-awaiting-host-evidence',
1160
- });
1161
- }
1162
- return receipt;
1163
- }
1164
- listRecentNativeAgentDispatches(limit = 20) {
1165
- const bounded = Math.max(1, Math.min(50, Math.floor(limit)));
1166
- const rows = this.db.prepare(`
19
+ `).run(o.dispatchKey,o.revision,o.state,o.handle,o.parentHandle,o.topology,o.observedAt,o.terminalAt,o.freshness,o.observationSource,o.reasonCode),Object.freeze({receipt:o,applied:!0,replay:!1})})}catch(t){throw m(t,"sqlite-mutation-failed")}}getNativeAgentDispatchStatus(e,t={}){const r=this.db.prepare("SELECT * FROM native_agent_dispatches WHERE dispatch_key = ?").get(e);if(!r)return null;const s=$(r),n=Math.max(1e3,t.staleAfterMs??3e4),d=Date.parse(t.nowIso??this.now()),o=s.observedAt?Date.parse(s.observedAt):NaN;return!oe.has(s.state)&&Number.isFinite(d)&&(!Number.isFinite(o)||d-o>n)?Object.freeze({...s,state:"unknown",freshness:s.observedAt?"stale":"unknown",reasonCode:s.observedAt?"native-agent-observation-stale":"native-agent-awaiting-host-evidence"}):s}listRecentNativeAgentDispatches(e=20){const t=Math.max(1,Math.min(50,Math.floor(e))),r=this.db.prepare(`
1167
20
  SELECT * FROM native_agent_dispatches
1168
21
  ORDER BY COALESCE(observed_at, requested_at) DESC, dispatch_key DESC
1169
22
  LIMIT ?
1170
- `).all(bounded);
1171
- return Object.freeze(rows.map(toNativeAgentReceipt));
1172
- }
1173
- findNativeAgentDispatchRow(dispatchKey, storyId, graphRevision) {
1174
- return this.db.prepare(`
23
+ `).all(t);return Object.freeze(r.map($))}findNativeAgentDispatchRow(e,t,r){return this.db.prepare(`
1175
24
  SELECT * FROM native_agent_dispatches
1176
25
  WHERE dispatch_key = ? OR (story_id = ? AND graph_revision = ?)
1177
26
  ORDER BY CASE WHEN dispatch_key = ? THEN 0 ELSE 1 END
1178
27
  LIMIT 1
1179
- `).get(dispatchKey, storyId, graphRevision, dispatchKey);
1180
- }
1181
- getLocalConsoleAttachmentState(jobId) {
1182
- assertBoundedPublicText(jobId, 'local_console.jobId', 160, true);
1183
- const row = this.db.prepare(`
28
+ `).get(e,t,r,e)}getLocalConsoleAttachmentState(e){c(e,"local_console.jobId",160,!0);const t=this.db.prepare(`
1184
29
  SELECT j.job_id AS job_id,
1185
30
  j.kind AS kind,
1186
31
  j.status AS status,
@@ -1189,210 +34,41 @@ export class ContinuitySqliteStore {
1189
34
  FROM jobs j
1190
35
  WHERE j.job_id = ?
1191
36
  LIMIT 1
1192
- `).get(jobId);
1193
- if (!isRecord(row) || typeof row.job_id !== 'string')
1194
- return null;
1195
- return {
1196
- jobId: row.job_id,
1197
- kind: typeof row.kind === 'string' ? row.kind : 'unknown',
1198
- status: typeof row.status === 'string' ? row.status : 'unknown',
1199
- activeLeaseCount: typeof row.active_lease_count === 'number' ? row.active_lease_count : 0,
1200
- runningHeadlessAttemptCount: typeof row.running_headless_attempt_count === 'number' ? row.running_headless_attempt_count : 0,
1201
- publicSafe: true,
1202
- };
1203
- }
1204
- recordJob(input) {
1205
- this.assertCanMutate();
1206
- assertPublicSafeText(input.summary, 'jobs.summary');
1207
- const metadataJson = stringifyPublicMetadata(input.metadata);
1208
- try {
1209
- const result = this.db.prepare(`
37
+ `).get(e);return!E(t)||typeof t.job_id!="string"?null:{jobId:t.job_id,kind:typeof t.kind=="string"?t.kind:"unknown",status:typeof t.status=="string"?t.status:"unknown",activeLeaseCount:typeof t.active_lease_count=="number"?t.active_lease_count:0,runningHeadlessAttemptCount:typeof t.running_headless_attempt_count=="number"?t.running_headless_attempt_count:0,publicSafe:!0}}recordJob(e){this.assertCanMutate(),b(e.summary,"jobs.summary");const t=S(e.metadata);try{const r=this.db.prepare(`
1210
38
  INSERT OR IGNORE INTO jobs (
1211
39
  job_id, kind, status, summary, reason_code, created_at, updated_at,
1212
40
  next_run_at, goal_id, loop_id, campaign_id, operation_id,
1213
41
  idempotency_key, metadata_json
1214
42
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1215
- `).run(input.jobId, input.kind, input.status, input.summary ?? null, input.reasonCode ?? null, input.createdAt, input.updatedAt, input.nextRunAt ?? null, input.goalId ?? null, input.loopId ?? null, input.campaignId ?? null, input.operationId ?? null, input.idempotencyKey ?? null, metadataJson);
1216
- return this.toWriteResult(result);
1217
- }
1218
- catch (error) {
1219
- throw mapSqliteError(error, 'sqlite-mutation-failed');
1220
- }
1221
- }
1222
- appendEvent(input) {
1223
- this.assertCanMutate();
1224
- assertPublicSafeText(input.summary, 'job_events.summary');
1225
- const metadataJson = stringifyPublicMetadata(input.metadata);
1226
- try {
1227
- const result = this.db.prepare(`
43
+ `).run(e.jobId,e.kind,e.status,e.summary??null,e.reasonCode??null,e.createdAt,e.updatedAt,e.nextRunAt??null,e.goalId??null,e.loopId??null,e.campaignId??null,e.operationId??null,e.idempotencyKey??null,t);return this.toWriteResult(r)}catch(r){throw m(r,"sqlite-mutation-failed")}}appendEvent(e){this.assertCanMutate(),b(e.summary,"job_events.summary");const t=S(e.metadata);try{const r=this.db.prepare(`
1228
44
  INSERT OR IGNORE INTO job_events (
1229
45
  event_id, job_id, operation_id, idempotency_key, event_sequence,
1230
46
  type, summary, reason_code, created_at, metadata_json
1231
47
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1232
- `).run(input.eventId, input.jobId, input.operationId, input.idempotencyKey ?? null, input.sequence ?? null, input.type, input.summary, input.reasonCode ?? null, input.createdAt, metadataJson);
1233
- return this.toWriteResult(result);
1234
- }
1235
- catch (error) {
1236
- throw mapSqliteError(error, 'sqlite-mutation-failed');
1237
- }
1238
- }
1239
- recordProcessingOutputSummary(input) {
1240
- assertProcessingSummaryText(input.executionId, 'processing_output_summary.executionId', 160);
1241
- assertProcessingSummaryText(input.jobId, 'processing_output_summary.jobId', 160);
1242
- assertProcessingSummaryText(input.publicSummary, 'processing_output_summary.publicSummary', CONTINUITY_PROCESSING_OUTPUT_SUMMARY_MAX_CHARS);
1243
- assertProcessingSummaryText(input.createdAt, 'processing_output_summary.createdAt', 80);
1244
- assertPublicSafeText(input.eventId, 'processing_output_summary.eventId');
1245
- assertPublicSafeText(input.idempotencyKey, 'processing_output_summary.idempotencyKey');
1246
- assertPublicSafeText(input.goalId, 'processing_output_summary.goalId');
1247
- assertPublicSafeText(input.loopId, 'processing_output_summary.loopId');
1248
- assertPublicSafeText(input.reasonCode, 'processing_output_summary.reasonCode');
1249
- assertPublicSafeText(input.source, 'processing_output_summary.source');
1250
- const result = this.appendEvent({
1251
- eventId: processingSummaryEventId(input),
1252
- jobId: input.jobId,
1253
- operationId: input.executionId,
1254
- idempotencyKey: input.idempotencyKey ?? input.executionId,
1255
- type: CONTINUITY_PROCESSING_OUTPUT_SUMMARY_EVENT_TYPE,
1256
- summary: input.publicSummary,
1257
- reasonCode: input.reasonCode,
1258
- createdAt: input.createdAt,
1259
- metadata: buildProcessingOutputSummaryMetadata(input),
1260
- });
1261
- // P195.04: On a freshly applied tick summary, persist the producer-emitted
1262
- // stage/iteration into the job row and append one bounded stageHistory entry.
1263
- // Gating on `inserted` keeps this idempotent across replays and runner retries.
1264
- if (result.inserted) {
1265
- this.recordJobStageProgress({
1266
- jobId: input.jobId,
1267
- currentStage: input.metadata?.currentStage,
1268
- iteration: input.metadata?.iteration,
1269
- at: input.createdAt,
1270
- });
1271
- }
1272
- return result;
1273
- }
1274
- enqueueHeadlessExecutionAttempt(input) {
1275
- this.assertCanMutate();
1276
- validateHeadlessExecutionAttemptIdentity({
1277
- attemptId: input.attemptId,
1278
- executionId: input.executionId,
1279
- jobId: input.jobId,
1280
- directiveHash: input.directiveHash,
1281
- targetRef: input.targetRef,
1282
- storyId: input.storyId,
1283
- epicId: input.epicId,
1284
- runtimeId: input.runtimeId,
1285
- modelId: input.modelId,
1286
- runtimeSource: input.runtimeSource,
1287
- reasonCode: input.reasonCode,
1288
- publicSummary: input.publicSummary,
1289
- timestamp: input.enqueuedAt,
1290
- });
1291
- assertBoundedPublicText(input.notBeforeAt, 'headless_execution.notBeforeAt', 80);
1292
- assertBoundedPublicText(input.operationId, 'headless_execution.operationId', 160);
1293
- assertBoundedPublicText(input.idempotencyKey, 'headless_execution.idempotencyKey', 160);
1294
- assertNonNegativeInteger(input.retryCount, 'headless_execution.retryCount');
1295
- assertNonNegativeInteger(input.maxAttempts, 'headless_execution.maxAttempts');
1296
- if (input.maxAttempts !== undefined && input.maxAttempts < 1) {
1297
- throw new ContinuitySqliteStoreError('sqlite-public-safety-rejected', 'Continuity SQLite maxAttempts must be greater than zero.');
1298
- }
1299
- if (input.metadata)
1300
- assertPublicSafeValue(input.metadata, 'headless_execution.metadata');
1301
- const queuePolicy = normalizeContinuityQueuePolicy({
1302
- maxItems: input.queueCap,
1303
- maxAttempts: input.maxAttempts,
1304
- });
1305
- const queuePriority = normalizeContinuityQueuePriority(input.queuePriority);
1306
- const queueScope = normalizeContinuityQueueScope(input.queueScope);
1307
- const relativePaths = normalizePublicRelativePaths(input.relativePaths);
1308
- const metadata = buildHeadlessExecutionPublicMetadata({
1309
- attemptId: input.attemptId,
1310
- executionId: input.executionId,
1311
- jobId: input.jobId,
1312
- directiveHash: input.directiveHash,
1313
- runtimeId: input.runtimeId,
1314
- modelId: input.modelId,
1315
- runtimeSource: input.runtimeSource,
1316
- status: input.status ?? 'queued',
1317
- reasonCode: input.reasonCode,
1318
- retryCount: input.retryCount ?? 0,
1319
- attemptCount: 0,
1320
- maxAttempts: queuePolicy.maxAttempts,
1321
- queuePriority,
1322
- queueScope,
1323
- counts: input.counts,
1324
- hashes: input.hashes,
1325
- redactionSummary: input.redactionSummary,
1326
- relativePaths,
1327
- metadata: input.metadata,
1328
- });
1329
- return this.runInTransaction(() => {
1330
- const configuredPolicyRow = this.db.prepare(`
48
+ `).run(e.eventId,e.jobId,e.operationId,e.idempotencyKey??null,e.sequence??null,e.type,e.summary,e.reasonCode??null,e.createdAt,t);return this.toWriteResult(r)}catch(r){throw m(r,"sqlite-mutation-failed")}}recordProcessingOutputSummary(e){Y(e.executionId,"processing_output_summary.executionId",160),Y(e.jobId,"processing_output_summary.jobId",160),Y(e.publicSummary,"processing_output_summary.publicSummary",Be),Y(e.createdAt,"processing_output_summary.createdAt",80),b(e.eventId,"processing_output_summary.eventId"),b(e.idempotencyKey,"processing_output_summary.idempotencyKey"),b(e.goalId,"processing_output_summary.goalId"),b(e.loopId,"processing_output_summary.loopId"),b(e.reasonCode,"processing_output_summary.reasonCode"),b(e.source,"processing_output_summary.source");const t=this.appendEvent({eventId:it(e),jobId:e.jobId,operationId:e.executionId,idempotencyKey:e.idempotencyKey??e.executionId,type:O,summary:e.publicSummary,reasonCode:e.reasonCode,createdAt:e.createdAt,metadata:dt(e)});return t.inserted&&this.recordJobStageProgress({jobId:e.jobId,currentStage:e.metadata?.currentStage,iteration:e.metadata?.iteration,at:e.createdAt}),t}enqueueHeadlessExecutionAttempt(e){if(this.assertCanMutate(),L({attemptId:e.attemptId,executionId:e.executionId,jobId:e.jobId,directiveHash:e.directiveHash,targetRef:e.targetRef,storyId:e.storyId,epicId:e.epicId,runtimeId:e.runtimeId,modelId:e.modelId,runtimeSource:e.runtimeSource,reasonCode:e.reasonCode,publicSummary:e.publicSummary,timestamp:e.enqueuedAt}),c(e.notBeforeAt,"headless_execution.notBeforeAt",80),c(e.operationId,"headless_execution.operationId",160),c(e.idempotencyKey,"headless_execution.idempotencyKey",160),N(e.retryCount,"headless_execution.retryCount"),N(e.maxAttempts,"headless_execution.maxAttempts"),e.maxAttempts!==void 0&&e.maxAttempts<1)throw new i("sqlite-public-safety-rejected","Continuity SQLite maxAttempts must be greater than zero.");e.metadata&&g(e.metadata,"headless_execution.metadata");const t=B({maxItems:e.queueCap,maxAttempts:e.maxAttempts}),r=qe(e.queuePriority),s=Pe(e.queueScope),n=U(e.relativePaths),d=V({attemptId:e.attemptId,executionId:e.executionId,jobId:e.jobId,directiveHash:e.directiveHash,runtimeId:e.runtimeId,modelId:e.modelId,runtimeSource:e.runtimeSource,status:e.status??"queued",reasonCode:e.reasonCode,retryCount:e.retryCount??0,attemptCount:0,maxAttempts:t.maxAttempts,queuePriority:r,queueScope:s,counts:e.counts,hashes:e.hashes,redactionSummary:e.redactionSummary,relativePaths:n,metadata:e.metadata});return this.runInTransaction(()=>{const o=this.db.prepare(`
1331
49
  SELECT value_json
1332
50
  FROM runner_config
1333
51
  WHERE key = 'headless_queue_policy'
1334
52
  LIMIT 1
1335
- `).get();
1336
- let effectiveQueueCap = queuePolicy.maxItems;
1337
- if (input.queueCap === undefined && isRecord(configuredPolicyRow) && typeof configuredPolicyRow.value_json === 'string') {
1338
- try {
1339
- const configuredPolicy = JSON.parse(configuredPolicyRow.value_json);
1340
- if (isRecord(configuredPolicy) && typeof configuredPolicy.maxItems === 'number') {
1341
- effectiveQueueCap = normalizeContinuityQueuePolicy({ maxItems: configuredPolicy.maxItems }).maxItems;
1342
- }
1343
- }
1344
- catch {
1345
- // runner_config has a json_valid constraint; retain the bounded default defensively.
1346
- }
1347
- }
1348
- this.db.prepare(`
53
+ `).get();let l=t.maxItems;if(e.queueCap===void 0&&E(o)&&typeof o.value_json=="string")try{const u=JSON.parse(o.value_json);E(u)&&typeof u.maxItems=="number"&&(l=B({maxItems:u.maxItems}).maxItems)}catch{}this.db.prepare(`
1349
54
  INSERT INTO runner_config (key, value_json, updated_at)
1350
55
  VALUES ('headless_queue_policy', ?, ?)
1351
56
  ON CONFLICT(key) DO UPDATE SET
1352
57
  value_json = excluded.value_json,
1353
58
  updated_at = excluded.updated_at
1354
- `).run(JSON.stringify({ maxItems: effectiveQueueCap }), input.enqueuedAt);
1355
- const replay = this.db.prepare(`
59
+ `).run(JSON.stringify({maxItems:l}),e.enqueuedAt);const I=this.db.prepare(`
1356
60
  SELECT attempt_id
1357
61
  FROM headless_execution_attempts
1358
62
  WHERE attempt_id = ?
1359
63
  OR (? IS NOT NULL AND operation_id = ?)
1360
64
  OR (? IS NOT NULL AND idempotency_key = ?)
1361
65
  LIMIT 1
1362
- `).get(input.attemptId, input.operationId ?? null, input.operationId ?? null, input.idempotencyKey ?? null, input.idempotencyKey ?? null);
1363
- const depthRow = this.db.prepare(`
66
+ `).get(e.attemptId,e.operationId??null,e.operationId??null,e.idempotencyKey??null,e.idempotencyKey??null),y=this.db.prepare(`
1364
67
  SELECT COUNT(*) AS count
1365
68
  FROM headless_execution_attempts
1366
69
  WHERE status IN ('queued', 'backoff', 'running')
1367
70
  AND dead_lettered_at IS NULL
1368
- `).get();
1369
- const queueDepth = isRecord(depthRow) && typeof depthRow.count === 'number' ? depthRow.count : 0;
1370
- if (replay) {
1371
- return {
1372
- inserted: false,
1373
- replay: true,
1374
- changes: 0,
1375
- accepted: true,
1376
- backpressure: queueDepth >= effectiveQueueCap,
1377
- queueDepth,
1378
- queueCap: effectiveQueueCap,
1379
- publicSafe: true,
1380
- };
1381
- }
1382
- if (queueDepth >= effectiveQueueCap) {
1383
- return {
1384
- inserted: false,
1385
- replay: false,
1386
- changes: 0,
1387
- accepted: false,
1388
- backpressure: true,
1389
- queueDepth,
1390
- queueCap: effectiveQueueCap,
1391
- reasonCode: CONTINUITY_QUEUE_REASON_CODES.CAP_REACHED,
1392
- publicSafe: true,
1393
- };
1394
- }
1395
- const result = this.db.prepare(`
71
+ `).get(),f=E(y)&&typeof y.count=="number"?y.count:0;if(I)return{inserted:!1,replay:!0,changes:0,accepted:!0,backpressure:f>=l,queueDepth:f,queueCap:l,publicSafe:!0};if(f>=l)return{inserted:!1,replay:!1,changes:0,accepted:!1,backpressure:!0,queueDepth:f,queueCap:l,reasonCode:k.CAP_REACHED,publicSafe:!0};const _=this.db.prepare(`
1396
72
  INSERT INTO headless_execution_attempts (
1397
73
  attempt_id, execution_id, job_id, directive_hash, directive_kind,
1398
74
  command_class, target_kind, target_ref, story_id, epic_id, runtime_id,
@@ -1402,23 +78,7 @@ export class ContinuitySqliteStore {
1402
78
  relative_paths_json, metadata_json, queue_priority, queue_scope,
1403
79
  attempt_count, next_retry_at
1404
80
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, ?)
1405
- `).run(input.attemptId, input.executionId, input.jobId, input.directiveHash, input.directiveKind, input.commandClass, input.targetKind, input.targetRef, input.storyId ?? null, input.epicId ?? null, input.runtimeId ?? null, input.modelId ?? null, input.runtimeSource ?? null, input.status ?? 'queued', input.reasonCode ?? null, input.publicSummary ?? null, input.enqueuedAt, input.status === 'backoff' ? (input.notBeforeAt ?? input.enqueuedAt) : null, input.retryCount ?? 0, queuePolicy.maxAttempts, input.operationId ?? null, input.idempotencyKey ?? null, stringifyPublicJsonValue(input.counts, 'headless_execution.counts'), stringifyPublicJsonValue(input.hashes, 'headless_execution.hashes'), stringifyPublicJsonValue(input.redactionSummary, 'headless_execution.redactionSummary'), stringifyPublicJsonValue(relativePaths, 'headless_execution.relativePaths'), stringifyPublicMetadata(metadata), queuePriority, queueScope, input.notBeforeAt ?? input.enqueuedAt);
1406
- return {
1407
- inserted: result.changes > 0,
1408
- replay: false,
1409
- changes: result.changes,
1410
- accepted: result.changes > 0,
1411
- backpressure: queueDepth + result.changes >= effectiveQueueCap,
1412
- queueDepth: queueDepth + result.changes,
1413
- queueCap: effectiveQueueCap,
1414
- publicSafe: true,
1415
- };
1416
- });
1417
- }
1418
- listDueHeadlessExecutionAttempts(options) {
1419
- const limit = Math.max(1, Math.min(50, Math.floor(options.limit)));
1420
- assertBoundedPublicText(options.nowIso, 'headless_execution.nowIso', 80, true);
1421
- return this.db.prepare(`
81
+ `).run(e.attemptId,e.executionId,e.jobId,e.directiveHash,e.directiveKind,e.commandClass,e.targetKind,e.targetRef,e.storyId??null,e.epicId??null,e.runtimeId??null,e.modelId??null,e.runtimeSource??null,e.status??"queued",e.reasonCode??null,e.publicSummary??null,e.enqueuedAt,e.status==="backoff"?e.notBeforeAt??e.enqueuedAt:null,e.retryCount??0,t.maxAttempts,e.operationId??null,e.idempotencyKey??null,T(e.counts,"headless_execution.counts"),T(e.hashes,"headless_execution.hashes"),T(e.redactionSummary,"headless_execution.redactionSummary"),T(n,"headless_execution.relativePaths"),S(d),r,s,e.notBeforeAt??e.enqueuedAt);return{inserted:_.changes>0,replay:!1,changes:_.changes,accepted:_.changes>0,backpressure:f+_.changes>=l,queueDepth:f+_.changes,queueCap:l,publicSafe:!0}})}listDueHeadlessExecutionAttempts(e){const t=Math.max(1,Math.min(50,Math.floor(e.limit)));return c(e.nowIso,"headless_execution.nowIso",80,!0),this.db.prepare(`
1422
82
  SELECT attempt_id, execution_id, job_id, directive_hash, directive_kind,
1423
83
  command_class, target_kind, target_ref, story_id, epic_id,
1424
84
  runtime_id, model_id, runtime_source, status, reason_code,
@@ -1437,36 +97,13 @@ export class ContinuitySqliteStore {
1437
97
  ORDER BY queue_priority DESC, COALESCE(next_retry_at, not_before_at, enqueued_at) ASC,
1438
98
  enqueued_at ASC, attempt_id ASC
1439
99
  LIMIT ?
1440
- `).all(options.nowIso, options.nowIso, limit);
1441
- }
1442
- hasCompletedHeadlessExecutionDirectiveHash(directiveHash, excludeAttemptId) {
1443
- assertBoundedPublicText(directiveHash, 'headless_execution.directiveHash', 160, true);
1444
- assertBoundedPublicText(excludeAttemptId, 'headless_execution.attemptId', 160);
1445
- const excludePredicate = excludeAttemptId ? ' AND attempt_id != ?' : '';
1446
- const params = excludeAttemptId ? [directiveHash, excludeAttemptId] : [directiveHash];
1447
- const row = this.db.prepare(`
100
+ `).all(e.nowIso,e.nowIso,t)}hasCompletedHeadlessExecutionDirectiveHash(e,t){c(e,"headless_execution.directiveHash",160,!0),c(t,"headless_execution.attemptId",160);const r=t?" AND attempt_id != ?":"",s=t?[e,t]:[e];return this.db.prepare(`
1448
101
  SELECT 1 AS found
1449
102
  FROM headless_execution_attempts
1450
103
  WHERE directive_hash = ?
1451
- AND status = 'completed'${excludePredicate}
104
+ AND status = 'completed'${r}
1452
105
  LIMIT 1
1453
- `).get(...params);
1454
- return row?.found === 1;
1455
- }
1456
- claimHeadlessExecutionAttempt(input) {
1457
- this.assertCanMutate();
1458
- validateHeadlessExecutionAttemptIdentity({
1459
- attemptId: input.attemptId,
1460
- executionId: input.attemptId,
1461
- jobId: input.attemptId,
1462
- leaseId: input.leaseId,
1463
- runnerId: input.runnerId,
1464
- reasonCode: input.reasonCode,
1465
- timestamp: input.claimedAt,
1466
- });
1467
- assertBoundedPublicText(input.leaseExpiresAt, 'headless_execution.leaseExpiresAt', 80, true);
1468
- try {
1469
- const result = this.db.prepare(`
106
+ `).get(...s)?.found===1}claimHeadlessExecutionAttempt(e){this.assertCanMutate(),L({attemptId:e.attemptId,executionId:e.attemptId,jobId:e.attemptId,leaseId:e.leaseId,runnerId:e.runnerId,reasonCode:e.reasonCode,timestamp:e.claimedAt}),c(e.leaseExpiresAt,"headless_execution.leaseExpiresAt",80,!0);try{const t=this.db.prepare(`
1470
107
  UPDATE headless_execution_attempts
1471
108
  SET status = 'running',
1472
109
  lease_id = ?,
@@ -1482,27 +119,7 @@ export class ContinuitySqliteStore {
1482
119
  AND dead_lettered_at IS NULL
1483
120
  AND (COALESCE(next_retry_at, not_before_at) IS NULL OR COALESCE(next_retry_at, not_before_at) <= ?)
1484
121
  AND (lease_id IS NULL OR lease_released_at IS NOT NULL OR lease_expires_at <= ?)
1485
- `).run(input.leaseId, input.runnerId, input.claimedAt, input.claimedAt, input.leaseExpiresAt, input.reasonCode ?? null, input.attemptId, input.claimedAt, input.claimedAt);
1486
- return this.toWriteResult(result);
1487
- }
1488
- catch (error) {
1489
- throw mapSqliteError(error, 'sqlite-mutation-failed');
1490
- }
1491
- }
1492
- acquireNextHeadlessExecutionAttempt(input) {
1493
- this.assertCanMutate();
1494
- validateHeadlessExecutionAttemptIdentity({
1495
- attemptId: input.leaseId,
1496
- executionId: input.leaseId,
1497
- jobId: input.leaseId,
1498
- leaseId: input.leaseId,
1499
- runnerId: input.runnerId,
1500
- reasonCode: input.reasonCode,
1501
- timestamp: input.claimedAt,
1502
- });
1503
- assertBoundedPublicText(input.leaseExpiresAt, 'headless_execution.leaseExpiresAt', 80, true);
1504
- return this.runInTransaction(() => {
1505
- const row = this.db.prepare(`
122
+ `).run(e.leaseId,e.runnerId,e.claimedAt,e.claimedAt,e.leaseExpiresAt,e.reasonCode??null,e.attemptId,e.claimedAt,e.claimedAt);return this.toWriteResult(t)}catch(t){throw m(t,"sqlite-mutation-failed")}}acquireNextHeadlessExecutionAttempt(e){return this.assertCanMutate(),L({attemptId:e.leaseId,executionId:e.leaseId,jobId:e.leaseId,leaseId:e.leaseId,runnerId:e.runnerId,reasonCode:e.reasonCode,timestamp:e.claimedAt}),c(e.leaseExpiresAt,"headless_execution.leaseExpiresAt",80,!0),this.runInTransaction(()=>{const t=this.db.prepare(`
1506
123
  SELECT attempt_id
1507
124
  FROM headless_execution_attempts
1508
125
  WHERE status IN ('queued', 'backoff')
@@ -1515,11 +132,7 @@ export class ContinuitySqliteStore {
1515
132
  enqueued_at ASC,
1516
133
  attempt_id ASC
1517
134
  LIMIT 1
1518
- `).get(input.directiveKind ?? null, input.directiveKind ?? null, input.claimedAt, input.claimedAt);
1519
- if (typeof row?.attempt_id !== 'string') {
1520
- return { acquired: false, reasonCode: 'continuity-ok', publicSafe: true };
1521
- }
1522
- const result = this.db.prepare(`
135
+ `).get(e.directiveKind??null,e.directiveKind??null,e.claimedAt,e.claimedAt);if(typeof t?.attempt_id!="string")return{acquired:!1,reasonCode:"continuity-ok",publicSafe:!0};if(this.db.prepare(`
1523
136
  UPDATE headless_execution_attempts
1524
137
  SET status = 'running',
1525
138
  lease_id = ?,
@@ -1535,29 +148,7 @@ export class ContinuitySqliteStore {
1535
148
  AND dead_lettered_at IS NULL
1536
149
  AND (COALESCE(next_retry_at, not_before_at) IS NULL OR COALESCE(next_retry_at, not_before_at) <= ?)
1537
150
  AND (lease_id IS NULL OR lease_released_at IS NOT NULL OR lease_expires_at <= ?)
1538
- `).run(input.leaseId, input.runnerId, input.claimedAt, input.claimedAt, input.leaseExpiresAt, input.reasonCode ?? null, row.attempt_id, input.claimedAt, input.claimedAt);
1539
- if (result.changes === 0) {
1540
- return { acquired: false, reasonCode: 'lease-conflict', publicSafe: true };
1541
- }
1542
- const attempt = this.getHeadlessExecutionAttempt(row.attempt_id);
1543
- return attempt
1544
- ? { acquired: true, attempt, reasonCode: input.reasonCode ?? 'continuity-ok', publicSafe: true }
1545
- : { acquired: false, reasonCode: 'lease-conflict', publicSafe: true };
1546
- });
1547
- }
1548
- heartbeatHeadlessExecutionAttempt(input) {
1549
- this.assertCanMutate();
1550
- validateHeadlessExecutionAttemptIdentity({
1551
- attemptId: input.attemptId,
1552
- executionId: input.attemptId,
1553
- jobId: input.attemptId,
1554
- leaseId: input.leaseId,
1555
- runnerId: input.runnerId,
1556
- timestamp: input.heartbeatAt,
1557
- });
1558
- assertBoundedPublicText(input.leaseExpiresAt, 'headless_execution.leaseExpiresAt', 80);
1559
- try {
1560
- const result = this.db.prepare(`
151
+ `).run(e.leaseId,e.runnerId,e.claimedAt,e.claimedAt,e.leaseExpiresAt,e.reasonCode??null,t.attempt_id,e.claimedAt,e.claimedAt).changes===0)return{acquired:!1,reasonCode:"lease-conflict",publicSafe:!0};const s=this.getHeadlessExecutionAttempt(t.attempt_id);return s?{acquired:!0,attempt:s,reasonCode:e.reasonCode??"continuity-ok",publicSafe:!0}:{acquired:!1,reasonCode:"lease-conflict",publicSafe:!0}})}heartbeatHeadlessExecutionAttempt(e){this.assertCanMutate(),L({attemptId:e.attemptId,executionId:e.attemptId,jobId:e.attemptId,leaseId:e.leaseId,runnerId:e.runnerId,timestamp:e.heartbeatAt}),c(e.leaseExpiresAt,"headless_execution.leaseExpiresAt",80);try{const t=this.db.prepare(`
1561
152
  UPDATE headless_execution_attempts
1562
153
  SET heartbeat_at = ?,
1563
154
  lease_expires_at = COALESCE(?, lease_expires_at)
@@ -1567,27 +158,7 @@ export class ContinuitySqliteStore {
1567
158
  AND status = 'running'
1568
159
  AND lease_released_at IS NULL
1569
160
  AND lease_expires_at > ?
1570
- `).run(input.heartbeatAt, input.leaseExpiresAt ?? null, input.attemptId, input.leaseId, input.runnerId, input.heartbeatAt);
1571
- return this.toWriteResult(result);
1572
- }
1573
- catch (error) {
1574
- throw mapSqliteError(error, 'sqlite-mutation-failed');
1575
- }
1576
- }
1577
- releaseHeadlessExecutionAttempt(input) {
1578
- this.assertCanMutate();
1579
- validateHeadlessExecutionAttemptIdentity({
1580
- attemptId: input.attemptId,
1581
- executionId: input.attemptId,
1582
- jobId: input.attemptId,
1583
- leaseId: input.leaseId,
1584
- reasonCode: input.reasonCode,
1585
- timestamp: input.releasedAt,
1586
- });
1587
- assertBoundedPublicText(input.notBeforeAt, 'headless_execution.notBeforeAt', 80);
1588
- assertNonNegativeInteger(input.retryCount, 'headless_execution.retryCount');
1589
- try {
1590
- const result = this.db.prepare(`
161
+ `).run(e.heartbeatAt,e.leaseExpiresAt??null,e.attemptId,e.leaseId,e.runnerId,e.heartbeatAt);return this.toWriteResult(t)}catch(t){throw m(t,"sqlite-mutation-failed")}}releaseHeadlessExecutionAttempt(e){this.assertCanMutate(),L({attemptId:e.attemptId,executionId:e.attemptId,jobId:e.attemptId,leaseId:e.leaseId,reasonCode:e.reasonCode,timestamp:e.releasedAt}),c(e.notBeforeAt,"headless_execution.notBeforeAt",80),N(e.retryCount,"headless_execution.retryCount");try{const t=this.db.prepare(`
1591
162
  UPDATE headless_execution_attempts
1592
163
  SET status = COALESCE(?, status),
1593
164
  reason_code = COALESCE(?, reason_code),
@@ -1603,76 +174,8 @@ export class ContinuitySqliteStore {
1603
174
  AND lease_id = ?
1604
175
  AND status = 'running'
1605
176
  AND lease_released_at IS NULL
1606
- `).run(input.status ?? null, input.reasonCode ?? null, input.notBeforeAt ?? null, input.retryCount ?? null, input.status ?? null, input.notBeforeAt ?? null, input.status ?? null, input.releasedAt, input.attemptId, input.leaseId);
1607
- return this.toWriteResult(result);
1608
- }
1609
- catch (error) {
1610
- throw mapSqliteError(error, 'sqlite-mutation-failed');
1611
- }
1612
- }
1613
- completeHeadlessExecutionAttempt(input) {
1614
- this.assertCanMutate();
1615
- validateHeadlessExecutionAttemptIdentity({
1616
- attemptId: input.attemptId,
1617
- executionId: input.attemptId,
1618
- jobId: input.attemptId,
1619
- leaseId: input.leaseId,
1620
- runnerId: input.runnerId,
1621
- reasonCode: input.reasonCode,
1622
- publicSummary: input.publicSummary,
1623
- timestamp: input.completedAt,
1624
- });
1625
- assertBoundedPublicText(input.notBeforeAt, 'headless_execution.notBeforeAt', 80);
1626
- assertNonNegativeInteger(input.durationMs, 'headless_execution.durationMs');
1627
- assertNonNegativeInteger(input.retryCount, 'headless_execution.retryCount');
1628
- if (input.metadata)
1629
- assertPublicSafeValue(input.metadata, 'headless_execution.metadata');
1630
- const relativePaths = normalizePublicRelativePaths(input.relativePaths);
1631
- const metadata = buildHeadlessExecutionPublicMetadata({
1632
- attemptId: input.attemptId,
1633
- executionId: input.executionId ?? input.attemptId,
1634
- jobId: input.jobId ?? input.attemptId,
1635
- status: input.status,
1636
- reasonCode: input.reasonCode,
1637
- durationMs: input.durationMs,
1638
- retryCount: input.retryCount,
1639
- counts: input.counts,
1640
- hashes: input.hashes,
1641
- redactionSummary: input.redactionSummary,
1642
- relativePaths,
1643
- metadata: input.metadata,
1644
- });
1645
- const leasePredicate = input.leaseId
1646
- ? ` AND lease_id = ?
1647
- AND lease_released_at IS NULL${input.runnerId ? ' AND runner_id = ?' : ''}`
1648
- : '';
1649
- const statusPredicate = input.leaseId
1650
- ? "status = 'running'"
1651
- : "status NOT IN ('completed', 'failed', 'timed_out', 'skipped')";
1652
- const params = [
1653
- input.status,
1654
- input.completedAt,
1655
- input.durationMs ?? null,
1656
- input.reasonCode ?? null,
1657
- input.publicSummary ?? null,
1658
- input.notBeforeAt ?? null,
1659
- input.retryCount ?? null,
1660
- input.status,
1661
- input.notBeforeAt ?? null,
1662
- input.counts ? stringifyPublicJsonValue(input.counts, 'headless_execution.counts') : null,
1663
- input.hashes ? stringifyPublicJsonValue(input.hashes, 'headless_execution.hashes') : null,
1664
- input.redactionSummary ? stringifyPublicJsonValue(input.redactionSummary, 'headless_execution.redactionSummary') : null,
1665
- relativePaths ? stringifyPublicJsonValue(relativePaths, 'headless_execution.relativePaths') : null,
1666
- stringifyPublicMetadata(metadata),
1667
- input.completedAt,
1668
- input.attemptId,
1669
- ];
1670
- if (input.leaseId)
1671
- params.push(input.leaseId);
1672
- if (input.leaseId && input.runnerId)
1673
- params.push(input.runnerId);
1674
- try {
1675
- const result = this.db.prepare(`
177
+ `).run(e.status??null,e.reasonCode??null,e.notBeforeAt??null,e.retryCount??null,e.status??null,e.notBeforeAt??null,e.status??null,e.releasedAt,e.attemptId,e.leaseId);return this.toWriteResult(t)}catch(t){throw m(t,"sqlite-mutation-failed")}}completeHeadlessExecutionAttempt(e){this.assertCanMutate(),L({attemptId:e.attemptId,executionId:e.attemptId,jobId:e.attemptId,leaseId:e.leaseId,runnerId:e.runnerId,reasonCode:e.reasonCode,publicSummary:e.publicSummary,timestamp:e.completedAt}),c(e.notBeforeAt,"headless_execution.notBeforeAt",80),N(e.durationMs,"headless_execution.durationMs"),N(e.retryCount,"headless_execution.retryCount"),e.metadata&&g(e.metadata,"headless_execution.metadata");const t=U(e.relativePaths),r=V({attemptId:e.attemptId,executionId:e.executionId??e.attemptId,jobId:e.jobId??e.attemptId,status:e.status,reasonCode:e.reasonCode,durationMs:e.durationMs,retryCount:e.retryCount,counts:e.counts,hashes:e.hashes,redactionSummary:e.redactionSummary,relativePaths:t,metadata:e.metadata}),s=e.leaseId?` AND lease_id = ?
178
+ AND lease_released_at IS NULL${e.runnerId?" AND runner_id = ?":""}`:"",n=e.leaseId?"status = 'running'":"status NOT IN ('completed', 'failed', 'timed_out', 'skipped')",d=[e.status,e.completedAt,e.durationMs??null,e.reasonCode??null,e.publicSummary??null,e.notBeforeAt??null,e.retryCount??null,e.status,e.notBeforeAt??null,e.counts?T(e.counts,"headless_execution.counts"):null,e.hashes?T(e.hashes,"headless_execution.hashes"):null,e.redactionSummary?T(e.redactionSummary,"headless_execution.redactionSummary"):null,t?T(t,"headless_execution.relativePaths"):null,S(r),e.completedAt,e.attemptId];e.leaseId&&d.push(e.leaseId),e.leaseId&&e.runnerId&&d.push(e.runnerId);try{const o=this.db.prepare(`
1676
179
  UPDATE headless_execution_attempts
1677
180
  SET status = ?,
1678
181
  completed_at = ?,
@@ -1691,426 +194,53 @@ export class ContinuitySqliteStore {
1691
194
  relative_paths_json = COALESCE(?, relative_paths_json),
1692
195
  metadata_json = COALESCE(?, metadata_json),
1693
196
  lease_released_at = COALESCE(lease_released_at, ?)
1694
- WHERE attempt_id = ?${leasePredicate}
1695
- AND ${statusPredicate}
1696
- `).run(...params);
1697
- return this.toWriteResult(result);
1698
- }
1699
- catch (error) {
1700
- throw mapSqliteError(error, 'sqlite-mutation-failed');
1701
- }
1702
- }
1703
- recordHeadlessExecutionReceipt(input) {
1704
- validateHeadlessExecutionAttemptIdentity({
1705
- attemptId: input.attemptId,
1706
- executionId: input.executionId,
1707
- jobId: input.jobId,
1708
- reasonCode: input.reasonCode,
1709
- timestamp: input.createdAt,
1710
- });
1711
- if (input.metadata)
1712
- assertPublicSafeValue(input.metadata, 'headless_execution.metadata');
1713
- const relativePaths = normalizePublicRelativePaths(input.relativePaths);
1714
- const metadata = buildHeadlessExecutionPublicMetadata({
1715
- attemptId: input.attemptId,
1716
- executionId: input.executionId,
1717
- jobId: input.jobId,
1718
- status: input.status,
1719
- reasonCode: input.reasonCode,
1720
- counts: input.counts,
1721
- hashes: input.hashes,
1722
- redactionSummary: input.redactionSummary,
1723
- relativePaths,
1724
- metadata: input.metadata,
1725
- });
1726
- return this.appendReceipt({
1727
- receiptId: input.receiptId,
1728
- jobId: input.jobId,
1729
- operationId: input.operationId ?? input.executionId,
1730
- idempotencyKey: input.idempotencyKey ?? input.executionId,
1731
- status: input.status,
1732
- reasonCode: input.reasonCode,
1733
- appliedOperationIds: input.status === 'applied' ? [input.executionId] : undefined,
1734
- ignoredOperationIds: input.status === 'ignored' ? [input.executionId] : undefined,
1735
- createdAt: input.createdAt,
1736
- metadata,
1737
- });
1738
- }
1739
- recordHeadlessExecutionSummary(input) {
1740
- validateHeadlessExecutionAttemptIdentity({
1741
- attemptId: input.attemptId,
1742
- executionId: input.executionId,
1743
- jobId: input.jobId,
1744
- runtimeId: input.runtimeId,
1745
- modelId: input.modelId,
1746
- reasonCode: input.reasonCode,
1747
- publicSummary: input.publicSummary,
1748
- timestamp: input.createdAt,
1749
- });
1750
- assertNonNegativeInteger(input.durationMs, 'headless_execution.durationMs');
1751
- if (input.metadata)
1752
- assertPublicSafeValue(input.metadata, 'headless_execution.metadata');
1753
- const relativePaths = normalizePublicRelativePaths(input.relativePaths);
1754
- const metadata = buildHeadlessExecutionPublicMetadata({
1755
- attemptId: input.attemptId,
1756
- executionId: input.executionId,
1757
- jobId: input.jobId,
1758
- runtimeId: input.runtimeId,
1759
- modelId: input.modelId,
1760
- status: input.status ?? 'completed',
1761
- reasonCode: input.reasonCode,
1762
- durationMs: input.durationMs,
1763
- counts: input.counts,
1764
- hashes: input.hashes,
1765
- redactionSummary: input.redactionSummary,
1766
- relativePaths,
1767
- metadata: input.metadata,
1768
- });
1769
- return this.recordProcessingOutputSummary({
1770
- eventId: input.eventId,
1771
- executionId: input.executionId,
1772
- jobId: input.jobId,
1773
- idempotencyKey: input.executionId,
1774
- publicSummary: input.publicSummary,
1775
- reasonCode: input.reasonCode,
1776
- createdAt: input.createdAt,
1777
- source: 'headless_execution',
1778
- metadata,
1779
- });
1780
- }
1781
- appendReceipt(input) {
1782
- this.assertCanMutate();
1783
- const metadataJson = stringifyPublicMetadata(input.metadata);
1784
- try {
1785
- const result = this.db.prepare(`
197
+ WHERE attempt_id = ?${s}
198
+ AND ${n}
199
+ `).run(...d);return this.toWriteResult(o)}catch(o){throw m(o,"sqlite-mutation-failed")}}recordHeadlessExecutionReceipt(e){L({attemptId:e.attemptId,executionId:e.executionId,jobId:e.jobId,reasonCode:e.reasonCode,timestamp:e.createdAt}),e.metadata&&g(e.metadata,"headless_execution.metadata");const t=U(e.relativePaths),r=V({attemptId:e.attemptId,executionId:e.executionId,jobId:e.jobId,status:e.status,reasonCode:e.reasonCode,counts:e.counts,hashes:e.hashes,redactionSummary:e.redactionSummary,relativePaths:t,metadata:e.metadata});return this.appendReceipt({receiptId:e.receiptId,jobId:e.jobId,operationId:e.operationId??e.executionId,idempotencyKey:e.idempotencyKey??e.executionId,status:e.status,reasonCode:e.reasonCode,appliedOperationIds:e.status==="applied"?[e.executionId]:void 0,ignoredOperationIds:e.status==="ignored"?[e.executionId]:void 0,createdAt:e.createdAt,metadata:r})}recordHeadlessExecutionSummary(e){L({attemptId:e.attemptId,executionId:e.executionId,jobId:e.jobId,runtimeId:e.runtimeId,modelId:e.modelId,reasonCode:e.reasonCode,publicSummary:e.publicSummary,timestamp:e.createdAt}),N(e.durationMs,"headless_execution.durationMs"),e.metadata&&g(e.metadata,"headless_execution.metadata");const t=U(e.relativePaths),r=V({attemptId:e.attemptId,executionId:e.executionId,jobId:e.jobId,runtimeId:e.runtimeId,modelId:e.modelId,status:e.status??"completed",reasonCode:e.reasonCode,durationMs:e.durationMs,counts:e.counts,hashes:e.hashes,redactionSummary:e.redactionSummary,relativePaths:t,metadata:e.metadata});return this.recordProcessingOutputSummary({eventId:e.eventId,executionId:e.executionId,jobId:e.jobId,idempotencyKey:e.executionId,publicSummary:e.publicSummary,reasonCode:e.reasonCode,createdAt:e.createdAt,source:"headless_execution",metadata:r})}appendReceipt(e){this.assertCanMutate();const t=S(e.metadata);try{const r=this.db.prepare(`
1786
200
  INSERT OR IGNORE INTO job_receipts (
1787
201
  receipt_id, job_id, operation_id, idempotency_key, status, reason_code,
1788
202
  applied_operation_ids_json, ignored_operation_ids_json, created_at, metadata_json
1789
203
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1790
- `).run(input.receiptId, input.jobId, input.operationId ?? null, input.idempotencyKey ?? null, input.status, input.reasonCode ?? null, input.appliedOperationIds ? JSON.stringify(input.appliedOperationIds) : null, input.ignoredOperationIds ? JSON.stringify(input.ignoredOperationIds) : null, input.createdAt, metadataJson);
1791
- return this.toWriteResult(result);
1792
- }
1793
- catch (error) {
1794
- throw mapSqliteError(error, 'sqlite-mutation-failed');
1795
- }
1796
- }
1797
- /**
1798
- * P199.06: Apply TTL/count/byte retention to public receipts and persisted
1799
- * processing summaries. Deleted content is replaced with bounded audit evidence
1800
- * that hashes only public identifiers/status/reason codes, never summaries.
1801
- */
1802
- compactPublicReceiptsAndOutputs(input) {
1803
- this.assertCanMutate();
1804
- const nowMs = Date.parse(input.nowIso);
1805
- if (!Number.isFinite(nowMs)) {
1806
- throw new ContinuitySqliteStoreError('sqlite-public-safety-rejected', 'Continuity receipt compaction requires a valid public timestamp.');
1807
- }
1808
- const policy = normalizeContinuityRetentionPolicy(input.policy);
1809
- const receiptCutoff = new Date(nowMs - policy.receipts.maxAgeMs).toISOString();
1810
- const outputCutoff = new Date(nowMs - policy.processingOutputs.maxAgeMs).toISOString();
1811
- try {
1812
- return this.runInTransaction(() => {
1813
- const receiptCandidates = this.selectReceiptRetentionCandidates({
1814
- cutoff: receiptCutoff,
1815
- maxItems: policy.receipts.maxItems,
1816
- maxBytes: policy.receipts.maxBytes,
1817
- limit: policy.maxDeleteRowsPerRun,
1818
- });
1819
- const outputCandidates = this.selectOutputRetentionCandidates({
1820
- cutoff: outputCutoff,
1821
- maxItems: policy.processingOutputs.maxItems,
1822
- maxBytes: policy.processingOutputs.maxBytes,
1823
- limit: policy.maxDeleteRowsPerRun,
1824
- });
1825
- const candidates = [...receiptCandidates, ...outputCandidates]
1826
- .sort((left, right) => left.createdAt.localeCompare(right.createdAt) || left.id.localeCompare(right.id))
1827
- .slice(0, policy.maxDeleteRowsPerRun);
1828
- if (candidates.length === 0) {
1829
- return {
1830
- status: 'noop',
1831
- summaries: [],
1832
- counts: { receiptsRemoved: 0, outputsRemoved: 0, bytesRemoved: 0, summariesCreated: 0 },
1833
- reasonCode: CONTINUITY_RECEIPT_RETENTION_REASON_CODES.NOOP,
1834
- publicSafe: true,
1835
- };
1836
- }
1837
- const selectedReceipts = candidates.filter((candidate) => candidate.kind === 'receipt');
1838
- const selectedOutputs = candidates.filter((candidate) => candidate.kind === 'output');
1839
- for (const jobId of uniqueValues(selectedReceipts.map((candidate) => candidate.jobId))) {
1840
- this.preserveReceiptDerivedIteration(jobId, input.nowIso);
1841
- }
1842
- this.deleteRetentionCandidates('job_receipts', 'receipt_id', selectedReceipts.map((candidate) => candidate.id));
1843
- this.deleteRetentionCandidates('job_events', 'event_id', selectedOutputs.map((candidate) => candidate.id));
1844
- const byJob = new Map();
1845
- for (const candidate of candidates) {
1846
- byJob.set(candidate.jobId, [...(byJob.get(candidate.jobId) ?? []), candidate]);
1847
- }
1848
- const summaries = [];
1849
- for (const [jobId, removed] of [...byJob.entries()].sort(([left], [right]) => left.localeCompare(right))) {
1850
- const removedReceipts = removed.filter((candidate) => candidate.kind === 'receipt');
1851
- const removedOutputs = removed.filter((candidate) => candidate.kind === 'output');
1852
- const retained = this.retainedReceiptOutputCounts(jobId);
1853
- const hashes = retentionHashes(jobId, removedReceipts, removedOutputs);
1854
- const counts = {
1855
- receiptsRemoved: removedReceipts.length,
1856
- receiptsRetained: retained.receipts,
1857
- outputsRemoved: removedOutputs.length,
1858
- outputsRetained: retained.outputs,
1859
- bytesRemoved: removed.reduce((total, candidate) => total + candidate.approxBytes, 0),
1860
- bytesRetained: retained.bytes,
1861
- redactions: 0,
1862
- };
1863
- const publicSummary = `Compacted public receipt and processing evidence: receipts removed ${counts.receiptsRemoved}, retained ${counts.receiptsRetained}; summaries removed ${counts.outputsRemoved}, retained ${counts.outputsRetained}; bytes removed ${counts.bytesRemoved}, retained ${counts.bytesRetained}.`;
1864
- assertBoundedPublicText(publicSummary, 'receipt_compaction.publicSummary', 500, true);
1865
- const eventDigest = createHash('sha256')
1866
- .update(`${jobId}:${input.nowIso}:${hashes.aggregate}`)
1867
- .digest('hex');
1868
- const summaryId = `receipt-compaction-${eventDigest.slice(0, 32)}`;
1869
- const summary = {
1870
- schemaVersion: 1,
1871
- contractVersion: 'continuity-v1',
1872
- policyVersion: 1,
1873
- type: CONTINUITY_RECEIPT_COMPACTION_EVENT_TYPE,
1874
- summaryId,
1875
- jobId,
1876
- scope: 'local_sqlite',
1877
- status: 'compacted',
1878
- createdAt: input.nowIso,
1879
- reasonCodes: [CONTINUITY_RECEIPT_RETENTION_REASON_CODES.COMPACTED],
1880
- counts,
1881
- hashes,
1882
- publicSummary,
1883
- publicSafe: true,
1884
- };
1885
- this.appendEvent({
1886
- eventId: summaryId,
1887
- jobId,
1888
- operationId: `op-${summaryId}`,
1889
- idempotencyKey: `idem-${summaryId}`,
1890
- type: CONTINUITY_RECEIPT_COMPACTION_EVENT_TYPE,
1891
- summary: publicSummary,
1892
- reasonCode: CONTINUITY_RECEIPT_RETENTION_REASON_CODES.COMPACTED,
1893
- createdAt: input.nowIso,
1894
- metadata: {
1895
- schemaVersion: 1,
1896
- contractVersion: 'continuity-v1',
1897
- eventType: CONTINUITY_RECEIPT_COMPACTION_EVENT_TYPE,
1898
- jobId,
1899
- reasonCodes: summary.reasonCodes,
1900
- counts,
1901
- hashes,
1902
- publicSummary,
1903
- },
1904
- });
1905
- this.pruneReceiptCompactionSummaries(jobId, policy.maxCompactionSummariesPerJob);
1906
- summaries.push(summary);
1907
- }
1908
- return {
1909
- status: 'compacted',
1910
- summaries,
1911
- counts: {
1912
- receiptsRemoved: selectedReceipts.length,
1913
- outputsRemoved: selectedOutputs.length,
1914
- bytesRemoved: candidates.reduce((total, candidate) => total + candidate.approxBytes, 0),
1915
- summariesCreated: summaries.length,
1916
- },
1917
- reasonCode: CONTINUITY_RECEIPT_RETENTION_REASON_CODES.COMPACTED,
1918
- publicSafe: true,
1919
- };
1920
- });
1921
- }
1922
- catch (error) {
1923
- throw mapSqliteError(error, 'sqlite-mutation-failed');
1924
- }
1925
- }
1926
- updateJobStatus(input) {
1927
- this.assertCanMutate();
1928
- try {
1929
- const result = this.db.prepare(`
204
+ `).run(e.receiptId,e.jobId,e.operationId??null,e.idempotencyKey??null,e.status,e.reasonCode??null,e.appliedOperationIds?JSON.stringify(e.appliedOperationIds):null,e.ignoredOperationIds?JSON.stringify(e.ignoredOperationIds):null,e.createdAt,t);return this.toWriteResult(r)}catch(r){throw m(r,"sqlite-mutation-failed")}}compactPublicReceiptsAndOutputs(e){this.assertCanMutate();const t=Date.parse(e.nowIso);if(!Number.isFinite(t))throw new i("sqlite-public-safety-rejected","Continuity receipt compaction requires a valid public timestamp.");const r=He(e.policy),s=new Date(t-r.receipts.maxAgeMs).toISOString(),n=new Date(t-r.processingOutputs.maxAgeMs).toISOString();try{return this.runInTransaction(()=>{const d=this.selectReceiptRetentionCandidates({cutoff:s,maxItems:r.receipts.maxItems,maxBytes:r.receipts.maxBytes,limit:r.maxDeleteRowsPerRun}),o=this.selectOutputRetentionCandidates({cutoff:n,maxItems:r.processingOutputs.maxItems,maxBytes:r.processingOutputs.maxBytes,limit:r.maxDeleteRowsPerRun}),l=[...d,...o].sort((u,p)=>u.createdAt.localeCompare(p.createdAt)||u.id.localeCompare(p.id)).slice(0,r.maxDeleteRowsPerRun);if(l.length===0)return{status:"noop",summaries:[],counts:{receiptsRemoved:0,outputsRemoved:0,bytesRemoved:0,summariesCreated:0},reasonCode:F.NOOP,publicSafe:!0};const I=l.filter(u=>u.kind==="receipt"),y=l.filter(u=>u.kind==="output");for(const u of q(I.map(p=>p.jobId)))this.preserveReceiptDerivedIteration(u,e.nowIso);this.deleteRetentionCandidates("job_receipts","receipt_id",I.map(u=>u.id)),this.deleteRetentionCandidates("job_events","event_id",y.map(u=>u.id));const f=new Map;for(const u of l)f.set(u.jobId,[...f.get(u.jobId)??[],u]);const _=[];for(const[u,p]of[...f.entries()].sort(([x],[A])=>x.localeCompare(A))){const x=p.filter(j=>j.kind==="receipt"),A=p.filter(j=>j.kind==="output"),G=this.retainedReceiptOutputCounts(u),Q=ut(u,x,A),R={receiptsRemoved:x.length,receiptsRetained:G.receipts,outputsRemoved:A.length,outputsRetained:G.outputs,bytesRemoved:p.reduce((j,Se)=>j+Se.approxBytes,0),bytesRetained:G.bytes,redactions:0},P=`Compacted public receipt and processing evidence: receipts removed ${R.receiptsRemoved}, retained ${R.receiptsRetained}; summaries removed ${R.outputsRemoved}, retained ${R.outputsRetained}; bytes removed ${R.bytesRemoved}, retained ${R.bytesRetained}.`;c(P,"receipt_compaction.publicSummary",500,!0);const H=`receipt-compaction-${M("sha256").update(`${u}:${e.nowIso}:${Q.aggregate}`).digest("hex").slice(0,32)}`,ee={schemaVersion:1,contractVersion:"continuity-v1",policyVersion:1,type:W,summaryId:H,jobId:u,scope:"local_sqlite",status:"compacted",createdAt:e.nowIso,reasonCodes:[F.COMPACTED],counts:R,hashes:Q,publicSummary:P,publicSafe:!0};this.appendEvent({eventId:H,jobId:u,operationId:`op-${H}`,idempotencyKey:`idem-${H}`,type:W,summary:P,reasonCode:F.COMPACTED,createdAt:e.nowIso,metadata:{schemaVersion:1,contractVersion:"continuity-v1",eventType:W,jobId:u,reasonCodes:ee.reasonCodes,counts:R,hashes:Q,publicSummary:P}}),this.pruneReceiptCompactionSummaries(u,r.maxCompactionSummariesPerJob),_.push(ee)}return{status:"compacted",summaries:_,counts:{receiptsRemoved:I.length,outputsRemoved:y.length,bytesRemoved:l.reduce((u,p)=>u+p.approxBytes,0),summariesCreated:_.length},reasonCode:F.COMPACTED,publicSafe:!0}})}catch(d){throw m(d,"sqlite-mutation-failed")}}updateJobStatus(e){this.assertCanMutate();try{const t=this.db.prepare(`
1930
205
  UPDATE jobs
1931
206
  SET status = ?,
1932
207
  reason_code = COALESCE(?, reason_code),
1933
208
  updated_at = ?,
1934
209
  next_run_at = COALESCE(?, next_run_at)
1935
210
  WHERE job_id = ?
1936
- `).run(input.status, input.reasonCode ?? null, input.updatedAt, input.nextRunAt ?? null, input.jobId);
1937
- if (result.changes === 0) {
1938
- throw new ContinuitySqliteStoreError('sqlite-foreign-key-failed', `Continuity SQLite job ${input.jobId} was not found for status update.`);
1939
- }
1940
- return this.toWriteResult(result);
1941
- }
1942
- catch (error) {
1943
- throw mapSqliteError(error, 'sqlite-mutation-failed');
1944
- }
1945
- }
1946
- /**
1947
- * P195.04: Additively persist producer-emitted process stage/iteration into the
1948
- * existing `jobs.metadata_json` column and append a bounded `stageHistory`
1949
- * (newest `CONTINUITY_STAGE_HISTORY_MAX` kept). No schema migration is required.
1950
- * Unknown stages, unsafe timestamps and missing jobs are no-ops so older DBs and
1951
- * non-tick summaries never throw.
1952
- */
1953
- recordJobStageProgress(input) {
1954
- this.assertCanMutate();
1955
- assertBoundedPublicText(input.jobId, 'job_stage.jobId', 160, true);
1956
- const stage = isContinuityPublicStage(input.currentStage) ? input.currentStage : undefined;
1957
- const iteration = normalizeContinuityStageIteration(input.iteration);
1958
- if (!stage && iteration === undefined) {
1959
- return { inserted: false, replay: false, changes: 0 };
1960
- }
1961
- const at = sanitizeContinuityStageTimestamp(input.at);
1962
- try {
1963
- const existing = this.db
1964
- .prepare('SELECT metadata_json FROM jobs WHERE job_id = ?')
1965
- .get(input.jobId);
1966
- if (!existing)
1967
- return { inserted: false, replay: false, changes: 0 };
1968
- const metadata = parseJobMetadataObject(typeof existing.metadata_json === 'string' ? existing.metadata_json : null);
1969
- if (stage)
1970
- metadata.currentStage = stage;
1971
- if (iteration !== undefined)
1972
- metadata.iteration = iteration;
1973
- if (stage && at && input.appendHistory !== false) {
1974
- const history = sanitizeContinuityStageHistory(metadata.stageHistory);
1975
- history.push({ stage, at });
1976
- metadata.stageHistory = history.slice(-CONTINUITY_STAGE_HISTORY_MAX);
1977
- }
1978
- const result = this.db
1979
- .prepare('UPDATE jobs SET metadata_json = ? WHERE job_id = ?')
1980
- .run(stringifyPublicMetadata(metadata), input.jobId);
1981
- return this.toWriteResult(result);
1982
- }
1983
- catch (error) {
1984
- throw mapSqliteError(error, 'sqlite-mutation-failed');
1985
- }
1986
- }
1987
- updateJobAndExistingGoalCampaignStatus(input) {
1988
- return this.runInTransaction(() => {
1989
- const jobResult = this.db.prepare(`
211
+ `).run(e.status,e.reasonCode??null,e.updatedAt,e.nextRunAt??null,e.jobId);if(t.changes===0)throw new i("sqlite-foreign-key-failed",`Continuity SQLite job ${e.jobId} was not found for status update.`);return this.toWriteResult(t)}catch(t){throw m(t,"sqlite-mutation-failed")}}recordJobStageProgress(e){this.assertCanMutate(),c(e.jobId,"job_stage.jobId",160,!0);const t=le(e.currentStage)?e.currentStage:void 0,r=Qe(e.iteration);if(!t&&r===void 0)return{inserted:!1,replay:!1,changes:0};const s=ue(e.at);try{const n=this.db.prepare("SELECT metadata_json FROM jobs WHERE job_id = ?").get(e.jobId);if(!n)return{inserted:!1,replay:!1,changes:0};const d=_t(typeof n.metadata_json=="string"?n.metadata_json:null);if(t&&(d.currentStage=t),r!==void 0&&(d.iteration=r),t&&s&&e.appendHistory!==!1){const l=Ge(d.stageHistory);l.push({stage:t,at:s}),d.stageHistory=l.slice(-ce)}const o=this.db.prepare("UPDATE jobs SET metadata_json = ? WHERE job_id = ?").run(S(d),e.jobId);return this.toWriteResult(o)}catch(n){throw m(n,"sqlite-mutation-failed")}}updateJobAndExistingGoalCampaignStatus(e){return this.runInTransaction(()=>{const t=this.db.prepare(`
1990
212
  UPDATE jobs
1991
213
  SET status = ?,
1992
214
  reason_code = COALESCE(?, reason_code),
1993
215
  updated_at = ?,
1994
216
  next_run_at = COALESCE(?, next_run_at)
1995
217
  WHERE job_id = ?
1996
- `).run(input.status, input.reasonCode ?? null, input.updatedAt, input.nextRunAt ?? null, input.jobId);
1997
- if (jobResult.changes === 0) {
1998
- throw new ContinuitySqliteStoreError('sqlite-foreign-key-failed', `Continuity SQLite job ${input.jobId} was not found for status parity update.`);
1999
- }
2000
- const campaignResult = this.db.prepare(`
218
+ `).run(e.status,e.reasonCode??null,e.updatedAt,e.nextRunAt??null,e.jobId);if(t.changes===0)throw new i("sqlite-foreign-key-failed",`Continuity SQLite job ${e.jobId} was not found for status parity update.`);const r=this.db.prepare(`
2001
219
  UPDATE goal_campaigns
2002
220
  SET status = ?,
2003
221
  reason_code = COALESCE(?, reason_code),
2004
222
  updated_at = ?
2005
223
  WHERE job_id = ?
2006
- `).run(input.status, input.reasonCode ?? null, input.updatedAt, input.jobId);
2007
- return this.toWriteResult({ changes: jobResult.changes + campaignResult.changes });
2008
- });
2009
- }
2010
- scheduleNextTick(input) {
2011
- return this.updateJobStatus({
2012
- jobId: input.jobId,
2013
- status: 'active',
2014
- updatedAt: input.updatedAt,
2015
- reasonCode: input.reasonCode,
2016
- nextRunAt: input.scheduledAt,
2017
- });
2018
- }
2019
- acquireLease(input) {
2020
- this.assertCanMutate();
2021
- const metadataJson = stringifyPublicMetadata(input.metadata);
2022
- try {
2023
- const result = this.db.prepare(`
224
+ `).run(e.status,e.reasonCode??null,e.updatedAt,e.jobId);return this.toWriteResult({changes:t.changes+r.changes})})}scheduleNextTick(e){return this.updateJobStatus({jobId:e.jobId,status:"active",updatedAt:e.updatedAt,reasonCode:e.reasonCode,nextRunAt:e.scheduledAt})}acquireLease(e){this.assertCanMutate();const t=S(e.metadata);try{const r=this.db.prepare(`
2024
225
  INSERT OR IGNORE INTO job_leases (
2025
226
  lease_id, job_id, runner_id, status, acquired_at, expires_at,
2026
227
  operation_id, idempotency_key, metadata_json
2027
228
  ) VALUES (?, ?, ?, 'acquired', ?, ?, ?, ?, ?)
2028
- `).run(input.leaseId, input.jobId, input.runnerId, input.acquiredAt, input.expiresAt, input.operationId ?? null, input.idempotencyKey ?? null, metadataJson);
2029
- return this.toWriteResult(result);
2030
- }
2031
- catch (error) {
2032
- throw mapSqliteError(error, 'sqlite-mutation-failed');
2033
- }
2034
- }
2035
- releaseLease(input) {
2036
- this.assertCanMutate();
2037
- try {
2038
- const result = this.db.prepare(`
229
+ `).run(e.leaseId,e.jobId,e.runnerId,e.acquiredAt,e.expiresAt,e.operationId??null,e.idempotencyKey??null,t);return this.toWriteResult(r)}catch(r){throw m(r,"sqlite-mutation-failed")}}releaseLease(e){this.assertCanMutate();try{const t=this.db.prepare(`
2039
230
  UPDATE job_leases
2040
231
  SET status = 'released', released_at = ?
2041
232
  WHERE lease_id = ? AND released_at IS NULL
2042
- `).run(input.releasedAt, input.leaseId);
2043
- if (result.changes === 0) {
2044
- throw new ContinuitySqliteStoreError('sqlite-foreign-key-failed', `Continuity SQLite lease ${input.leaseId} was not found for release.`);
2045
- }
2046
- return this.toWriteResult(result);
2047
- }
2048
- catch (error) {
2049
- throw mapSqliteError(error, 'sqlite-mutation-failed');
2050
- }
2051
- }
2052
- recordStateSnapshot(input) {
2053
- this.assertCanMutate();
2054
- assertPublicSafeText(input.summary, 'state_snapshots.summary');
2055
- assertPublicSafeText(input.relativePath, 'state_snapshots.relative_path');
2056
- const metadataJson = stringifyPublicMetadata(input.metadata);
2057
- try {
2058
- const result = this.db.prepare(`
233
+ `).run(e.releasedAt,e.leaseId);if(t.changes===0)throw new i("sqlite-foreign-key-failed",`Continuity SQLite lease ${e.leaseId} was not found for release.`);return this.toWriteResult(t)}catch(t){throw m(t,"sqlite-mutation-failed")}}recordStateSnapshot(e){this.assertCanMutate(),b(e.summary,"state_snapshots.summary"),b(e.relativePath,"state_snapshots.relative_path");const t=S(e.metadata);try{const r=this.db.prepare(`
2059
234
  INSERT OR IGNORE INTO state_snapshots (
2060
235
  snapshot_id, job_id, kind, relative_path, sha256, summary, captured_at,
2061
236
  operation_id, idempotency_key, metadata_json
2062
237
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2063
- `).run(input.snapshotId, input.jobId ?? null, input.kind, input.relativePath ?? null, input.sha256 ?? null, input.summary ?? null, input.capturedAt, input.operationId ?? null, input.idempotencyKey ?? null, metadataJson);
2064
- return this.toWriteResult(result);
2065
- }
2066
- catch (error) {
2067
- throw mapSqliteError(error, 'sqlite-mutation-failed');
2068
- }
2069
- }
2070
- recordArtifact(input) {
2071
- this.assertCanMutate();
2072
- assertPublicSafeText(input.kind, 'artifacts.kind');
2073
- assertPublicSafeText(input.relativePath, 'artifacts.relative_path');
2074
- assertPublicSafeText(input.summary, 'artifacts.summary');
2075
- const metadataJson = stringifyPublicMetadata(input.metadata);
2076
- try {
2077
- const result = this.db.prepare(`
238
+ `).run(e.snapshotId,e.jobId??null,e.kind,e.relativePath??null,e.sha256??null,e.summary??null,e.capturedAt,e.operationId??null,e.idempotencyKey??null,t);return this.toWriteResult(r)}catch(r){throw m(r,"sqlite-mutation-failed")}}recordArtifact(e){this.assertCanMutate(),b(e.kind,"artifacts.kind"),b(e.relativePath,"artifacts.relative_path"),b(e.summary,"artifacts.summary");const t=S(e.metadata);try{const r=this.db.prepare(`
2078
239
  INSERT OR IGNORE INTO artifacts (
2079
240
  artifact_id, job_id, kind, relative_path, sha256, summary,
2080
241
  operation_id, idempotency_key, created_at, metadata_json
2081
242
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2082
- `).run(input.artifactId, input.jobId ?? null, input.kind, input.relativePath, input.sha256 ?? null, input.summary ?? null, input.operationId ?? null, input.idempotencyKey ?? null, input.createdAt, metadataJson);
2083
- return this.toWriteResult(result);
2084
- }
2085
- catch (error) {
2086
- throw mapSqliteError(error, 'sqlite-mutation-failed');
2087
- }
2088
- }
2089
- enqueueOutbox(input) {
2090
- this.assertCanMutate();
2091
- assertOutboxIdentity({
2092
- outboxId: input.outboxId,
2093
- jobId: input.jobId,
2094
- operationId: input.operationId,
2095
- idempotencyKey: input.idempotencyKey,
2096
- type: input.type,
2097
- status: input.status,
2098
- deliveryCursor: input.deliveryCursor,
2099
- timestamp: input.createdAt,
2100
- reasonCode: input.lastErrorReason ?? input.lastErrorCode,
2101
- summary: input.lastErrorSummary ?? input.deadLetterSummary,
2102
- });
2103
- assertOutboxIdentity({ outboxId: input.outboxId, timestamp: input.updatedAt });
2104
- assertBoundedPublicText(input.nextAttemptAt, 'outbox.nextAttemptAt', 80);
2105
- assertBoundedPublicText(input.lastAttemptAt, 'outbox.lastAttemptAt', 80);
2106
- assertBoundedPublicText(input.sentAt, 'outbox.sentAt', 80);
2107
- assertBoundedPublicText(input.ackedAt, 'outbox.ackedAt', 80);
2108
- assertBoundedPublicText(input.deadLetteredAt, 'outbox.deadLetteredAt', 80);
2109
- assertNonNegativeInteger(input.retryCount, 'outbox.retryCount');
2110
- assertNonNegativeInteger(input.attemptCount, 'outbox.attemptCount');
2111
- const payloadJson = stringifyPublicMetadata(input.payload);
2112
- try {
2113
- const result = this.db.prepare(`
243
+ `).run(e.artifactId,e.jobId??null,e.kind,e.relativePath,e.sha256??null,e.summary??null,e.operationId??null,e.idempotencyKey??null,e.createdAt,t);return this.toWriteResult(r)}catch(r){throw m(r,"sqlite-mutation-failed")}}enqueueOutbox(e){this.assertCanMutate(),v({outboxId:e.outboxId,jobId:e.jobId,operationId:e.operationId,idempotencyKey:e.idempotencyKey,type:e.type,status:e.status,deliveryCursor:e.deliveryCursor,timestamp:e.createdAt,reasonCode:e.lastErrorReason??e.lastErrorCode,summary:e.lastErrorSummary??e.deadLetterSummary}),v({outboxId:e.outboxId,timestamp:e.updatedAt}),c(e.nextAttemptAt,"outbox.nextAttemptAt",80),c(e.lastAttemptAt,"outbox.lastAttemptAt",80),c(e.sentAt,"outbox.sentAt",80),c(e.ackedAt,"outbox.ackedAt",80),c(e.deadLetteredAt,"outbox.deadLetteredAt",80),N(e.retryCount,"outbox.retryCount"),N(e.attemptCount,"outbox.attemptCount");const t=S(e.payload);try{const r=this.db.prepare(`
2114
244
  INSERT OR IGNORE INTO outbox (
2115
245
  outbox_id, job_id, operation_id, idempotency_key, type, status,
2116
246
  payload_json, retry_count, attempt_count, delivery_cursor,
@@ -2118,57 +248,18 @@ export class ContinuitySqliteStore {
2118
248
  last_error_reason, last_error_summary, dead_letter_summary,
2119
249
  dead_lettered_at, created_at, updated_at
2120
250
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2121
- `).run(input.outboxId, input.jobId ?? null, input.operationId, input.idempotencyKey ?? null, input.type, input.status ?? 'pending', payloadJson ?? '{}', input.retryCount ?? 0, input.attemptCount ?? 0, input.deliveryCursor ?? null, input.nextAttemptAt ?? null, input.lastAttemptAt ?? null, input.sentAt ?? null, input.ackedAt ?? null, input.lastErrorCode ?? null, input.lastErrorReason ?? input.lastErrorCode ?? null, input.lastErrorSummary ?? null, input.deadLetterSummary ?? null, input.deadLetteredAt ?? null, input.createdAt, input.updatedAt);
2122
- return this.toWriteResult(result);
2123
- }
2124
- catch (error) {
2125
- throw mapSqliteError(error, 'sqlite-mutation-failed');
2126
- }
2127
- }
2128
- selectOutboxBatch(options) {
2129
- assertOutboxIdentity({ outboxId: 'outbox-query', timestamp: options.nowIso });
2130
- const limit = Math.max(1, Math.min(100, Math.floor(options.limit)));
2131
- const statuses = uniqueValues(options.statuses?.length ? options.statuses : ['pending', 'failed']);
2132
- for (const status of statuses) {
2133
- if (!isOutboxStatus(status)) {
2134
- throw new ContinuitySqliteStoreError('sqlite-public-safety-rejected', 'Continuity SQLite outbox batch query rejected an unknown status.');
2135
- }
2136
- }
2137
- const placeholders = statuses.map(() => '?').join(', ');
2138
- const rows = this.db.prepare(`
251
+ `).run(e.outboxId,e.jobId??null,e.operationId,e.idempotencyKey??null,e.type,e.status??"pending",t??"{}",e.retryCount??0,e.attemptCount??0,e.deliveryCursor??null,e.nextAttemptAt??null,e.lastAttemptAt??null,e.sentAt??null,e.ackedAt??null,e.lastErrorCode??null,e.lastErrorReason??e.lastErrorCode??null,e.lastErrorSummary??null,e.deadLetterSummary??null,e.deadLetteredAt??null,e.createdAt,e.updatedAt);return this.toWriteResult(r)}catch(r){throw m(r,"sqlite-mutation-failed")}}selectOutboxBatch(e){v({outboxId:"outbox-query",timestamp:e.nowIso});const t=Math.max(1,Math.min(100,Math.floor(e.limit))),r=q(e.statuses?.length?e.statuses:["pending","failed"]);for(const d of r)if(!Ie(d))throw new i("sqlite-public-safety-rejected","Continuity SQLite outbox batch query rejected an unknown status.");const s=r.map(()=>"?").join(", ");return this.db.prepare(`
2139
252
  SELECT outbox_id, job_id, operation_id, idempotency_key, type, status,
2140
253
  payload_json, retry_count, attempt_count, delivery_cursor,
2141
254
  next_attempt_at, last_attempt_at, sent_at, acked_at, last_error_code,
2142
255
  last_error_reason, last_error_summary, dead_letter_summary,
2143
256
  dead_lettered_at, created_at, updated_at
2144
257
  FROM outbox
2145
- WHERE status IN (${placeholders})
258
+ WHERE status IN (${s})
2146
259
  AND (status NOT IN ('pending', 'failed') OR next_attempt_at IS NULL OR next_attempt_at <= ?)
2147
260
  ORDER BY COALESCE(next_attempt_at, created_at) ASC, created_at ASC, outbox_id ASC
2148
261
  LIMIT ?
2149
- `).all(...statuses, options.nowIso, limit);
2150
- return rows.map(toOutboxDeliveryRow);
2151
- }
2152
- markOutboxSent(input) {
2153
- this.assertCanMutate();
2154
- assertOutboxIdentity({
2155
- outboxId: input.outboxId,
2156
- deliveryCursor: input.deliveryCursor,
2157
- timestamp: input.sentAt,
2158
- reasonCode: input.reasonCode,
2159
- });
2160
- try {
2161
- return this.runInTransaction(() => {
2162
- const row = this.getOutboxTransitionRow(input.outboxId);
2163
- if (!row)
2164
- return outboxTransitionResult({ outboxId: input.outboxId, status: 'missing', changes: 0, replay: false, reasonCode: 'outbox-missing' });
2165
- if ((row.status === 'sent' || row.status === 'acked') && row.delivery_cursor === input.deliveryCursor) {
2166
- return outboxTransitionResult({ outboxId: input.outboxId, status: row.status, changes: 0, replay: true, deliveryCursor: input.deliveryCursor, reasonCode: input.reasonCode ?? 'outbox-delivery-replay' });
2167
- }
2168
- if (row.status === 'sent' || row.status === 'acked' || row.status === 'dead_letter') {
2169
- return outboxTransitionResult({ outboxId: input.outboxId, status: 'conflict', changes: 0, replay: false, deliveryCursor: row.delivery_cursor ?? undefined, reasonCode: 'outbox-delivery-conflict' });
2170
- }
2171
- const result = this.db.prepare(`
262
+ `).all(...r,e.nowIso,t).map(ht)}markOutboxSent(e){this.assertCanMutate(),v({outboxId:e.outboxId,deliveryCursor:e.deliveryCursor,timestamp:e.sentAt,reasonCode:e.reasonCode});try{return this.runInTransaction(()=>{const t=this.getOutboxTransitionRow(e.outboxId);if(!t)return C({outboxId:e.outboxId,status:"missing",changes:0,replay:!1,reasonCode:"outbox-missing"});if((t.status==="sent"||t.status==="acked")&&t.delivery_cursor===e.deliveryCursor)return C({outboxId:e.outboxId,status:t.status,changes:0,replay:!0,deliveryCursor:e.deliveryCursor,reasonCode:e.reasonCode??"outbox-delivery-replay"});if(t.status==="sent"||t.status==="acked"||t.status==="dead_letter")return C({outboxId:e.outboxId,status:"conflict",changes:0,replay:!1,deliveryCursor:t.delivery_cursor??void 0,reasonCode:"outbox-delivery-conflict"});const r=this.db.prepare(`
2172
263
  UPDATE outbox
2173
264
  SET status = 'sent',
2174
265
  delivery_cursor = ?,
@@ -2179,34 +270,7 @@ export class ContinuitySqliteStore {
2179
270
  updated_at = ?
2180
271
  WHERE outbox_id = ?
2181
272
  AND status IN ('pending', 'failed')
2182
- `).run(input.deliveryCursor, input.sentAt, input.sentAt, input.sentAt, input.outboxId);
2183
- return outboxTransitionResult({ outboxId: input.outboxId, status: 'sent', changes: result.changes, replay: false, deliveryCursor: input.deliveryCursor, reasonCode: input.reasonCode ?? 'outbox-sent' });
2184
- });
2185
- }
2186
- catch (error) {
2187
- throw mapSqliteError(error, 'sqlite-mutation-failed');
2188
- }
2189
- }
2190
- ackOutbox(input) {
2191
- this.assertCanMutate();
2192
- assertOutboxIdentity({
2193
- outboxId: input.outboxId,
2194
- deliveryCursor: input.deliveryCursor,
2195
- timestamp: input.ackedAt,
2196
- reasonCode: input.reasonCode,
2197
- });
2198
- try {
2199
- return this.runInTransaction(() => {
2200
- const row = this.getOutboxTransitionRow(input.outboxId);
2201
- if (!row)
2202
- return outboxTransitionResult({ outboxId: input.outboxId, status: 'missing', changes: 0, replay: false, reasonCode: 'outbox-missing' });
2203
- if (row.status === 'acked' && (!input.deliveryCursor || row.delivery_cursor === input.deliveryCursor)) {
2204
- return outboxTransitionResult({ outboxId: input.outboxId, status: 'acked', changes: 0, replay: true, deliveryCursor: row.delivery_cursor ?? undefined, reasonCode: input.reasonCode ?? 'outbox-ack-replay' });
2205
- }
2206
- if (row.status !== 'sent' || (input.deliveryCursor && row.delivery_cursor !== input.deliveryCursor)) {
2207
- return outboxTransitionResult({ outboxId: input.outboxId, status: 'conflict', changes: 0, replay: false, deliveryCursor: row.delivery_cursor ?? undefined, reasonCode: 'outbox-ack-conflict' });
2208
- }
2209
- const result = this.db.prepare(`
273
+ `).run(e.deliveryCursor,e.sentAt,e.sentAt,e.sentAt,e.outboxId);return C({outboxId:e.outboxId,status:"sent",changes:r.changes,replay:!1,deliveryCursor:e.deliveryCursor,reasonCode:e.reasonCode??"outbox-sent"})})}catch(t){throw m(t,"sqlite-mutation-failed")}}ackOutbox(e){this.assertCanMutate(),v({outboxId:e.outboxId,deliveryCursor:e.deliveryCursor,timestamp:e.ackedAt,reasonCode:e.reasonCode});try{return this.runInTransaction(()=>{const t=this.getOutboxTransitionRow(e.outboxId);if(!t)return C({outboxId:e.outboxId,status:"missing",changes:0,replay:!1,reasonCode:"outbox-missing"});if(t.status==="acked"&&(!e.deliveryCursor||t.delivery_cursor===e.deliveryCursor))return C({outboxId:e.outboxId,status:"acked",changes:0,replay:!0,deliveryCursor:t.delivery_cursor??void 0,reasonCode:e.reasonCode??"outbox-ack-replay"});if(t.status!=="sent"||e.deliveryCursor&&t.delivery_cursor!==e.deliveryCursor)return C({outboxId:e.outboxId,status:"conflict",changes:0,replay:!1,deliveryCursor:t.delivery_cursor??void 0,reasonCode:"outbox-ack-conflict"});const r=this.db.prepare(`
2210
274
  UPDATE outbox
2211
275
  SET status = 'acked',
2212
276
  acked_at = ?,
@@ -2216,48 +280,7 @@ export class ContinuitySqliteStore {
2216
280
  last_error_reason = NULL
2217
281
  WHERE outbox_id = ?
2218
282
  AND status = 'sent'
2219
- `).run(input.ackedAt, input.ackedAt, input.outboxId);
2220
- return outboxTransitionResult({ outboxId: input.outboxId, status: 'acked', changes: result.changes, replay: false, deliveryCursor: row.delivery_cursor ?? undefined, reasonCode: input.reasonCode ?? 'outbox-acked' });
2221
- });
2222
- }
2223
- catch (error) {
2224
- throw mapSqliteError(error, 'sqlite-mutation-failed');
2225
- }
2226
- }
2227
- failOutboxDelivery(input) {
2228
- this.assertCanMutate();
2229
- assertOutboxIdentity({
2230
- outboxId: input.outboxId,
2231
- deliveryCursor: input.deliveryCursor,
2232
- timestamp: input.failedAt,
2233
- reasonCode: input.reasonCode,
2234
- summary: input.summary,
2235
- });
2236
- assertBoundedPublicText(input.nextRetryAt, 'outbox.nextRetryAt', 80);
2237
- try {
2238
- return this.runInTransaction(() => {
2239
- const row = this.getOutboxTransitionRow(input.outboxId);
2240
- if (!row)
2241
- return outboxTransitionResult({ outboxId: input.outboxId, status: 'missing', changes: 0, replay: false, reasonCode: 'outbox-missing' });
2242
- if (row.status === 'acked') {
2243
- return outboxTransitionResult({ outboxId: input.outboxId, status: 'acked', changes: 0, replay: true, deliveryCursor: row.delivery_cursor ?? undefined, reasonCode: 'outbox-already-acked' });
2244
- }
2245
- if (row.status === 'dead_letter') {
2246
- return outboxTransitionResult({ outboxId: input.outboxId, status: 'dead_letter', changes: 0, replay: true, deliveryCursor: row.delivery_cursor ?? undefined, deadLetteredAt: row.dead_lettered_at ?? undefined, reasonCode: row.last_error_reason ?? row.last_error_code ?? input.reasonCode });
2247
- }
2248
- if (input.deliveryCursor && row.delivery_cursor && row.delivery_cursor !== input.deliveryCursor) {
2249
- return outboxTransitionResult({ outboxId: input.outboxId, status: 'conflict', changes: 0, replay: false, deliveryCursor: row.delivery_cursor, reasonCode: 'outbox-fail-conflict' });
2250
- }
2251
- const attemptCount = Math.max(1, row.attempt_count ?? 0, row.retry_count ?? 0);
2252
- const decision = computeContinuityOutboxFailureDecision({
2253
- nowIso: input.failedAt,
2254
- attemptCount,
2255
- reasonCode: input.reasonCode,
2256
- summary: input.summary,
2257
- nextRetryAt: input.nextRetryAt,
2258
- policy: input.policy,
2259
- });
2260
- const result = this.db.prepare(`
283
+ `).run(e.ackedAt,e.ackedAt,e.outboxId);return C({outboxId:e.outboxId,status:"acked",changes:r.changes,replay:!1,deliveryCursor:t.delivery_cursor??void 0,reasonCode:e.reasonCode??"outbox-acked"})})}catch(t){throw m(t,"sqlite-mutation-failed")}}failOutboxDelivery(e){this.assertCanMutate(),v({outboxId:e.outboxId,deliveryCursor:e.deliveryCursor,timestamp:e.failedAt,reasonCode:e.reasonCode,summary:e.summary}),c(e.nextRetryAt,"outbox.nextRetryAt",80);try{return this.runInTransaction(()=>{const t=this.getOutboxTransitionRow(e.outboxId);if(!t)return C({outboxId:e.outboxId,status:"missing",changes:0,replay:!1,reasonCode:"outbox-missing"});if(t.status==="acked")return C({outboxId:e.outboxId,status:"acked",changes:0,replay:!0,deliveryCursor:t.delivery_cursor??void 0,reasonCode:"outbox-already-acked"});if(t.status==="dead_letter")return C({outboxId:e.outboxId,status:"dead_letter",changes:0,replay:!0,deliveryCursor:t.delivery_cursor??void 0,deadLetteredAt:t.dead_lettered_at??void 0,reasonCode:t.last_error_reason??t.last_error_code??e.reasonCode});if(e.deliveryCursor&&t.delivery_cursor&&t.delivery_cursor!==e.deliveryCursor)return C({outboxId:e.outboxId,status:"conflict",changes:0,replay:!1,deliveryCursor:t.delivery_cursor,reasonCode:"outbox-fail-conflict"});const r=Math.max(1,t.attempt_count??0,t.retry_count??0),s=we({nowIso:e.failedAt,attemptCount:r,reasonCode:e.reasonCode,summary:e.summary,nextRetryAt:e.nextRetryAt,policy:e.policy}),n=this.db.prepare(`
2261
284
  UPDATE outbox
2262
285
  SET status = ?,
2263
286
  retry_count = MAX(retry_count, ?),
@@ -2270,40 +293,7 @@ export class ContinuitySqliteStore {
2270
293
  updated_at = ?
2271
294
  WHERE outbox_id = ?
2272
295
  AND status IN ('pending', 'sent', 'failed')
2273
- `).run(decision.status, attemptCount, decision.status === 'failed' ? decision.nextRetryAt ?? input.failedAt : null, input.reasonCode, input.reasonCode, input.summary ?? null, decision.status === 'dead_letter' ? input.summary ?? input.reasonCode : null, decision.deadLetteredAt ?? null, input.failedAt, input.outboxId);
2274
- return outboxTransitionResult({
2275
- outboxId: input.outboxId,
2276
- status: decision.status,
2277
- changes: result.changes,
2278
- replay: false,
2279
- deliveryCursor: row.delivery_cursor ?? input.deliveryCursor,
2280
- reasonCode: decision.reasonCode,
2281
- ...(decision.nextRetryAt ? { nextRetryAt: decision.nextRetryAt } : {}),
2282
- ...(decision.deadLetteredAt ? { deadLetteredAt: decision.deadLetteredAt } : {}),
2283
- });
2284
- });
2285
- }
2286
- catch (error) {
2287
- throw mapSqliteError(error, 'sqlite-mutation-failed');
2288
- }
2289
- }
2290
- deadLetterOutbox(input) {
2291
- this.assertCanMutate();
2292
- assertOutboxIdentity({
2293
- outboxId: input.outboxId,
2294
- timestamp: input.deadLetteredAt,
2295
- reasonCode: input.reasonCode,
2296
- summary: input.summary,
2297
- });
2298
- try {
2299
- return this.runInTransaction(() => {
2300
- const row = this.getOutboxTransitionRow(input.outboxId);
2301
- if (!row)
2302
- return outboxTransitionResult({ outboxId: input.outboxId, status: 'missing', changes: 0, replay: false, reasonCode: 'outbox-missing' });
2303
- if (row.status === 'dead_letter') {
2304
- return outboxTransitionResult({ outboxId: input.outboxId, status: 'dead_letter', changes: 0, replay: true, deliveryCursor: row.delivery_cursor ?? undefined, deadLetteredAt: row.dead_lettered_at ?? undefined, reasonCode: row.last_error_reason ?? row.last_error_code ?? input.reasonCode });
2305
- }
2306
- const result = this.db.prepare(`
296
+ `).run(s.status,r,s.status==="failed"?s.nextRetryAt??e.failedAt:null,e.reasonCode,e.reasonCode,e.summary??null,s.status==="dead_letter"?e.summary??e.reasonCode:null,s.deadLetteredAt??null,e.failedAt,e.outboxId);return C({outboxId:e.outboxId,status:s.status,changes:n.changes,replay:!1,deliveryCursor:t.delivery_cursor??e.deliveryCursor,reasonCode:s.reasonCode,...s.nextRetryAt?{nextRetryAt:s.nextRetryAt}:{},...s.deadLetteredAt?{deadLetteredAt:s.deadLetteredAt}:{}})})}catch(t){throw m(t,"sqlite-mutation-failed")}}deadLetterOutbox(e){this.assertCanMutate(),v({outboxId:e.outboxId,timestamp:e.deadLetteredAt,reasonCode:e.reasonCode,summary:e.summary});try{return this.runInTransaction(()=>{const t=this.getOutboxTransitionRow(e.outboxId);if(!t)return C({outboxId:e.outboxId,status:"missing",changes:0,replay:!1,reasonCode:"outbox-missing"});if(t.status==="dead_letter")return C({outboxId:e.outboxId,status:"dead_letter",changes:0,replay:!0,deliveryCursor:t.delivery_cursor??void 0,deadLetteredAt:t.dead_lettered_at??void 0,reasonCode:t.last_error_reason??t.last_error_code??e.reasonCode});const r=this.db.prepare(`
2307
297
  UPDATE outbox
2308
298
  SET status = 'dead_letter',
2309
299
  next_attempt_at = NULL,
@@ -2315,39 +305,13 @@ export class ContinuitySqliteStore {
2315
305
  updated_at = ?
2316
306
  WHERE outbox_id = ?
2317
307
  AND status <> 'acked'
2318
- `).run(input.reasonCode, input.reasonCode, input.summary, input.summary, input.deadLetteredAt, input.deadLetteredAt, input.outboxId);
2319
- return outboxTransitionResult({ outboxId: input.outboxId, status: 'dead_letter', changes: result.changes, replay: false, deliveryCursor: row.delivery_cursor ?? undefined, deadLetteredAt: input.deadLetteredAt, reasonCode: input.reasonCode });
2320
- });
2321
- }
2322
- catch (error) {
2323
- throw mapSqliteError(error, 'sqlite-mutation-failed');
2324
- }
2325
- }
2326
- retryOutboxEntries(options) {
2327
- this.assertCanMutate();
2328
- const limit = Math.max(1, Math.min(100, Math.floor(options.limit)));
2329
- const statuses = uniqueValues(options.statuses?.length ? options.statuses : ['failed', 'dead_letter']);
2330
- const reasonCode = options.reasonCode ?? 'backoff-scheduled';
2331
- assertPublicSafeText(reasonCode, 'outbox.last_error_code');
2332
- assertPublicSafeText(options.nextAttemptAt, 'outbox.next_attempt_at');
2333
- try {
2334
- return this.runInTransaction(() => {
2335
- const placeholders = statuses.map(() => '?').join(', ');
2336
- const rows = this.db.prepare(`
308
+ `).run(e.reasonCode,e.reasonCode,e.summary,e.summary,e.deadLetteredAt,e.deadLetteredAt,e.outboxId);return C({outboxId:e.outboxId,status:"dead_letter",changes:r.changes,replay:!1,deliveryCursor:t.delivery_cursor??void 0,deadLetteredAt:e.deadLetteredAt,reasonCode:e.reasonCode})})}catch(t){throw m(t,"sqlite-mutation-failed")}}retryOutboxEntries(e){this.assertCanMutate();const t=Math.max(1,Math.min(100,Math.floor(e.limit))),r=q(e.statuses?.length?e.statuses:["failed","dead_letter"]),s=e.reasonCode??"backoff-scheduled";b(s,"outbox.last_error_code"),b(e.nextAttemptAt,"outbox.next_attempt_at");try{return this.runInTransaction(()=>{const n=r.map(()=>"?").join(", "),o=this.db.prepare(`
2337
309
  SELECT outbox_id
2338
310
  FROM outbox
2339
- WHERE status IN (${placeholders})
311
+ WHERE status IN (${n})
2340
312
  ORDER BY updated_at ASC, outbox_id ASC
2341
313
  LIMIT ?
2342
- `).all(...statuses, limit);
2343
- const retriedOutboxIds = rows
2344
- .map((row) => row.outbox_id)
2345
- .filter((outboxId) => typeof outboxId === 'string' && outboxId.length > 0);
2346
- if (retriedOutboxIds.length === 0) {
2347
- return { retriedOutboxIds: [], changes: 0, reasonCode, publicSafe: true };
2348
- }
2349
- const idPlaceholders = retriedOutboxIds.map(() => '?').join(', ');
2350
- const result = this.db.prepare(`
314
+ `).all(...r,t).map(y=>y.outbox_id).filter(y=>typeof y=="string"&&y.length>0);if(o.length===0)return{retriedOutboxIds:[],changes:0,reasonCode:s,publicSafe:!0};const l=o.map(()=>"?").join(", "),I=this.db.prepare(`
2351
315
  UPDATE outbox
2352
316
  SET status = 'pending',
2353
317
  retry_count = retry_count + 1,
@@ -2360,22 +324,8 @@ export class ContinuitySqliteStore {
2360
324
  dead_letter_summary = NULL,
2361
325
  dead_lettered_at = NULL,
2362
326
  updated_at = ?
2363
- WHERE outbox_id IN (${idPlaceholders})
2364
- `).run(options.nextAttemptAt ?? options.nowIso, reasonCode, reasonCode, options.nowIso, ...retriedOutboxIds);
2365
- return { retriedOutboxIds, changes: result.changes, reasonCode, publicSafe: true };
2366
- });
2367
- }
2368
- catch (error) {
2369
- throw mapSqliteError(error, 'sqlite-mutation-failed');
2370
- }
2371
- }
2372
- upsertGoalCampaign(input) {
2373
- this.assertCanMutate();
2374
- assertPublicSafeText(input.title, 'goal_campaigns.title');
2375
- assertPublicSafeText(input.publicSummary, 'goal_campaigns.public_summary');
2376
- const metadataJson = stringifyPublicMetadata(input.metadata);
2377
- try {
2378
- const result = this.db.prepare(`
327
+ WHERE outbox_id IN (${l})
328
+ `).run(e.nextAttemptAt??e.nowIso,s,s,e.nowIso,...o);return{retriedOutboxIds:o,changes:I.changes,reasonCode:s,publicSafe:!0}})}catch(n){throw m(n,"sqlite-mutation-failed")}}upsertGoalCampaign(e){this.assertCanMutate(),b(e.title,"goal_campaigns.title"),b(e.publicSummary,"goal_campaigns.public_summary");const t=S(e.metadata);try{const r=this.db.prepare(`
2379
329
  INSERT INTO goal_campaigns (
2380
330
  campaign_id, job_id, status, title, public_summary, reason_code,
2381
331
  created_at, updated_at, operation_id, idempotency_key, metadata_json
@@ -2389,20 +339,7 @@ export class ContinuitySqliteStore {
2389
339
  operation_id = excluded.operation_id,
2390
340
  idempotency_key = excluded.idempotency_key,
2391
341
  metadata_json = excluded.metadata_json
2392
- `).run(input.campaignId, input.jobId, input.status, input.title ?? null, input.publicSummary ?? null, input.reasonCode ?? null, input.createdAt, input.updatedAt, input.operationId ?? null, input.idempotencyKey ?? null, metadataJson);
2393
- return this.toWriteResult(result);
2394
- }
2395
- catch (error) {
2396
- throw mapSqliteError(error, 'sqlite-mutation-failed');
2397
- }
2398
- }
2399
- upsertGoalLedgerItem(input) {
2400
- this.assertCanMutate();
2401
- assertPublicSafeText(input.summary, 'goal_ledger_items.summary');
2402
- const metadataJson = stringifyPublicMetadata(input.metadata);
2403
- try {
2404
- this.ensureGoalCampaign(input.campaignId, input.jobId, input.createdAt, input.updatedAt);
2405
- const result = this.db.prepare(`
342
+ `).run(e.campaignId,e.jobId,e.status,e.title??null,e.publicSummary??null,e.reasonCode??null,e.createdAt,e.updatedAt,e.operationId??null,e.idempotencyKey??null,t);return this.toWriteResult(r)}catch(r){throw m(r,"sqlite-mutation-failed")}}upsertGoalLedgerItem(e){this.assertCanMutate(),b(e.summary,"goal_ledger_items.summary");const t=S(e.metadata);try{this.ensureGoalCampaign(e.campaignId,e.jobId,e.createdAt,e.updatedAt);const r=this.db.prepare(`
2406
343
  INSERT INTO goal_ledger_items (
2407
344
  item_id, campaign_id, operation_id, idempotency_key, item_type, status,
2408
345
  summary, reason_code, created_at, updated_at, metadata_json
@@ -2413,21 +350,7 @@ export class ContinuitySqliteStore {
2413
350
  reason_code = excluded.reason_code,
2414
351
  updated_at = excluded.updated_at,
2415
352
  metadata_json = excluded.metadata_json
2416
- `).run(input.itemId, input.campaignId, input.operationId ?? null, input.idempotencyKey ?? null, input.itemType, input.status, input.summary, input.reasonCode ?? null, input.createdAt, input.updatedAt, metadataJson);
2417
- return this.toWriteResult(result);
2418
- }
2419
- catch (error) {
2420
- throw mapSqliteError(error, 'sqlite-mutation-failed');
2421
- }
2422
- }
2423
- upsertGoalVerifier(input) {
2424
- this.assertCanMutate();
2425
- assertPublicSafeText(input.summary, 'goal_verifiers.summary');
2426
- const metadataJson = stringifyPublicMetadata(input.metadata);
2427
- try {
2428
- const fallbackTimestamp = input.lastCheckedAt ?? this.now();
2429
- this.ensureGoalCampaign(input.campaignId, input.jobId, fallbackTimestamp, fallbackTimestamp);
2430
- const result = this.db.prepare(`
353
+ `).run(e.itemId,e.campaignId,e.operationId??null,e.idempotencyKey??null,e.itemType,e.status,e.summary,e.reasonCode??null,e.createdAt,e.updatedAt,t);return this.toWriteResult(r)}catch(r){throw m(r,"sqlite-mutation-failed")}}upsertGoalVerifier(e){this.assertCanMutate(),b(e.summary,"goal_verifiers.summary");const t=S(e.metadata);try{const r=e.lastCheckedAt??this.now();this.ensureGoalCampaign(e.campaignId,e.jobId,r,r);const s=this.db.prepare(`
2431
354
  INSERT INTO goal_verifiers (
2432
355
  verifier_id, campaign_id, kind, status, summary, reason_code,
2433
356
  last_checked_at, operation_id, idempotency_key, metadata_json
@@ -2438,21 +361,7 @@ export class ContinuitySqliteStore {
2438
361
  reason_code = excluded.reason_code,
2439
362
  last_checked_at = excluded.last_checked_at,
2440
363
  metadata_json = excluded.metadata_json
2441
- `).run(input.verifierId, input.campaignId, input.kind, input.status, input.summary ?? null, input.reasonCode ?? null, input.lastCheckedAt ?? null, input.operationId ?? null, input.idempotencyKey ?? null, metadataJson);
2442
- return this.toWriteResult(result);
2443
- }
2444
- catch (error) {
2445
- throw mapSqliteError(error, 'sqlite-mutation-failed');
2446
- }
2447
- }
2448
- upsertGoalReviewGate(input) {
2449
- this.assertCanMutate();
2450
- assertPublicSafeText(input.summary, 'goal_review_gates.summary');
2451
- const metadataJson = stringifyPublicMetadata(input.metadata);
2452
- try {
2453
- const fallbackTimestamp = this.now();
2454
- this.ensureGoalCampaign(input.campaignId, input.jobId, fallbackTimestamp, fallbackTimestamp);
2455
- const result = this.db.prepare(`
364
+ `).run(e.verifierId,e.campaignId,e.kind,e.status,e.summary??null,e.reasonCode??null,e.lastCheckedAt??null,e.operationId??null,e.idempotencyKey??null,t);return this.toWriteResult(s)}catch(r){throw m(r,"sqlite-mutation-failed")}}upsertGoalReviewGate(e){this.assertCanMutate(),b(e.summary,"goal_review_gates.summary");const t=S(e.metadata);try{const r=this.now();this.ensureGoalCampaign(e.campaignId,e.jobId,r,r);const s=this.db.prepare(`
2456
365
  INSERT INTO goal_review_gates (
2457
366
  gate_id, campaign_id, status, summary, required, reason_code,
2458
367
  operation_id, idempotency_key, metadata_json
@@ -2463,45 +372,7 @@ export class ContinuitySqliteStore {
2463
372
  required = excluded.required,
2464
373
  reason_code = excluded.reason_code,
2465
374
  metadata_json = excluded.metadata_json
2466
- `).run(input.gateId, input.campaignId, input.status, input.summary ?? null, input.required ? 1 : 0, input.reasonCode ?? null, input.operationId ?? null, input.idempotencyKey ?? null, metadataJson);
2467
- return this.toWriteResult(result);
2468
- }
2469
- catch (error) {
2470
- throw mapSqliteError(error, 'sqlite-mutation-failed');
2471
- }
2472
- }
2473
- recordOperationAudit(input) {
2474
- return this.appendReceipt(input);
2475
- }
2476
- hasOperationRef(operationId, idempotencyKey) {
2477
- this.assertCanMutate();
2478
- const tables = [
2479
- 'jobs',
2480
- 'job_leases',
2481
- 'job_events',
2482
- 'job_receipts',
2483
- 'goal_campaigns',
2484
- 'goal_ledger_items',
2485
- 'goal_verifiers',
2486
- 'goal_review_gates',
2487
- 'loop_jobs',
2488
- 'artifacts',
2489
- 'state_snapshots',
2490
- 'headless_execution_attempts',
2491
- 'runner_config',
2492
- 'outbox',
2493
- ];
2494
- const clauses = tables.map((table) => `SELECT 1 AS found FROM ${table} WHERE operation_id = ?${idempotencyKey ? ' OR idempotency_key = ?' : ''}`);
2495
- const params = tables.flatMap(() => idempotencyKey ? [operationId, idempotencyKey] : [operationId]);
2496
- const row = this.db.prepare(`${clauses.join(' UNION ALL ')} LIMIT 1`).get(...params);
2497
- return Boolean(row);
2498
- }
2499
- jobExists(jobId) {
2500
- const row = this.db.prepare('SELECT 1 AS found FROM jobs WHERE job_id = ? LIMIT 1').get(jobId);
2501
- return Boolean(row);
2502
- }
2503
- getHeadlessExecutionAttempt(attemptId) {
2504
- return this.db.prepare(`
375
+ `).run(e.gateId,e.campaignId,e.status,e.summary??null,e.required?1:0,e.reasonCode??null,e.operationId??null,e.idempotencyKey??null,t);return this.toWriteResult(s)}catch(r){throw m(r,"sqlite-mutation-failed")}}recordOperationAudit(e){return this.appendReceipt(e)}hasOperationRef(e,t){this.assertCanMutate();const r=["jobs","job_leases","job_events","job_receipts","goal_campaigns","goal_ledger_items","goal_verifiers","goal_review_gates","loop_jobs","artifacts","state_snapshots","headless_execution_attempts","runner_config","outbox"],s=r.map(o=>`SELECT 1 AS found FROM ${o} WHERE operation_id = ?${t?" OR idempotency_key = ?":""}`),n=r.flatMap(()=>t?[e,t]:[e]);return!!this.db.prepare(`${s.join(" UNION ALL ")} LIMIT 1`).get(...n)}jobExists(e){return!!this.db.prepare("SELECT 1 AS found FROM jobs WHERE job_id = ? LIMIT 1").get(e)}getHeadlessExecutionAttempt(e){return this.db.prepare(`
2505
376
  SELECT attempt_id, execution_id, job_id, directive_hash, directive_kind,
2506
377
  command_class, target_kind, target_ref, story_id, epic_id,
2507
378
  runtime_id, model_id, runtime_source, status, reason_code,
@@ -2515,22 +386,13 @@ export class ContinuitySqliteStore {
2515
386
  FROM headless_execution_attempts
2516
387
  WHERE attempt_id = ?
2517
388
  LIMIT 1
2518
- `).get(attemptId);
2519
- }
2520
- getOutboxTransitionRow(outboxId) {
2521
- const row = this.db.prepare(`
389
+ `).get(e)}getOutboxTransitionRow(e){const t=this.db.prepare(`
2522
390
  SELECT outbox_id, status, delivery_cursor, retry_count, attempt_count,
2523
391
  last_error_code, last_error_reason, dead_lettered_at
2524
392
  FROM outbox
2525
393
  WHERE outbox_id = ?
2526
394
  LIMIT 1
2527
- `).get(outboxId);
2528
- if (!row || !isRecord(row))
2529
- return undefined;
2530
- return row;
2531
- }
2532
- selectReceiptRetentionCandidates(input) {
2533
- const rows = this.db.prepare(`
395
+ `).get(e);if(!(!t||!E(t)))return t}selectReceiptRetentionCandidates(e){return this.db.prepare(`
2534
396
  WITH sized AS (
2535
397
  SELECT receipt_id, job_id, status, reason_code, created_at,
2536
398
  length(receipt_id) + length(job_id) + length(status) + length(created_at)
@@ -2549,23 +411,7 @@ export class ContinuitySqliteStore {
2549
411
  WHERE created_at < ? OR retain_rank > ? OR cumulative_bytes > ?
2550
412
  ORDER BY created_at ASC, receipt_id ASC
2551
413
  LIMIT ?
2552
- `).all(input.cutoff, input.maxItems, input.maxBytes, input.limit);
2553
- return rows.flatMap((row) => {
2554
- if (typeof row.receipt_id !== 'string' || typeof row.job_id !== 'string')
2555
- return [];
2556
- return [{
2557
- kind: 'receipt',
2558
- id: row.receipt_id,
2559
- jobId: row.job_id,
2560
- status: typeof row.status === 'string' ? row.status : 'unknown',
2561
- reasonCode: typeof row.reason_code === 'string' ? row.reason_code : 'continuity-ok',
2562
- createdAt: typeof row.created_at === 'string' ? row.created_at : input.cutoff,
2563
- approxBytes: typeof row.approx_bytes === 'number' && row.approx_bytes >= 0 ? Math.floor(row.approx_bytes) : 0,
2564
- }];
2565
- });
2566
- }
2567
- selectOutputRetentionCandidates(input) {
2568
- const rows = this.db.prepare(`
414
+ `).all(e.cutoff,e.maxItems,e.maxBytes,e.limit).flatMap(r=>typeof r.receipt_id!="string"||typeof r.job_id!="string"?[]:[{kind:"receipt",id:r.receipt_id,jobId:r.job_id,status:typeof r.status=="string"?r.status:"unknown",reasonCode:typeof r.reason_code=="string"?r.reason_code:"continuity-ok",createdAt:typeof r.created_at=="string"?r.created_at:e.cutoff,approxBytes:typeof r.approx_bytes=="number"&&r.approx_bytes>=0?Math.floor(r.approx_bytes):0}])}selectOutputRetentionCandidates(e){return this.db.prepare(`
2569
415
  WITH sized AS (
2570
416
  SELECT event_id, job_id, type, reason_code, created_at,
2571
417
  length(event_id) + length(job_id) + length(operation_id) + length(type)
@@ -2584,42 +430,13 @@ export class ContinuitySqliteStore {
2584
430
  WHERE created_at < ? OR retain_rank > ? OR cumulative_bytes > ?
2585
431
  ORDER BY created_at ASC, event_id ASC
2586
432
  LIMIT ?
2587
- `).all(CONTINUITY_PROCESSING_OUTPUT_SUMMARY_EVENT_TYPE, input.cutoff, input.maxItems, input.maxBytes, input.limit);
2588
- return rows.flatMap((row) => {
2589
- if (typeof row.event_id !== 'string' || typeof row.job_id !== 'string')
2590
- return [];
2591
- return [{
2592
- kind: 'output',
2593
- id: row.event_id,
2594
- jobId: row.job_id,
2595
- status: typeof row.type === 'string' ? row.type : CONTINUITY_PROCESSING_OUTPUT_SUMMARY_EVENT_TYPE,
2596
- reasonCode: typeof row.reason_code === 'string' ? row.reason_code : 'continuity-ok',
2597
- createdAt: typeof row.created_at === 'string' ? row.created_at : input.cutoff,
2598
- approxBytes: typeof row.approx_bytes === 'number' && row.approx_bytes >= 0 ? Math.floor(row.approx_bytes) : 0,
2599
- }];
2600
- });
2601
- }
2602
- deleteRetentionCandidates(table, idColumn, ids) {
2603
- if (ids.length === 0)
2604
- return;
2605
- const placeholders = ids.map(() => '?').join(', ');
2606
- this.db.prepare(`DELETE FROM ${table} WHERE ${idColumn} IN (${placeholders})`).run(...ids);
2607
- }
2608
- preserveReceiptDerivedIteration(jobId, at) {
2609
- const row = this.db.prepare(`
433
+ `).all(O,e.cutoff,e.maxItems,e.maxBytes,e.limit).flatMap(r=>typeof r.event_id!="string"||typeof r.job_id!="string"?[]:[{kind:"output",id:r.event_id,jobId:r.job_id,status:typeof r.type=="string"?r.type:O,reasonCode:typeof r.reason_code=="string"?r.reason_code:"continuity-ok",createdAt:typeof r.created_at=="string"?r.created_at:e.cutoff,approxBytes:typeof r.approx_bytes=="number"&&r.approx_bytes>=0?Math.floor(r.approx_bytes):0}])}deleteRetentionCandidates(e,t,r){if(r.length===0)return;const s=r.map(()=>"?").join(", ");this.db.prepare(`DELETE FROM ${e} WHERE ${t} IN (${s})`).run(...r)}preserveReceiptDerivedIteration(e,t){const r=this.db.prepare(`
2610
434
  SELECT COUNT(*) AS iteration
2611
435
  FROM job_receipts
2612
436
  WHERE job_id = ?
2613
437
  AND (operation_id LIKE 'tick-%' OR operation_id LIKE 'loop-tick-%')
2614
438
  AND status IN ('accepted', 'applied')
2615
- `).get(jobId);
2616
- const iteration = typeof row?.iteration === 'number' ? row.iteration : 0;
2617
- if (iteration > 0) {
2618
- this.recordJobStageProgress({ jobId, iteration, at, appendHistory: false });
2619
- }
2620
- }
2621
- retainedReceiptOutputCounts(jobId) {
2622
- const receipt = this.db.prepare(`
439
+ `).get(e),s=typeof r?.iteration=="number"?r.iteration:0;s>0&&this.recordJobStageProgress({jobId:e,iteration:s,at:t,appendHistory:!1})}retainedReceiptOutputCounts(e){const t=this.db.prepare(`
2623
440
  SELECT COUNT(*) AS count,
2624
441
  COALESCE(SUM(length(receipt_id) + length(job_id) + length(status) + length(created_at)
2625
442
  + COALESCE(length(operation_id), 0) + COALESCE(length(idempotency_key), 0)
@@ -2627,23 +444,14 @@ export class ContinuitySqliteStore {
2627
444
  + COALESCE(length(ignored_operation_ids_json), 0) + COALESCE(length(metadata_json), 0)), 0) AS bytes
2628
445
  FROM job_receipts
2629
446
  WHERE job_id = ?
2630
- `).get(jobId);
2631
- const output = this.db.prepare(`
447
+ `).get(e),r=this.db.prepare(`
2632
448
  SELECT COUNT(*) AS count,
2633
449
  COALESCE(SUM(length(event_id) + length(job_id) + length(operation_id) + length(type)
2634
450
  + length(summary) + length(created_at) + COALESCE(length(idempotency_key), 0)
2635
451
  + COALESCE(length(reason_code), 0) + COALESCE(length(metadata_json), 0)), 0) AS bytes
2636
452
  FROM job_events
2637
453
  WHERE job_id = ? AND type = ?
2638
- `).get(jobId, CONTINUITY_PROCESSING_OUTPUT_SUMMARY_EVENT_TYPE);
2639
- return {
2640
- receipts: typeof receipt?.count === 'number' ? receipt.count : 0,
2641
- outputs: typeof output?.count === 'number' ? output.count : 0,
2642
- bytes: (typeof receipt?.bytes === 'number' ? receipt.bytes : 0) + (typeof output?.bytes === 'number' ? output.bytes : 0),
2643
- };
2644
- }
2645
- pruneReceiptCompactionSummaries(jobId, limit) {
2646
- this.db.prepare(`
454
+ `).get(e,O);return{receipts:typeof t?.count=="number"?t.count:0,outputs:typeof r?.count=="number"?r.count:0,bytes:(typeof t?.bytes=="number"?t.bytes:0)+(typeof r?.bytes=="number"?r.bytes:0)}}pruneReceiptCompactionSummaries(e,t){this.db.prepare(`
2647
455
  DELETE FROM job_events
2648
456
  WHERE event_id IN (
2649
457
  SELECT event_id
@@ -2652,69 +460,20 @@ export class ContinuitySqliteStore {
2652
460
  ORDER BY created_at DESC, event_id DESC
2653
461
  LIMIT -1 OFFSET ?
2654
462
  )
2655
- `).run(jobId, CONTINUITY_RECEIPT_COMPACTION_EVENT_TYPE, limit);
2656
- }
2657
- runInTransaction(fn) {
2658
- this.assertCanMutate();
2659
- const tx = this.db.transaction(fn);
2660
- try {
2661
- if (typeof tx.immediate === 'function')
2662
- return tx.immediate();
2663
- return tx();
2664
- }
2665
- catch (error) {
2666
- throw mapSqliteError(error, 'sqlite-mutation-failed');
2667
- }
2668
- }
2669
- collectSnapshotRows(options) {
2670
- const countRows = (sql, ...params) => {
2671
- const row = this.db.prepare(sql).get(...params);
2672
- return isRecord(row) && typeof row.count === 'number' ? row.count : 0;
2673
- };
2674
- const outboxRows = this.db.prepare(`
463
+ `).run(e,W,t)}runInTransaction(e){this.assertCanMutate();const t=this.db.transaction(e);try{return typeof t.immediate=="function"?t.immediate():t()}catch(r){throw m(r,"sqlite-mutation-failed")}}collectSnapshotRows(e){const t=(p,...x)=>{const A=this.db.prepare(p).get(...x);return E(A)&&typeof A.count=="number"?A.count:0},r=this.db.prepare(`
2675
464
  SELECT status, COUNT(*) AS count FROM outbox GROUP BY status
2676
- `).all();
2677
- const outboxCounts = {};
2678
- for (const row of outboxRows) {
2679
- if (typeof row.status === 'string' && typeof row.count === 'number')
2680
- outboxCounts[row.status] = row.count;
2681
- }
2682
- const queuePolicyRow = this.db.prepare(`
465
+ `).all(),s={};for(const p of r)typeof p.status=="string"&&typeof p.count=="number"&&(s[p.status]=p.count);const n=this.db.prepare(`
2683
466
  SELECT value_json
2684
467
  FROM runner_config
2685
468
  WHERE key = 'headless_queue_policy'
2686
469
  LIMIT 1
2687
- `).get();
2688
- let queuePolicy = DEFAULT_CONTINUITY_QUEUE_POLICY;
2689
- if (isRecord(queuePolicyRow) && typeof queuePolicyRow.value_json === 'string') {
2690
- try {
2691
- const parsed = JSON.parse(queuePolicyRow.value_json);
2692
- if (isRecord(parsed) && typeof parsed.maxItems === 'number') {
2693
- queuePolicy = normalizeContinuityQueuePolicy({ maxItems: parsed.maxItems });
2694
- }
2695
- }
2696
- catch {
2697
- // SQLite json_valid guards this row; retain bounded defaults on unexpected parse failure.
2698
- }
2699
- }
2700
- const queueQueued = countRows("SELECT COUNT(*) AS count FROM headless_execution_attempts WHERE status = 'queued' AND dead_lettered_at IS NULL");
2701
- const queueRetrying = countRows("SELECT COUNT(*) AS count FROM headless_execution_attempts WHERE status = 'backoff' AND dead_lettered_at IS NULL");
2702
- const queueRunning = countRows("SELECT COUNT(*) AS count FROM headless_execution_attempts WHERE status = 'running' AND dead_lettered_at IS NULL");
2703
- const queueDeadLetter = countRows('SELECT COUNT(*) AS count FROM headless_execution_attempts WHERE dead_lettered_at IS NOT NULL');
2704
- const queueDepth = queueQueued + queueRetrying + queueRunning;
2705
- const nextRetryRow = this.db.prepare(`
470
+ `).get();let d=De;if(E(n)&&typeof n.value_json=="string")try{const p=JSON.parse(n.value_json);E(p)&&typeof p.maxItems=="number"&&(d=B({maxItems:p.maxItems}))}catch{}const o=t("SELECT COUNT(*) AS count FROM headless_execution_attempts WHERE status = 'queued' AND dead_lettered_at IS NULL"),l=t("SELECT COUNT(*) AS count FROM headless_execution_attempts WHERE status = 'backoff' AND dead_lettered_at IS NULL"),I=t("SELECT COUNT(*) AS count FROM headless_execution_attempts WHERE status = 'running' AND dead_lettered_at IS NULL"),y=t("SELECT COUNT(*) AS count FROM headless_execution_attempts WHERE dead_lettered_at IS NOT NULL"),f=o+l+I,_=this.db.prepare(`
2706
471
  SELECT MIN(next_retry_at) AS next_retry_at
2707
472
  FROM headless_execution_attempts
2708
473
  WHERE status = 'backoff'
2709
474
  AND dead_lettered_at IS NULL
2710
475
  AND next_retry_at IS NOT NULL
2711
- `).get();
2712
- const nextRetryAt = isRecord(nextRetryRow) && typeof nextRetryRow.next_retry_at === 'string'
2713
- ? nextRetryRow.next_retry_at
2714
- : undefined;
2715
- return {
2716
- schemaVersion: this.getSchemaVersion(),
2717
- jobs: this.db.prepare(`
476
+ `).get(),u=E(_)&&typeof _.next_retry_at=="string"?_.next_retry_at:void 0;return{schemaVersion:this.getSchemaVersion(),jobs:this.db.prepare(`
2718
477
  SELECT job_id, kind, status, summary, reason_code, created_at, updated_at,
2719
478
  next_run_at, metadata_json,
2720
479
  (SELECT COUNT(*)
@@ -2764,35 +523,30 @@ export class ContinuitySqliteStore {
2764
523
  FROM jobs
2765
524
  ORDER BY updated_at DESC, job_id ASC
2766
525
  LIMIT ?
2767
- `).all(options.jobLimit),
2768
- staleLeases: this.db.prepare(`
526
+ `).all(e.jobLimit),staleLeases:this.db.prepare(`
2769
527
  SELECT lease_id, job_id, runner_id, expires_at
2770
528
  FROM job_leases
2771
529
  WHERE released_at IS NULL AND expires_at <= ?
2772
530
  ORDER BY expires_at ASC, lease_id ASC
2773
531
  LIMIT ?
2774
- `).all(options.nowIso, options.staleLeaseLimit),
2775
- receipts: this.db.prepare(`
532
+ `).all(e.nowIso,e.staleLeaseLimit),receipts:this.db.prepare(`
2776
533
  SELECT receipt_id, job_id, operation_id, status, reason_code, created_at, metadata_json
2777
534
  FROM job_receipts
2778
535
  ORDER BY created_at DESC, receipt_id ASC
2779
536
  LIMIT ?
2780
- `).all(options.receiptLimit),
2781
- events: this.db.prepare(`
537
+ `).all(e.receiptLimit),events:this.db.prepare(`
2782
538
  SELECT event_id, job_id, operation_id, type, summary, reason_code, created_at, metadata_json
2783
539
  FROM job_events
2784
540
  ORDER BY created_at DESC, event_id ASC
2785
541
  LIMIT ?
2786
- `).all(options.eventLimit),
2787
- stateSnapshots: this.db.prepare(`
542
+ `).all(e.eventLimit),stateSnapshots:this.db.prepare(`
2788
543
  SELECT snapshot_id, job_id, kind, relative_path, sha256, summary,
2789
544
  captured_at, operation_id, metadata_json
2790
545
  FROM state_snapshots
2791
546
  WHERE kind = 'context_compaction'
2792
547
  ORDER BY captured_at DESC, snapshot_id ASC
2793
548
  LIMIT ?
2794
- `).all(options.snapshotLimit),
2795
- headlessExecutionAttempts: this.db.prepare(`
549
+ `).all(e.snapshotLimit),headlessExecutionAttempts:this.db.prepare(`
2796
550
  SELECT attempt_id, execution_id, job_id, status, reason_code, runtime_id,
2797
551
  model_id, retry_count, max_attempts, lease_id, runner_id,
2798
552
  lease_expires_at, heartbeat_at, public_summary, enqueued_at,
@@ -2803,90 +557,19 @@ export class ContinuitySqliteStore {
2803
557
  FROM headless_execution_attempts
2804
558
  ORDER BY enqueued_at DESC, attempt_id ASC
2805
559
  LIMIT ?
2806
- `).all(options.jobLimit),
2807
- counts: {
2808
- jobs: countRows('SELECT COUNT(*) AS count FROM jobs'),
2809
- activeJobs: countRows("SELECT COUNT(*) AS count FROM jobs WHERE status IN ('planned', 'active', 'running', 'waiting_external', 'waiting_human', 'backoff', 'paused', 'blocked')"),
2810
- activeLeases: countRows('SELECT COUNT(*) AS count FROM job_leases WHERE released_at IS NULL'),
2811
- dueJobs: countRows("SELECT COUNT(*) AS count FROM jobs WHERE next_run_at IS NOT NULL AND next_run_at <= ? AND status NOT IN ('completed', 'aborted', 'cancelled')", options.nowIso),
2812
- staleLeases: countRows('SELECT COUNT(*) AS count FROM job_leases WHERE released_at IS NULL AND expires_at <= ?', options.nowIso),
2813
- receipts: countRows('SELECT COUNT(*) AS count FROM job_receipts'),
2814
- events: countRows('SELECT COUNT(*) AS count FROM job_events'),
2815
- processingOutputSummaries: countRows('SELECT COUNT(*) AS count FROM job_events WHERE type = ?', CONTINUITY_PROCESSING_OUTPUT_SUMMARY_EVENT_TYPE),
2816
- goalLedgerItems: countRows('SELECT COUNT(*) AS count FROM goal_ledger_items'),
2817
- goalVerifiers: countRows('SELECT COUNT(*) AS count FROM goal_verifiers'),
2818
- goalReviewGates: countRows('SELECT COUNT(*) AS count FROM goal_review_gates'),
2819
- stateSnapshots: countRows('SELECT COUNT(*) AS count FROM state_snapshots'),
2820
- headlessExecutionAttempts: countRows('SELECT COUNT(*) AS count FROM headless_execution_attempts'),
2821
- headlessQueuedAttempts: countRows("SELECT COUNT(*) AS count FROM headless_execution_attempts WHERE status IN ('queued', 'backoff')"),
2822
- headlessRunningAttempts: countRows("SELECT COUNT(*) AS count FROM headless_execution_attempts WHERE status = 'running'"),
2823
- headlessRetryingAttempts: queueRetrying,
2824
- headlessDeadLetterAttempts: queueDeadLetter,
2825
- staleHeadlessExecutionAttempts: countRows("SELECT COUNT(*) AS count FROM headless_execution_attempts WHERE status = 'running' AND lease_released_at IS NULL AND lease_expires_at <= ?", options.nowIso),
2826
- outbox: countRows('SELECT COUNT(*) AS count FROM outbox'),
2827
- },
2828
- outboxCounts,
2829
- queue: {
2830
- depth: queueDepth,
2831
- queued: queueQueued,
2832
- running: queueRunning,
2833
- retrying: queueRetrying,
2834
- deadLetter: queueDeadLetter,
2835
- capacity: queuePolicy.maxItems,
2836
- available: Math.max(0, queuePolicy.maxItems - queueDepth),
2837
- backpressure: queueDepth >= queuePolicy.maxItems,
2838
- generatedAt: options.nowIso,
2839
- ...(nextRetryAt ? { nextRetryAt } : {}),
2840
- ...(queueDepth >= queuePolicy.maxItems ? { reasonCode: CONTINUITY_QUEUE_REASON_CODES.CAP_REACHED } : {}),
2841
- publicSafe: true,
2842
- },
2843
- };
2844
- }
2845
- listProcessingOutputSummaries(options = {}) {
2846
- const limit = normalizeProcessingSummaryLimit(options.limit);
2847
- const targetPredicate = processingSummaryTargetPredicate(options);
2848
- const selectSql = `
560
+ `).all(e.jobLimit),counts:{jobs:t("SELECT COUNT(*) AS count FROM jobs"),activeJobs:t("SELECT COUNT(*) AS count FROM jobs WHERE status IN ('planned', 'active', 'running', 'waiting_external', 'waiting_human', 'backoff', 'paused', 'blocked')"),activeLeases:t("SELECT COUNT(*) AS count FROM job_leases WHERE released_at IS NULL"),dueJobs:t("SELECT COUNT(*) AS count FROM jobs WHERE next_run_at IS NOT NULL AND next_run_at <= ? AND status NOT IN ('completed', 'aborted', 'cancelled')",e.nowIso),staleLeases:t("SELECT COUNT(*) AS count FROM job_leases WHERE released_at IS NULL AND expires_at <= ?",e.nowIso),receipts:t("SELECT COUNT(*) AS count FROM job_receipts"),events:t("SELECT COUNT(*) AS count FROM job_events"),processingOutputSummaries:t("SELECT COUNT(*) AS count FROM job_events WHERE type = ?",O),goalLedgerItems:t("SELECT COUNT(*) AS count FROM goal_ledger_items"),goalVerifiers:t("SELECT COUNT(*) AS count FROM goal_verifiers"),goalReviewGates:t("SELECT COUNT(*) AS count FROM goal_review_gates"),stateSnapshots:t("SELECT COUNT(*) AS count FROM state_snapshots"),headlessExecutionAttempts:t("SELECT COUNT(*) AS count FROM headless_execution_attempts"),headlessQueuedAttempts:t("SELECT COUNT(*) AS count FROM headless_execution_attempts WHERE status IN ('queued', 'backoff')"),headlessRunningAttempts:t("SELECT COUNT(*) AS count FROM headless_execution_attempts WHERE status = 'running'"),headlessRetryingAttempts:l,headlessDeadLetterAttempts:y,staleHeadlessExecutionAttempts:t("SELECT COUNT(*) AS count FROM headless_execution_attempts WHERE status = 'running' AND lease_released_at IS NULL AND lease_expires_at <= ?",e.nowIso),outbox:t("SELECT COUNT(*) AS count FROM outbox")},outboxCounts:s,queue:{depth:f,queued:o,running:I,retrying:l,deadLetter:y,capacity:d.maxItems,available:Math.max(0,d.maxItems-f),backpressure:f>=d.maxItems,generatedAt:e.nowIso,...u?{nextRetryAt:u}:{},...f>=d.maxItems?{reasonCode:k.CAP_REACHED}:{},publicSafe:!0}}}listProcessingOutputSummaries(e={}){const t=ye(e.limit),r=pe(e),s=`
2849
561
  SELECT event_id, job_id, operation_id, summary, reason_code, created_at, metadata_json
2850
562
  FROM job_events
2851
- WHERE type = ?${targetPredicate.sql}
563
+ WHERE type = ?${r.sql}
2852
564
  ORDER BY created_at DESC, event_id DESC
2853
565
  LIMIT ?
2854
- `;
2855
- const rows = this.db.prepare(`SELECT * FROM (${selectSql}) ORDER BY created_at ASC, event_id ASC`).all(CONTINUITY_PROCESSING_OUTPUT_SUMMARY_EVENT_TYPE, ...targetPredicate.params, limit);
2856
- return rows.map(toProcessingOutputSummaryRow);
2857
- }
2858
- pollProcessingOutputSummaries(options = {}) {
2859
- const limit = normalizeProcessingSummaryLimit(options.limit);
2860
- const decodedCursor = decodeProcessingSummaryCursor(options.cursor);
2861
- const targetPredicate = processingSummaryTargetPredicate(options);
2862
- const cursorPredicate = decodedCursor
2863
- ? ' AND (created_at > ? OR (created_at = ? AND event_id > ?))'
2864
- : '';
2865
- const sql = `
566
+ `;return this.db.prepare(`SELECT * FROM (${s}) ORDER BY created_at ASC, event_id ASC`).all(O,...r.params,t).map(be)}pollProcessingOutputSummaries(e={}){const t=ye(e.limit),r=lt(e.cursor),s=pe(e),n=r?" AND (created_at > ? OR (created_at = ? AND event_id > ?))":"",d=`
2866
567
  SELECT event_id, job_id, operation_id, summary, reason_code, created_at, metadata_json
2867
568
  FROM job_events
2868
- WHERE type = ?${targetPredicate.sql}${cursorPredicate}
569
+ WHERE type = ?${s.sql}${n}
2869
570
  ORDER BY created_at ASC, event_id ASC
2870
571
  LIMIT ?
2871
- `;
2872
- const params = [CONTINUITY_PROCESSING_OUTPUT_SUMMARY_EVENT_TYPE];
2873
- params.push(...targetPredicate.params);
2874
- if (decodedCursor)
2875
- params.push(decodedCursor.createdAt, decodedCursor.createdAt, decodedCursor.eventId);
2876
- params.push(limit + 1);
2877
- const rows = this.db.prepare(sql).all(...params);
2878
- const summaries = rows.slice(0, limit).map(toProcessingOutputSummaryRow);
2879
- const last = summaries[summaries.length - 1];
2880
- return {
2881
- summaries,
2882
- ...(last ? { nextCursor: last.cursor } : options.cursor ? { nextCursor: options.cursor } : {}),
2883
- hasMore: rows.length > limit,
2884
- publicSafe: true,
2885
- };
2886
- }
2887
- listDueJobs(options) {
2888
- const limit = Math.max(1, Math.min(50, Math.floor(options.limit)));
2889
- return this.db.prepare(`
572
+ `,o=[O];o.push(...s.params),r&&o.push(r.createdAt,r.createdAt,r.eventId),o.push(t+1);const l=this.db.prepare(d).all(...o),I=l.slice(0,t).map(be),y=I[I.length-1];return{summaries:I,...y?{nextCursor:y.cursor}:e.cursor?{nextCursor:e.cursor}:{},hasMore:l.length>t,publicSafe:!0}}listDueJobs(e){const t=Math.max(1,Math.min(50,Math.floor(e.limit)));return this.db.prepare(`
2890
573
  SELECT j.job_id, j.kind, j.status, j.summary, j.reason_code, j.created_at,
2891
574
  j.updated_at, j.next_run_at, j.metadata_json,
2892
575
  (SELECT COUNT(*)
@@ -2904,63 +587,19 @@ export class ContinuitySqliteStore {
2904
587
  AND j.status NOT IN ('waiting_external', 'waiting_human', 'paused', 'blocked', 'completed', 'aborted', 'cancelled')
2905
588
  ORDER BY j.next_run_at ASC, j.job_id ASC
2906
589
  LIMIT ?
2907
- `).all(options.nowIso, limit);
2908
- }
2909
- recoverStaleLeases(options) {
2910
- this.assertCanMutate();
2911
- const nowMs = Date.parse(options.nowIso);
2912
- if (!Number.isFinite(nowMs)) {
2913
- throw new ContinuitySqliteStoreError('sqlite-public-safety-rejected', 'Continuity SQLite stale lease recovery received an invalid timestamp.');
2914
- }
2915
- const staleAfterMs = Math.max(0, Math.floor(options.staleAfterMs));
2916
- const limit = Math.max(1, Math.min(100, Math.floor(options.limit)));
2917
- const safeExpiredAtOrBefore = new Date(nowMs - staleAfterMs).toISOString();
2918
- try {
2919
- return this.runInTransaction(() => {
2920
- const rows = this.db.prepare(`
590
+ `).all(e.nowIso,t)}recoverStaleLeases(e){this.assertCanMutate();const t=Date.parse(e.nowIso);if(!Number.isFinite(t))throw new i("sqlite-public-safety-rejected","Continuity SQLite stale lease recovery received an invalid timestamp.");const r=Math.max(0,Math.floor(e.staleAfterMs)),s=Math.max(1,Math.min(100,Math.floor(e.limit))),n=new Date(t-r).toISOString();try{return this.runInTransaction(()=>{const o=this.db.prepare(`
2921
591
  SELECT lease_id
2922
592
  FROM job_leases
2923
593
  WHERE released_at IS NULL
2924
594
  AND expires_at <= ?
2925
595
  ORDER BY expires_at ASC, lease_id ASC
2926
596
  LIMIT ?
2927
- `).all(safeExpiredAtOrBefore, limit);
2928
- const recoveredLeaseIds = rows
2929
- .map((row) => row.lease_id)
2930
- .filter((leaseId) => typeof leaseId === 'string' && leaseId.length > 0);
2931
- if (recoveredLeaseIds.length === 0) {
2932
- return { recoveredLeaseIds: [], safeExpiredAtOrBefore, changes: 0 };
2933
- }
2934
- const placeholders = recoveredLeaseIds.map(() => '?').join(', ');
2935
- const result = this.db.prepare(`
597
+ `).all(n,s).map(y=>y.lease_id).filter(y=>typeof y=="string"&&y.length>0);if(o.length===0)return{recoveredLeaseIds:[],safeExpiredAtOrBefore:n,changes:0};const l=o.map(()=>"?").join(", "),I=this.db.prepare(`
2936
598
  UPDATE job_leases
2937
599
  SET status = 'expired', released_at = ?
2938
600
  WHERE released_at IS NULL
2939
- AND lease_id IN (${placeholders})
2940
- `).run(options.nowIso, ...recoveredLeaseIds);
2941
- return {
2942
- recoveredLeaseIds,
2943
- safeExpiredAtOrBefore,
2944
- changes: result.changes,
2945
- };
2946
- });
2947
- }
2948
- catch (error) {
2949
- throw mapSqliteError(error, 'sqlite-mutation-failed');
2950
- }
2951
- }
2952
- recoverStaleHeadlessExecutionAttempts(options) {
2953
- this.assertCanMutate();
2954
- const nowMs = Date.parse(options.nowIso);
2955
- if (!Number.isFinite(nowMs)) {
2956
- throw new ContinuitySqliteStoreError('sqlite-public-safety-rejected', 'Continuity SQLite headless stale recovery received an invalid timestamp.');
2957
- }
2958
- const staleAfterMs = Math.max(0, Math.floor(options.staleAfterMs));
2959
- const limit = Math.max(1, Math.min(100, Math.floor(options.limit)));
2960
- const safeExpiredAtOrBefore = new Date(nowMs - staleAfterMs).toISOString();
2961
- try {
2962
- return this.runInTransaction(() => {
2963
- const rows = this.db.prepare(`
601
+ AND lease_id IN (${l})
602
+ `).run(e.nowIso,...o);return{recoveredLeaseIds:o,safeExpiredAtOrBefore:n,changes:I.changes}})}catch(d){throw m(d,"sqlite-mutation-failed")}}recoverStaleHeadlessExecutionAttempts(e){this.assertCanMutate();const t=Date.parse(e.nowIso);if(!Number.isFinite(t))throw new i("sqlite-public-safety-rejected","Continuity SQLite headless stale recovery received an invalid timestamp.");const r=Math.max(0,Math.floor(e.staleAfterMs)),s=Math.max(1,Math.min(100,Math.floor(e.limit))),n=new Date(t-r).toISOString();try{return this.runInTransaction(()=>{const d=this.db.prepare(`
2964
603
  SELECT attempt_id, retry_count, max_attempts, attempt_count
2965
604
  FROM headless_execution_attempts
2966
605
  WHERE status = 'running'
@@ -2968,30 +607,7 @@ export class ContinuitySqliteStore {
2968
607
  AND lease_expires_at <= ?
2969
608
  ORDER BY lease_expires_at ASC, attempt_id ASC
2970
609
  LIMIT ?
2971
- `).all(safeExpiredAtOrBefore, limit);
2972
- const retryableAttempts = [];
2973
- const deadLetteredAttemptIds = [];
2974
- const policy = normalizeContinuityQueuePolicy(options.policy);
2975
- for (const row of rows) {
2976
- if (typeof row.attempt_id !== 'string' || row.attempt_id.length === 0)
2977
- continue;
2978
- const retryCount = typeof row.retry_count === 'number' ? row.retry_count : 0;
2979
- const maxAttempts = typeof row.max_attempts === 'number' ? row.max_attempts : 1;
2980
- const attemptCount = typeof row.attempt_count === 'number' ? row.attempt_count : 0;
2981
- const consumedAttempts = Math.max(attemptCount, retryCount + 1);
2982
- if (consumedAttempts >= maxAttempts) {
2983
- deadLetteredAttemptIds.push(row.attempt_id);
2984
- }
2985
- else {
2986
- retryableAttempts.push({
2987
- attemptId: row.attempt_id,
2988
- nextRetryAt: computeContinuityQueueRetryAt(options.nowIso, retryCount + 1, policy),
2989
- });
2990
- }
2991
- }
2992
- let changes = 0;
2993
- for (const retryable of retryableAttempts) {
2994
- changes += this.db.prepare(`
610
+ `).all(n,s),o=[],l=[],I=B(e.policy);for(const _ of d){if(typeof _.attempt_id!="string"||_.attempt_id.length===0)continue;const u=typeof _.retry_count=="number"?_.retry_count:0,p=typeof _.max_attempts=="number"?_.max_attempts:1,x=typeof _.attempt_count=="number"?_.attempt_count:0;Math.max(x,u+1)>=p?l.push(_.attempt_id):o.push({attemptId:_.attempt_id,nextRetryAt:Ue(e.nowIso,u+1,I)})}let y=0;for(const _ of o)y+=this.db.prepare(`
2995
611
  UPDATE headless_execution_attempts
2996
612
  SET status = 'backoff',
2997
613
  retry_count = retry_count + 1,
@@ -3002,11 +618,7 @@ export class ContinuitySqliteStore {
3002
618
  WHERE attempt_id = ?
3003
619
  AND status = 'running'
3004
620
  AND lease_released_at IS NULL
3005
- `).run(CONTINUITY_QUEUE_REASON_CODES.LEASE_TIMEOUT_RETRY, options.nowIso, retryable.nextRetryAt, retryable.nextRetryAt, retryable.attemptId).changes;
3006
- }
3007
- if (deadLetteredAttemptIds.length > 0) {
3008
- const placeholders = deadLetteredAttemptIds.map(() => '?').join(', ');
3009
- changes += this.db.prepare(`
621
+ `).run(k.LEASE_TIMEOUT_RETRY,e.nowIso,_.nextRetryAt,_.nextRetryAt,_.attemptId).changes;if(l.length>0){const _=l.map(()=>"?").join(", ");y+=this.db.prepare(`
3010
622
  UPDATE headless_execution_attempts
3011
623
  SET status = 'failed',
3012
624
  retry_count = retry_count + 1,
@@ -3016,85 +628,18 @@ export class ContinuitySqliteStore {
3016
628
  dead_lettered_at = ?,
3017
629
  dead_letter_reason_code = ?,
3018
630
  next_retry_at = NULL
3019
- WHERE attempt_id IN (${placeholders})
631
+ WHERE attempt_id IN (${_})
3020
632
  AND status = 'running'
3021
633
  AND lease_released_at IS NULL
3022
- `).run(CONTINUITY_QUEUE_REASON_CODES.ATTEMPTS_EXHAUSTED, options.nowIso, options.nowIso, options.nowIso, CONTINUITY_QUEUE_REASON_CODES.ATTEMPTS_EXHAUSTED, ...deadLetteredAttemptIds).changes;
3023
- }
3024
- const retryingAttemptIds = retryableAttempts.map((attempt) => attempt.attemptId);
3025
- return {
3026
- recoveredAttemptIds: [...retryingAttemptIds, ...deadLetteredAttemptIds],
3027
- failedAttemptIds: deadLetteredAttemptIds,
3028
- retryingAttemptIds,
3029
- deadLetteredAttemptIds,
3030
- safeExpiredAtOrBefore,
3031
- changes,
3032
- publicSafe: true,
3033
- };
3034
- });
3035
- }
3036
- catch (error) {
3037
- throw mapSqliteError(error, 'sqlite-mutation-failed');
3038
- }
3039
- }
3040
- ensureGoalCampaign(campaignId, jobId, createdAt, updatedAt) {
3041
- this.db.prepare(`
634
+ `).run(k.ATTEMPTS_EXHAUSTED,e.nowIso,e.nowIso,e.nowIso,k.ATTEMPTS_EXHAUSTED,...l).changes}const f=o.map(_=>_.attemptId);return{recoveredAttemptIds:[...f,...l],failedAttemptIds:l,retryingAttemptIds:f,deadLetteredAttemptIds:l,safeExpiredAtOrBefore:n,changes:y,publicSafe:!0}})}catch(d){throw m(d,"sqlite-mutation-failed")}}ensureGoalCampaign(e,t,r,s){this.db.prepare(`
3042
635
  INSERT OR IGNORE INTO goal_campaigns (
3043
636
  campaign_id, job_id, status, public_summary, created_at, updated_at
3044
637
  ) VALUES (?, ?, 'active', 'Public goal campaign summary unavailable', ?, ?)
3045
- `).run(campaignId, jobId, createdAt, updatedAt);
3046
- }
3047
- configureConnection() {
3048
- const journalMode = String(asSinglePragmaValue(this.db.pragma('journal_mode = WAL', { simple: true })) ?? '').toLowerCase();
3049
- if (journalMode !== 'wal') {
3050
- throw new ContinuitySqliteStoreError('sqlite-migration-failed', `Continuity SQLite journal_mode did not switch to WAL (observed ${journalMode || 'unknown'}).`);
3051
- }
3052
- this.db.pragma('foreign_keys = ON');
3053
- this.db.pragma(`busy_timeout = ${this.busyTimeoutMs}`);
3054
- const foreignKeys = Number(asSinglePragmaValue(this.db.pragma('foreign_keys', { simple: true })) ?? 0);
3055
- if (foreignKeys !== 1) {
3056
- throw new ContinuitySqliteStoreError('sqlite-migration-failed', 'Continuity SQLite foreign_keys pragma is not enabled.');
3057
- }
3058
- }
3059
- ensureMigrationTable() {
3060
- this.db.exec(`
638
+ `).run(e,t,r,s)}configureConnection(){const e=String(w(this.db.pragma("journal_mode = WAL",{simple:!0}))??"").toLowerCase();if(e!=="wal")throw new i("sqlite-migration-failed",`Continuity SQLite journal_mode did not switch to WAL (observed ${e||"unknown"}).`);if(this.db.pragma("foreign_keys = ON"),this.db.pragma(`busy_timeout = ${this.busyTimeoutMs}`),Number(w(this.db.pragma("foreign_keys",{simple:!0}))??0)!==1)throw new i("sqlite-migration-failed","Continuity SQLite foreign_keys pragma is not enabled.")}ensureMigrationTable(){this.db.exec(`
3061
639
  CREATE TABLE IF NOT EXISTS continuity_migrations (
3062
640
  version INTEGER PRIMARY KEY,
3063
641
  name TEXT NOT NULL,
3064
642
  checksum TEXT NOT NULL,
3065
643
  applied_at TEXT NOT NULL
3066
644
  );
3067
- `);
3068
- }
3069
- applyMigrations() {
3070
- const apply = this.db.transaction(() => {
3071
- for (const migration of CONTINUITY_MIGRATIONS) {
3072
- const checksum = checksumMigration(migration.sql);
3073
- const existing = this.db.prepare('SELECT version, name, checksum FROM continuity_migrations WHERE version = ?').get(migration.version);
3074
- if (existing) {
3075
- if (existing.checksum !== checksum || existing.name !== migration.name) {
3076
- throw new ContinuitySqliteStoreError('sqlite-migration-conflict', `Continuity SQLite migration ${migration.version} checksum conflict.`);
3077
- }
3078
- continue;
3079
- }
3080
- this.db.exec(migration.sql);
3081
- this.db.prepare('INSERT INTO continuity_migrations (version, name, checksum, applied_at) VALUES (?, ?, ?, ?)').run(migration.version, migration.name, checksum, this.now());
3082
- this.db.pragma(`user_version = ${migration.version}`);
3083
- }
3084
- });
3085
- apply();
3086
- }
3087
- assertCanMutate() {
3088
- if (this.mutationBlocked) {
3089
- throw new ContinuitySqliteStoreError('sqlite-mutation-blocked', 'Continuity SQLite mutation is blocked after integrity failure.', { mutationBlocked: true });
3090
- }
3091
- }
3092
- toWriteResult(result) {
3093
- const inserted = result.changes > 0;
3094
- return {
3095
- inserted,
3096
- replay: !inserted,
3097
- changes: result.changes,
3098
- };
3099
- }
3100
- }
645
+ `)}applyMigrations(){this.db.transaction(()=>{for(const t of Je){const r=st(t.sql),s=this.db.prepare("SELECT version, name, checksum FROM continuity_migrations WHERE version = ?").get(t.version);if(s){if(s.checksum!==r||s.name!==t.name)throw new i("sqlite-migration-conflict",`Continuity SQLite migration ${t.version} checksum conflict.`);continue}this.db.exec(t.sql),this.db.prepare("INSERT INTO continuity_migrations (version, name, checksum, applied_at) VALUES (?, ?, ?, ?)").run(t.version,t.name,r,this.now()),this.db.pragma(`user_version = ${t.version}`)}})()}assertCanMutate(){if(this.mutationBlocked)throw new i("sqlite-mutation-blocked","Continuity SQLite mutation is blocked after integrity failure.",{mutationBlocked:!0})}toWriteResult(e){const t=e.changes>0;return{inserted:t,replay:!t,changes:e.changes}}}export{O as CONTINUITY_PROCESSING_OUTPUT_SUMMARY_EVENT_TYPE,Be as CONTINUITY_PROCESSING_OUTPUT_SUMMARY_MAX_CHARS,Ye as CONTINUITY_PUBLIC_STAGES,We as CONTINUITY_SQLITE_FILENAME,ne as CONTINUITY_SQLITE_RELATIVE_DIR,kt as CONTINUITY_SQLITE_SCHEMA_VERSION,ce as CONTINUITY_STAGE_HISTORY_MAX,ft as ContinuitySqliteStore,i as ContinuitySqliteStoreError,de as DEFAULT_CONTINUITY_PROCESSING_SUMMARY_LIMIT,Fe as DEFAULT_CONTINUITY_SQLITE_BUSY_TIMEOUT_MS,$e as MAX_CONTINUITY_PROCESSING_SUMMARY_LIMIT,ie as SQLITE_ADAPTER_INSTALL_GUIDANCE,Ee as continuitySqliteStorePath,le as isContinuityPublicStage,It as loadBetterSqlite3Adapter,Qe as normalizeContinuityStageIteration,Ut as openContinuitySqliteReadOnly,Dt as openContinuitySqliteStore,Ge as sanitizeContinuityStageHistory,ue as sanitizeContinuityStageTimestamp};