@ornexus/neocortex 4.60.68 → 4.60.70

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (232) hide show
  1. package/dist/release-source.json +1 -1
  2. package/dist/sbom.cdx.json +1194 -1194
  3. package/install.ps1 +1 -1
  4. package/install.sh +1 -1
  5. package/package.json +3 -3
  6. package/packages/client/dist/adapters/adapter-registry.js +1 -108
  7. package/packages/client/dist/adapters/antigravity-adapter.js +2 -77
  8. package/packages/client/dist/adapters/claude-code-adapter.js +3 -79
  9. package/packages/client/dist/adapters/codex-adapter.js +2 -80
  10. package/packages/client/dist/adapters/cursor-adapter.js +4 -115
  11. package/packages/client/dist/adapters/gemini-adapter.js +2 -71
  12. package/packages/client/dist/adapters/grok-adapter.js +2 -42
  13. package/packages/client/dist/adapters/index.js +1 -22
  14. package/packages/client/dist/adapters/platform-detector.js +1 -212
  15. package/packages/client/dist/adapters/target-adapter.js +0 -12
  16. package/packages/client/dist/adapters/vscode-adapter.js +2 -72
  17. package/packages/client/dist/agent/refresh-stubs.js +2 -417
  18. package/packages/client/dist/agent/update-agent-yaml.js +1 -102
  19. package/packages/client/dist/agent/update-description.js +1 -251
  20. package/packages/client/dist/cache/crypto-utils.js +1 -78
  21. package/packages/client/dist/cache/encrypted-cache.js +1 -108
  22. package/packages/client/dist/cache/in-memory-asset-cache.js +1 -73
  23. package/packages/client/dist/cache/index.js +1 -13
  24. package/packages/client/dist/cache/protected-pi-boundary.js +1 -44
  25. package/packages/client/dist/checkpoint/checkpoint-client-reader.js +2 -127
  26. package/packages/client/dist/checkpoint/index.js +1 -11
  27. package/packages/client/dist/checkpoint/shared-checkpoint-types.js +1 -32
  28. package/packages/client/dist/cli.js +3 -290
  29. package/packages/client/dist/commands/activate.js +8 -394
  30. package/packages/client/dist/commands/architecture-answers.js +7 -274
  31. package/packages/client/dist/commands/architecture-memory-terminal.js +4 -253
  32. package/packages/client/dist/commands/architecture-non-ui-continuation.js +1 -100
  33. package/packages/client/dist/commands/architecture-root-binding.js +1 -51
  34. package/packages/client/dist/commands/cache-status.js +2 -112
  35. package/packages/client/dist/commands/config.js +1 -88
  36. package/packages/client/dist/commands/doctor.js +2 -635
  37. package/packages/client/dist/commands/invoke.d.ts +6 -0
  38. package/packages/client/dist/commands/invoke.js +142 -7140
  39. package/packages/client/dist/commands/legacy-epic-terminal.js +1 -71
  40. package/packages/client/dist/commands/legacy-story-branch.js +1 -96
  41. package/packages/client/dist/commands/legacy-story-pr.js +1 -276
  42. package/packages/client/dist/commands/mutation-qa-checkpoint.js +5 -285
  43. package/packages/client/dist/commands/mutation-qa-gate.js +1 -54
  44. package/packages/client/dist/commands/mutation-qa-git.js +1 -41
  45. package/packages/client/dist/commands/mutation-qa-policy.js +1 -102
  46. package/packages/client/dist/commands/mutation-qa-run.js +3 -219
  47. package/packages/client/dist/commands/mutation-qa-verdict.js +1 -149
  48. package/packages/client/dist/commands/mutation-qa.js +1 -220
  49. package/packages/client/dist/commands/native-agent.js +2 -136
  50. package/packages/client/dist/commands/planning-diagnosis.js +1 -43
  51. package/packages/client/dist/commands/planning-generation.js +1 -53
  52. package/packages/client/dist/commands/planning-memory.js +1 -40
  53. package/packages/client/dist/commands/planning-research.js +1 -42
  54. package/packages/client/dist/commands/private-session.js +1 -75
  55. package/packages/client/dist/commands/property-qa.js +1 -139
  56. package/packages/client/dist/commands/qa-control.js +1 -16
  57. package/packages/client/dist/commands/qa-scan-browser-launch.js +1 -55
  58. package/packages/client/dist/commands/qa-scan-browser-route.js +1 -28
  59. package/packages/client/dist/commands/qa-scan-capture.js +5 -663
  60. package/packages/client/dist/commands/qa-scan-discovery.js +3 -376
  61. package/packages/client/dist/commands/qa-scan-review.js +5 -826
  62. package/packages/client/dist/commands/qa-synthetic-backend-capture.js +1 -266
  63. package/packages/client/dist/commands/qa-synthetic-backend-review.js +1 -297
  64. package/packages/client/dist/commands/qa-synthetic-backend.js +2 -340
  65. package/packages/client/dist/commands/qa-synthetic-broker-port.js +1 -85
  66. package/packages/client/dist/commands/qa-synthetic-browser.js +1 -39
  67. package/packages/client/dist/commands/refactor-characterization.js +21 -671
  68. package/packages/client/dist/commands/refactor-commit.js +13 -399
  69. package/packages/client/dist/commands/refactor-control.js +3 -142
  70. package/packages/client/dist/commands/refactor-cpp-assertions.js +3 -93
  71. package/packages/client/dist/commands/refactor-review.js +5 -283
  72. package/packages/client/dist/commands/refactor-root.js +7 -588
  73. package/packages/client/dist/commands/refresh-memory.js +3 -156
  74. package/packages/client/dist/commands/shared-architecture-non-ui.js +1 -24
  75. package/packages/client/dist/commands/story-checkpoint.js +1 -171
  76. package/packages/client/dist/config/profile-directory.js +1 -13
  77. package/packages/client/dist/config/resolver-selection.js +1 -278
  78. package/packages/client/dist/config/secure-config.js +12 -281
  79. package/packages/client/dist/config.js +2 -223
  80. package/packages/client/dist/constants.js +1 -25
  81. package/packages/client/dist/context/context-collector.js +2 -222
  82. package/packages/client/dist/context/context-sanitizer.js +1 -145
  83. package/packages/client/dist/continuity/autonomous-governance.js +1 -285
  84. package/packages/client/dist/continuity/continuity-client-state-store.js +1 -1587
  85. package/packages/client/dist/continuity/invoke-hooks.js +1 -129
  86. package/packages/client/dist/continuity/migrations/001-initial-schema.js +2 -12
  87. package/packages/client/dist/continuity/migrations/002-processing-output-summary-indexes.js +2 -12
  88. package/packages/client/dist/continuity/migrations/003-headless-execution-attempts.js +2 -12
  89. package/packages/client/dist/continuity/migrations/004-outbox-delivery-state.js +2 -12
  90. package/packages/client/dist/continuity/migrations/005-bounded-headless-queue.js +2 -12
  91. package/packages/client/dist/continuity/migrations/006-native-agent-dispatches.js +2 -10
  92. package/packages/client/dist/continuity/native-agent-store.js +1 -69
  93. package/packages/client/dist/continuity/native-agent-types.js +1 -90
  94. package/packages/client/dist/continuity/outbox.js +1 -117
  95. package/packages/client/dist/continuity/runner-bootstrap-policy.js +1 -172
  96. package/packages/client/dist/continuity/sqlite-store.js +81 -2536
  97. package/packages/client/dist/effects/invoke-effect-gateways.js +1 -62
  98. package/packages/client/dist/errors/error-messages.js +2 -228
  99. package/packages/client/dist/errors/frontmatter-diagnostics.js +1 -125
  100. package/packages/client/dist/evidence/collector.js +1 -347
  101. package/packages/client/dist/evidence/index.js +1 -1
  102. package/packages/client/dist/evidence/types.js +0 -2
  103. package/packages/client/dist/fs/atomic-state-replace.js +1 -152
  104. package/packages/client/dist/generated/command-registry.generated.js +1 -4076
  105. package/packages/client/dist/generated/public-projections.generated.js +1 -149
  106. package/packages/client/dist/git/direct-yolo-c01-retry.js +4 -138
  107. package/packages/client/dist/git/direct-yolo-checkpoint.js +4 -1234
  108. package/packages/client/dist/git/git-lifecycle-step-ids.js +1 -14
  109. package/packages/client/dist/git/git-lifecycle-store.js +9 -1938
  110. package/packages/client/dist/git/legacy-story-branch-checkpoint.js +9 -259
  111. package/packages/client/dist/git/legacy-story-branch-setup.js +4 -348
  112. package/packages/client/dist/git/legacy-story-pr.js +70 -3538
  113. package/packages/client/dist/git/secure-filesystem-boundary.js +5 -182
  114. package/packages/client/dist/graph-retrieval/pre-command-hook.js +1 -284
  115. package/packages/client/dist/graph-retrieval/shared-graph-retrieval-contract.js +1 -237
  116. package/packages/client/dist/i18n/first-run.js +2 -60
  117. package/packages/client/dist/index.js +1 -47
  118. package/packages/client/dist/invocation/invoke-input.js +1 -24
  119. package/packages/client/dist/license/index.js +1 -5
  120. package/packages/client/dist/license/license-client.js +1 -304
  121. package/packages/client/dist/machine/fingerprint.js +2 -163
  122. package/packages/client/dist/machine/index.js +1 -5
  123. package/packages/client/dist/memory/project-memory-path-scope.js +1 -62
  124. package/packages/client/dist/memory/project-memory-writer.js +40 -1007
  125. package/packages/client/dist/memory/shared-project-memory-types.js +2 -253
  126. package/packages/client/dist/migrations/project-identity-capability.js +1 -50
  127. package/packages/client/dist/migrations/project-identity.js +7 -493
  128. package/packages/client/dist/migrations/state-json.js +4 -344
  129. package/packages/client/dist/policy/architecture-policy.js +2 -184
  130. package/packages/client/dist/policy/index.js +1 -7
  131. package/packages/client/dist/policy/shared-policy-types.js +0 -38
  132. package/packages/client/dist/policy/subagent-approval-policy.js +1 -55
  133. package/packages/client/dist/private-execution/clean-baseline.js +2 -279
  134. package/packages/client/dist/private-execution/codex-executor.js +4 -574
  135. package/packages/client/dist/private-execution/codex-host-runtime.js +1 -137
  136. package/packages/client/dist/private-execution/codex-lifecycle-guard.js +1 -97
  137. package/packages/client/dist/private-execution/codex-scoped-permissions.js +1 -128
  138. package/packages/client/dist/private-execution/delivery.js +13 -410
  139. package/packages/client/dist/private-execution/diagnosis-root-review.js +2 -294
  140. package/packages/client/dist/private-execution/frozen-handoff.js +6 -19
  141. package/packages/client/dist/private-execution/implementation-validation.js +2 -106
  142. package/packages/client/dist/private-execution/isolated-admission.js +23 -1432
  143. package/packages/client/dist/private-execution/isolated-session.js +5 -725
  144. package/packages/client/dist/private-execution/isolated-workspace.js +1 -122
  145. package/packages/client/dist/private-execution/pact-consumer-runtime.js +3 -409
  146. package/packages/client/dist/private-execution/pact-test-source.js +1 -258
  147. package/packages/client/dist/private-execution/pact-toolchain.js +3 -290
  148. package/packages/client/dist/private-execution/pact-write-set.js +1 -172
  149. package/packages/client/dist/private-execution/planning-diagnosis-checkpoint.js +5 -406
  150. package/packages/client/dist/private-execution/planning-generation-checkpoint.js +5 -462
  151. package/packages/client/dist/private-execution/planning-memory-checkpoint.js +3 -139
  152. package/packages/client/dist/private-execution/planning-research-checkpoint.js +4 -325
  153. package/packages/client/dist/private-execution/private-report-store.js +1 -42
  154. package/packages/client/dist/private-execution/property-test-candidate.js +1 -103
  155. package/packages/client/dist/private-execution/property-test-choices.js +3 -147
  156. package/packages/client/dist/private-execution/property-test-plan.js +7 -271
  157. package/packages/client/dist/private-execution/property-test-review.js +4 -258
  158. package/packages/client/dist/private-execution/property-test-runtime.js +9 -372
  159. package/packages/client/dist/private-execution/property-test-source.js +1 -139
  160. package/packages/client/dist/private-execution/root-checkpoint.js +1 -80
  161. package/packages/client/dist/private-execution/root-memory-git-evidence.js +3 -135
  162. package/packages/client/dist/private-execution/root-memory-recovery.js +1 -93
  163. package/packages/client/dist/private-execution/root-memory-request.js +1 -29
  164. package/packages/client/dist/private-execution/root-memory-scope.js +1 -178
  165. package/packages/client/dist/private-execution/root-memory-writer.js +1 -102
  166. package/packages/client/dist/private-execution/session-lease.js +1 -63
  167. package/packages/client/dist/private-execution/shared-private-admission-types.js +1 -50
  168. package/packages/client/dist/private-execution/shared-project-validation.js +1 -66
  169. package/packages/client/dist/private-execution/shared-root-memory-types.js +1 -61
  170. package/packages/client/dist/private-execution/ux-source-validation.js +1 -372
  171. package/packages/client/dist/private-execution/ux-write-set.js +2 -191
  172. package/packages/client/dist/resilience/circuit-breaker.js +1 -170
  173. package/packages/client/dist/resilience/degradation-manager.js +1 -164
  174. package/packages/client/dist/resilience/freshness-indicator.js +1 -100
  175. package/packages/client/dist/resilience/index.js +1 -8
  176. package/packages/client/dist/resilience/recovery-detector.js +1 -74
  177. package/packages/client/dist/resolvers/asset-resolver.js +0 -13
  178. package/packages/client/dist/resolvers/local-resolver.js +8 -255
  179. package/packages/client/dist/resolvers/remote-resolver.js +1 -233
  180. package/packages/client/dist/runner/binary.js +1 -404
  181. package/packages/client/dist/runner/cli.js +17 -1571
  182. package/packages/client/dist/runner/headless/adapters.js +1 -49
  183. package/packages/client/dist/runner/headless/claude-code-adapter.js +1 -79
  184. package/packages/client/dist/runner/headless/continuous-story-workspace.js +11 -1628
  185. package/packages/client/dist/runner/headless/epic-git-reconciliation-inventory.js +1 -69
  186. package/packages/client/dist/runner/headless/epic-merge-candidate-qa.js +2 -110
  187. package/packages/client/dist/runner/headless/epic-terminal-git-cli-adapter.js +8 -1236
  188. package/packages/client/dist/runner/headless/epic-terminal-git-runner.js +2 -318
  189. package/packages/client/dist/runner/headless/fake-adapter.js +1 -62
  190. package/packages/client/dist/runner/headless/managed-profile.js +1 -132
  191. package/packages/client/dist/runner/headless/opencode-adapter.js +2 -145
  192. package/packages/client/dist/runner/headless/prepared-story-bundle.js +1 -43
  193. package/packages/client/dist/runner/headless/resolver.js +1 -231
  194. package/packages/client/dist/runner/headless/run-yolo.js +1 -885
  195. package/packages/client/dist/runner/headless/run-yoloop.js +2 -2574
  196. package/packages/client/dist/runner/headless/types.js +2 -574
  197. package/packages/client/dist/runner/local-console.js +13 -396
  198. package/packages/client/dist/runner/portal-sync-worker.js +1 -1487
  199. package/packages/client/dist/runner/project-identity.js +1 -106
  200. package/packages/client/dist/runner/scheduler.js +7 -995
  201. package/packages/client/dist/runner-cli.js +2 -18
  202. package/packages/client/dist/state/invocation-shape.js +1 -13
  203. package/packages/client/dist/state/project-state-snapshot.js +1 -1677
  204. package/packages/client/dist/state/state-json-repair.js +3 -785
  205. package/packages/client/dist/state/structural-dag-projection.js +1 -118
  206. package/packages/client/dist/telemetry/index.js +1 -5
  207. package/packages/client/dist/telemetry/offline-queue.js +1 -131
  208. package/packages/client/dist/tier/index.js +1 -5
  209. package/packages/client/dist/tier/tier-aware-client.js +1 -405
  210. package/packages/client/dist/types/index.js +1 -38
  211. package/packages/client/dist/version.js +1 -27
  212. package/packages/client/dist/yolo/change-evidence-collector.js +1 -126
  213. package/packages/client/dist/yolo/invoke-envelope-capability.js +1 -62
  214. package/packages/client/dist/yolo/spec-envelope-collector.js +5 -185
  215. package/packages/client/dist/yoloop/discovery-hook.js +2 -300
  216. package/packages/client/dist/yoloop/index.js +1 -8
  217. package/packages/client/dist/yoloop/invoke-hooks.js +12 -669
  218. package/packages/client/dist/yoloop/shared-discover-epics.js +1 -1007
  219. package/packages/client/dist/yoloop/shared-yoloop-types.js +1 -33
  220. package/packages/client/dist/yoloop/target-normalization.js +1 -180
  221. package/packages/client/dist/yoloop/yoloop-client-state-store.js +1 -1723
  222. package/targets-stubs/antigravity/gemini.md +1 -1
  223. package/targets-stubs/antigravity/skill/SKILL.md +1 -1
  224. package/targets-stubs/claude-code/neocortex-root.agent.yaml +1 -1
  225. package/targets-stubs/claude-code/neocortex-root.md +2 -2
  226. package/targets-stubs/claude-code/neocortex.agent.yaml +1 -1
  227. package/targets-stubs/claude-code/neocortex.md +2 -2
  228. package/targets-stubs/codex/AGENTS.md +1 -1
  229. package/targets-stubs/cursor/agent.md +2 -2
  230. package/targets-stubs/gemini-cli/agent.md +2 -2
  231. package/targets-stubs/opencode/neocortex-root.md +1 -1
  232. package/targets-stubs/vscode/neocortex.agent.md +2 -2
@@ -1,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};