@ornexus/neocortex 4.60.73 → 4.60.75

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 (269) hide show
  1. package/dist/release-source.json +1 -1
  2. package/dist/sbom.cdx.json +1216 -1210
  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.d.ts +27 -0
  22. package/packages/client/dist/cache/encrypted-cache.js +1 -108
  23. package/packages/client/dist/cache/in-memory-asset-cache.js +1 -73
  24. package/packages/client/dist/cache/index.js +1 -13
  25. package/packages/client/dist/cache/protected-pi-boundary.js +1 -44
  26. package/packages/client/dist/checkpoint/checkpoint-client-reader.js +2 -127
  27. package/packages/client/dist/checkpoint/index.js +1 -11
  28. package/packages/client/dist/checkpoint/shared-checkpoint-types.js +1 -32
  29. package/packages/client/dist/cli.js +5 -358
  30. package/packages/client/dist/commands/activate.js +8 -394
  31. package/packages/client/dist/commands/architecture-answers.js +7 -274
  32. package/packages/client/dist/commands/architecture-memory-terminal.js +4 -253
  33. package/packages/client/dist/commands/architecture-non-ui-continuation.js +1 -100
  34. package/packages/client/dist/commands/architecture-root-binding.js +1 -51
  35. package/packages/client/dist/commands/cache-status.js +2 -112
  36. package/packages/client/dist/commands/config.js +1 -88
  37. package/packages/client/dist/commands/doctor.js +2 -635
  38. package/packages/client/dist/commands/invoke.d.ts +38 -1
  39. package/packages/client/dist/commands/invoke.js +143 -7200
  40. package/packages/client/dist/commands/legacy-epic-terminal.js +1 -71
  41. package/packages/client/dist/commands/legacy-story-branch.js +1 -96
  42. package/packages/client/dist/commands/legacy-story-pr.js +1 -276
  43. package/packages/client/dist/commands/mutation-qa-checkpoint.js +5 -285
  44. package/packages/client/dist/commands/mutation-qa-gate.js +1 -54
  45. package/packages/client/dist/commands/mutation-qa-git.js +1 -41
  46. package/packages/client/dist/commands/mutation-qa-policy.js +1 -102
  47. package/packages/client/dist/commands/mutation-qa-run.js +3 -219
  48. package/packages/client/dist/commands/mutation-qa-verdict.js +1 -149
  49. package/packages/client/dist/commands/mutation-qa.js +1 -220
  50. package/packages/client/dist/commands/native-agent.js +2 -136
  51. package/packages/client/dist/commands/planning-diagnosis.js +1 -43
  52. package/packages/client/dist/commands/planning-generation.js +1 -53
  53. package/packages/client/dist/commands/planning-memory.js +1 -40
  54. package/packages/client/dist/commands/planning-research.js +1 -42
  55. package/packages/client/dist/commands/private-session.js +1 -75
  56. package/packages/client/dist/commands/property-qa.js +1 -139
  57. package/packages/client/dist/commands/qa-control.js +1 -16
  58. package/packages/client/dist/commands/qa-scan-browser-launch.js +1 -55
  59. package/packages/client/dist/commands/qa-scan-browser-route.js +1 -28
  60. package/packages/client/dist/commands/qa-scan-capture.js +5 -663
  61. package/packages/client/dist/commands/qa-scan-discovery.js +3 -376
  62. package/packages/client/dist/commands/qa-scan-review.js +5 -826
  63. package/packages/client/dist/commands/qa-synthetic-backend-capture.js +1 -266
  64. package/packages/client/dist/commands/qa-synthetic-backend-review.js +1 -297
  65. package/packages/client/dist/commands/qa-synthetic-backend.js +2 -340
  66. package/packages/client/dist/commands/qa-synthetic-broker-port.js +1 -85
  67. package/packages/client/dist/commands/qa-synthetic-browser.js +1 -39
  68. package/packages/client/dist/commands/refactor-characterization.js +21 -671
  69. package/packages/client/dist/commands/refactor-commit.js +13 -399
  70. package/packages/client/dist/commands/refactor-control.js +3 -142
  71. package/packages/client/dist/commands/refactor-cpp-assertions.js +3 -93
  72. package/packages/client/dist/commands/refactor-review.js +5 -283
  73. package/packages/client/dist/commands/refactor-root.js +7 -588
  74. package/packages/client/dist/commands/refresh-memory.js +3 -156
  75. package/packages/client/dist/commands/shared-architecture-non-ui.js +1 -24
  76. package/packages/client/dist/commands/story-checkpoint.js +1 -171
  77. package/packages/client/dist/config/host-auth-broker.d.ts +31 -0
  78. package/packages/client/dist/config/host-auth-broker.js +1 -0
  79. package/packages/client/dist/config/host-auth-reference.d.ts +32 -0
  80. package/packages/client/dist/config/host-auth-reference.js +1 -0
  81. package/packages/client/dist/config/profile-directory.js +1 -13
  82. package/packages/client/dist/config/resolver-selection.js +1 -278
  83. package/packages/client/dist/config/secure-config.js +12 -281
  84. package/packages/client/dist/config.js +2 -223
  85. package/packages/client/dist/constants.js +1 -25
  86. package/packages/client/dist/context/context-collector.js +2 -222
  87. package/packages/client/dist/context/context-sanitizer.js +1 -145
  88. package/packages/client/dist/continuity/autonomous-governance.js +1 -285
  89. package/packages/client/dist/continuity/continuity-client-state-store.js +1 -1587
  90. package/packages/client/dist/continuity/invoke-hooks.js +1 -129
  91. package/packages/client/dist/continuity/migrations/001-initial-schema.js +2 -12
  92. package/packages/client/dist/continuity/migrations/002-processing-output-summary-indexes.js +2 -12
  93. package/packages/client/dist/continuity/migrations/003-headless-execution-attempts.js +2 -12
  94. package/packages/client/dist/continuity/migrations/004-outbox-delivery-state.js +2 -12
  95. package/packages/client/dist/continuity/migrations/005-bounded-headless-queue.js +2 -12
  96. package/packages/client/dist/continuity/migrations/006-native-agent-dispatches.js +2 -10
  97. package/packages/client/dist/continuity/native-agent-store.js +1 -69
  98. package/packages/client/dist/continuity/native-agent-types.d.ts +11 -0
  99. package/packages/client/dist/continuity/native-agent-types.js +1 -90
  100. package/packages/client/dist/continuity/outbox.js +1 -117
  101. package/packages/client/dist/continuity/runner-bootstrap-policy.js +1 -172
  102. package/packages/client/dist/continuity/sqlite-store.js +81 -2612
  103. package/packages/client/dist/effects/invoke-effect-gateways.js +1 -62
  104. package/packages/client/dist/errors/error-messages.d.ts +3 -0
  105. package/packages/client/dist/errors/error-messages.js +2 -228
  106. package/packages/client/dist/errors/frontmatter-diagnostics.js +1 -125
  107. package/packages/client/dist/evidence/collector.js +1 -347
  108. package/packages/client/dist/evidence/index.js +1 -1
  109. package/packages/client/dist/evidence/types.js +0 -2
  110. package/packages/client/dist/fs/atomic-state-replace.js +1 -152
  111. package/packages/client/dist/generated/command-registry.generated.js +1 -4076
  112. package/packages/client/dist/generated/public-projections.generated.js +1 -149
  113. package/packages/client/dist/git/direct-yolo-c01-retry.js +4 -138
  114. package/packages/client/dist/git/direct-yolo-checkpoint.d.ts +11 -1
  115. package/packages/client/dist/git/direct-yolo-checkpoint.js +4 -1234
  116. package/packages/client/dist/git/epic-operational-state.js +2 -672
  117. package/packages/client/dist/git/git-lifecycle-step-ids.js +1 -14
  118. package/packages/client/dist/git/git-lifecycle-store.d.ts +23 -0
  119. package/packages/client/dist/git/git-lifecycle-store.js +10 -1981
  120. package/packages/client/dist/git/legacy-story-branch-checkpoint.js +9 -259
  121. package/packages/client/dist/git/legacy-story-branch-setup.js +4 -348
  122. package/packages/client/dist/git/legacy-story-pr.js +70 -3538
  123. package/packages/client/dist/git/secure-filesystem-boundary.js +5 -182
  124. package/packages/client/dist/graph-retrieval/pre-command-hook.js +1 -284
  125. package/packages/client/dist/graph-retrieval/shared-graph-retrieval-contract.js +1 -237
  126. package/packages/client/dist/i18n/first-run.js +2 -60
  127. package/packages/client/dist/index.d.ts +2 -0
  128. package/packages/client/dist/index.js +1 -47
  129. package/packages/client/dist/invocation/invoke-input.js +1 -24
  130. package/packages/client/dist/license/host-auth-recovery.d.ts +158 -0
  131. package/packages/client/dist/license/host-auth-recovery.js +1 -0
  132. package/packages/client/dist/license/index.js +1 -5
  133. package/packages/client/dist/license/license-client.d.ts +10 -1
  134. package/packages/client/dist/license/license-client.js +1 -304
  135. package/packages/client/dist/machine/fingerprint.js +2 -163
  136. package/packages/client/dist/machine/index.js +1 -5
  137. package/packages/client/dist/memory/project-memory-path-scope.js +1 -62
  138. package/packages/client/dist/memory/project-memory-writer.js +40 -1007
  139. package/packages/client/dist/memory/shared-project-memory-types.js +2 -253
  140. package/packages/client/dist/migrations/epic-lifecycle-backfill.js +3 -1060
  141. package/packages/client/dist/migrations/project-identity-capability.js +1 -50
  142. package/packages/client/dist/migrations/project-identity.js +7 -493
  143. package/packages/client/dist/migrations/state-json.js +4 -393
  144. package/packages/client/dist/policy/architecture-policy.js +2 -184
  145. package/packages/client/dist/policy/index.js +1 -7
  146. package/packages/client/dist/policy/shared-policy-types.js +0 -38
  147. package/packages/client/dist/policy/subagent-approval-policy.js +1 -55
  148. package/packages/client/dist/private-execution/clean-baseline.js +2 -279
  149. package/packages/client/dist/private-execution/codex-executor.js +4 -574
  150. package/packages/client/dist/private-execution/codex-host-runtime.js +1 -137
  151. package/packages/client/dist/private-execution/codex-lifecycle-guard.js +1 -97
  152. package/packages/client/dist/private-execution/codex-scoped-permissions.js +1 -128
  153. package/packages/client/dist/private-execution/delivery.js +13 -410
  154. package/packages/client/dist/private-execution/diagnosis-root-review.js +2 -294
  155. package/packages/client/dist/private-execution/frozen-handoff.js +6 -19
  156. package/packages/client/dist/private-execution/implementation-validation.js +2 -106
  157. package/packages/client/dist/private-execution/isolated-admission.js +23 -1432
  158. package/packages/client/dist/private-execution/isolated-session.js +5 -725
  159. package/packages/client/dist/private-execution/isolated-workspace.js +1 -122
  160. package/packages/client/dist/private-execution/pact-consumer-runtime.js +3 -409
  161. package/packages/client/dist/private-execution/pact-test-source.js +1 -258
  162. package/packages/client/dist/private-execution/pact-toolchain.js +3 -290
  163. package/packages/client/dist/private-execution/pact-write-set.js +1 -172
  164. package/packages/client/dist/private-execution/planning-diagnosis-checkpoint.js +5 -406
  165. package/packages/client/dist/private-execution/planning-generation-checkpoint.js +5 -462
  166. package/packages/client/dist/private-execution/planning-memory-checkpoint.js +3 -139
  167. package/packages/client/dist/private-execution/planning-research-checkpoint.js +4 -325
  168. package/packages/client/dist/private-execution/private-report-store.js +1 -42
  169. package/packages/client/dist/private-execution/property-test-candidate.js +1 -103
  170. package/packages/client/dist/private-execution/property-test-choices.js +3 -147
  171. package/packages/client/dist/private-execution/property-test-plan.js +7 -271
  172. package/packages/client/dist/private-execution/property-test-review.js +4 -258
  173. package/packages/client/dist/private-execution/property-test-runtime.js +9 -372
  174. package/packages/client/dist/private-execution/property-test-source.js +1 -139
  175. package/packages/client/dist/private-execution/root-checkpoint.js +1 -80
  176. package/packages/client/dist/private-execution/root-memory-git-evidence.js +3 -135
  177. package/packages/client/dist/private-execution/root-memory-recovery.js +1 -93
  178. package/packages/client/dist/private-execution/root-memory-request.js +1 -29
  179. package/packages/client/dist/private-execution/root-memory-scope.js +1 -178
  180. package/packages/client/dist/private-execution/root-memory-writer.js +1 -102
  181. package/packages/client/dist/private-execution/session-lease.js +1 -63
  182. package/packages/client/dist/private-execution/shared-private-admission-types.js +1 -50
  183. package/packages/client/dist/private-execution/shared-project-validation.js +1 -66
  184. package/packages/client/dist/private-execution/shared-root-memory-types.js +1 -61
  185. package/packages/client/dist/private-execution/ux-source-validation.js +1 -372
  186. package/packages/client/dist/private-execution/ux-write-set.js +2 -191
  187. package/packages/client/dist/registry/registry-reconciliation.js +1 -234
  188. package/packages/client/dist/resilience/circuit-breaker.js +1 -170
  189. package/packages/client/dist/resilience/degradation-manager.js +1 -164
  190. package/packages/client/dist/resilience/freshness-indicator.js +1 -100
  191. package/packages/client/dist/resilience/index.js +1 -8
  192. package/packages/client/dist/resilience/recovery-detector.js +1 -74
  193. package/packages/client/dist/resolvers/asset-resolver.js +0 -13
  194. package/packages/client/dist/resolvers/local-resolver.js +8 -255
  195. package/packages/client/dist/resolvers/remote-resolver.js +1 -233
  196. package/packages/client/dist/runner/binary.js +1 -404
  197. package/packages/client/dist/runner/cli.js +17 -1571
  198. package/packages/client/dist/runner/epic-background-dispatcher.d.ts +61 -0
  199. package/packages/client/dist/runner/epic-background-dispatcher.js +1 -0
  200. package/packages/client/dist/runner/headless/adapters.js +1 -49
  201. package/packages/client/dist/runner/headless/background-root.d.ts +65 -0
  202. package/packages/client/dist/runner/headless/background-root.js +1 -0
  203. package/packages/client/dist/runner/headless/claude-code-adapter.js +1 -79
  204. package/packages/client/dist/runner/headless/continuous-story-workspace.js +11 -1628
  205. package/packages/client/dist/runner/headless/epic-git-reconciliation-inventory.js +1 -69
  206. package/packages/client/dist/runner/headless/epic-merge-candidate-qa.d.ts +1 -1
  207. package/packages/client/dist/runner/headless/epic-merge-candidate-qa.js +2 -110
  208. package/packages/client/dist/runner/headless/epic-terminal-git-cli-adapter.d.ts +2 -0
  209. package/packages/client/dist/runner/headless/epic-terminal-git-cli-adapter.js +10 -1236
  210. package/packages/client/dist/runner/headless/epic-terminal-git-runner.d.ts +27 -2
  211. package/packages/client/dist/runner/headless/epic-terminal-git-runner.js +2 -318
  212. package/packages/client/dist/runner/headless/fake-adapter.js +1 -62
  213. package/packages/client/dist/runner/headless/managed-environment-cleanup.d.ts +95 -0
  214. package/packages/client/dist/runner/headless/managed-environment-cleanup.js +1 -0
  215. package/packages/client/dist/runner/headless/managed-profile.js +1 -132
  216. package/packages/client/dist/runner/headless/opencode-adapter.js +2 -145
  217. package/packages/client/dist/runner/headless/prepared-story-bundle.js +1 -43
  218. package/packages/client/dist/runner/headless/resolver.js +1 -231
  219. package/packages/client/dist/runner/headless/run-yolo.js +1 -885
  220. package/packages/client/dist/runner/headless/run-yoloop.d.ts +1 -0
  221. package/packages/client/dist/runner/headless/run-yoloop.js +2 -2574
  222. package/packages/client/dist/runner/headless/types.js +2 -574
  223. package/packages/client/dist/runner/local-console.js +13 -396
  224. package/packages/client/dist/runner/portal-sync-worker.js +1 -1487
  225. package/packages/client/dist/runner/project-identity.js +1 -106
  226. package/packages/client/dist/runner/scheduler.js +7 -995
  227. package/packages/client/dist/runner-cli.js +2 -18
  228. package/packages/client/dist/state/invocation-shape.js +1 -13
  229. package/packages/client/dist/state/project-state-snapshot.d.ts +7 -0
  230. package/packages/client/dist/state/project-state-snapshot.js +1 -1731
  231. package/packages/client/dist/state/state-json-repair.js +3 -785
  232. package/packages/client/dist/state/structural-dag-projection.js +1 -118
  233. package/packages/client/dist/telemetry/index.js +1 -5
  234. package/packages/client/dist/telemetry/offline-queue.js +1 -131
  235. package/packages/client/dist/tier/index.js +1 -5
  236. package/packages/client/dist/tier/tier-aware-client.js +1 -405
  237. package/packages/client/dist/types/index.js +1 -38
  238. package/packages/client/dist/version.js +1 -27
  239. package/packages/client/dist/yolo/change-evidence-collector.d.ts +21 -1
  240. package/packages/client/dist/yolo/change-evidence-collector.js +1 -126
  241. package/packages/client/dist/yolo/invoke-envelope-capability.js +1 -62
  242. package/packages/client/dist/yolo/spec-envelope-collector.js +5 -184
  243. package/packages/client/dist/yoloop/discovery-hook.js +2 -300
  244. package/packages/client/dist/yoloop/index.js +1 -8
  245. package/packages/client/dist/yoloop/invoke-hooks.js +12 -669
  246. package/packages/client/dist/yoloop/shared-discover-epics.js +1 -1007
  247. package/packages/client/dist/yoloop/shared-yoloop-types.js +1 -33
  248. package/packages/client/dist/yoloop/target-normalization.js +1 -180
  249. package/packages/client/dist/yoloop/yoloop-client-state-store.js +1 -1920
  250. package/packages/shared/dist/invocation/explicit-intent-recovery.d.ts +83 -0
  251. package/packages/shared/dist/invocation/explicit-intent-recovery.js +187 -0
  252. package/packages/shared/dist/invocation/index.d.ts +7 -0
  253. package/packages/shared/dist/invocation/index.js +22 -0
  254. package/packages/shared/dist/yolo/change-evidence.js +1 -1
  255. package/packages/shared/dist/yoloop/epic-terminal-git.js +144 -2
  256. package/targets-stubs/antigravity/gemini.md +1 -1
  257. package/targets-stubs/antigravity/skill/SKILL.md +1 -1
  258. package/targets-stubs/claude-code/neocortex-root.agent.yaml +1 -1
  259. package/targets-stubs/claude-code/neocortex-root.md +2 -2
  260. package/targets-stubs/claude-code/neocortex.agent.yaml +1 -1
  261. package/targets-stubs/claude-code/neocortex.md +2 -2
  262. package/targets-stubs/codex/AGENTS.md +24 -21
  263. package/targets-stubs/codex/README.md +6 -1
  264. package/targets-stubs/codex/neocortex.toml +7 -2
  265. package/targets-stubs/cursor/agent.md +2 -2
  266. package/targets-stubs/gemini-cli/agent.md +2 -2
  267. package/targets-stubs/opencode/neocortex-root.md +16 -1
  268. package/targets-stubs/opencode/neocortex.md +6 -0
  269. package/targets-stubs/vscode/neocortex.agent.md +2 -2
@@ -1,1364 +1,19 @@
1
- /**
2
- * @license FSL-1.1
3
- * Copyright (c) 2026 OrNexus AI
4
- *
5
- * P180.06 -- neocortex-runner core tick loop, leases, receipts and recovery.
6
- */
7
- import { createHash, randomBytes } from 'node:crypto';
8
- import { resolve } from 'node:path';
9
- import { CONTINUITY_PROCESSING_OUTPUT_SUMMARY_EVENT_TYPE, ContinuitySqliteStoreError, openContinuitySqliteStore, } from '../continuity/sqlite-store.js';
10
- import { invoke } from '../commands/invoke.js';
11
- import { checkRunnerBinaryTrust, } from './binary.js';
12
- import { runRunnerSchedulerCommand, } from './scheduler.js';
13
- import { runYoloExecutionPhase, } from './headless/run-yolo.js';
14
- import { runYoloopExecutionPhase, } from './headless/run-yoloop.js';
15
- import { DEFAULT_RUNNER_HEARTBEAT_BACKOFF_MS, DEFAULT_RUNNER_HEARTBEAT_INTERVAL_MS, DEFAULT_RUNNER_HEARTBEAT_JITTER_RATIO, DEFAULT_RUNNER_HEARTBEAT_TIMEOUT_MS, runRunnerPortalHeartbeat, runRunnerPortalHeartbeatLoop, runRunnerPortalSync, runRunnerPortalSyncLoop, } from './portal-sync-worker.js';
16
- export { RUNNER_BINARY_REASON_CODES, checkRunnerBinaryTrust, runnerTrustRelativePath } from './binary.js';
17
- export { LOCAL_JOBS_CONSOLE_REASON_CODES, createLocalJobConsoleServer, isLocalJobsConsoleArgs, localJobConsoleSocketPath, parseLocalJobsConsoleArgs, runLocalJobsConsoleCommand, } from './local-console.js';
18
- export const RUNNER_REASON_CODES = Object.freeze({
19
- OK: 'continuity-ok',
20
- LEASE_CONFLICT: 'lease-conflict',
21
- LEASE_EXPIRED: 'lease-expired',
22
- RUNNER_BINARY_UNTRUSTED: 'runner-binary-untrusted',
23
- RUNNER_DIRECTIVE_UNTRUSTED: 'runner-directive-untrusted',
24
- RUNNER_SERVER_INVOKE_FAILED: 'runner-server-invoke-failed',
25
- SQLITE_ADAPTER_UNAVAILABLE: 'sqlite-adapter-unavailable',
26
- SQLITE_INTEGRITY_FAILED: 'sqlite-integrity-failed',
27
- BACKOFF_SCHEDULED: 'backoff-scheduled',
28
- MISSED_RUN_CATCHUP: 'missed-run-catchup',
29
- HEADLESS_RUNTIME_UNRESOLVED: 'headless-runtime-unresolved',
30
- HEADLESS_RUNTIME_UNTRUSTED: 'headless-runtime-untrusted',
31
- HEADLESS_ADAPTER_UNAVAILABLE: 'headless-adapter-unavailable',
32
- HEADLESS_RUNTIME_AUTH_REQUIRED: 'headless-runtime-auth-required',
33
- HEADLESS_EXECUTION_BACKOFF: 'headless-execution-backoff',
34
- HEADLESS_EXECUTION_TIMEOUT: 'headless-execution-timeout',
35
- HEADLESS_EXECUTION_FAILED: 'headless-execution-failed',
36
- HEADLESS_RECEIPT_REDACTED: 'headless-receipt-redacted',
37
- AUTONOMOUS_GOVERNANCE_BYPASS_BLOCKED: 'headless-directive-rejected',
38
- });
39
- export const DEFAULT_RUNNER_LEASE_MS = 5 * 60 * 1000;
40
- export const DEFAULT_STALE_LEASE_AFTER_MS = 30 * 1000;
41
- export const DEFAULT_RUNNER_BACKOFF_MS = 30 * 1000;
42
- export const DEFAULT_DUE_JOB_LIMIT = 1;
43
- export const MAX_DUE_JOB_LIMIT = 10;
44
- const ALLOWED_DIRECTIVE_KINDS = new Set([
45
- 'append_event',
46
- 'append_receipt',
47
- 'update_job_status',
48
- 'schedule_next_tick',
49
- 'release_lease',
50
- 'noop',
51
- ]);
52
- const ALLOWED_COMMAND_CLASSES = new Set(['goal-tick', 'loop-tick', 'jobs', 'runner']);
53
- const RECEIPT_STATUSES = new Set(['accepted', 'applied', 'ignored', 'failed', 'deferred']);
54
- const JOB_STATUSES = new Set([
55
- 'draft',
56
- 'planned',
57
- 'active',
58
- 'running',
59
- 'waiting_external',
60
- 'waiting_human',
61
- 'backoff',
62
- 'paused',
63
- 'blocked',
64
- 'completed',
65
- 'aborted',
66
- 'cancelled',
67
- ]);
68
- const UNSAFE_KEY_PATTERN = /(raw[_-]?logs?|prompt(?:[_-]?(?:body|text))?|private[_-]?urls?|secret|token|password|protected(?:[_-]?(?:field|body|internals))?|workflow(?:[_-]?body)?|step(?:[_-]?body)?|customer(?:[_-]?(?:pii|data))?|authorization|api[_-]?key|license[_-]?key)/i;
69
- const UNSAFE_VALUE_PATTERN = /(https?:\/\/\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[_-]?log|transcript|stack trace|stderr:|stdout:|system prompt|workflow body|step body|PROTECTED_|PRIVATE_INTERNALS|CUSTOMER_PII|VENDOR_CORPUS|P180[_-]?SYNTHETIC[_-]?CONTINUITY|P187[_-]?SYNTHETIC[_-]?(?:CONTINUITY|RUNNER|WATCH))/i;
70
- const RUNNER_HEADLESS_TRUST_FAILURE_REASON_CODES = new Set([
71
- RUNNER_REASON_CODES.HEADLESS_RUNTIME_UNRESOLVED,
72
- RUNNER_REASON_CODES.HEADLESS_RUNTIME_UNTRUSTED,
73
- 'headless-model-rejected',
74
- 'headless-directive-rejected',
75
- 'headless-prompt-rejected',
76
- 'headless-yoloop-metadata-invalid',
77
- 'headless-yoloop-isolation-violation',
78
- ]);
79
- const RUNNER_HEADLESS_ADAPTER_UNAVAILABLE_REASON_CODES = new Set([
80
- RUNNER_REASON_CODES.HEADLESS_ADAPTER_UNAVAILABLE,
81
- ]);
82
- const RUNNER_HEADLESS_AUTH_REQUIRED_REASON_CODES = new Set([
83
- RUNNER_REASON_CODES.HEADLESS_RUNTIME_AUTH_REQUIRED,
84
- ]);
85
- const RUNNER_HEADLESS_BACKOFF_REASON_CODES = new Set([
86
- RUNNER_REASON_CODES.HEADLESS_EXECUTION_BACKOFF,
87
- ]);
88
- const RUNNER_HEADLESS_TIMEOUT_REASON_CODES = new Set([
89
- RUNNER_REASON_CODES.HEADLESS_EXECUTION_TIMEOUT,
90
- ]);
91
- const RUNNER_HEADLESS_GENERIC_FAILURE_REASON_CODES = new Set([
92
- RUNNER_REASON_CODES.HEADLESS_EXECUTION_FAILED,
93
- ]);
94
- export async function defaultRunnerServerInvoker(input) {
95
- const result = await invoke({
96
- args: input.args,
97
- projectRoot: input.projectRoot,
98
- format: 'json',
99
- platformTarget: 'runner',
100
- serverUrl: input.serverUrl,
101
- strictReadOnly: input.strictReadOnly,
102
- });
103
- return {
104
- success: result.success,
105
- instructions: result.instructions,
106
- metadata: result.metadata,
107
- error: result.error,
108
- exitCode: result.exitCode,
109
- };
110
- }
111
- export async function runRunnerTick(options = {}) {
112
- const projectRoot = resolve(options.projectRoot ?? process.cwd());
113
- const runnerId = sanitizeId(options.runnerId ?? defaultRunnerId(projectRoot));
114
- const now = options.now ?? (() => new Date().toISOString());
115
- const dueJobLimit = boundedInteger(options.dueJobLimit ?? DEFAULT_DUE_JOB_LIMIT, 1, MAX_DUE_JOB_LIMIT);
116
- const leaseMs = boundedInteger(options.leaseMs ?? DEFAULT_RUNNER_LEASE_MS, 1_000, 60 * 60 * 1000);
117
- const staleLeaseAfterMs = boundedInteger(options.staleLeaseAfterMs ?? DEFAULT_STALE_LEASE_AFTER_MS, 0, 24 * 60 * 60 * 1000);
118
- const idFactory = options.idFactory ?? defaultIdFactory;
119
- const processingSummarySource = normalizeRunnerProcessingSummarySource(options.processingSummarySource ?? (options.tickMode === 'scheduled' ? 'scheduled_tick' : 'manual_tick'));
120
- const reasonCodes = [];
121
- const receipts = [];
122
- const processingSummaries = [];
123
- const executionMetadata = [];
124
- if (options.trustCheck !== false) {
125
- const trust = checkRunnerBinaryTrust(options.executablePath ?? process.argv[1]);
126
- if (!trust.trusted) {
127
- return {
128
- ok: false,
129
- runnerId,
130
- processedJobCount: 0,
131
- dueJobCount: 0,
132
- recoveredLeaseIds: [],
133
- receipts: [],
134
- reasonCodes: [trust.reasonCode ?? RUNNER_REASON_CODES.RUNNER_BINARY_UNTRUSTED],
135
- publicSafe: true,
136
- };
137
- }
138
- }
139
- const store = await openContinuitySqliteStore({
140
- projectRoot,
141
- adapter: options.adapter,
142
- adapterLoader: options.adapterLoader,
143
- now,
144
- });
145
- const heartbeatLoop = startRunnerTickHeartbeatLoop({ options, projectRoot, runnerId, now });
146
- let summary;
147
- try {
148
- const recovered = store.recoverStaleLeases({
149
- nowIso: now(),
150
- staleAfterMs: staleLeaseAfterMs,
151
- limit: dueJobLimit * 2,
152
- });
153
- if (recovered.recoveredLeaseIds.length > 0) {
154
- reasonCodes.push(RUNNER_REASON_CODES.LEASE_EXPIRED);
155
- for (const leaseId of recovered.recoveredLeaseIds) {
156
- // Recovery evidence is intentionally compact and public. It is not tied to
157
- // a synthetic job receipt because the lease row already carries job ID.
158
- void leaseId;
159
- }
160
- }
161
- const dueJobs = store.listDueJobs({ nowIso: now(), limit: dueJobLimit });
162
- for (const job of dueJobs) {
163
- const result = await processDueJob({
164
- store,
165
- job,
166
- projectRoot,
167
- runnerId,
168
- leaseMs,
169
- now,
170
- idFactory,
171
- processingSummarySource,
172
- serverUrl: options.serverUrl,
173
- serverInvoker: options.serverInvoker ?? defaultRunnerServerInvoker,
174
- });
175
- receipts.push(result.receipt);
176
- processingSummaries.push(...result.processingSummaries);
177
- if (result.executionMetadata)
178
- executionMetadata.push(result.executionMetadata);
179
- reasonCodes.push(result.receipt.reasonCode);
180
- }
181
- const headlessExecution = await runYoloExecutionPhase({
182
- store,
183
- projectRoot,
184
- runnerId,
185
- now,
186
- metadata: executionMetadata,
187
- adapterRegistry: options.headlessAdapterRegistry,
188
- adapterOptions: options.headlessAdapterOptions,
189
- runtimeDefaults: options.headlessRuntimeDefaults,
190
- runtimeFallback: options.headlessRuntimeFallback,
191
- limit: options.headlessExecutionLimit,
192
- leaseMs: options.headlessExecutionLeaseMs ?? leaseMs,
193
- staleLeaseAfterMs: options.headlessExecutionStaleLeaseAfterMs ?? staleLeaseAfterMs,
194
- timeoutMs: options.headlessExecutionTimeoutMs,
195
- backoffMs: options.headlessExecutionBackoffMs,
196
- maxAttempts: options.headlessExecutionMaxAttempts,
197
- queueCap: options.headlessExecutionQueueCap,
198
- idFactory: options.headlessExecutionIdFactory,
199
- });
200
- reasonCodes.push(...headlessExecution.reasonCodes);
201
- const headlessYoloopExecution = await runYoloopExecutionPhase({
202
- store,
203
- projectRoot,
204
- runnerId,
205
- now,
206
- metadata: executionMetadata,
207
- adapterRegistry: options.headlessAdapterRegistry,
208
- adapterOptions: options.headlessAdapterOptions,
209
- runtimeDefaults: options.headlessRuntimeDefaults,
210
- runtimeFallback: options.headlessRuntimeFallback,
211
- limit: options.headlessExecutionLimit,
212
- leaseMs: options.headlessExecutionLeaseMs ?? leaseMs,
213
- staleLeaseAfterMs: options.headlessExecutionStaleLeaseAfterMs ?? staleLeaseAfterMs,
214
- timeoutMs: options.headlessExecutionTimeoutMs,
215
- backoffMs: options.headlessExecutionBackoffMs,
216
- maxAttempts: options.headlessExecutionMaxAttempts,
217
- queueCap: options.headlessExecutionQueueCap,
218
- maxIterations: options.headlessYoloopMaxIterations,
219
- maxDurationMs: options.headlessYoloopMaxDurationMs,
220
- localConcurrencyCap: options.headlessYoloopLocalConcurrencyCap,
221
- yoloopInvoker: options.headlessYoloopInvoker,
222
- serverUrl: options.serverUrl,
223
- idFactory: options.headlessExecutionIdFactory,
224
- });
225
- reasonCodes.push(...headlessYoloopExecution.reasonCodes);
226
- summary = {
227
- ok: receipts.every((receipt) => receipt.outcome !== 'failed') && headlessExecution.ok && headlessYoloopExecution.ok,
228
- runnerId,
229
- processedJobCount: receipts.length,
230
- dueJobCount: dueJobs.length,
231
- recoveredLeaseIds: recovered.recoveredLeaseIds,
232
- receipts,
233
- ...(processingSummaries.length > 0 ? { processingSummaries } : {}),
234
- headlessExecution,
235
- headlessYoloopExecution,
236
- reasonCodes: unique(reasonCodes.length > 0 ? reasonCodes : [RUNNER_REASON_CODES.OK]),
237
- publicSafe: true,
238
- };
239
- }
240
- finally {
241
- await heartbeatLoop.stop();
242
- store.close();
243
- }
244
- if (!summary)
245
- throw new Error('runner-tick-failed');
246
- return await withPostTickPortalSync(summary, {
247
- options,
248
- projectRoot,
249
- runnerId,
250
- localStateChanged: runnerTickChangedLocalState(summary),
251
- });
252
- }
253
- async function withPostTickPortalSync(summary, input) {
254
- const portalSync = input.options.portalSync;
255
- if (portalSync === false || portalSync === undefined)
256
- return summary;
257
- if (!input.localStateChanged) {
258
- const heartbeat = await runRunnerPortalHeartbeat({
259
- ...portalSync,
260
- projectRoot: portalSync.projectRoot ?? input.projectRoot,
261
- runnerId: portalSync.runnerId ?? input.runnerId,
262
- sessionId: portalSync.sessionId ?? input.options.portalHeartbeatSessionId,
263
- serverUrl: portalSync.serverUrl ?? input.options.serverUrl,
264
- adapter: portalSync.adapter ?? input.options.adapter,
265
- adapterLoader: portalSync.adapterLoader ?? input.options.adapterLoader,
266
- now: portalSync.now ?? input.options.now,
267
- mode: portalSync.mode ?? (input.options.tickMode === 'scheduled' ? 'scheduled' : 'background'),
268
- timeoutMs: portalSync.heartbeatTimeoutMs ?? input.options.portalHeartbeatTimeoutMs,
269
- });
270
- return { ...summary, portalHeartbeat: heartbeat };
271
- }
272
- const sync = await runRunnerPortalSync({
273
- ...portalSync,
274
- projectRoot: portalSync.projectRoot ?? input.projectRoot,
275
- runnerId: portalSync.runnerId ?? input.runnerId,
276
- sessionId: portalSync.sessionId ?? input.options.portalHeartbeatSessionId,
277
- serverUrl: portalSync.serverUrl ?? input.options.serverUrl,
278
- adapter: portalSync.adapter ?? input.options.adapter,
279
- adapterLoader: portalSync.adapterLoader ?? input.options.adapterLoader,
280
- now: portalSync.now ?? input.options.now,
281
- mode: portalSync.mode ?? (input.options.tickMode === 'scheduled' ? 'scheduled' : 'background'),
282
- });
283
- return { ...summary, portalSync: sync };
284
- }
285
- function runnerTickChangedLocalState(summary) {
286
- return summary.recoveredLeaseIds.length > 0
287
- || summary.receipts.length > 0
288
- || (summary.processingSummaries?.length ?? 0) > 0
289
- || (summary.headlessExecution?.processedAttemptCount ?? 0) > 0
290
- || (summary.headlessExecution?.recoveredAttemptIds.length ?? 0) > 0
291
- || (summary.headlessYoloopExecution?.processedAttemptCount ?? 0) > 0
292
- || (summary.headlessYoloopExecution?.recoveredAttemptIds.length ?? 0) > 0;
293
- }
294
- function startRunnerTickHeartbeatLoop(input) {
295
- const portalSync = input.options.portalSync;
296
- if (portalSync === false || portalSync === undefined)
297
- return { stop: async () => undefined };
298
- const intervalMs = boundedInteger(input.options.portalHeartbeatIntervalMs ?? DEFAULT_RUNNER_HEARTBEAT_INTERVAL_MS, 0, 24 * 60 * 60 * 1000);
299
- if (intervalMs <= 0)
300
- return { stop: async () => undefined };
301
- const backoffMs = boundedInteger(input.options.portalHeartbeatBackoffMs ?? DEFAULT_RUNNER_HEARTBEAT_BACKOFF_MS, 1_000, 30 * 60 * 1000);
302
- const jitterRatio = boundedNumber(input.options.portalHeartbeatJitterRatio ?? DEFAULT_RUNNER_HEARTBEAT_JITTER_RATIO, 0, 0.5);
303
- const timeoutMs = boundedInteger(input.options.portalHeartbeatTimeoutMs ?? portalSync.heartbeatTimeoutMs ?? DEFAULT_RUNNER_HEARTBEAT_TIMEOUT_MS, 1_000, 60_000);
304
- let stopped = false;
305
- let timer;
306
- let wake;
307
- let nextDelayMs = jitteredDelayMs(intervalMs, jitterRatio, Math.random);
308
- const waitForDelayOrStop = (delayMs) => new Promise((resolvePromise) => {
309
- wake = resolvePromise;
310
- timer = setTimeout(resolvePromise, delayMs);
311
- if (typeof timer.unref === 'function')
312
- timer.unref();
313
- });
314
- const loop = (async () => {
315
- while (!stopped) {
316
- if (nextDelayMs > 0)
317
- await waitForDelayOrStop(nextDelayMs);
318
- if (stopped)
319
- break;
320
- const heartbeat = await runRunnerPortalHeartbeat({
321
- ...portalSync,
322
- projectRoot: portalSync.projectRoot ?? input.projectRoot,
323
- runnerId: portalSync.runnerId ?? input.runnerId,
324
- sessionId: portalSync.sessionId ?? input.options.portalHeartbeatSessionId,
325
- serverUrl: portalSync.serverUrl ?? input.options.serverUrl,
326
- adapter: portalSync.adapter ?? input.options.adapter,
327
- adapterLoader: portalSync.adapterLoader ?? input.options.adapterLoader,
328
- now: portalSync.now ?? input.now,
329
- mode: portalSync.mode ?? (input.options.tickMode === 'scheduled' ? 'scheduled' : 'background'),
330
- timeoutMs,
331
- }).catch(() => ({
332
- ok: false,
333
- runnerId: input.runnerId,
334
- workspaceId: 'workspace-default',
335
- workspaceLabel: 'Workspace workspace-default',
336
- projectId: 'project-default',
337
- projectLabel: 'Project project-default',
338
- mode: input.options.tickMode === 'scheduled' ? 'scheduled' : 'background',
339
- generatedAt: input.now(),
340
- serverAccessToLocalhost: false,
341
- serverHeldStream: false,
342
- localhostCallback: false,
343
- posted: { heartbeat: false },
344
- counts: { redactions: 0 },
345
- reasonCodes: [RUNNER_REASON_CODES.BACKOFF_SCHEDULED],
346
- redactionEvidence: [],
347
- publicSafe: true,
348
- }));
349
- nextDelayMs = jitteredDelayMs(heartbeat.ok ? intervalMs : backoffMs, jitterRatio, Math.random);
350
- }
351
- })();
352
- return {
353
- stop: async () => {
354
- stopped = true;
355
- if (timer)
356
- clearTimeout(timer);
357
- wake?.();
358
- await loop.catch(() => undefined);
359
- },
360
- };
361
- }
362
- export async function runRunnerDoctor(options = {}) {
363
- const projectRoot = resolve(options.projectRoot ?? process.cwd());
364
- const now = options.now ?? (() => new Date().toISOString());
365
- const generatedAt = now();
366
- const checks = {};
367
- let snapshotRows = null;
368
- let store = null;
369
- try {
370
- store = await openContinuitySqliteStore({
371
- projectRoot,
372
- adapter: options.adapter,
373
- adapterLoader: options.adapterLoader,
374
- now,
375
- });
376
- snapshotRows = store.collectSnapshotRows({
377
- nowIso: generatedAt,
378
- jobLimit: 10,
379
- staleLeaseLimit: 10,
380
- receiptLimit: 10,
381
- eventLimit: 10,
382
- snapshotLimit: 5,
383
- });
384
- const journalMode = String(store.pragmaValue('journal_mode') ?? '').toLowerCase();
385
- checks['db-integrity'] = doctorCheck('db-integrity', 'DB integrity', 'ok', [RUNNER_REASON_CODES.OK], 'Continuity SQLite integrity check completed.', {
386
- jobs: count(snapshotRows.counts, 'jobs'),
387
- receipts: count(snapshotRows.counts, 'receipts'),
388
- events: count(snapshotRows.counts, 'events'),
389
- });
390
- checks['sqlite-wal'] = doctorCheck('sqlite-wal', 'SQLite WAL', journalMode === 'wal' ? 'ok' : 'blocked', journalMode === 'wal' ? [RUNNER_REASON_CODES.OK] : [RUNNER_REASON_CODES.SQLITE_INTEGRITY_FAILED], journalMode === 'wal' ? 'SQLite journal mode is WAL.' : 'SQLite journal mode is not WAL.');
391
- checks.migrations = doctorCheck('migrations', 'Migrations', snapshotRows.schemaVersion >= 1 ? 'ok' : 'blocked', snapshotRows.schemaVersion >= 1 ? [RUNNER_REASON_CODES.OK] : [RUNNER_REASON_CODES.SQLITE_INTEGRITY_FAILED], `Continuity schema version ${snapshotRows.schemaVersion} is readable.`);
392
- }
393
- catch (error) {
394
- const reasonCode = classifyRunnerError(error);
395
- checks['db-integrity'] = doctorCheck('db-integrity', 'DB integrity', 'blocked', [reasonCode], 'Continuity SQLite could not be opened safely.');
396
- checks['sqlite-wal'] = doctorCheck('sqlite-wal', 'SQLite WAL', 'blocked', [reasonCode], 'SQLite WAL could not be verified.');
397
- checks.migrations = doctorCheck('migrations', 'Migrations', 'blocked', [reasonCode], 'Continuity migrations could not be verified.');
398
- }
399
- finally {
400
- if (store) {
401
- try {
402
- store.close();
403
- }
404
- catch { /* ignore close failures for doctor output */ }
405
- }
406
- }
407
- const staleLeases = snapshotRows ? count(snapshotRows.counts, 'staleLeases') : 0;
408
- const staleHeadlessExecutionAttempts = snapshotRows ? count(snapshotRows.counts, 'staleHeadlessExecutionAttempts') : 0;
409
- checks.leases = doctorCheck('leases', 'Lease health', staleLeases + staleHeadlessExecutionAttempts > 0 ? 'degraded' : 'ok', staleLeases + staleHeadlessExecutionAttempts > 0 ? [RUNNER_REASON_CODES.LEASE_EXPIRED] : [RUNNER_REASON_CODES.OK], staleLeases + staleHeadlessExecutionAttempts > 0 ? 'Stale job leases or headless execution attempts are eligible for bounded recovery.' : 'No stale leases or headless attempts reported.', { staleLeases, staleHeadlessExecutionAttempts });
410
- const headlessAttempts = snapshotRows?.headlessExecutionAttempts ?? [];
411
- const headlessReasonCodes = unique(headlessAttempts
412
- .map((attempt) => attempt.reason_code)
413
- .filter((reasonCode) => typeof reasonCode === 'string' && reasonCode.length > 0));
414
- const headlessDiagnostics = summarizeRunnerHeadlessDiagnostics(headlessAttempts, headlessReasonCodes);
415
- const headlessBlockingReasons = new Set([
416
- RUNNER_REASON_CODES.HEADLESS_RUNTIME_UNRESOLVED,
417
- RUNNER_REASON_CODES.HEADLESS_RUNTIME_UNTRUSTED,
418
- RUNNER_REASON_CODES.HEADLESS_ADAPTER_UNAVAILABLE,
419
- RUNNER_REASON_CODES.HEADLESS_RUNTIME_AUTH_REQUIRED,
420
- 'headless-model-rejected',
421
- 'headless-directive-rejected',
422
- 'headless-prompt-rejected',
423
- 'headless-yoloop-metadata-invalid',
424
- 'headless-yoloop-isolation-violation',
425
- ]);
426
- const headlessAttemptCount = snapshotRows ? count(snapshotRows.counts, 'headlessExecutionAttempts') : 0;
427
- const headlessBlocked = headlessReasonCodes.some((reasonCode) => headlessBlockingReasons.has(reasonCode));
428
- const headlessBackoff = headlessReasonCodes.includes(RUNNER_REASON_CODES.HEADLESS_EXECUTION_BACKOFF)
429
- || headlessAttempts.some((attempt) => attempt.status === 'backoff');
430
- const headlessRedacted = headlessReasonCodes.includes(RUNNER_REASON_CODES.HEADLESS_RECEIPT_REDACTED);
431
- const headlessAuthRequired = headlessDiagnostics.authRequired > 0;
432
- const headlessTrustOrAdapterBlocked = headlessDiagnostics.trustFailures > 0 || headlessDiagnostics.adapterUnavailable > 0;
433
- const headlessDegraded = staleHeadlessExecutionAttempts > 0
434
- || headlessBackoff
435
- || headlessRedacted
436
- || headlessDiagnostics.timeout > 0
437
- || headlessDiagnostics.genericFailed > 0
438
- || headlessDiagnostics.redactions > 0;
439
- checks['headless-execution'] = doctorCheck('headless-execution', 'Headless execution', headlessBlocked ? 'blocked' : headlessDegraded ? 'degraded' : headlessAttemptCount > 0 ? 'ok' : 'delegated', headlessReasonCodes.length > 0
440
- ? headlessReasonCodes
441
- : headlessAttemptCount > 0
442
- ? [RUNNER_REASON_CODES.OK]
443
- : [RUNNER_REASON_CODES.HEADLESS_RUNTIME_UNRESOLVED], headlessBlocked
444
- ? headlessAuthRequired && !headlessTrustOrAdapterBlocked
445
- ? 'Complete local runtime login/auth setup before starting a new headless attempt; default retry/backoff is not scheduled for auth-required.'
446
- : 'Resolve public runtime/model trust, adapter availability or protected directive hard-stop reason codes before executing headless attempts.'
447
- : headlessDegraded
448
- ? 'Headless attempts need bounded recovery, retry/backoff, timeout/generic failure follow-up or redaction review.'
449
- : headlessAttemptCount > 0
450
- ? 'Headless attempts are represented by public status, runtime/model and reason-code evidence.'
451
- : 'Headless resolver checks are delegated until durable headless work is queued.', {
452
- attempts: headlessAttemptCount,
453
- queuedBackoff: snapshotRows ? count(snapshotRows.counts, 'headlessQueuedAttempts') : 0,
454
- running: snapshotRows ? count(snapshotRows.counts, 'headlessRunningAttempts') : 0,
455
- staleHeadlessExecutionAttempts,
456
- trustFailures: headlessDiagnostics.trustFailures,
457
- adapterUnavailable: headlessDiagnostics.adapterUnavailable,
458
- authRequired: headlessDiagnostics.authRequired,
459
- retryBackoff: headlessDiagnostics.retryBackoff,
460
- timeout: headlessDiagnostics.timeout,
461
- genericFailed: headlessDiagnostics.genericFailed,
462
- redactions: headlessDiagnostics.redactions,
463
- });
464
- const outboxCounts = snapshotRows?.outboxCounts ?? {};
465
- const failedOutbox = count(outboxCounts, 'failed') + count(outboxCounts, 'dead_letter');
466
- checks.outbox = doctorCheck('outbox', 'Outbox state', failedOutbox > 0 ? 'degraded' : 'ok', failedOutbox > 0 ? [RUNNER_REASON_CODES.BACKOFF_SCHEDULED] : [RUNNER_REASON_CODES.OK], failedOutbox > 0 ? 'Failed/dead-letter outbox entries can be retried with dedupe.' : 'Outbox has no failed/dead-letter entries.', {
467
- pending: count(outboxCounts, 'pending'),
468
- failed: count(outboxCounts, 'failed'),
469
- deadLetter: count(outboxCounts, 'dead_letter'),
470
- });
471
- checks.metering = doctorCheck('metering', 'Metering receipts', 'ok', [RUNNER_REASON_CODES.OK], 'Metering evidence is represented by public job receipts and counts only.', { receipts: snapshotRows ? count(snapshotRows.counts, 'receipts') : 0 });
472
- checks['portal-sync'] = doctorCheck('portal-sync', 'Portal sync', failedOutbox > 0 ? 'degraded' : 'ok', failedOutbox > 0 ? [RUNNER_REASON_CODES.BACKOFF_SCHEDULED] : [RUNNER_REASON_CODES.OK], failedOutbox > 0
473
- ? 'Portal sync worker is enabled but has retryable outbox debt; outbound sync remains client-owned.'
474
- : 'Portal sync worker is enabled for outbound heartbeat, jobs sync, command polling and acknowledgement only.', {
475
- pending: count(outboxCounts, 'pending'),
476
- failed: count(outboxCounts, 'failed'),
477
- deadLetter: count(outboxCounts, 'dead_letter'),
478
- });
479
- const trust = options.trustCheck === false
480
- ? { trusted: true, publicSafe: true }
481
- : checkRunnerBinaryTrust(options.executablePath ?? process.argv[1]);
482
- checks.runner = doctorCheck('runner', 'Runner health', trust.trusted ? 'ok' : 'blocked', trust.trusted ? [RUNNER_REASON_CODES.OK] : [trust.reasonCode ?? RUNNER_REASON_CODES.RUNNER_BINARY_UNTRUSTED], trust.trusted ? 'Runner binary trust check passed.' : 'Runner binary trust check failed.');
483
- try {
484
- const scheduler = await runRunnerSchedulerCommand({
485
- command: 'doctor',
486
- projectRoot,
487
- executablePath: options.scheduler?.executablePath ?? options.executablePath ?? process.argv[1],
488
- platform: options.scheduler?.platform,
489
- env: options.scheduler?.env,
490
- dryRun: true,
491
- intervalSeconds: options.scheduler?.intervalSeconds,
492
- strategy: options.scheduler?.strategy,
493
- allowCronFallback: options.scheduler?.allowCronFallback,
494
- trustCheck: options.scheduler?.trustCheck ?? options.trustCheck,
495
- uid: options.scheduler?.uid,
496
- commandAvailable: options.scheduler?.commandAvailable,
497
- commandRunner: options.scheduler?.commandRunner,
498
- trustChecker: options.scheduler?.trustChecker ?? checkRunnerBinaryTrust,
499
- });
500
- const schedulerBlocked = scheduler.reasonCodes.includes(RUNNER_REASON_CODES.RUNNER_BINARY_UNTRUSTED)
501
- || scheduler.reasonCodes.some((reasonCode) => reasonCode.startsWith('runner-binary-'))
502
- || scheduler.reasonCodes.includes('scheduler-install-unsafe');
503
- checks.scheduler = doctorCheck('scheduler', 'Scheduler install', schedulerBlocked ? 'blocked' : scheduler.ok ? 'ok' : 'degraded', scheduler.reasonCodes, scheduler.ok ? 'Scheduler doctor selected a user-scope strategy.' : 'Scheduler doctor found public guidance or degraded capability.', { manifests: scheduler.manifestPaths.length, controlCommands: scheduler.controlCommands.length, legacyEntries: scheduler.legacyEntries.length });
504
- }
505
- catch (error) {
506
- checks.scheduler = doctorCheck('scheduler', 'Scheduler install', 'degraded', ['scheduler-command-failed'], publicErrorMessage(error));
507
- }
508
- const completeChecks = checks;
509
- const reasonCodes = unique(Object.values(completeChecks).flatMap((check) => check.reasonCodes));
510
- return {
511
- ok: Object.values(completeChecks).every((check) => check.status !== 'blocked'),
512
- generatedAt,
513
- checks: completeChecks,
514
- reasonCodes,
515
- publicSafe: true,
516
- };
517
- }
518
- export function extractRunnerDirectives(metadata) {
519
- if (!metadata)
520
- return [];
521
- const candidates = [
522
- metadata.continuityRunnerDirectives,
523
- metadata.runnerDirectives,
524
- metadata.continuityDirectives,
525
- ];
526
- for (const candidate of candidates) {
527
- if (Array.isArray(candidate))
528
- return candidate.filter(isRecord).map(normalizeDirective).filter(isRunnerDirective);
529
- }
530
- return [];
531
- }
532
- export function assertRunnerDirectiveTrusted(directive, jobId) {
533
- if (!isRecord(directive))
534
- throw new RunnerDirectiveError('Directive is not a public record.');
535
- if (directive.publicSafe !== true)
536
- throw new RunnerDirectiveError('Directive is not marked public-safe.');
537
- const kind = typeof directive.kind === 'string' ? directive.kind : typeof directive.type === 'string' ? directive.type : '';
538
- if (!ALLOWED_DIRECTIVE_KINDS.has(kind))
539
- throw new RunnerDirectiveError('Directive kind is not allowlisted.');
540
- const commandClass = typeof directive.commandClass === 'string' ? directive.commandClass : 'runner';
541
- if (!ALLOWED_COMMAND_CLASSES.has(commandClass))
542
- throw new RunnerDirectiveError('Directive command class is not allowlisted.');
543
- const directiveJobId = typeof directive.jobId === 'string' ? directive.jobId : jobId;
544
- if (directiveJobId !== jobId)
545
- throw new RunnerDirectiveError('Directive job ID does not match the leased job.');
546
- assertPublicSafeDirectiveValue(directive, 'directive');
547
- }
548
- export async function runRunnerCli(argv, options = {}) {
549
- const parsed = parseRunnerArgs(argv);
550
- if (parsed.command === 'help') {
551
- process.stdout.write(`${RUNNER_HELP}\n`);
552
- return 0;
553
- }
554
- if (parsed.command === 'version') {
555
- const trust = checkRunnerBinaryTrust(options.executablePath ?? process.argv[1]);
556
- process.stdout.write(`${trust.packageVersion ?? '0.0.0'}\n`);
557
- return trust.trusted ? 0 : 1;
558
- }
559
- if (parsed.command === 'doctor') {
560
- try {
561
- const summary = await runRunnerDoctor({
562
- ...options,
563
- projectRoot: parsed.projectRoot ?? options.projectRoot,
564
- executablePath: options.executablePath ?? process.argv[1],
565
- scheduler: {
566
- ...options.scheduler,
567
- strategy: parsed.strategy ?? options.scheduler?.strategy,
568
- allowCronFallback: parsed.allowCronFallback ?? options.scheduler?.allowCronFallback,
569
- executablePath: options.scheduler?.executablePath ?? options.executablePath ?? process.argv[1],
570
- },
571
- });
572
- process.stdout.write(JSON.stringify(summary) + '\n');
573
- return summary.ok ? 0 : 1;
574
- }
575
- catch (error) {
576
- process.stderr.write(JSON.stringify({ error_code: 'runner-doctor-failed', message: publicErrorMessage(error) }) + '\n');
577
- return 1;
578
- }
579
- }
580
- if (isSchedulerCommand(parsed.command)) {
581
- try {
582
- const summary = await runRunnerSchedulerCommand({
583
- command: parsed.command,
584
- projectRoot: parsed.projectRoot ?? options.projectRoot,
585
- executablePath: options.scheduler?.executablePath ?? options.executablePath ?? process.argv[1],
586
- platform: options.scheduler?.platform,
587
- env: options.scheduler?.env,
588
- dryRun: parsed.dryRun ?? options.scheduler?.dryRun,
589
- intervalSeconds: parsed.intervalSeconds ?? options.scheduler?.intervalSeconds,
590
- strategy: parsed.strategy ?? options.scheduler?.strategy,
591
- allowCronFallback: parsed.allowCronFallback ?? options.scheduler?.allowCronFallback,
592
- trustCheck: options.scheduler?.trustCheck,
593
- uid: options.scheduler?.uid,
594
- commandAvailable: options.scheduler?.commandAvailable,
595
- commandRunner: options.scheduler?.commandRunner,
596
- trustChecker: options.scheduler?.trustChecker ?? checkRunnerBinaryTrust,
597
- });
598
- process.stdout.write(JSON.stringify(publicRunnerSchedulerCommandResult(summary)) + '\n');
599
- return summary.ok ? 0 : 1;
600
- }
601
- catch (error) {
602
- process.stderr.write(JSON.stringify({ error_code: 'scheduler-command-failed', message: publicErrorMessage(error) }) + '\n');
603
- return 1;
604
- }
605
- }
606
- if (parsed.command === 'heartbeat') {
607
- try {
608
- if (parsed.loop) {
609
- const summary = await runRunnerPortalHeartbeatLoop({
610
- ...options,
611
- projectRoot: parsed.projectRoot ?? options.projectRoot,
612
- runnerId: parsed.runnerId ?? options.runnerId,
613
- sessionId: parsed.sessionId ?? options.portalHeartbeatSessionId,
614
- serverUrl: parsed.serverUrl ?? options.serverUrl,
615
- workspaceId: parsed.workspaceId,
616
- workspaceLabel: parsed.workspaceLabel,
617
- projectId: parsed.projectId,
618
- projectLabel: parsed.projectLabel,
619
- mode: parsed.scheduled ? 'scheduled' : 'background',
620
- intervalMs: parsed.intervalMs ?? options.portalHeartbeatIntervalMs,
621
- maxRuns: parsed.maxRuns ?? options.portalSyncMaxRuns,
622
- backoffMs: parsed.heartbeatBackoffMs ?? options.portalHeartbeatBackoffMs,
623
- timeoutMs: parsed.heartbeatTimeoutMs ?? options.portalHeartbeatTimeoutMs,
624
- jitterRatio: parsed.heartbeatJitterRatio ?? options.portalHeartbeatJitterRatio,
625
- });
626
- process.stdout.write(JSON.stringify(summary) + '\n');
627
- return summary.ok ? 0 : 1;
628
- }
629
- const summary = await runRunnerPortalHeartbeat({
630
- ...options,
631
- projectRoot: parsed.projectRoot ?? options.projectRoot,
632
- runnerId: parsed.runnerId ?? options.runnerId,
633
- sessionId: parsed.sessionId ?? options.portalHeartbeatSessionId,
634
- serverUrl: parsed.serverUrl ?? options.serverUrl,
635
- workspaceId: parsed.workspaceId,
636
- workspaceLabel: parsed.workspaceLabel,
637
- projectId: parsed.projectId,
638
- projectLabel: parsed.projectLabel,
639
- mode: parsed.scheduled ? 'scheduled' : 'manual',
640
- timeoutMs: parsed.heartbeatTimeoutMs ?? options.portalHeartbeatTimeoutMs,
641
- });
642
- process.stdout.write(JSON.stringify(summary) + '\n');
643
- return summary.ok ? 0 : 1;
644
- }
645
- catch (error) {
646
- process.stderr.write(JSON.stringify({ error_code: 'portal-heartbeat-failed', message: publicErrorMessage(error) }) + '\n');
647
- return 1;
648
- }
649
- }
650
- if (parsed.command === 'sync') {
651
- try {
652
- if (parsed.loop) {
653
- const summary = await runRunnerPortalSyncLoop({
654
- ...options,
655
- projectRoot: parsed.projectRoot ?? options.projectRoot,
656
- runnerId: parsed.runnerId ?? options.runnerId,
657
- serverUrl: parsed.serverUrl ?? options.serverUrl,
658
- workspaceId: parsed.workspaceId,
659
- workspaceLabel: parsed.workspaceLabel,
660
- projectId: parsed.projectId,
661
- projectLabel: parsed.projectLabel,
662
- syncCursor: parsed.syncCursor,
663
- mode: parsed.scheduled ? 'scheduled' : 'background',
664
- intervalMs: parsed.intervalMs ?? options.portalSyncIntervalMs,
665
- maxRuns: parsed.maxRuns ?? options.portalSyncMaxRuns,
666
- });
667
- process.stdout.write(JSON.stringify(summary) + '\n');
668
- return summary.ok ? 0 : 1;
669
- }
670
- const summary = await runRunnerPortalSync({
671
- ...options,
672
- projectRoot: parsed.projectRoot ?? options.projectRoot,
673
- runnerId: parsed.runnerId ?? options.runnerId,
674
- serverUrl: parsed.serverUrl ?? options.serverUrl,
675
- workspaceId: parsed.workspaceId,
676
- workspaceLabel: parsed.workspaceLabel,
677
- projectId: parsed.projectId,
678
- projectLabel: parsed.projectLabel,
679
- syncCursor: parsed.syncCursor,
680
- mode: parsed.scheduled ? 'scheduled' : 'manual',
681
- });
682
- process.stdout.write(JSON.stringify(summary) + '\n');
683
- return summary.ok ? 0 : 1;
684
- }
685
- catch (error) {
686
- process.stderr.write(JSON.stringify({ error_code: 'portal-sync-failed', message: publicErrorMessage(error) }) + '\n');
687
- return 1;
688
- }
689
- }
690
- if (parsed.command !== 'tick') {
691
- process.stderr.write(JSON.stringify({ error_code: 'runner-command-unknown', message: 'Unknown neocortex-runner command.' }) + '\n');
692
- return 1;
693
- }
694
- try {
695
- const summary = await runRunnerTick({
696
- ...options,
697
- projectRoot: parsed.projectRoot ?? options.projectRoot,
698
- runnerId: parsed.runnerId ?? options.runnerId,
699
- dueJobLimit: parsed.limit ?? options.dueJobLimit,
700
- leaseMs: parsed.leaseMs ?? options.leaseMs,
701
- staleLeaseAfterMs: parsed.staleLeaseAfterMs ?? options.staleLeaseAfterMs,
702
- headlessYoloopLocalConcurrencyCap: parsed.yoloopLocalCap ?? options.headlessYoloopLocalConcurrencyCap,
703
- tickMode: parsed.scheduled ? 'scheduled' : options.tickMode,
704
- processingSummarySource: parsed.scheduled ? 'scheduled_tick' : options.processingSummarySource,
705
- serverUrl: parsed.serverUrl ?? options.serverUrl,
706
- portalSync: runnerCliPortalSyncOptions(parsed, options, parsed.scheduled ? 'scheduled' : 'background'),
707
- });
708
- process.stdout.write(JSON.stringify(summary) + '\n');
709
- return summary.ok ? 0 : 1;
710
- }
711
- catch (error) {
712
- const reasonCode = classifyRunnerError(error);
713
- process.stderr.write(JSON.stringify({ error_code: reasonCode, message: publicErrorMessage(error) }) + '\n');
714
- return 1;
715
- }
716
- }
717
- async function processDueJob(input) {
718
- const { store, job, projectRoot, runnerId, leaseMs, now, idFactory, processingSummarySource, serverUrl, serverInvoker } = input;
719
- const jobId = job.job_id;
720
- const tickId = idFactory('tick', jobId);
721
- const leaseId = idFactory('lease', jobId);
722
- const acquiredAt = now();
723
- const expiresAt = new Date(Date.parse(acquiredAt) + leaseMs).toISOString();
724
- let leaseHeld = false;
725
- let leaseReleasedByDirective = false;
726
- let finalReasonCode = RUNNER_REASON_CODES.OK;
727
- let finalOutcome = 'applied';
728
- let redactionSummary = [];
729
- const acquired = store.acquireLease({
730
- leaseId,
731
- jobId,
732
- runnerId,
733
- acquiredAt,
734
- expiresAt,
735
- operationId: `${tickId}:acquire`,
736
- idempotencyKey: `${tickId}:acquire`,
737
- metadata: receiptMetadata({ jobId, operationId: tickId, runnerId, leaseId, status: 'running', reasonCode: RUNNER_REASON_CODES.OK }),
738
- });
739
- if (!acquired.inserted) {
740
- const receipt = appendTickReceipt(store, {
741
- jobId,
742
- tickId,
743
- leaseId,
744
- status: 'ignored',
745
- reasonCode: RUNNER_REASON_CODES.LEASE_CONFLICT,
746
- createdAt: now(),
747
- redactionSummary,
748
- });
749
- return { receipt, processingSummaries: [] };
750
- }
751
- leaseHeld = true;
752
- try {
753
- store.appendEvent({
754
- eventId: `${tickId}:started`,
755
- jobId,
756
- operationId: `${tickId}:event:started`,
757
- idempotencyKey: `${tickId}:event:started`,
758
- type: 'runner_tick_started',
759
- summary: `Runner tick started for ${jobId}.`,
760
- reasonCode: RUNNER_REASON_CODES.OK,
761
- createdAt: acquiredAt,
762
- metadata: receiptMetadata({ jobId, operationId: tickId, runnerId, leaseId, status: 'running', reasonCode: RUNNER_REASON_CODES.OK }),
763
- });
764
- const args = job.kind === 'loop'
765
- ? `*loop tick @${jobId} --tick-id ${tickId} --lease-id ${leaseId} --runner-id ${runnerId}`
766
- : job.kind === 'goal'
767
- ? `*goal tick @${jobId} --tick-id ${tickId} --lease-id ${leaseId} --runner-id ${runnerId}`
768
- : `*runner tick --job-id ${jobId} --tick-id ${tickId} --lease-id ${leaseId} --runner-id ${runnerId}`;
769
- const serverResult = await serverInvoker({ projectRoot, args, jobId, tickId, leaseId, runnerId, serverUrl });
770
- if (!serverResult.success) {
771
- finalReasonCode = RUNNER_REASON_CODES.RUNNER_SERVER_INVOKE_FAILED;
772
- finalOutcome = 'failed';
773
- const failedAt = now();
774
- store.updateJobStatus({
775
- jobId,
776
- status: 'backoff',
777
- updatedAt: failedAt,
778
- reasonCode: RUNNER_REASON_CODES.BACKOFF_SCHEDULED,
779
- nextRunAt: addMilliseconds(failedAt, DEFAULT_RUNNER_BACKOFF_MS),
780
- });
781
- store.appendEvent({
782
- eventId: `${tickId}:server-failed`,
783
- jobId,
784
- operationId: `${tickId}:event:server-failed`,
785
- idempotencyKey: `${tickId}:event:server-failed`,
786
- type: 'runner_tick_failed',
787
- summary: 'Runner server tick invocation failed with public reason code.',
788
- reasonCode: finalReasonCode,
789
- createdAt: failedAt,
790
- metadata: receiptMetadata({ jobId, operationId: tickId, runnerId, leaseId, status: 'failed', reasonCode: finalReasonCode }),
791
- });
792
- const receipt = appendTickReceipt(store, { jobId, tickId, leaseId, status: finalOutcome, reasonCode: finalReasonCode, createdAt: now(), redactionSummary });
793
- return { receipt, processingSummaries: [] };
794
- }
795
- const directives = extractRunnerDirectives(serverResult.metadata);
796
- const mutationDirectives = directives.filter((directive) => !isProcessingSummaryDirective(directive));
797
- let directiveResult = { appliedCount: 0, releasedLease: false, changedStatus: false, redactionSummary: [] };
798
- try {
799
- directiveResult = executeRunnerDirectives(store, mutationDirectives, { jobId, tickId, leaseId, runnerId, now });
800
- leaseReleasedByDirective = directiveResult.releasedLease;
801
- redactionSummary = [...directiveResult.redactionSummary];
802
- finalReasonCode = extractPublicRunnerReasonCode(serverResult.metadata) ?? finalReasonCode;
803
- }
804
- catch (error) {
805
- finalReasonCode = RUNNER_REASON_CODES.RUNNER_DIRECTIVE_UNTRUSTED;
806
- finalOutcome = 'failed';
807
- redactionSummary = [error instanceof Error ? error.message : 'Directive rejected.'];
808
- store.updateJobStatus({ jobId, status: 'blocked', updatedAt: now(), reasonCode: finalReasonCode });
809
- store.appendEvent({
810
- eventId: `${tickId}:directive-rejected`,
811
- jobId,
812
- operationId: `${tickId}:event:directive-rejected`,
813
- idempotencyKey: `${tickId}:event:directive-rejected`,
814
- type: 'runner_directive_rejected',
815
- summary: 'Runner rejected an untrusted continuity directive.',
816
- reasonCode: finalReasonCode,
817
- createdAt: now(),
818
- metadata: receiptMetadata({
819
- jobId,
820
- operationId: tickId,
821
- runnerId,
822
- leaseId,
823
- status: 'failed',
824
- reasonCode: finalReasonCode,
825
- redactionSummary,
826
- }),
827
- });
828
- const receipt = appendTickReceipt(store, { jobId, tickId, leaseId, status: finalOutcome, reasonCode: finalReasonCode, createdAt: now(), redactionSummary });
829
- return { receipt, processingSummaries: [] };
830
- }
831
- const processingSummaries = persistRunnerProcessingSummaries(store, serverResult.metadata, {
832
- jobId,
833
- tickId,
834
- leaseId,
835
- runnerId,
836
- source: processingSummarySource,
837
- createdAt: now(),
838
- });
839
- const serverAlreadyMutated = Boolean(serverResult.metadata?.continuityStateUpdate);
840
- if (!directiveResult.changedStatus && !serverAlreadyMutated) {
841
- store.updateJobStatus({ jobId, status: 'active', updatedAt: now(), reasonCode: RUNNER_REASON_CODES.OK });
842
- }
843
- store.appendEvent({
844
- eventId: `${tickId}:completed`,
845
- jobId,
846
- operationId: `${tickId}:event:completed`,
847
- idempotencyKey: `${tickId}:event:completed`,
848
- type: 'runner_tick_completed',
849
- summary: `Runner tick completed with ${directiveResult.appliedCount} public directive(s).`,
850
- reasonCode: finalReasonCode,
851
- createdAt: now(),
852
- metadata: receiptMetadata({ jobId, operationId: tickId, runnerId, leaseId, status: 'applied', reasonCode: finalReasonCode }),
853
- });
854
- const receipt = appendTickReceipt(store, { jobId, tickId, leaseId, status: finalOutcome, reasonCode: finalReasonCode, createdAt: now(), redactionSummary });
855
- return { receipt, processingSummaries, ...(serverResult.metadata ? { executionMetadata: serverResult.metadata } : {}) };
856
- }
857
- finally {
858
- if (leaseHeld && !leaseReleasedByDirective) {
859
- try {
860
- store.releaseLease({ leaseId, releasedAt: now(), reasonCode: finalReasonCode });
861
- }
862
- catch {
863
- // The tick receipt above remains the durable public outcome. A stale
864
- // recovery pass can expire the lease if this release races or crashes.
865
- }
866
- }
867
- }
868
- }
869
- function executeRunnerDirectives(store, directives, ctx) {
870
- let appliedCount = 0;
871
- let releasedLease = false;
872
- let changedStatus = false;
873
- const redactionSummary = [];
874
- for (const directive of directives) {
875
- assertRunnerDirectiveTrusted(directive, ctx.jobId);
876
- switch (directive.kind) {
877
- case 'noop':
878
- appliedCount += 1;
879
- break;
880
- case 'append_event':
881
- store.appendEvent({
882
- eventId: stringField(directive, 'eventId') ?? `${ctx.tickId}:directive:${appliedCount}:event`,
883
- jobId: ctx.jobId,
884
- operationId: stringField(directive, 'operationId') ?? `${ctx.tickId}:directive:${appliedCount}:event`,
885
- idempotencyKey: stringField(directive, 'idempotencyKey') ?? `${ctx.tickId}:directive:${appliedCount}:event`,
886
- type: stringField(directive, 'eventType') ?? 'runner_directive_event',
887
- summary: stringField(directive, 'summary') ?? 'Runner applied public continuity event directive.',
888
- reasonCode: stringField(directive, 'reasonCode') ?? RUNNER_REASON_CODES.OK,
889
- createdAt: stringField(directive, 'createdAt') ?? ctx.now(),
890
- metadata: metadataField(directive),
891
- });
892
- appliedCount += 1;
893
- break;
894
- case 'append_receipt':
895
- store.appendReceipt({
896
- receiptId: stringField(directive, 'receiptId') ?? `${ctx.tickId}:directive:${appliedCount}:receipt`,
897
- jobId: ctx.jobId,
898
- operationId: stringField(directive, 'operationId') ?? `${ctx.tickId}:directive:${appliedCount}:receipt`,
899
- idempotencyKey: stringField(directive, 'idempotencyKey') ?? `${ctx.tickId}:directive:${appliedCount}:receipt`,
900
- status: receiptStatus(stringField(directive, 'status')),
901
- reasonCode: stringField(directive, 'reasonCode') ?? RUNNER_REASON_CODES.OK,
902
- createdAt: stringField(directive, 'createdAt') ?? ctx.now(),
903
- metadata: metadataField(directive),
904
- });
905
- appliedCount += 1;
906
- break;
907
- case 'update_job_status':
908
- store.updateJobStatus({
909
- jobId: ctx.jobId,
910
- status: jobStatus(stringField(directive, 'status')),
911
- updatedAt: stringField(directive, 'updatedAt') ?? ctx.now(),
912
- reasonCode: stringField(directive, 'reasonCode') ?? RUNNER_REASON_CODES.OK,
913
- nextRunAt: stringField(directive, 'nextRunAt'),
914
- });
915
- appliedCount += 1;
916
- changedStatus = true;
917
- break;
918
- case 'schedule_next_tick':
919
- store.scheduleNextTick({
920
- jobId: ctx.jobId,
921
- scheduledAt: requiredDirectiveString(directive, 'scheduledAt'),
922
- updatedAt: stringField(directive, 'updatedAt') ?? ctx.now(),
923
- reasonCode: stringField(directive, 'reasonCode') ?? RUNNER_REASON_CODES.OK,
924
- });
925
- appliedCount += 1;
926
- changedStatus = true;
927
- break;
928
- case 'release_lease':
929
- store.releaseLease({
930
- leaseId: stringField(directive, 'leaseId') ?? ctx.leaseId,
931
- releasedAt: stringField(directive, 'releasedAt') ?? ctx.now(),
932
- reasonCode: stringField(directive, 'reasonCode') ?? RUNNER_REASON_CODES.OK,
933
- });
934
- appliedCount += 1;
935
- releasedLease = true;
936
- break;
937
- }
938
- const redactions = directive.redactionSummary;
939
- if (Array.isArray(redactions)) {
940
- redactionSummary.push(...redactions.filter((item) => typeof item === 'string').slice(0, 5));
941
- }
942
- }
943
- return { appliedCount, releasedLease, changedStatus, redactionSummary };
944
- }
945
- function persistRunnerProcessingSummaries(store, metadata, ctx) {
946
- const persisted = [];
947
- const seenExecutionIds = new Set();
948
- for (const candidate of extractRunnerProcessingSummaryCandidates(metadata)) {
949
- const input = normalizeRunnerProcessingSummaryCandidate(candidate, ctx);
950
- if (!input || seenExecutionIds.has(input.executionId))
951
- continue;
952
- seenExecutionIds.add(input.executionId);
953
- try {
954
- store.recordProcessingOutputSummary(input);
955
- persisted.push(publicRunnerProcessingSummary(input));
956
- }
957
- catch (error) {
958
- if (error instanceof ContinuitySqliteStoreError && error.code === 'sqlite-public-safety-rejected')
959
- continue;
960
- throw error;
961
- }
962
- }
963
- return persisted;
964
- }
965
- function extractRunnerProcessingSummaryCandidates(metadata) {
966
- if (!metadata)
967
- return [];
968
- const candidates = [];
969
- const stateUpdate = recordField(metadata, 'continuityStateUpdate');
970
- const operations = arrayField(stateUpdate, 'operations');
971
- for (const operation of operations) {
972
- if (!isRecord(operation))
973
- continue;
974
- const event = recordField(operation, 'event');
975
- if (!event || stringField(event, 'type') !== CONTINUITY_PROCESSING_OUTPUT_SUMMARY_EVENT_TYPE)
976
- continue;
977
- candidates.push({ operation, event, summary: recordField(event, 'processingOutputSummary') ?? event });
978
- }
979
- for (const directive of extractRunnerDirectives(metadata)) {
980
- if (!isProcessingSummaryDirective(directive))
981
- continue;
982
- candidates.push({ directive, event: directive, summary: recordField(directive, 'processingOutputSummary') ?? directive });
983
- }
984
- for (const summary of [
985
- ...arrayField(metadata, 'processingSummaries'),
986
- ...arrayField(metadata, 'processingOutputSummaries'),
987
- ...arrayField(metadata, 'continuityProcessingSummaries'),
988
- ...arrayField(recordField(metadata, 'continuity'), 'processingSummaries'),
989
- ]) {
990
- if (isRecord(summary))
991
- candidates.push({ event: summary, summary });
992
- }
993
- return candidates;
994
- }
995
- function normalizeRunnerProcessingSummaryCandidate(candidate, ctx) {
996
- if (!candidateMarkedPublicSafe(candidate))
997
- return null;
998
- const event = candidate.event ?? {};
999
- const summary = candidate.summary ?? event;
1000
- const operation = candidate.operation ?? {};
1001
- const directive = candidate.directive ?? {};
1002
- const executionId = stringField(summary, 'executionId')
1003
- ?? stringField(event, 'executionId')
1004
- ?? stringField(operation, 'operationId')
1005
- ?? stringField(directive, 'operationId')
1006
- ?? ctx.tickId;
1007
- const jobId = stringField(summary, 'jobId')
1008
- ?? stringField(event, 'jobId')
1009
- ?? stringField(operation, 'jobId')
1010
- ?? stringField(directive, 'jobId')
1011
- ?? ctx.jobId;
1012
- if (jobId !== ctx.jobId)
1013
- return null;
1014
- const publicSummary = stringField(summary, 'publicSummary')
1015
- ?? stringField(event, 'publicSummary')
1016
- ?? stringField(event, 'summary')
1017
- ?? stringField(directive, 'summary');
1018
- if (!executionId || !publicSummary)
1019
- return null;
1020
- const metadata = mergePublicSummaryMetadata(recordField(operation, 'metadata'), recordField(event, 'metadata'), recordField(summary, 'metadata'), recordField(directive, 'metadata'), {
1021
- schemaVersion: 1,
1022
- contractVersion: 'continuity-v1',
1023
- executionId,
1024
- jobId,
1025
- operationId: executionId,
1026
- runnerId: ctx.runnerId,
1027
- leaseId: ctx.leaseId,
1028
- processingOutputSource: ctx.source,
1029
- publicSummary,
1030
- });
1031
- return {
1032
- eventId: stringField(event, 'eventId') ?? stringField(summary, 'eventId') ?? stringField(directive, 'eventId'),
1033
- executionId,
1034
- jobId,
1035
- idempotencyKey: stringField(operation, 'idempotencyKey') ?? stringField(directive, 'idempotencyKey') ?? executionId,
1036
- goalId: stringField(summary, 'goalId') ?? stringField(event, 'goalId') ?? stringField(operation, 'goalId'),
1037
- loopId: stringField(summary, 'loopId') ?? stringField(event, 'loopId') ?? stringField(operation, 'loopId'),
1038
- publicSummary,
1039
- reasonCode: stringField(summary, 'reasonCode') ?? stringField(event, 'reasonCode') ?? stringField(operation, 'reasonCode') ?? stringField(directive, 'reasonCode'),
1040
- createdAt: stringField(summary, 'createdAt') ?? stringField(event, 'createdAt') ?? stringField(operation, 'createdAt') ?? stringField(directive, 'createdAt') ?? ctx.createdAt,
1041
- source: ctx.source,
1042
- metadata,
1043
- };
1044
- }
1045
- function isProcessingSummaryDirective(directive) {
1046
- return directive.kind === 'append_event'
1047
- && (stringField(directive, 'eventType') ?? stringField(directive, 'type')) === CONTINUITY_PROCESSING_OUTPUT_SUMMARY_EVENT_TYPE;
1048
- }
1049
- function candidateMarkedPublicSafe(candidate) {
1050
- const markers = [candidate.operation, candidate.event, candidate.summary, candidate.directive]
1051
- .filter((item) => Boolean(item))
1052
- .map((item) => item.publicSafe);
1053
- if (markers.some((marker) => marker === false))
1054
- return false;
1055
- return markers.some((marker) => marker === true);
1056
- }
1057
- function mergePublicSummaryMetadata(...records) {
1058
- const output = {};
1059
- for (const record of records) {
1060
- if (record)
1061
- Object.assign(output, record);
1062
- }
1063
- return output;
1064
- }
1065
- function publicRunnerProcessingSummary(input) {
1066
- return {
1067
- eventId: input.eventId ?? processingSummaryOutputEventId(input.executionId),
1068
- executionId: input.executionId,
1069
- jobId: input.jobId,
1070
- publicSummary: input.publicSummary,
1071
- ...(input.reasonCode ? { reasonCode: input.reasonCode } : {}),
1072
- createdAt: input.createdAt,
1073
- source: normalizeRunnerProcessingSummarySource(input.source),
1074
- publicSafe: true,
1075
- };
1076
- }
1077
- function processingSummaryOutputEventId(executionId) {
1078
- const safe = executionId.replace(/[^a-z0-9_.:-]+/gi, '-').replace(/^-+|-+$/g, '');
1079
- if (safe.length > 0)
1080
- return `event-processing-summary-${safe.slice(0, 160)}`;
1081
- return `event-processing-summary-${createHash('sha256').update(executionId).digest('hex').slice(0, 32)}`;
1082
- }
1083
- function appendTickReceipt(store, input) {
1084
- const receipt = {
1085
- receiptId: `receipt-${sanitizeId(input.tickId)}`,
1086
- jobId: input.jobId,
1087
- operationId: input.tickId,
1088
- idempotencyKey: input.tickId,
1089
- status: input.status,
1090
- reasonCode: input.reasonCode,
1091
- appliedOperationIds: input.status === 'applied' ? [input.tickId] : undefined,
1092
- ignoredOperationIds: input.status === 'ignored' ? [input.tickId] : undefined,
1093
- createdAt: input.createdAt,
1094
- metadata: receiptMetadata({
1095
- jobId: input.jobId,
1096
- operationId: input.tickId,
1097
- leaseId: input.leaseId,
1098
- status: input.status,
1099
- reasonCode: input.reasonCode,
1100
- redactionSummary: input.redactionSummary,
1101
- }),
1102
- };
1103
- store.appendReceipt(receipt);
1104
- return {
1105
- jobId: input.jobId,
1106
- tickId: input.tickId,
1107
- leaseId: input.leaseId,
1108
- outcome: input.status,
1109
- reasonCode: input.reasonCode,
1110
- timestamp: input.createdAt,
1111
- redactionSummary: input.redactionSummary,
1112
- publicSafe: true,
1113
- };
1114
- }
1115
- function receiptMetadata(input) {
1116
- return {
1117
- schemaVersion: 1,
1118
- contractVersion: 'continuity-v1',
1119
- jobId: input.jobId,
1120
- operationId: input.operationId,
1121
- ...(input.runnerId ? { runnerId: input.runnerId } : {}),
1122
- ...(input.leaseId ? { leaseId: input.leaseId } : {}),
1123
- status: input.status,
1124
- reasonCode: input.reasonCode,
1125
- redactionEvidence: (input.redactionSummary ?? []).slice(0, 20).map((summary) => ({
1126
- field: boundPublicString(summary, 80),
1127
- reason: 'unsafe_key',
1128
- evidence: 'operation-denied',
1129
- publicSafe: true,
1130
- })),
1131
- };
1132
- }
1133
- function extractPublicRunnerReasonCode(metadata) {
1134
- if (!metadata)
1135
- return null;
1136
- const continuity = isRecord(metadata.continuity) ? metadata.continuity : undefined;
1137
- const loop = continuity && isRecord(continuity.loop) ? continuity.loop : undefined;
1138
- const goal = continuity && isRecord(continuity.goal) ? continuity.goal : undefined;
1139
- const candidates = [
1140
- loop?.reasonCode,
1141
- goal?.reasonCode,
1142
- continuity?.reasonCode,
1143
- ];
1144
- for (const candidate of candidates) {
1145
- if (typeof candidate === 'string' && /^[a-z0-9][a-z0-9-]{0,79}$/.test(candidate) && !UNSAFE_VALUE_PATTERN.test(candidate)) {
1146
- return candidate;
1147
- }
1148
- }
1149
- return null;
1150
- }
1151
- function parseRunnerArgs(argv) {
1152
- const first = argv[0];
1153
- if (!first || first === '--help' || first === '-h' || first === 'help')
1154
- return { command: 'help' };
1155
- if (first === '--version' || first === '-v' || first === 'version')
1156
- return { command: 'version' };
1157
- if (first !== 'tick' && first !== 'sync' && first !== 'heartbeat' && !isSchedulerCommand(first))
1158
- return { command: 'unknown' };
1159
- let projectRoot;
1160
- let runnerId;
1161
- let serverUrl;
1162
- let limit;
1163
- let leaseMs;
1164
- let staleLeaseAfterMs;
1165
- let dryRun;
1166
- let intervalSeconds;
1167
- let intervalMs;
1168
- let maxRuns;
1169
- let heartbeatBackoffMs;
1170
- let heartbeatTimeoutMs;
1171
- let heartbeatJitterRatio;
1172
- let sessionId;
1173
- let strategy;
1174
- let allowCronFallback;
1175
- let scheduled;
1176
- let loop;
1177
- let workspaceId;
1178
- let workspaceLabel;
1179
- let projectId;
1180
- let projectLabel;
1181
- let syncCursor;
1182
- let yoloopLocalCap;
1183
- for (let index = 1; index < argv.length; index++) {
1184
- const arg = argv[index];
1185
- const next = argv[index + 1];
1186
- if (arg === '--project-root') {
1187
- projectRoot = next;
1188
- index += 1;
1189
- continue;
1190
- }
1191
- if (arg === '--runner-id') {
1192
- runnerId = next;
1193
- index += 1;
1194
- continue;
1195
- }
1196
- if (arg === '--server-url') {
1197
- serverUrl = next;
1198
- index += 1;
1199
- continue;
1200
- }
1201
- if (arg === '--limit') {
1202
- limit = Number(next);
1203
- index += 1;
1204
- continue;
1205
- }
1206
- if (arg === '--lease-ms') {
1207
- leaseMs = Number(next);
1208
- index += 1;
1209
- continue;
1210
- }
1211
- if (arg === '--stale-lease-after-ms') {
1212
- staleLeaseAfterMs = Number(next);
1213
- index += 1;
1214
- continue;
1215
- }
1216
- if (arg === '--dry-run') {
1217
- dryRun = true;
1218
- continue;
1219
- }
1220
- if (arg === '--interval-seconds') {
1221
- intervalSeconds = Number(next);
1222
- index += 1;
1223
- continue;
1224
- }
1225
- if (arg === '--interval-ms') {
1226
- intervalMs = Number(next);
1227
- index += 1;
1228
- continue;
1229
- }
1230
- if (arg === '--max-runs') {
1231
- maxRuns = Number(next);
1232
- index += 1;
1233
- continue;
1234
- }
1235
- if (arg === '--backoff-ms') {
1236
- heartbeatBackoffMs = Number(next);
1237
- index += 1;
1238
- continue;
1239
- }
1240
- if (arg === '--heartbeat-timeout-ms' || arg === '--timeout-ms') {
1241
- heartbeatTimeoutMs = Number(next);
1242
- index += 1;
1243
- continue;
1244
- }
1245
- if (arg === '--jitter-ratio') {
1246
- heartbeatJitterRatio = Number(next);
1247
- index += 1;
1248
- continue;
1249
- }
1250
- if (arg === '--session-id') {
1251
- sessionId = next;
1252
- index += 1;
1253
- continue;
1254
- }
1255
- if (arg === '--strategy' && isSchedulerStrategy(next)) {
1256
- strategy = next;
1257
- index += 1;
1258
- continue;
1259
- }
1260
- if (arg === '--allow-cron-fallback') {
1261
- allowCronFallback = true;
1262
- continue;
1263
- }
1264
- if (arg === '--no-cron-fallback') {
1265
- allowCronFallback = false;
1266
- continue;
1267
- }
1268
- if (arg === '--scheduled') {
1269
- scheduled = true;
1270
- continue;
1271
- }
1272
- if (arg === '--manual') {
1273
- scheduled = false;
1274
- continue;
1275
- }
1276
- if (arg === '--loop' || arg === '--background') {
1277
- loop = true;
1278
- continue;
1279
- }
1280
- if (arg === '--once') {
1281
- loop = false;
1282
- continue;
1283
- }
1284
- if (arg === '--workspace-id') {
1285
- workspaceId = next;
1286
- index += 1;
1287
- continue;
1288
- }
1289
- if (arg === '--workspace-label') {
1290
- workspaceLabel = next;
1291
- index += 1;
1292
- continue;
1293
- }
1294
- if (arg === '--project-id') {
1295
- projectId = next;
1296
- index += 1;
1297
- continue;
1298
- }
1299
- if (arg === '--project-label') {
1300
- projectLabel = next;
1301
- index += 1;
1302
- continue;
1303
- }
1304
- if (arg === '--sync-cursor') {
1305
- syncCursor = next;
1306
- index += 1;
1307
- continue;
1308
- }
1309
- if (arg === '--yoloop-local-cap') {
1310
- yoloopLocalCap = Number(next);
1311
- index += 1;
1312
- continue;
1313
- }
1314
- }
1315
- return {
1316
- command: first,
1317
- projectRoot,
1318
- runnerId,
1319
- serverUrl,
1320
- limit,
1321
- leaseMs,
1322
- staleLeaseAfterMs,
1323
- dryRun,
1324
- intervalSeconds,
1325
- intervalMs,
1326
- maxRuns,
1327
- heartbeatBackoffMs,
1328
- heartbeatTimeoutMs,
1329
- heartbeatJitterRatio,
1330
- sessionId,
1331
- strategy,
1332
- allowCronFallback,
1333
- scheduled,
1334
- loop,
1335
- workspaceId,
1336
- workspaceLabel,
1337
- projectId,
1338
- projectLabel,
1339
- syncCursor,
1340
- yoloopLocalCap,
1341
- };
1342
- }
1343
- function runnerCliPortalSyncOptions(parsed, options, mode) {
1344
- if (options.portalSync === false)
1345
- return false;
1346
- const base = options.portalSync ?? {};
1347
- return {
1348
- ...base,
1349
- ...(parsed.projectRoot ?? options.projectRoot ? { projectRoot: parsed.projectRoot ?? options.projectRoot } : {}),
1350
- ...(parsed.runnerId ?? options.runnerId ? { runnerId: parsed.runnerId ?? options.runnerId } : {}),
1351
- ...(parsed.sessionId ?? options.portalHeartbeatSessionId ? { sessionId: parsed.sessionId ?? options.portalHeartbeatSessionId } : {}),
1352
- ...(parsed.serverUrl ?? options.serverUrl ? { serverUrl: parsed.serverUrl ?? options.serverUrl } : {}),
1353
- ...(parsed.workspaceId ? { workspaceId: parsed.workspaceId } : {}),
1354
- ...(parsed.workspaceLabel ? { workspaceLabel: parsed.workspaceLabel } : {}),
1355
- ...(parsed.projectId ? { projectId: parsed.projectId } : {}),
1356
- ...(parsed.projectLabel ? { projectLabel: parsed.projectLabel } : {}),
1357
- ...(parsed.heartbeatTimeoutMs ?? options.portalHeartbeatTimeoutMs ? { heartbeatTimeoutMs: parsed.heartbeatTimeoutMs ?? options.portalHeartbeatTimeoutMs } : {}),
1358
- mode,
1359
- };
1360
- }
1361
- const RUNNER_HELP = `
1
+ import{createHash as W,randomBytes as Ee}from"node:crypto";import{resolve as G}from"node:path";import{CONTINUITY_PROCESSING_OUTPUT_SUMMARY_EVENT_TYPE as X,ContinuitySqliteStoreError as Ie,openContinuitySqliteStore as Z}from"../continuity/sqlite-store.js";import{invoke as ye}from"../commands/invoke.js";import{checkRunnerBinaryTrust as P}from"./binary.js";import{runRunnerSchedulerCommand as ee}from"./scheduler.js";import{runYoloExecutionPhase as Se}from"./headless/run-yolo.js";import{runYoloopExecutionPhase as Re}from"./headless/run-yoloop.js";import{DEFAULT_RUNNER_HEARTBEAT_BACKOFF_MS as _e,DEFAULT_RUNNER_HEARTBEAT_INTERVAL_MS as ke,DEFAULT_RUNNER_HEARTBEAT_JITTER_RATIO as ge,DEFAULT_RUNNER_HEARTBEAT_TIMEOUT_MS as Ae,runRunnerPortalHeartbeat as V,runRunnerPortalHeartbeatLoop as ve,runRunnerPortalSync as te,runRunnerPortalSyncLoop as we}from"./portal-sync-worker.js";import{RUNNER_BINARY_REASON_CODES as Tt,checkRunnerBinaryTrust as jt,runnerTrustRelativePath as Nt}from"./binary.js";import{LOCAL_JOBS_CONSOLE_REASON_CODES as Dt,createLocalJobConsoleServer as Ot,isLocalJobsConsoleArgs as Mt,localJobConsoleSocketPath as xt,parseLocalJobsConsoleArgs as Ft,runLocalJobsConsoleCommand as Ht}from"./local-console.js";const d=Object.freeze({OK:"continuity-ok",LEASE_CONFLICT:"lease-conflict",LEASE_EXPIRED:"lease-expired",RUNNER_BINARY_UNTRUSTED:"runner-binary-untrusted",RUNNER_DIRECTIVE_UNTRUSTED:"runner-directive-untrusted",RUNNER_SERVER_INVOKE_FAILED:"runner-server-invoke-failed",SQLITE_ADAPTER_UNAVAILABLE:"sqlite-adapter-unavailable",SQLITE_INTEGRITY_FAILED:"sqlite-integrity-failed",BACKOFF_SCHEDULED:"backoff-scheduled",MISSED_RUN_CATCHUP:"missed-run-catchup",HEADLESS_RUNTIME_UNRESOLVED:"headless-runtime-unresolved",HEADLESS_RUNTIME_UNTRUSTED:"headless-runtime-untrusted",HEADLESS_ADAPTER_UNAVAILABLE:"headless-adapter-unavailable",HEADLESS_RUNTIME_AUTH_REQUIRED:"headless-runtime-auth-required",HEADLESS_EXECUTION_BACKOFF:"headless-execution-backoff",HEADLESS_EXECUTION_TIMEOUT:"headless-execution-timeout",HEADLESS_EXECUTION_FAILED:"headless-execution-failed",HEADLESS_RECEIPT_REDACTED:"headless-receipt-redacted",AUTONOMOUS_GOVERNANCE_BYPASS_BLOCKED:"headless-directive-rejected"}),Le=300*1e3,Ce=30*1e3,Te=30*1e3,je=1,Ne=10,Ue=new Set(["append_event","append_receipt","update_job_status","schedule_next_tick","release_lease","noop"]),De=new Set(["goal-tick","loop-tick","jobs","runner"]),Oe=new Set(["accepted","applied","ignored","failed","deferred"]),Me=new Set(["draft","planned","active","running","waiting_external","waiting_human","backoff","paused","blocked","completed","aborted","cancelled"]),xe=/(raw[_-]?logs?|prompt(?:[_-]?(?:body|text))?|private[_-]?urls?|secret|token|password|protected(?:[_-]?(?:field|body|internals))?|workflow(?:[_-]?body)?|step(?:[_-]?body)?|customer(?:[_-]?(?:pii|data))?|authorization|api[_-]?key|license[_-]?key)/i,q=/(https?:\/\/\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[_-]?log|transcript|stack trace|stderr:|stdout:|system prompt|workflow body|step body|PROTECTED_|PRIVATE_INTERNALS|CUSTOMER_PII|VENDOR_CORPUS|P180[_-]?SYNTHETIC[_-]?CONTINUITY|P187[_-]?SYNTHETIC[_-]?(?:CONTINUITY|RUNNER|WATCH))/i,re=new Set([d.HEADLESS_RUNTIME_UNRESOLVED,d.HEADLESS_RUNTIME_UNTRUSTED,"headless-model-rejected","headless-directive-rejected","headless-prompt-rejected","headless-yoloop-metadata-invalid","headless-yoloop-isolation-violation"]),oe=new Set([d.HEADLESS_ADAPTER_UNAVAILABLE]),ae=new Set([d.HEADLESS_RUNTIME_AUTH_REQUIRED]),se=new Set([d.HEADLESS_EXECUTION_BACKOFF]),ne=new Set([d.HEADLESS_EXECUTION_TIMEOUT]),ce=new Set([d.HEADLESS_EXECUTION_FAILED]);async function Fe(e){const t=await ye({args:e.args,projectRoot:e.projectRoot,format:"json",platformTarget:"runner",serverUrl:e.serverUrl,strictReadOnly:e.strictReadOnly});return{success:t.success,instructions:t.instructions,metadata:t.metadata,error:t.error,exitCode:t.exitCode}}async function He(e={}){const t=G(e.projectRoot??process.cwd()),r=Y(e.runnerId??nt(t)),o=e.now??(()=>new Date().toISOString()),a=F(e.dueJobLimit??je,1,Ne),s=F(e.leaseMs??Le,1e3,3600*1e3),l=F(e.staleLeaseAfterMs??Ce,0,1440*60*1e3),c=e.idFactory??ct,I=he(e.processingSummarySource??(e.tickMode==="scheduled"?"scheduled_tick":"manual_tick")),y=[],R=[],i=[],u=[];if(e.trustCheck!==!1){const A=P(e.executablePath??process.argv[1]);if(!A.trusted)return{ok:!1,runnerId:r,processedJobCount:0,dueJobCount:0,recoveredLeaseIds:[],receipts:[],reasonCodes:[A.reasonCode??d.RUNNER_BINARY_UNTRUSTED],publicSafe:!0}}const m=await Z({projectRoot:t,adapter:e.adapter,adapterLoader:e.adapterLoader,now:o}),L=Be({options:e,projectRoot:t,runnerId:r,now:o});let C;try{const A=m.recoverStaleLeases({nowIso:o(),staleAfterMs:l,limit:a*2});if(A.recoveredLeaseIds.length>0){y.push(d.LEASE_EXPIRED);for(const E of A.recoveredLeaseIds);}const T=m.listDueJobs({nowIso:o(),limit:a});for(const E of T){const g=await Ve({store:m,job:E,projectRoot:t,runnerId:r,leaseMs:s,now:o,idFactory:c,processingSummarySource:I,serverUrl:e.serverUrl,serverInvoker:e.serverInvoker??Fe});R.push(g.receipt),i.push(...g.processingSummaries),g.executionMetadata&&u.push(g.executionMetadata),y.push(g.receipt.reasonCode)}const b=await Se({store:m,projectRoot:t,runnerId:r,now:o,metadata:u,adapterRegistry:e.headlessAdapterRegistry,adapterOptions:e.headlessAdapterOptions,runtimeDefaults:e.headlessRuntimeDefaults,runtimeFallback:e.headlessRuntimeFallback,limit:e.headlessExecutionLimit,leaseMs:e.headlessExecutionLeaseMs??s,staleLeaseAfterMs:e.headlessExecutionStaleLeaseAfterMs??l,timeoutMs:e.headlessExecutionTimeoutMs,backoffMs:e.headlessExecutionBackoffMs,maxAttempts:e.headlessExecutionMaxAttempts,queueCap:e.headlessExecutionQueueCap,idFactory:e.headlessExecutionIdFactory});y.push(...b.reasonCodes);const k=await Re({store:m,projectRoot:t,runnerId:r,now:o,metadata:u,adapterRegistry:e.headlessAdapterRegistry,adapterOptions:e.headlessAdapterOptions,runtimeDefaults:e.headlessRuntimeDefaults,runtimeFallback:e.headlessRuntimeFallback,limit:e.headlessExecutionLimit,leaseMs:e.headlessExecutionLeaseMs??s,staleLeaseAfterMs:e.headlessExecutionStaleLeaseAfterMs??l,timeoutMs:e.headlessExecutionTimeoutMs,backoffMs:e.headlessExecutionBackoffMs,maxAttempts:e.headlessExecutionMaxAttempts,queueCap:e.headlessExecutionQueueCap,maxIterations:e.headlessYoloopMaxIterations,maxDurationMs:e.headlessYoloopMaxDurationMs,localConcurrencyCap:e.headlessYoloopLocalConcurrencyCap,yoloopInvoker:e.headlessYoloopInvoker,serverUrl:e.serverUrl,idFactory:e.headlessExecutionIdFactory});y.push(...k.reasonCodes),C={ok:R.every(E=>E.outcome!=="failed")&&b.ok&&k.ok,runnerId:r,processedJobCount:R.length,dueJobCount:T.length,recoveredLeaseIds:A.recoveredLeaseIds,receipts:R,...i.length>0?{processingSummaries:i}:{},headlessExecution:b,headlessYoloopExecution:k,reasonCodes:J(y.length>0?y:[d.OK]),publicSafe:!0}}finally{await L.stop(),m.close()}if(!C)throw new Error("runner-tick-failed");return await Pe(C,{options:e,projectRoot:t,runnerId:r,localStateChanged:$e(C)})}async function Pe(e,t){const r=t.options.portalSync;if(r===!1||r===void 0)return e;if(!t.localStateChanged){const a=await V({...r,projectRoot:r.projectRoot??t.projectRoot,runnerId:r.runnerId??t.runnerId,sessionId:r.sessionId??t.options.portalHeartbeatSessionId,serverUrl:r.serverUrl??t.options.serverUrl,adapter:r.adapter??t.options.adapter,adapterLoader:r.adapterLoader??t.options.adapterLoader,now:r.now??t.options.now,mode:r.mode??(t.options.tickMode==="scheduled"?"scheduled":"background"),timeoutMs:r.heartbeatTimeoutMs??t.options.portalHeartbeatTimeoutMs});return{...e,portalHeartbeat:a}}const o=await te({...r,projectRoot:r.projectRoot??t.projectRoot,runnerId:r.runnerId??t.runnerId,sessionId:r.sessionId??t.options.portalHeartbeatSessionId,serverUrl:r.serverUrl??t.options.serverUrl,adapter:r.adapter??t.options.adapter,adapterLoader:r.adapterLoader??t.options.adapterLoader,now:r.now??t.options.now,mode:r.mode??(t.options.tickMode==="scheduled"?"scheduled":"background")});return{...e,portalSync:o}}function $e(e){return e.recoveredLeaseIds.length>0||e.receipts.length>0||(e.processingSummaries?.length??0)>0||(e.headlessExecution?.processedAttemptCount??0)>0||(e.headlessExecution?.recoveredAttemptIds.length??0)>0||(e.headlessYoloopExecution?.processedAttemptCount??0)>0||(e.headlessYoloopExecution?.recoveredAttemptIds.length??0)>0}function Be(e){const t=e.options.portalSync;if(t===!1||t===void 0)return{stop:async()=>{}};const r=F(e.options.portalHeartbeatIntervalMs??ke,0,1440*60*1e3);if(r<=0)return{stop:async()=>{}};const o=F(e.options.portalHeartbeatBackoffMs??_e,1e3,1800*1e3),a=pe(e.options.portalHeartbeatJitterRatio??ge,0,.5),s=F(e.options.portalHeartbeatTimeoutMs??t.heartbeatTimeoutMs??Ae,1e3,6e4);let l=!1,c,I,y=me(r,a,Math.random);const R=u=>new Promise(m=>{I=m,c=setTimeout(m,u),typeof c.unref=="function"&&c.unref()}),i=(async()=>{for(;!l&&(y>0&&await R(y),!l);){const u=await V({...t,projectRoot:t.projectRoot??e.projectRoot,runnerId:t.runnerId??e.runnerId,sessionId:t.sessionId??e.options.portalHeartbeatSessionId,serverUrl:t.serverUrl??e.options.serverUrl,adapter:t.adapter??e.options.adapter,adapterLoader:t.adapterLoader??e.options.adapterLoader,now:t.now??e.now,mode:t.mode??(e.options.tickMode==="scheduled"?"scheduled":"background"),timeoutMs:s}).catch(()=>({ok:!1,runnerId:e.runnerId,workspaceId:"workspace-default",workspaceLabel:"Workspace workspace-default",projectId:"project-default",projectLabel:"Project project-default",mode:e.options.tickMode==="scheduled"?"scheduled":"background",generatedAt:e.now(),serverAccessToLocalhost:!1,serverHeldStream:!1,localhostCallback:!1,posted:{heartbeat:!1},counts:{redactions:0},reasonCodes:[d.BACKOFF_SCHEDULED],redactionEvidence:[],publicSafe:!0}));y=me(u.ok?r:o,a,Math.random)}})();return{stop:async()=>{l=!0,c&&clearTimeout(c),I?.(),await i.catch(()=>{})}}}async function Ke(e={}){const t=G(e.projectRoot??process.cwd()),r=e.now??(()=>new Date().toISOString()),o=r(),a={};let s=null,l=null;try{l=await Z({projectRoot:t,adapter:e.adapter,adapterLoader:e.adapterLoader,now:r}),s=l.collectSnapshotRows({nowIso:o,jobLimit:10,staleLeaseLimit:10,receiptLimit:10,eventLimit:10,snapshotLimit:5});const f=String(l.pragmaValue("journal_mode")??"").toLowerCase();a["db-integrity"]=w("db-integrity","DB integrity","ok",[d.OK],"Continuity SQLite integrity check completed.",{jobs:_(s.counts,"jobs"),receipts:_(s.counts,"receipts"),events:_(s.counts,"events")}),a["sqlite-wal"]=w("sqlite-wal","SQLite WAL",f==="wal"?"ok":"blocked",f==="wal"?[d.OK]:[d.SQLITE_INTEGRITY_FAILED],f==="wal"?"SQLite journal mode is WAL.":"SQLite journal mode is not WAL."),a.migrations=w("migrations","Migrations",s.schemaVersion>=1?"ok":"blocked",s.schemaVersion>=1?[d.OK]:[d.SQLITE_INTEGRITY_FAILED],`Continuity schema version ${s.schemaVersion} is readable.`)}catch(f){const p=ue(f);a["db-integrity"]=w("db-integrity","DB integrity","blocked",[p],"Continuity SQLite could not be opened safely."),a["sqlite-wal"]=w("sqlite-wal","SQLite WAL","blocked",[p],"SQLite WAL could not be verified."),a.migrations=w("migrations","Migrations","blocked",[p],"Continuity migrations could not be verified.")}finally{if(l)try{l.close()}catch{}}const c=s?_(s.counts,"staleLeases"):0,I=s?_(s.counts,"staleHeadlessExecutionAttempts"):0;a.leases=w("leases","Lease health",c+I>0?"degraded":"ok",c+I>0?[d.LEASE_EXPIRED]:[d.OK],c+I>0?"Stale job leases or headless execution attempts are eligible for bounded recovery.":"No stale leases or headless attempts reported.",{staleLeases:c,staleHeadlessExecutionAttempts:I});const y=s?.headlessExecutionAttempts??[],R=J(y.map(f=>f.reason_code).filter(f=>typeof f=="string"&&f.length>0)),i=ft(y,R),u=new Set([d.HEADLESS_RUNTIME_UNRESOLVED,d.HEADLESS_RUNTIME_UNTRUSTED,d.HEADLESS_ADAPTER_UNAVAILABLE,d.HEADLESS_RUNTIME_AUTH_REQUIRED,"headless-model-rejected","headless-directive-rejected","headless-prompt-rejected","headless-yoloop-metadata-invalid","headless-yoloop-isolation-violation"]),m=s?_(s.counts,"headlessExecutionAttempts"):0,L=R.some(f=>u.has(f)),C=R.includes(d.HEADLESS_EXECUTION_BACKOFF)||y.some(f=>f.status==="backoff"),A=R.includes(d.HEADLESS_RECEIPT_REDACTED),T=i.authRequired>0,b=i.trustFailures>0||i.adapterUnavailable>0,k=I>0||C||A||i.timeout>0||i.genericFailed>0||i.redactions>0;a["headless-execution"]=w("headless-execution","Headless execution",L?"blocked":k?"degraded":m>0?"ok":"delegated",R.length>0?R:m>0?[d.OK]:[d.HEADLESS_RUNTIME_UNRESOLVED],L?T&&!b?"Complete local runtime login/auth setup before starting a new headless attempt; default retry/backoff is not scheduled for auth-required.":"Resolve public runtime/model trust, adapter availability or protected directive hard-stop reason codes before executing headless attempts.":k?"Headless attempts need bounded recovery, retry/backoff, timeout/generic failure follow-up or redaction review.":m>0?"Headless attempts are represented by public status, runtime/model and reason-code evidence.":"Headless resolver checks are delegated until durable headless work is queued.",{attempts:m,queuedBackoff:s?_(s.counts,"headlessQueuedAttempts"):0,running:s?_(s.counts,"headlessRunningAttempts"):0,staleHeadlessExecutionAttempts:I,trustFailures:i.trustFailures,adapterUnavailable:i.adapterUnavailable,authRequired:i.authRequired,retryBackoff:i.retryBackoff,timeout:i.timeout,genericFailed:i.genericFailed,redactions:i.redactions});const E=s?.outboxCounts??{},g=_(E,"failed")+_(E,"dead_letter");a.outbox=w("outbox","Outbox state",g>0?"degraded":"ok",g>0?[d.BACKOFF_SCHEDULED]:[d.OK],g>0?"Failed/dead-letter outbox entries can be retried with dedupe.":"Outbox has no failed/dead-letter entries.",{pending:_(E,"pending"),failed:_(E,"failed"),deadLetter:_(E,"dead_letter")}),a.metering=w("metering","Metering receipts","ok",[d.OK],"Metering evidence is represented by public job receipts and counts only.",{receipts:s?_(s.counts,"receipts"):0}),a["portal-sync"]=w("portal-sync","Portal sync",g>0?"degraded":"ok",g>0?[d.BACKOFF_SCHEDULED]:[d.OK],g>0?"Portal sync worker is enabled but has retryable outbox debt; outbound sync remains client-owned.":"Portal sync worker is enabled for outbound heartbeat, jobs sync, command polling and acknowledgement only.",{pending:_(E,"pending"),failed:_(E,"failed"),deadLetter:_(E,"dead_letter")});const N=e.trustCheck===!1?{trusted:!0,publicSafe:!0}:P(e.executablePath??process.argv[1]);a.runner=w("runner","Runner health",N.trusted?"ok":"blocked",N.trusted?[d.OK]:[N.reasonCode??d.RUNNER_BINARY_UNTRUSTED],N.trusted?"Runner binary trust check passed.":"Runner binary trust check failed.");try{const f=await ee({command:"doctor",projectRoot:t,executablePath:e.scheduler?.executablePath??e.executablePath??process.argv[1],platform:e.scheduler?.platform,env:e.scheduler?.env,dryRun:!0,intervalSeconds:e.scheduler?.intervalSeconds,strategy:e.scheduler?.strategy,allowCronFallback:e.scheduler?.allowCronFallback,trustCheck:e.scheduler?.trustCheck??e.trustCheck,uid:e.scheduler?.uid,commandAvailable:e.scheduler?.commandAvailable,commandRunner:e.scheduler?.commandRunner,trustChecker:e.scheduler?.trustChecker??P}),p=f.reasonCodes.includes(d.RUNNER_BINARY_UNTRUSTED)||f.reasonCodes.some(h=>h.startsWith("runner-binary-"))||f.reasonCodes.includes("scheduler-install-unsafe");a.scheduler=w("scheduler","Scheduler install",p?"blocked":f.ok?"ok":"degraded",f.reasonCodes,f.ok?"Scheduler doctor selected a user-scope strategy.":"Scheduler doctor found public guidance or degraded capability.",{manifests:f.manifestPaths.length,controlCommands:f.controlCommands.length,legacyEntries:f.legacyEntries.length})}catch(f){a.scheduler=w("scheduler","Scheduler install","degraded",["scheduler-command-failed"],x(f))}const v=a,H=J(Object.values(v).flatMap(f=>f.reasonCodes));return{ok:Object.values(v).every(f=>f.status!=="blocked"),generatedAt:o,checks:v,reasonCodes:H,publicSafe:!0}}function de(e){if(!e)return[];const t=[e.continuityRunnerDirectives,e.runnerDirectives,e.continuityDirectives];for(const r of t)if(Array.isArray(r))return r.filter(j).map(it).filter(lt);return[]}function Je(e,t){if(!j(e))throw new U("Directive is not a public record.");if(e.publicSafe!==!0)throw new U("Directive is not marked public-safe.");const r=typeof e.kind=="string"?e.kind:typeof e.type=="string"?e.type:"";if(!Ue.has(r))throw new U("Directive kind is not allowlisted.");const o=typeof e.commandClass=="string"?e.commandClass:"runner";if(!De.has(o))throw new U("Directive command class is not allowlisted.");if((typeof e.jobId=="string"?e.jobId:t)!==t)throw new U("Directive job ID does not match the leased job.");Q(e,"directive")}async function wt(e,t={}){const r=tt(e);if(r.command==="help")return process.stdout.write(`${ot}
2
+ `),0;if(r.command==="version"){const o=P(t.executablePath??process.argv[1]);return process.stdout.write(`${o.packageVersion??"0.0.0"}
3
+ `),o.trusted?0:1}if(r.command==="doctor")try{const o=await Ke({...t,projectRoot:r.projectRoot??t.projectRoot,executablePath:t.executablePath??process.argv[1],scheduler:{...t.scheduler,strategy:r.strategy??t.scheduler?.strategy,allowCronFallback:r.allowCronFallback??t.scheduler?.allowCronFallback,executablePath:t.scheduler?.executablePath??t.executablePath??process.argv[1]}});return process.stdout.write(JSON.stringify(o)+`
4
+ `),o.ok?0:1}catch(o){return process.stderr.write(JSON.stringify({error_code:"runner-doctor-failed",message:x(o)})+`
5
+ `),1}if(le(r.command))try{const o=await ee({command:r.command,projectRoot:r.projectRoot??t.projectRoot,executablePath:t.scheduler?.executablePath??t.executablePath??process.argv[1],platform:t.scheduler?.platform,env:t.scheduler?.env,dryRun:r.dryRun??t.scheduler?.dryRun,intervalSeconds:r.intervalSeconds??t.scheduler?.intervalSeconds,strategy:r.strategy??t.scheduler?.strategy,allowCronFallback:r.allowCronFallback??t.scheduler?.allowCronFallback,trustCheck:t.scheduler?.trustCheck,uid:t.scheduler?.uid,commandAvailable:t.scheduler?.commandAvailable,commandRunner:t.scheduler?.commandRunner,trustChecker:t.scheduler?.trustChecker??P});return process.stdout.write(JSON.stringify(st(o))+`
6
+ `),o.ok?0:1}catch(o){return process.stderr.write(JSON.stringify({error_code:"scheduler-command-failed",message:x(o)})+`
7
+ `),1}if(r.command==="heartbeat")try{if(r.loop){const a=await ve({...t,projectRoot:r.projectRoot??t.projectRoot,runnerId:r.runnerId??t.runnerId,sessionId:r.sessionId??t.portalHeartbeatSessionId,serverUrl:r.serverUrl??t.serverUrl,workspaceId:r.workspaceId,workspaceLabel:r.workspaceLabel,projectId:r.projectId,projectLabel:r.projectLabel,mode:r.scheduled?"scheduled":"background",intervalMs:r.intervalMs??t.portalHeartbeatIntervalMs,maxRuns:r.maxRuns??t.portalSyncMaxRuns,backoffMs:r.heartbeatBackoffMs??t.portalHeartbeatBackoffMs,timeoutMs:r.heartbeatTimeoutMs??t.portalHeartbeatTimeoutMs,jitterRatio:r.heartbeatJitterRatio??t.portalHeartbeatJitterRatio});return process.stdout.write(JSON.stringify(a)+`
8
+ `),a.ok?0:1}const o=await V({...t,projectRoot:r.projectRoot??t.projectRoot,runnerId:r.runnerId??t.runnerId,sessionId:r.sessionId??t.portalHeartbeatSessionId,serverUrl:r.serverUrl??t.serverUrl,workspaceId:r.workspaceId,workspaceLabel:r.workspaceLabel,projectId:r.projectId,projectLabel:r.projectLabel,mode:r.scheduled?"scheduled":"manual",timeoutMs:r.heartbeatTimeoutMs??t.portalHeartbeatTimeoutMs});return process.stdout.write(JSON.stringify(o)+`
9
+ `),o.ok?0:1}catch(o){return process.stderr.write(JSON.stringify({error_code:"portal-heartbeat-failed",message:x(o)})+`
10
+ `),1}if(r.command==="sync")try{if(r.loop){const a=await we({...t,projectRoot:r.projectRoot??t.projectRoot,runnerId:r.runnerId??t.runnerId,serverUrl:r.serverUrl??t.serverUrl,workspaceId:r.workspaceId,workspaceLabel:r.workspaceLabel,projectId:r.projectId,projectLabel:r.projectLabel,syncCursor:r.syncCursor,mode:r.scheduled?"scheduled":"background",intervalMs:r.intervalMs??t.portalSyncIntervalMs,maxRuns:r.maxRuns??t.portalSyncMaxRuns});return process.stdout.write(JSON.stringify(a)+`
11
+ `),a.ok?0:1}const o=await te({...t,projectRoot:r.projectRoot??t.projectRoot,runnerId:r.runnerId??t.runnerId,serverUrl:r.serverUrl??t.serverUrl,workspaceId:r.workspaceId,workspaceLabel:r.workspaceLabel,projectId:r.projectId,projectLabel:r.projectLabel,syncCursor:r.syncCursor,mode:r.scheduled?"scheduled":"manual"});return process.stdout.write(JSON.stringify(o)+`
12
+ `),o.ok?0:1}catch(o){return process.stderr.write(JSON.stringify({error_code:"portal-sync-failed",message:x(o)})+`
13
+ `),1}if(r.command!=="tick")return process.stderr.write(JSON.stringify({error_code:"runner-command-unknown",message:"Unknown neocortex-runner command."})+`
14
+ `),1;try{const o=await He({...t,projectRoot:r.projectRoot??t.projectRoot,runnerId:r.runnerId??t.runnerId,dueJobLimit:r.limit??t.dueJobLimit,leaseMs:r.leaseMs??t.leaseMs,staleLeaseAfterMs:r.staleLeaseAfterMs??t.staleLeaseAfterMs,headlessYoloopLocalConcurrencyCap:r.yoloopLocalCap??t.headlessYoloopLocalConcurrencyCap,tickMode:r.scheduled?"scheduled":t.tickMode,processingSummarySource:r.scheduled?"scheduled_tick":t.processingSummarySource,serverUrl:r.serverUrl??t.serverUrl,portalSync:rt(r,t,r.scheduled?"scheduled":"background")});return process.stdout.write(JSON.stringify(o)+`
15
+ `),o.ok?0:1}catch(o){const a=ue(o);return process.stderr.write(JSON.stringify({error_code:a,message:x(o)})+`
16
+ `),1}}async function Ve(e){const{store:t,job:r,projectRoot:o,runnerId:a,leaseMs:s,now:l,idFactory:c,processingSummarySource:I,serverUrl:y,serverInvoker:R}=e,i=r.job_id,u=c("tick",i),m=c("lease",i),L=l(),C=new Date(Date.parse(L)+s).toISOString();let A=!1,T=!1,b=d.OK,k="applied",E=[];if(!t.acquireLease({leaseId:m,jobId:i,runnerId:a,acquiredAt:L,expiresAt:C,operationId:`${u}:acquire`,idempotencyKey:`${u}:acquire`,metadata:M({jobId:i,operationId:u,runnerId:a,leaseId:m,status:"running",reasonCode:d.OK})}).inserted)return{receipt:K(t,{jobId:i,tickId:u,leaseId:m,status:"ignored",reasonCode:d.LEASE_CONFLICT,createdAt:l(),redactionSummary:E}),processingSummaries:[]};A=!0;try{t.appendEvent({eventId:`${u}:started`,jobId:i,operationId:`${u}:event:started`,idempotencyKey:`${u}:event:started`,type:"runner_tick_started",summary:`Runner tick started for ${i}.`,reasonCode:d.OK,createdAt:L,metadata:M({jobId:i,operationId:u,runnerId:a,leaseId:m,status:"running",reasonCode:d.OK})});const N=r.kind==="loop"?`*loop tick @${i} --tick-id ${u} --lease-id ${m} --runner-id ${a}`:r.kind==="goal"?`*goal tick @${i} --tick-id ${u} --lease-id ${m} --runner-id ${a}`:`*runner tick --job-id ${i} --tick-id ${u} --lease-id ${m} --runner-id ${a}`,v=await R({projectRoot:o,args:N,jobId:i,tickId:u,leaseId:m,runnerId:a,serverUrl:y});if(!v.success){b=d.RUNNER_SERVER_INVOKE_FAILED,k="failed";const O=l();return t.updateJobStatus({jobId:i,status:"backoff",updatedAt:O,reasonCode:d.BACKOFF_SCHEDULED,nextRunAt:dt(O,Te)}),t.appendEvent({eventId:`${u}:server-failed`,jobId:i,operationId:`${u}:event:server-failed`,idempotencyKey:`${u}:event:server-failed`,type:"runner_tick_failed",summary:"Runner server tick invocation failed with public reason code.",reasonCode:b,createdAt:O,metadata:M({jobId:i,operationId:u,runnerId:a,leaseId:m,status:"failed",reasonCode:b})}),{receipt:K(t,{jobId:i,tickId:u,leaseId:m,status:k,reasonCode:b,createdAt:l(),redactionSummary:E}),processingSummaries:[]}}const f=de(v.metadata).filter(O=>!ie(O));let p={appliedCount:0,releasedLease:!1,changedStatus:!1,redactionSummary:[]};try{p=qe(t,f,{jobId:i,tickId:u,leaseId:m,runnerId:a,now:l}),T=p.releasedLease,E=[...p.redactionSummary],b=et(v.metadata)??b}catch(O){return b=d.RUNNER_DIRECTIVE_UNTRUSTED,k="failed",E=[O instanceof Error?O.message:"Directive rejected."],t.updateJobStatus({jobId:i,status:"blocked",updatedAt:l(),reasonCode:b}),t.appendEvent({eventId:`${u}:directive-rejected`,jobId:i,operationId:`${u}:event:directive-rejected`,idempotencyKey:`${u}:event:directive-rejected`,type:"runner_directive_rejected",summary:"Runner rejected an untrusted continuity directive.",reasonCode:b,createdAt:l(),metadata:M({jobId:i,operationId:u,runnerId:a,leaseId:m,status:"failed",reasonCode:b,redactionSummary:E})}),{receipt:K(t,{jobId:i,tickId:u,leaseId:m,status:k,reasonCode:b,createdAt:l(),redactionSummary:E}),processingSummaries:[]}}const h=Ye(t,v.metadata,{jobId:i,tickId:u,leaseId:m,runnerId:a,source:I,createdAt:l()}),S=!!v.metadata?.continuityStateUpdate;return!p.changedStatus&&!S&&t.updateJobStatus({jobId:i,status:"active",updatedAt:l(),reasonCode:d.OK}),t.appendEvent({eventId:`${u}:completed`,jobId:i,operationId:`${u}:event:completed`,idempotencyKey:`${u}:event:completed`,type:"runner_tick_completed",summary:`Runner tick completed with ${p.appliedCount} public directive(s).`,reasonCode:b,createdAt:l(),metadata:M({jobId:i,operationId:u,runnerId:a,leaseId:m,status:"applied",reasonCode:b})}),{receipt:K(t,{jobId:i,tickId:u,leaseId:m,status:k,reasonCode:b,createdAt:l(),redactionSummary:E}),processingSummaries:h,...v.metadata?{executionMetadata:v.metadata}:{}}}finally{if(A&&!T)try{t.releaseLease({leaseId:m,releasedAt:l(),reasonCode:b})}catch{}}}function qe(e,t,r){let o=0,a=!1,s=!1;const l=[];for(const c of t){switch(Je(c,r.jobId),c.kind){case"noop":o+=1;break;case"append_event":e.appendEvent({eventId:n(c,"eventId")??`${r.tickId}:directive:${o}:event`,jobId:r.jobId,operationId:n(c,"operationId")??`${r.tickId}:directive:${o}:event`,idempotencyKey:n(c,"idempotencyKey")??`${r.tickId}:directive:${o}:event`,type:n(c,"eventType")??"runner_directive_event",summary:n(c,"summary")??"Runner applied public continuity event directive.",reasonCode:n(c,"reasonCode")??d.OK,createdAt:n(c,"createdAt")??r.now(),metadata:fe(c)}),o+=1;break;case"append_receipt":e.appendReceipt({receiptId:n(c,"receiptId")??`${r.tickId}:directive:${o}:receipt`,jobId:r.jobId,operationId:n(c,"operationId")??`${r.tickId}:directive:${o}:receipt`,idempotencyKey:n(c,"idempotencyKey")??`${r.tickId}:directive:${o}:receipt`,status:pt(n(c,"status")),reasonCode:n(c,"reasonCode")??d.OK,createdAt:n(c,"createdAt")??r.now(),metadata:fe(c)}),o+=1;break;case"update_job_status":e.updateJobStatus({jobId:r.jobId,status:mt(n(c,"status")),updatedAt:n(c,"updatedAt")??r.now(),reasonCode:n(c,"reasonCode")??d.OK,nextRunAt:n(c,"nextRunAt")}),o+=1,s=!0;break;case"schedule_next_tick":e.scheduleNextTick({jobId:r.jobId,scheduledAt:ut(c,"scheduledAt"),updatedAt:n(c,"updatedAt")??r.now(),reasonCode:n(c,"reasonCode")??d.OK}),o+=1,s=!0;break;case"release_lease":e.releaseLease({leaseId:n(c,"leaseId")??r.leaseId,releasedAt:n(c,"releasedAt")??r.now(),reasonCode:n(c,"reasonCode")??d.OK}),o+=1,a=!0;break}const I=c.redactionSummary;Array.isArray(I)&&l.push(...I.filter(y=>typeof y=="string").slice(0,5))}return{appliedCount:o,releasedLease:a,changedStatus:s,redactionSummary:l}}function Ye(e,t,r){const o=[],a=new Set;for(const s of Qe(t)){const l=ze(s,r);if(!(!l||a.has(l.executionId))){a.add(l.executionId);try{e.recordProcessingOutputSummary(l),o.push(Xe(l))}catch(c){if(c instanceof Ie&&c.code==="sqlite-public-safety-rejected")continue;throw c}}}return o}function Qe(e){if(!e)return[];const t=[],r=D(e,"continuityStateUpdate"),o=$(r,"operations");for(const a of o){if(!j(a))continue;const s=D(a,"event");!s||n(s,"type")!==X||t.push({operation:a,event:s,summary:D(s,"processingOutputSummary")??s})}for(const a of de(e))ie(a)&&t.push({directive:a,event:a,summary:D(a,"processingOutputSummary")??a});for(const a of[...$(e,"processingSummaries"),...$(e,"processingOutputSummaries"),...$(e,"continuityProcessingSummaries"),...$(D(e,"continuity"),"processingSummaries")])j(a)&&t.push({event:a,summary:a});return t}function ze(e,t){if(!We(e))return null;const r=e.event??{},o=e.summary??r,a=e.operation??{},s=e.directive??{},l=n(o,"executionId")??n(r,"executionId")??n(a,"operationId")??n(s,"operationId")??t.tickId,c=n(o,"jobId")??n(r,"jobId")??n(a,"jobId")??n(s,"jobId")??t.jobId;if(c!==t.jobId)return null;const I=n(o,"publicSummary")??n(r,"publicSummary")??n(r,"summary")??n(s,"summary");if(!l||!I)return null;const y=Ge(D(a,"metadata"),D(r,"metadata"),D(o,"metadata"),D(s,"metadata"),{schemaVersion:1,contractVersion:"continuity-v1",executionId:l,jobId:c,operationId:l,runnerId:t.runnerId,leaseId:t.leaseId,processingOutputSource:t.source,publicSummary:I});return{eventId:n(r,"eventId")??n(o,"eventId")??n(s,"eventId"),executionId:l,jobId:c,idempotencyKey:n(a,"idempotencyKey")??n(s,"idempotencyKey")??l,goalId:n(o,"goalId")??n(r,"goalId")??n(a,"goalId"),loopId:n(o,"loopId")??n(r,"loopId")??n(a,"loopId"),publicSummary:I,reasonCode:n(o,"reasonCode")??n(r,"reasonCode")??n(a,"reasonCode")??n(s,"reasonCode"),createdAt:n(o,"createdAt")??n(r,"createdAt")??n(a,"createdAt")??n(s,"createdAt")??t.createdAt,source:t.source,metadata:y}}function ie(e){return e.kind==="append_event"&&(n(e,"eventType")??n(e,"type"))===X}function We(e){const t=[e.operation,e.event,e.summary,e.directive].filter(r=>!!r).map(r=>r.publicSafe);return t.some(r=>r===!1)?!1:t.some(r=>r===!0)}function Ge(...e){const t={};for(const r of e)r&&Object.assign(t,r);return t}function Xe(e){return{eventId:e.eventId??Ze(e.executionId),executionId:e.executionId,jobId:e.jobId,publicSummary:e.publicSummary,...e.reasonCode?{reasonCode:e.reasonCode}:{},createdAt:e.createdAt,source:he(e.source),publicSafe:!0}}function Ze(e){const t=e.replace(/[^a-z0-9_.:-]+/gi,"-").replace(/^-+|-+$/g,"");return t.length>0?`event-processing-summary-${t.slice(0,160)}`:`event-processing-summary-${W("sha256").update(e).digest("hex").slice(0,32)}`}function K(e,t){const r={receiptId:`receipt-${Y(t.tickId)}`,jobId:t.jobId,operationId:t.tickId,idempotencyKey:t.tickId,status:t.status,reasonCode:t.reasonCode,appliedOperationIds:t.status==="applied"?[t.tickId]:void 0,ignoredOperationIds:t.status==="ignored"?[t.tickId]:void 0,createdAt:t.createdAt,metadata:M({jobId:t.jobId,operationId:t.tickId,leaseId:t.leaseId,status:t.status,reasonCode:t.reasonCode,redactionSummary:t.redactionSummary})};return e.appendReceipt(r),{jobId:t.jobId,tickId:t.tickId,leaseId:t.leaseId,outcome:t.status,reasonCode:t.reasonCode,timestamp:t.createdAt,redactionSummary:t.redactionSummary,publicSafe:!0}}function M(e){return{schemaVersion:1,contractVersion:"continuity-v1",jobId:e.jobId,operationId:e.operationId,...e.runnerId?{runnerId:e.runnerId}:{},...e.leaseId?{leaseId:e.leaseId}:{},status:e.status,reasonCode:e.reasonCode,redactionEvidence:(e.redactionSummary??[]).slice(0,20).map(t=>({field:z(t,80),reason:"unsafe_key",evidence:"operation-denied",publicSafe:!0}))}}function et(e){if(!e)return null;const t=j(e.continuity)?e.continuity:void 0,r=t&&j(t.loop)?t.loop:void 0,o=t&&j(t.goal)?t.goal:void 0,a=[r?.reasonCode,o?.reasonCode,t?.reasonCode];for(const s of a)if(typeof s=="string"&&/^[a-z0-9][a-z0-9-]{0,79}$/.test(s)&&!q.test(s))return s;return null}function tt(e){const t=e[0];if(!t||t==="--help"||t==="-h"||t==="help")return{command:"help"};if(t==="--version"||t==="-v"||t==="version")return{command:"version"};if(t!=="tick"&&t!=="sync"&&t!=="heartbeat"&&!le(t))return{command:"unknown"};let r,o,a,s,l,c,I,y,R,i,u,m,L,C,A,T,b,k,E,g,N,v,H,f;for(let p=1;p<e.length;p++){const h=e[p],S=e[p+1];if(h==="--project-root"){r=S,p+=1;continue}if(h==="--runner-id"){o=S,p+=1;continue}if(h==="--server-url"){a=S,p+=1;continue}if(h==="--limit"){s=Number(S),p+=1;continue}if(h==="--lease-ms"){l=Number(S),p+=1;continue}if(h==="--stale-lease-after-ms"){c=Number(S),p+=1;continue}if(h==="--dry-run"){I=!0;continue}if(h==="--interval-seconds"){y=Number(S),p+=1;continue}if(h==="--interval-ms"){R=Number(S),p+=1;continue}if(h==="--max-runs"){i=Number(S),p+=1;continue}if(h==="--backoff-ms"){u=Number(S),p+=1;continue}if(h==="--heartbeat-timeout-ms"||h==="--timeout-ms"){m=Number(S),p+=1;continue}if(h==="--jitter-ratio"){L=Number(S),p+=1;continue}if(h==="--session-id"){C=S,p+=1;continue}if(h==="--strategy"&&at(S)){A=S,p+=1;continue}if(h==="--allow-cron-fallback"){T=!0;continue}if(h==="--no-cron-fallback"){T=!1;continue}if(h==="--scheduled"){b=!0;continue}if(h==="--manual"){b=!1;continue}if(h==="--loop"||h==="--background"){k=!0;continue}if(h==="--once"){k=!1;continue}if(h==="--workspace-id"){E=S,p+=1;continue}if(h==="--workspace-label"){g=S,p+=1;continue}if(h==="--project-id"){N=S,p+=1;continue}if(h==="--project-label"){v=S,p+=1;continue}if(h==="--sync-cursor"){H=S,p+=1;continue}if(h==="--yoloop-local-cap"){f=Number(S),p+=1;continue}}return{command:t,projectRoot:r,runnerId:o,serverUrl:a,limit:s,leaseMs:l,staleLeaseAfterMs:c,dryRun:I,intervalSeconds:y,intervalMs:R,maxRuns:i,heartbeatBackoffMs:u,heartbeatTimeoutMs:m,heartbeatJitterRatio:L,sessionId:C,strategy:A,allowCronFallback:T,scheduled:b,loop:k,workspaceId:E,workspaceLabel:g,projectId:N,projectLabel:v,syncCursor:H,yoloopLocalCap:f}}function rt(e,t,r){return t.portalSync===!1?!1:{...t.portalSync??{},...e.projectRoot??t.projectRoot?{projectRoot:e.projectRoot??t.projectRoot}:{},...e.runnerId??t.runnerId?{runnerId:e.runnerId??t.runnerId}:{},...e.sessionId??t.portalHeartbeatSessionId?{sessionId:e.sessionId??t.portalHeartbeatSessionId}:{},...e.serverUrl??t.serverUrl?{serverUrl:e.serverUrl??t.serverUrl}:{},...e.workspaceId?{workspaceId:e.workspaceId}:{},...e.workspaceLabel?{workspaceLabel:e.workspaceLabel}:{},...e.projectId?{projectId:e.projectId}:{},...e.projectLabel?{projectLabel:e.projectLabel}:{},...e.heartbeatTimeoutMs??t.portalHeartbeatTimeoutMs?{heartbeatTimeoutMs:e.heartbeatTimeoutMs??t.portalHeartbeatTimeoutMs}:{},mode:r}}const ot=`
1362
17
  neocortex-runner - Enterprise continuity local runner
1363
18
 
1364
19
  Usage:
@@ -1412,213 +67,4 @@ Headless doctor evidence:
1412
67
  only bounded attempt status, runtime/model IDs, counts, reason codes, cursors and
1413
68
  redaction counts; raw adapter stdout/stderr, prompts, transcripts, private URLs,
1414
69
  local absolute paths, secrets and vendor payloads are never printed.
1415
- `.trim();
1416
- function isSchedulerCommand(value) {
1417
- return value === 'install' || value === 'start' || value === 'stop' || value === 'status' || value === 'doctor';
1418
- }
1419
- function isSchedulerStrategy(value) {
1420
- return value === 'systemd-user' || value === 'launchd-user' || value === 'windows-task-scheduler' || value === 'cron-fallback';
1421
- }
1422
- class RunnerDirectiveError extends Error {
1423
- constructor(message) {
1424
- super(message);
1425
- this.name = 'RunnerDirectiveError';
1426
- }
1427
- }
1428
- function classifyRunnerError(error) {
1429
- if (isRecord(error)) {
1430
- const code = typeof error.reasonCode === 'string' ? error.reasonCode : typeof error.code === 'string' ? error.code : undefined;
1431
- if (code === RUNNER_REASON_CODES.SQLITE_ADAPTER_UNAVAILABLE || code === RUNNER_REASON_CODES.SQLITE_INTEGRITY_FAILED)
1432
- return code;
1433
- }
1434
- const message = error instanceof Error ? error.message : String(error);
1435
- if (/sqlite-adapter-unavailable/i.test(message))
1436
- return RUNNER_REASON_CODES.SQLITE_ADAPTER_UNAVAILABLE;
1437
- if (/sqlite-integrity-failed|not a database|file is not a database|database disk image is malformed|malformed/i.test(message))
1438
- return RUNNER_REASON_CODES.SQLITE_INTEGRITY_FAILED;
1439
- return 'runner-tick-failed';
1440
- }
1441
- function publicErrorMessage(error) {
1442
- return boundPublicString(error instanceof Error ? error.message : String(error), 240);
1443
- }
1444
- function publicRunnerSchedulerCommandResult(summary) {
1445
- if (summary.manifests.length === 0)
1446
- return summary;
1447
- return {
1448
- ...summary,
1449
- manifests: summary.manifests.map((manifest) => ({
1450
- ...manifest,
1451
- content: `[redacted ${manifest.kind} for project ${summary.identity.projectKey}]`,
1452
- })),
1453
- };
1454
- }
1455
- function defaultRunnerId(projectRoot) {
1456
- const hash = createHash('sha256').update(projectRoot).update(process.pid.toString()).digest('hex').slice(0, 12);
1457
- return `runner-${hash}`;
1458
- }
1459
- function defaultIdFactory(prefix, jobId) {
1460
- const entropy = randomBytes(4).toString('hex');
1461
- return `${prefix}-${sanitizeId(jobId)}-${Date.now()}-${entropy}`;
1462
- }
1463
- function sanitizeId(value) {
1464
- return value.replace(/[^a-z0-9_.:-]+/gi, '-').slice(0, 120);
1465
- }
1466
- function boundedInteger(value, min, max) {
1467
- if (!Number.isFinite(value))
1468
- return min;
1469
- return Math.max(min, Math.min(max, Math.floor(value)));
1470
- }
1471
- function boundedNumber(value, min, max) {
1472
- if (!Number.isFinite(value))
1473
- return min;
1474
- return Math.max(min, Math.min(max, value));
1475
- }
1476
- function jitteredDelayMs(baseDelayMs, jitterRatio, random) {
1477
- if (baseDelayMs <= 0)
1478
- return 0;
1479
- if (jitterRatio <= 0)
1480
- return baseDelayMs;
1481
- const boundedRandom = boundedNumber(random(), 0, 1);
1482
- return Math.max(0, baseDelayMs + Math.floor(baseDelayMs * jitterRatio * boundedRandom));
1483
- }
1484
- function addMilliseconds(iso, ms) {
1485
- const base = Date.parse(iso);
1486
- return new Date((Number.isFinite(base) ? base : Date.now()) + ms).toISOString();
1487
- }
1488
- function normalizeDirective(value) {
1489
- return typeof value.kind === 'string' ? value : { ...value, kind: value.type };
1490
- }
1491
- function isRunnerDirective(value) {
1492
- return typeof value.kind === 'string' && value.publicSafe === true;
1493
- }
1494
- function assertPublicSafeDirectiveValue(value, path) {
1495
- if (typeof value === 'string') {
1496
- if (UNSAFE_VALUE_PATTERN.test(value))
1497
- throw new RunnerDirectiveError(`Unsafe directive value at ${path}.`);
1498
- return;
1499
- }
1500
- if (value === null || typeof value === 'number' || typeof value === 'boolean')
1501
- return;
1502
- if (Array.isArray(value)) {
1503
- value.forEach((item, index) => assertPublicSafeDirectiveValue(item, `${path}[${index}]`));
1504
- return;
1505
- }
1506
- if (isRecord(value)) {
1507
- for (const [key, child] of Object.entries(value)) {
1508
- if (UNSAFE_KEY_PATTERN.test(key))
1509
- throw new RunnerDirectiveError(`Unsafe directive key at ${path}.${key}.`);
1510
- assertPublicSafeDirectiveValue(child, `${path}.${key}`);
1511
- }
1512
- return;
1513
- }
1514
- throw new RunnerDirectiveError(`Unsupported directive value at ${path}.`);
1515
- }
1516
- function stringField(input, key) {
1517
- const value = input[key];
1518
- return typeof value === 'string' && value.length > 0 ? value : undefined;
1519
- }
1520
- function requiredDirectiveString(input, key) {
1521
- const value = stringField(input, key);
1522
- if (!value)
1523
- throw new RunnerDirectiveError(`Directive missing ${key}.`);
1524
- return value;
1525
- }
1526
- function metadataField(input) {
1527
- const value = input.metadata;
1528
- return isRecord(value) ? value : undefined;
1529
- }
1530
- function recordField(input, key) {
1531
- if (!input)
1532
- return undefined;
1533
- const value = input[key];
1534
- return isRecord(value) ? value : undefined;
1535
- }
1536
- function arrayField(input, key) {
1537
- if (!input)
1538
- return [];
1539
- const value = input[key];
1540
- return Array.isArray(value) ? value : [];
1541
- }
1542
- function receiptStatus(value) {
1543
- return value && RECEIPT_STATUSES.has(value) ? value : 'applied';
1544
- }
1545
- function jobStatus(value) {
1546
- if (!value || !JOB_STATUSES.has(value))
1547
- throw new RunnerDirectiveError('Directive status is not allowlisted.');
1548
- return value;
1549
- }
1550
- function normalizeRunnerProcessingSummarySource(value) {
1551
- return value === 'scheduled_tick' || value === 'runner_tick' || value === 'manual_tick' ? value : 'manual_tick';
1552
- }
1553
- function isRecord(value) {
1554
- return typeof value === 'object' && value !== null && !Array.isArray(value);
1555
- }
1556
- function unique(values) {
1557
- return Array.from(new Set(values));
1558
- }
1559
- function count(input, key) {
1560
- return typeof input[key] === 'number' ? input[key] : 0;
1561
- }
1562
- function summarizeRunnerHeadlessDiagnostics(attempts, reasonCodes) {
1563
- return {
1564
- trustFailures: countRunnerHeadlessSignals(attempts, reasonCodes, RUNNER_HEADLESS_TRUST_FAILURE_REASON_CODES),
1565
- adapterUnavailable: countRunnerHeadlessSignals(attempts, reasonCodes, RUNNER_HEADLESS_ADAPTER_UNAVAILABLE_REASON_CODES),
1566
- authRequired: countRunnerHeadlessSignals(attempts, reasonCodes, RUNNER_HEADLESS_AUTH_REQUIRED_REASON_CODES),
1567
- retryBackoff: countRunnerHeadlessSignals(attempts, reasonCodes, RUNNER_HEADLESS_BACKOFF_REASON_CODES, new Set(['backoff'])),
1568
- timeout: countRunnerHeadlessSignals(attempts, reasonCodes, RUNNER_HEADLESS_TIMEOUT_REASON_CODES, new Set(['timed_out'])),
1569
- genericFailed: countRunnerGenericHeadlessFailures(attempts, reasonCodes),
1570
- redactions: attempts.reduce((total, attempt) => total + runnerHeadlessRedactionCount(attempt), 0),
1571
- };
1572
- }
1573
- function countRunnerHeadlessSignals(attempts, snapshotReasonCodes, reasonCodes, statuses = new Set()) {
1574
- const attemptCount = attempts.filter((attempt) => {
1575
- const reasonMatch = attempt.reason_code ? reasonCodes.has(attempt.reason_code) : false;
1576
- return reasonMatch || statuses.has(attempt.status);
1577
- }).length;
1578
- if (attemptCount > 0)
1579
- return attemptCount;
1580
- return snapshotReasonCodes.some((reasonCode) => reasonCodes.has(reasonCode)) ? 1 : 0;
1581
- }
1582
- function countRunnerGenericHeadlessFailures(attempts, snapshotReasonCodes) {
1583
- const attemptCount = attempts.filter((attempt) => {
1584
- if (attempt.reason_code && RUNNER_HEADLESS_GENERIC_FAILURE_REASON_CODES.has(attempt.reason_code))
1585
- return true;
1586
- if (attempt.status !== 'failed')
1587
- return false;
1588
- return !attempt.reason_code
1589
- || (!RUNNER_HEADLESS_TRUST_FAILURE_REASON_CODES.has(attempt.reason_code)
1590
- && !RUNNER_HEADLESS_ADAPTER_UNAVAILABLE_REASON_CODES.has(attempt.reason_code)
1591
- && !RUNNER_HEADLESS_AUTH_REQUIRED_REASON_CODES.has(attempt.reason_code)
1592
- && !RUNNER_HEADLESS_BACKOFF_REASON_CODES.has(attempt.reason_code)
1593
- && !RUNNER_HEADLESS_TIMEOUT_REASON_CODES.has(attempt.reason_code));
1594
- }).length;
1595
- if (attemptCount > 0)
1596
- return attemptCount;
1597
- return snapshotReasonCodes.some((reasonCode) => RUNNER_HEADLESS_GENERIC_FAILURE_REASON_CODES.has(reasonCode)) ? 1 : 0;
1598
- }
1599
- function runnerHeadlessRedactionCount(attempt) {
1600
- if (!attempt.redaction_summary_json)
1601
- return 0;
1602
- try {
1603
- const parsed = JSON.parse(attempt.redaction_summary_json);
1604
- return Array.isArray(parsed) ? parsed.length : 0;
1605
- }
1606
- catch {
1607
- return 0;
1608
- }
1609
- }
1610
- function doctorCheck(id, label, status, reasonCodes, summary, counts) {
1611
- return {
1612
- id,
1613
- label,
1614
- status,
1615
- reasonCodes: unique(reasonCodes.length > 0 ? reasonCodes : [RUNNER_REASON_CODES.OK]),
1616
- summary: boundPublicString(summary, 180),
1617
- ...(counts ? { counts } : {}),
1618
- publicSafe: true,
1619
- };
1620
- }
1621
- function boundPublicString(value, maxChars) {
1622
- const redacted = UNSAFE_VALUE_PATTERN.test(value) ? '[REDACTED]' : value;
1623
- return redacted.length <= maxChars ? redacted : `${redacted.slice(0, maxChars)}…[TRUNCATED:${redacted.length - maxChars}]`;
1624
- }
70
+ `.trim();function le(e){return e==="install"||e==="start"||e==="stop"||e==="status"||e==="doctor"}function at(e){return e==="systemd-user"||e==="launchd-user"||e==="windows-task-scheduler"||e==="cron-fallback"}class U extends Error{constructor(t){super(t),this.name="RunnerDirectiveError"}}function ue(e){if(j(e)){const r=typeof e.reasonCode=="string"?e.reasonCode:typeof e.code=="string"?e.code:void 0;if(r===d.SQLITE_ADAPTER_UNAVAILABLE||r===d.SQLITE_INTEGRITY_FAILED)return r}const t=e instanceof Error?e.message:String(e);return/sqlite-adapter-unavailable/i.test(t)?d.SQLITE_ADAPTER_UNAVAILABLE:/sqlite-integrity-failed|not a database|file is not a database|database disk image is malformed|malformed/i.test(t)?d.SQLITE_INTEGRITY_FAILED:"runner-tick-failed"}function x(e){return z(e instanceof Error?e.message:String(e),240)}function st(e){return e.manifests.length===0?e:{...e,manifests:e.manifests.map(t=>({...t,content:`[redacted ${t.kind} for project ${e.identity.projectKey}]`}))}}function nt(e){return`runner-${W("sha256").update(e).update(process.pid.toString()).digest("hex").slice(0,12)}`}function ct(e,t){const r=Ee(4).toString("hex");return`${e}-${Y(t)}-${Date.now()}-${r}`}function Y(e){return e.replace(/[^a-z0-9_.:-]+/gi,"-").slice(0,120)}function F(e,t,r){return Number.isFinite(e)?Math.max(t,Math.min(r,Math.floor(e))):t}function pe(e,t,r){return Number.isFinite(e)?Math.max(t,Math.min(r,e)):t}function me(e,t,r){if(e<=0)return 0;if(t<=0)return e;const o=pe(r(),0,1);return Math.max(0,e+Math.floor(e*t*o))}function dt(e,t){const r=Date.parse(e);return new Date((Number.isFinite(r)?r:Date.now())+t).toISOString()}function it(e){return typeof e.kind=="string"?e:{...e,kind:e.type}}function lt(e){return typeof e.kind=="string"&&e.publicSafe===!0}function Q(e,t){if(typeof e=="string"){if(q.test(e))throw new U(`Unsafe directive value at ${t}.`);return}if(!(e===null||typeof e=="number"||typeof e=="boolean")){if(Array.isArray(e)){e.forEach((r,o)=>Q(r,`${t}[${o}]`));return}if(j(e)){for(const[r,o]of Object.entries(e)){if(xe.test(r))throw new U(`Unsafe directive key at ${t}.${r}.`);Q(o,`${t}.${r}`)}return}throw new U(`Unsupported directive value at ${t}.`)}}function n(e,t){const r=e[t];return typeof r=="string"&&r.length>0?r:void 0}function ut(e,t){const r=n(e,t);if(!r)throw new U(`Directive missing ${t}.`);return r}function fe(e){const t=e.metadata;return j(t)?t:void 0}function D(e,t){if(!e)return;const r=e[t];return j(r)?r:void 0}function $(e,t){if(!e)return[];const r=e[t];return Array.isArray(r)?r:[]}function pt(e){return e&&Oe.has(e)?e:"applied"}function mt(e){if(!e||!Me.has(e))throw new U("Directive status is not allowlisted.");return e}function he(e){return e==="scheduled_tick"||e==="runner_tick"||e==="manual_tick"?e:"manual_tick"}function j(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function J(e){return Array.from(new Set(e))}function _(e,t){return typeof e[t]=="number"?e[t]:0}function ft(e,t){return{trustFailures:B(e,t,re),adapterUnavailable:B(e,t,oe),authRequired:B(e,t,ae),retryBackoff:B(e,t,se,new Set(["backoff"])),timeout:B(e,t,ne,new Set(["timed_out"])),genericFailed:ht(e,t),redactions:e.reduce((r,o)=>r+bt(o),0)}}function B(e,t,r,o=new Set){const a=e.filter(s=>(s.reason_code?r.has(s.reason_code):!1)||o.has(s.status)).length;return a>0?a:t.some(s=>r.has(s))?1:0}function ht(e,t){const r=e.filter(o=>o.reason_code&&ce.has(o.reason_code)?!0:o.status!=="failed"?!1:!o.reason_code||!re.has(o.reason_code)&&!oe.has(o.reason_code)&&!ae.has(o.reason_code)&&!se.has(o.reason_code)&&!ne.has(o.reason_code)).length;return r>0?r:t.some(o=>ce.has(o))?1:0}function bt(e){if(!e.redaction_summary_json)return 0;try{const t=JSON.parse(e.redaction_summary_json);return Array.isArray(t)?t.length:0}catch{return 0}}function w(e,t,r,o,a,s){return{id:e,label:t,status:r,reasonCodes:J(o.length>0?o:[d.OK]),summary:z(a,180),...s?{counts:s}:{},publicSafe:!0}}function z(e,t){const r=q.test(e)?"[REDACTED]":e;return r.length<=t?r:`${r.slice(0,t)}\u2026[TRUNCATED:${r.length-t}]`}export{je as DEFAULT_DUE_JOB_LIMIT,Te as DEFAULT_RUNNER_BACKOFF_MS,Le as DEFAULT_RUNNER_LEASE_MS,Ce as DEFAULT_STALE_LEASE_AFTER_MS,Dt as LOCAL_JOBS_CONSOLE_REASON_CODES,Ne as MAX_DUE_JOB_LIMIT,Tt as RUNNER_BINARY_REASON_CODES,d as RUNNER_REASON_CODES,Je as assertRunnerDirectiveTrusted,jt as checkRunnerBinaryTrust,Ot as createLocalJobConsoleServer,Fe as defaultRunnerServerInvoker,de as extractRunnerDirectives,Mt as isLocalJobsConsoleArgs,xt as localJobConsoleSocketPath,Ft as parseLocalJobsConsoleArgs,Ht as runLocalJobsConsoleCommand,wt as runRunnerCli,Ke as runRunnerDoctor,He as runRunnerTick,Nt as runnerTrustRelativePath};