@revisium/orchestrator 0.0.0

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 (176) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +20 -0
  3. package/bin/revo.js +2 -0
  4. package/control-plane/bootstrap.config.json +931 -0
  5. package/dist/app.module.js +28 -0
  6. package/dist/app.module.js.map +1 -0
  7. package/dist/cli/commands/bootstrap.js +39 -0
  8. package/dist/cli/commands/bootstrap.js.map +1 -0
  9. package/dist/cli/commands/dev.js +143 -0
  10. package/dist/cli/commands/dev.js.map +1 -0
  11. package/dist/cli/commands/inbox.js +288 -0
  12. package/dist/cli/commands/inbox.js.map +1 -0
  13. package/dist/cli/commands/mcp.js +16 -0
  14. package/dist/cli/commands/mcp.js.map +1 -0
  15. package/dist/cli/commands/playbook.js +74 -0
  16. package/dist/cli/commands/playbook.js.map +1 -0
  17. package/dist/cli/commands/poll-workflow-state.js +144 -0
  18. package/dist/cli/commands/poll-workflow-state.js.map +1 -0
  19. package/dist/cli/commands/revisium-context.js +23 -0
  20. package/dist/cli/commands/revisium-context.js.map +1 -0
  21. package/dist/cli/commands/revisium-helpers.js +61 -0
  22. package/dist/cli/commands/revisium-helpers.js.map +1 -0
  23. package/dist/cli/commands/revisium.js +89 -0
  24. package/dist/cli/commands/revisium.js.map +1 -0
  25. package/dist/cli/commands/run.js +426 -0
  26. package/dist/cli/commands/run.js.map +1 -0
  27. package/dist/cli/commands/work.js +149 -0
  28. package/dist/cli/commands/work.js.map +1 -0
  29. package/dist/cli/config.js +2 -0
  30. package/dist/cli/config.js.map +1 -0
  31. package/dist/cli/index.js +32 -0
  32. package/dist/cli/index.js.map +1 -0
  33. package/dist/cli/live-guard.js +49 -0
  34. package/dist/cli/live-guard.js.map +1 -0
  35. package/dist/cli/needs-host.js +78 -0
  36. package/dist/cli/needs-host.js.map +1 -0
  37. package/dist/cli/program.js +37 -0
  38. package/dist/cli/program.js.map +1 -0
  39. package/dist/config.js +102 -0
  40. package/dist/config.js.map +1 -0
  41. package/dist/control-plane/client-transport.js +146 -0
  42. package/dist/control-plane/client-transport.js.map +1 -0
  43. package/dist/control-plane/data-access.js +99 -0
  44. package/dist/control-plane/data-access.js.map +1 -0
  45. package/dist/control-plane/definitions.js +120 -0
  46. package/dist/control-plane/definitions.js.map +1 -0
  47. package/dist/control-plane/errors.js +13 -0
  48. package/dist/control-plane/errors.js.map +1 -0
  49. package/dist/control-plane/inbox.js +261 -0
  50. package/dist/control-plane/inbox.js.map +1 -0
  51. package/dist/control-plane/index.js +6 -0
  52. package/dist/control-plane/index.js.map +1 -0
  53. package/dist/control-plane/json-fields.js +68 -0
  54. package/dist/control-plane/json-fields.js.map +1 -0
  55. package/dist/control-plane/resolve-cwd.js +75 -0
  56. package/dist/control-plane/resolve-cwd.js.map +1 -0
  57. package/dist/control-plane/steps.js +381 -0
  58. package/dist/control-plane/steps.js.map +1 -0
  59. package/dist/control-plane/tables.js +13 -0
  60. package/dist/control-plane/tables.js.map +1 -0
  61. package/dist/control-plane/versioned-meaning.js +53 -0
  62. package/dist/control-plane/versioned-meaning.js.map +1 -0
  63. package/dist/engine/dbos.module.js +18 -0
  64. package/dist/engine/dbos.module.js.map +1 -0
  65. package/dist/engine/dbos.service.js +296 -0
  66. package/dist/engine/dbos.service.js.map +1 -0
  67. package/dist/engine/ensure-postgres.js +80 -0
  68. package/dist/engine/ensure-postgres.js.map +1 -0
  69. package/dist/engine/types.js +2 -0
  70. package/dist/engine/types.js.map +1 -0
  71. package/dist/host/ensure-revisium.js +269 -0
  72. package/dist/host/ensure-revisium.js.map +1 -0
  73. package/dist/host/host.lifecycle.js +75 -0
  74. package/dist/host/host.lifecycle.js.map +1 -0
  75. package/dist/mcp/mcp-capabilities.js +47 -0
  76. package/dist/mcp/mcp-capabilities.js.map +1 -0
  77. package/dist/mcp/mcp-facade.service.js +130 -0
  78. package/dist/mcp/mcp-facade.service.js.map +1 -0
  79. package/dist/mcp/mcp-stdio.service.js +48 -0
  80. package/dist/mcp/mcp-stdio.service.js.map +1 -0
  81. package/dist/mcp/mcp-tools.js +264 -0
  82. package/dist/mcp/mcp-tools.js.map +1 -0
  83. package/dist/mcp/mcp.module.js +21 -0
  84. package/dist/mcp/mcp.module.js.map +1 -0
  85. package/dist/package-info.js +10 -0
  86. package/dist/package-info.js.map +1 -0
  87. package/dist/pipeline/await-human.js +62 -0
  88. package/dist/pipeline/await-human.js.map +1 -0
  89. package/dist/pipeline/develop-task.workflow.js +497 -0
  90. package/dist/pipeline/develop-task.workflow.js.map +1 -0
  91. package/dist/pipeline/pipeline.module.js +34 -0
  92. package/dist/pipeline/pipeline.module.js.map +1 -0
  93. package/dist/playbook/catalog-loader.js +145 -0
  94. package/dist/playbook/catalog-loader.js.map +1 -0
  95. package/dist/playbook/errors.js +14 -0
  96. package/dist/playbook/errors.js.map +1 -0
  97. package/dist/playbook/import-mapper.js +186 -0
  98. package/dist/playbook/import-mapper.js.map +1 -0
  99. package/dist/playbook/manifest.js +60 -0
  100. package/dist/playbook/manifest.js.map +1 -0
  101. package/dist/playbook/playbook-installer.js +51 -0
  102. package/dist/playbook/playbook-installer.js.map +1 -0
  103. package/dist/playbook/prompt-composer.js +34 -0
  104. package/dist/playbook/prompt-composer.js.map +1 -0
  105. package/dist/playbook/source-resolver.js +105 -0
  106. package/dist/playbook/source-resolver.js.map +1 -0
  107. package/dist/poller/pr-readiness-core.js +656 -0
  108. package/dist/poller/pr-readiness-core.js.map +1 -0
  109. package/dist/poller/pr-readiness.js +91 -0
  110. package/dist/poller/pr-readiness.js.map +1 -0
  111. package/dist/revisium/inbox.service.js +67 -0
  112. package/dist/revisium/inbox.service.js.map +1 -0
  113. package/dist/revisium/playbooks.service.js +99 -0
  114. package/dist/revisium/playbooks.service.js.map +1 -0
  115. package/dist/revisium/revisium.module.js +55 -0
  116. package/dist/revisium/revisium.module.js.map +1 -0
  117. package/dist/revisium/roles.service.js +79 -0
  118. package/dist/revisium/roles.service.js.map +1 -0
  119. package/dist/revisium/run.service.js +182 -0
  120. package/dist/revisium/run.service.js.map +1 -0
  121. package/dist/revisium/tokens.js +7 -0
  122. package/dist/revisium/tokens.js.map +1 -0
  123. package/dist/run/append-event.js +112 -0
  124. package/dist/run/append-event.js.map +1 -0
  125. package/dist/run/cancel-run.js +47 -0
  126. package/dist/run/cancel-run.js.map +1 -0
  127. package/dist/run/create-run.js +208 -0
  128. package/dist/run/create-run.js.map +1 -0
  129. package/dist/run/fail-run.js +20 -0
  130. package/dist/run/fail-run.js.map +1 -0
  131. package/dist/run/inspect-run.js +287 -0
  132. package/dist/run/inspect-run.js.map +1 -0
  133. package/dist/run/terminal-run-status.js +45 -0
  134. package/dist/run/terminal-run-status.js.map +1 -0
  135. package/dist/runners/claude-code.service.js +48 -0
  136. package/dist/runners/claude-code.service.js.map +1 -0
  137. package/dist/runners/gh-identity.js +108 -0
  138. package/dist/runners/gh-identity.js.map +1 -0
  139. package/dist/runners/integrator.js +417 -0
  140. package/dist/runners/integrator.js.map +1 -0
  141. package/dist/runners/runner.module.js +40 -0
  142. package/dist/runners/runner.module.js.map +1 -0
  143. package/dist/runners/tokens.js +7 -0
  144. package/dist/runners/tokens.js.map +1 -0
  145. package/dist/task-control-plane/pr-readiness.service.js +33 -0
  146. package/dist/task-control-plane/pr-readiness.service.js.map +1 -0
  147. package/dist/task-control-plane/task-control-plane-api.service.js +440 -0
  148. package/dist/task-control-plane/task-control-plane-api.service.js.map +1 -0
  149. package/dist/task-control-plane/task-control-plane.module.js +23 -0
  150. package/dist/task-control-plane/task-control-plane.module.js.map +1 -0
  151. package/dist/worker/build-context.js +39 -0
  152. package/dist/worker/build-context.js.map +1 -0
  153. package/dist/worker/claude-code-runner.js +135 -0
  154. package/dist/worker/claude-code-runner.js.map +1 -0
  155. package/dist/worker/git-worktree-manager.js +84 -0
  156. package/dist/worker/git-worktree-manager.js.map +1 -0
  157. package/dist/worker/loop.js +122 -0
  158. package/dist/worker/loop.js.map +1 -0
  159. package/dist/worker/process-executor.js +62 -0
  160. package/dist/worker/process-executor.js.map +1 -0
  161. package/dist/worker/result-envelope.js +134 -0
  162. package/dist/worker/result-envelope.js.map +1 -0
  163. package/dist/worker/runner-dispatch.js +20 -0
  164. package/dist/worker/runner-dispatch.js.map +1 -0
  165. package/dist/worker/runner.js +2 -0
  166. package/dist/worker/runner.js.map +1 -0
  167. package/dist/worker/script-runner.js +28 -0
  168. package/dist/worker/script-runner.js.map +1 -0
  169. package/dist/worker/stub-runner.js +59 -0
  170. package/dist/worker/stub-runner.js.map +1 -0
  171. package/dist/worker/worker-id.js +18 -0
  172. package/dist/worker/worker-id.js.map +1 -0
  173. package/dist/worker/worktree-manager.js +7 -0
  174. package/dist/worker/worktree-manager.js.map +1 -0
  175. package/package.json +66 -0
  176. package/revisium.config.json +10 -0
@@ -0,0 +1,120 @@
1
+ import { ControlPlaneError } from './errors.js';
2
+ import { createClientTransport } from './client-transport.js';
3
+ /** Fail-safe defaults — preserve pre-0008 behavior when the routing_policy row is absent. */
4
+ export const DEFAULT_PIPELINE_POLICY = {
5
+ maxReviewIterations: 3,
6
+ maxAttempts: 3,
7
+ budgetUsd: 0,
8
+ budgetTokens: 0,
9
+ };
10
+ const VALID_MODEL_LEVELS = ['cheap', 'standard', 'deep'];
11
+ function toModelLevel(raw) {
12
+ const s = toStr(raw) || 'standard';
13
+ if (!VALID_MODEL_LEVELS.includes(s)) {
14
+ throw new ControlPlaneError('VALIDATION_FAILURE', `Invalid model_level "${s}": expected one of ${VALID_MODEL_LEVELS.join(', ')}`);
15
+ }
16
+ return s;
17
+ }
18
+ function toStr(v) {
19
+ if (typeof v === 'string')
20
+ return v;
21
+ if (typeof v === 'number')
22
+ return String(v);
23
+ return '';
24
+ }
25
+ function parseJsonField(value) {
26
+ if (value === '' || value === null || value === undefined)
27
+ return {};
28
+ if (typeof value !== 'string')
29
+ return value;
30
+ return JSON.parse(value);
31
+ }
32
+ /** Coerce to a positive integer, else the fallback (for policy/role numeric fields). */
33
+ function toPosInt(value, fallback) {
34
+ const n = typeof value === 'number' ? value : Number(value);
35
+ return Number.isFinite(n) && Number.isInteger(n) && n > 0 ? n : fallback;
36
+ }
37
+ /** Coerce to a non-negative number, else the fallback (for budgets — 0 = unlimited). */
38
+ function toNonNegNum(value, fallback) {
39
+ const n = typeof value === 'number' ? value : Number(value);
40
+ return Number.isFinite(n) && n >= 0 ? n : fallback;
41
+ }
42
+ /** Optional positive-integer field — undefined when absent/invalid (role timeout). */
43
+ function toOptPosInt(value) {
44
+ const n = typeof value === 'number' ? value : Number(value);
45
+ return Number.isFinite(n) && Number.isInteger(n) && n > 0 ? n : undefined;
46
+ }
47
+ /** True for a "row not found" error — ControlPlaneError ROW_NOT_FOUND or any 404-shaped error. */
48
+ function isRowNotFound(err) {
49
+ if (err instanceof ControlPlaneError)
50
+ return err.code === 'ROW_NOT_FOUND';
51
+ const status = err?.statusCode;
52
+ return status === 404;
53
+ }
54
+ export async function loadRole(name, transport) {
55
+ const t = transport ?? createClientTransport('head');
56
+ const row = await t.getRow('roles', name);
57
+ const d = row.data ?? {};
58
+ return {
59
+ name: toStr(d.name) || name,
60
+ systemPrompt: toStr(d.system_prompt),
61
+ modelLevel: toModelLevel(d.model_level),
62
+ effort: toStr(d.effort),
63
+ runner: (toStr(d.runner) || 'claude-code'),
64
+ allowedTools: Array.isArray(d.allowed_tools) ? d.allowed_tools.map(toStr) : [],
65
+ scopeRules: parseJsonField(d.scope_rules),
66
+ playbookId: toStr(d.playbook_id) || undefined,
67
+ playbookRoleId: toStr(d.playbook_role_id) || undefined,
68
+ sourcePath: toStr(d.source_path) || undefined,
69
+ sourceHash: toStr(d.source_hash) || undefined,
70
+ surface: toStr(d.surface) || undefined,
71
+ rights: toStr(d.rights) || undefined,
72
+ timeoutMs: toOptPosInt(d.timeout_ms),
73
+ permissionMode: toStr(d.permission_mode) || undefined,
74
+ };
75
+ }
76
+ /**
77
+ * loadPipelinePolicy — read pipeline limits from the routing_policy table (0008 #5).
78
+ *
79
+ * Reads row `rowId` (default 'pipeline'); parses its `rule` JSON for max_review_iterations,
80
+ * max_attempts, budget_usd, budget_tokens. Falls back to DEFAULT_PIPELINE_POLICY when the row
81
+ * is absent (routing_policy starts EMPTY) so the pipeline keeps working before the seed lands.
82
+ */
83
+ export async function loadPipelinePolicy(transport, rowId = 'pipeline') {
84
+ const t = transport ?? createClientTransport('head');
85
+ // Default ONLY when the policy row is absent (routing_policy starts EMPTY). A transport failure
86
+ // or MALFORMED rule JSON must NOT silently disable the budget/limits — rethrow those so a typo in
87
+ // the budget config surfaces loudly instead of falling back to "unlimited" (codex 0008 finding).
88
+ let row;
89
+ try {
90
+ row = await t.getRow('routing_policy', rowId);
91
+ }
92
+ catch (err) {
93
+ if (isRowNotFound(err))
94
+ return { ...DEFAULT_PIPELINE_POLICY };
95
+ throw err;
96
+ }
97
+ // parseJsonField may throw on malformed JSON — intentionally NOT caught (see above).
98
+ const parsed = parseJsonField(row.data?.rule);
99
+ const rule = parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {};
100
+ return {
101
+ maxReviewIterations: toPosInt(rule.max_review_iterations, DEFAULT_PIPELINE_POLICY.maxReviewIterations),
102
+ maxAttempts: toPosInt(rule.max_attempts, DEFAULT_PIPELINE_POLICY.maxAttempts),
103
+ budgetUsd: toNonNegNum(rule.budget_usd, DEFAULT_PIPELINE_POLICY.budgetUsd),
104
+ budgetTokens: toNonNegNum(rule.budget_tokens, DEFAULT_PIPELINE_POLICY.budgetTokens),
105
+ };
106
+ }
107
+ export async function loadModelProfile(level, transport) {
108
+ const t = transport ?? createClientTransport('head');
109
+ const row = await t.getRow('model_profiles', level);
110
+ const d = row.data ?? {};
111
+ return {
112
+ level: (toStr(d.level) || level),
113
+ provider: toStr(d.provider),
114
+ modelId: toStr(d.model_id),
115
+ params: parseJsonField(d.params),
116
+ costPerInput: Number(d.cost_per_input ?? 0),
117
+ costPerOutput: Number(d.cost_per_output ?? 0),
118
+ };
119
+ }
120
+ //# sourceMappingURL=definitions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/control-plane/definitions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ export class ControlPlaneError extends Error {
2
+ code;
3
+ status;
4
+ details;
5
+ constructor(code, message, options = {}) {
6
+ super(message);
7
+ this.name = 'ControlPlaneError';
8
+ this.code = code;
9
+ this.status = options.status;
10
+ this.details = options.details;
11
+ }
12
+ }
13
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/control-plane/errors.ts"],"names":[],"mappings":"AAQA,MAAM,OAAO,iBAAkB,SAAQ,KAAK;IACjC,IAAI,CAAwB;IAC5B,MAAM,CAAU;IAChB,OAAO,CAAW;IAE3B,YAAY,IAA2B,EAAE,OAAe,EAAE,UAAkD,EAAE;QAC5G,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;QAChC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAC7B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IACjC,CAAC;CACF"}
@@ -0,0 +1,261 @@
1
+ /**
2
+ * inbox.ts — PURE Revisium-table inbox verbs. NO DBOS imports anywhere in this file.
3
+ *
4
+ * Covers: pushInbox, listInbox, getInbox, resolveInbox.
5
+ *
6
+ * RESUMABILITY guarantee (G9): resolveInbox COMPLETES idempotently on every call.
7
+ * A crash between the inbox patch and the step patch is repaired by a retry — the
8
+ * retry re-reads the already-resolved inbox row, takes the stored answer (stored
9
+ * answer wins; first resolver is authoritative), and idempotently drives the step
10
+ * from awaiting_approval → ready. Calling resolveInbox twice converges to:
11
+ * inbox resolved + step unblocked exactly once.
12
+ *
13
+ * KNOWN LIMITATION (G5 residual): data-access.ts patchRow is a plain write — no
14
+ * CAS. Two truly-concurrent first-time resolvers can both pass the `pending` read
15
+ * before either writes, and both proceed (each re-flips the step — harmless
16
+ * idempotent duplicate writes, but no CAS prevents dual entry). In practice the
17
+ * inbox has a single human resolver, so this is acceptable for 0002. A full
18
+ * conditional pending→resolved transition is a data-access-seam enhancement
19
+ * (add CAS/conditional write to data-access.ts) and is out of scope for 0002.
20
+ * Do NOT "fix" this by adding DBOS or hand-rolling a lock.
21
+ */
22
+ import { randomUUID } from 'node:crypto';
23
+ import { ControlPlaneError } from './errors.js';
24
+ import { compactStamp } from './steps.js'; // NOT from ./index.js — compactStamp is not re-exported there
25
+ // ─── secret redaction ────────────────────────────────────────
26
+ const SECRET_PATTERN = /(?:password|secret|token|key|credential|auth|api_key|apikey)/i;
27
+ /**
28
+ * Recursively strip obvious secret-shaped keys from a value before persisting.
29
+ * - Plain objects: redact matching keys at ANY depth, recurse into non-secret values.
30
+ * - Arrays: recurse into each element.
31
+ * - Primitives / null: return as-is.
32
+ * Full policy can firm up later; this covers the common cases.
33
+ */
34
+ export function redactSecrets(value) {
35
+ if (value === null || typeof value !== 'object')
36
+ return value;
37
+ if (Array.isArray(value)) {
38
+ return value.map(redactSecrets);
39
+ }
40
+ const result = {};
41
+ for (const [k, v] of Object.entries(value)) {
42
+ result[k] = SECRET_PATTERN.test(k) ? '[REDACTED]' : redactSecrets(v);
43
+ }
44
+ return result;
45
+ }
46
+ // ─── row mapping ─────────────────────────────────────────────
47
+ function str(v) {
48
+ return typeof v === 'string' ? v : '';
49
+ }
50
+ function mapInboxRow(rowId, data) {
51
+ return {
52
+ id: rowId,
53
+ kind: str(data.kind),
54
+ runId: str(data.run_id),
55
+ taskId: str(data.task_id),
56
+ stepId: str(data.step_id),
57
+ projectId: str(data.project_id),
58
+ title: str(data.title),
59
+ context: data.context ?? null,
60
+ options: Array.isArray(data.options) ? data.options.map(str) : [],
61
+ status: str(data.status),
62
+ answer: data.answer ?? null,
63
+ resolvedBy: str(data.resolved_by),
64
+ createdAt: str(data.created_at),
65
+ resolvedAt: str(data.resolved_at),
66
+ };
67
+ }
68
+ // ─── exported verbs ──────────────────────────────────────────
69
+ /**
70
+ * pushInbox — insert an inbox row; set the originating step to awaiting_approval
71
+ * (branch stops, siblings continue). Returns the inbox id.
72
+ *
73
+ * G10: seeds answer: null, resolved_by: '', resolved_at: '' so that the resolveInbox
74
+ * `replace` patches hit present paths (serializeData omits undefined fields, leaving
75
+ * absent paths that Revisium's patch handler rejects for `replace`).
76
+ *
77
+ * G1 (0004): accepts `opts.id` — when present, used VERBATIM (bypassing compactStamp+suffix).
78
+ * This is the fully-deterministic path used by gate pushes in the workflow body.
79
+ * Legacy callers pass no `id` → timestamp+suffix path is unchanged (backward-compatible).
80
+ *
81
+ * G1 idempotency: wraps createRow in a ROW_CONFLICT catch (mirrors append-event.ts:69-72).
82
+ * On a workflow-body replay the row already exists → swallows the conflict and returns the
83
+ * same id (no-op, no duplicate). Gate rows carry no stepId, so the step-park branch is moot
84
+ * on ROW_CONFLICT; we return before it.
85
+ *
86
+ * Redacts secrets in context before writing (contract line 163).
87
+ */
88
+ export async function pushInbox(da, item, opts) {
89
+ await da.assertReady();
90
+ // CR-C (0004 CR): avoid computing non-deterministic values on the deterministic gate path.
91
+ // When opts.id is present, we do NOT call new Date() until we are inside the actual insert —
92
+ // at which point ROW_CONFLICT makes the row exactly-once (first write wins; any replayed
93
+ // new Date() is discarded by the conflict and never persisted). The timestamp is a real wall-
94
+ // clock value on the FIRST insert and is irrelevant on replay. Only the legacy path (no
95
+ // opts.id) needs now for the id itself, so it is computed first in that branch only.
96
+ let id;
97
+ let now;
98
+ if (opts?.id === undefined) {
99
+ // Legacy path: timestamp is part of the id — must be computed before createRow.
100
+ now = opts?.now ?? new Date();
101
+ const suffix = opts?.idSuffix ?? randomUUID().replaceAll('-', '').slice(0, 8);
102
+ id = `inbox_${compactStamp(now)}_${suffix}`;
103
+ }
104
+ else {
105
+ // Deterministic gate path: id is caller-supplied verbatim; timestamp is only needed
106
+ // for created_at in the insert — evaluated lazily inside the try block below.
107
+ id = opts.id;
108
+ // Use opts.now if provided (test-seeded), otherwise defer new Date() to the insert.
109
+ now = opts?.now ?? new Date();
110
+ }
111
+ const safeContext = redactSecrets(item.context);
112
+ try {
113
+ await da.createRow('inbox', id, {
114
+ id,
115
+ kind: item.kind,
116
+ run_id: item.runId ?? '',
117
+ task_id: item.taskId ?? '',
118
+ step_id: item.stepId ?? '',
119
+ project_id: item.projectId ?? '',
120
+ title: item.title,
121
+ context: safeContext,
122
+ options: item.options ?? [],
123
+ status: 'pending',
124
+ // G10 — seed every later-patched optional column as a present value (never undefined).
125
+ // serializeData (json-fields.ts:38) omits undefined fields; an absent path causes
126
+ // Revisium's patch handler to reject a replace → resolveInbox patches would fail.
127
+ // answer: null is serialized by json-fields as JSON null → 'null' string on the JSON field.
128
+ answer: null,
129
+ resolved_by: '',
130
+ resolved_at: '',
131
+ // On the opts.id (gate) path, created_at uses `now` computed above. On a workflow-body
132
+ // replay, ROW_CONFLICT fires BEFORE this value is persisted — so a different now per
133
+ // replay is harmless (it is discarded). The FIRST insert's timestamp is the real one.
134
+ created_at: now.toISOString(),
135
+ });
136
+ }
137
+ catch (e) {
138
+ // Idempotent on workflow-body replay (mirror append-event.ts:69-72). Same row already exists → no-op.
139
+ // Gate rows carry no stepId, so the step-park branch below is moot on conflict; return early.
140
+ if (e instanceof ControlPlaneError && e.code === 'ROW_CONFLICT')
141
+ return id;
142
+ throw e;
143
+ }
144
+ // Park the originating step (bare path — G6):
145
+ if (item.stepId) {
146
+ await da.patchRow('steps', item.stepId, [
147
+ { op: 'replace', path: 'status', value: 'awaiting_approval' },
148
+ ]);
149
+ }
150
+ return id;
151
+ }
152
+ /**
153
+ * listInbox — read the inbox table, optionally filtered.
154
+ * Draft read; consumers never see Revisium types (returns InboxItem[]).
155
+ */
156
+ export async function listInbox(da, filter) {
157
+ await da.assertReady();
158
+ const rows = await da.listRows('inbox', { first: filter?.limit ?? 500 });
159
+ let items = rows.map((row) => mapInboxRow(row.rowId, row.data));
160
+ if (filter?.status)
161
+ items = items.filter((i) => i.status === filter.status);
162
+ if (filter?.runId)
163
+ items = items.filter((i) => i.runId === filter.runId);
164
+ return items;
165
+ }
166
+ /**
167
+ * getInbox — read a single inbox row.
168
+ * G12: intentionally returns InboxItem | null (maps da.getRow's null-on-missing path),
169
+ * superseding repo-layer-contract.md:172's non-null signature. Null is the safer shape.
170
+ */
171
+ export async function getInbox(da, id) {
172
+ await da.assertReady();
173
+ const row = await da.getRow('inbox', id);
174
+ if (!row)
175
+ return null;
176
+ return mapInboxRow(row.rowId, row.data);
177
+ }
178
+ /**
179
+ * resolveInbox — pure status flip + step unblock. NO DBOS signal (that is 0004).
180
+ *
181
+ * G2 (0004): now returns the STORED decision `{ status, answer }` so the caller (CLI gate path)
182
+ * can signal WHAT IS RECORDED, never the raw flag passed to this call.
183
+ * - `status` is the row's status BEFORE this call: 'pending' ⇒ just resolved; 'resolved' ⇒ already was.
184
+ * - `answer` is the EFFECTIVE stored answer (first-resolver wins; retry returns stored, not its arg).
185
+ *
186
+ * Existing 0002 callers (non-gate resolve, run cancel CLI) ignore the return — backward-compatible.
187
+ *
188
+ * RESUMABLE + idempotent (G9): COMPLETES on every call.
189
+ * - If inbox is pending: patch to resolved, then unblock the step.
190
+ * - If inbox is already resolved (retry / double-resolve): skip the inbox re-patch
191
+ * (stored answer wins), but STILL drive the step unblock from the stored answer.
192
+ * - This means a crash between the inbox patch and the step patch is repaired by a
193
+ * retry — the parked step is NEVER left stuck.
194
+ *
195
+ * CURRENT-STATE-SAFE (G5):
196
+ * - pending → resolved only (else VALIDATION_FAILURE).
197
+ * - Resurrection guard: if the step is no longer awaiting_approval (cancelled /
198
+ * re-driven / gone), console.warn and skip the step flip without throwing.
199
+ * - Deterministic continuation: step_cont_${itemId} so a re-run collides on the
200
+ * same id rather than creating a duplicate.
201
+ *
202
+ * KNOWN LIMITATION: no CAS (see module doc-comment above).
203
+ */
204
+ export async function resolveInbox(da, itemId, answer, resolvedBy, opts) {
205
+ await da.assertReady();
206
+ // (1) READ. getRow returns null for a missing inbox row.
207
+ const inbox = await da.getRow('inbox', itemId);
208
+ if (!inbox) {
209
+ throw new ControlPlaneError('ROW_NOT_FOUND', `inbox item not found: ${itemId}`);
210
+ }
211
+ // (2) STATE GUARD. Only pending or resolved are valid states to act on.
212
+ const status = inbox.data.status;
213
+ if (status !== 'pending' && status !== 'resolved') {
214
+ throw new ControlPlaneError('VALIDATION_FAILURE', `inbox ${itemId} cannot be resolved from status '${String(status)}'`);
215
+ }
216
+ const now = opts?.now ?? new Date();
217
+ // (3) IDEMPOTENT INBOX WRITE.
218
+ // pending → patch to resolved carrying answer / resolved_by / resolved_at.
219
+ // resolved → SKIP the re-patch. The stored answer wins (first resolver is authoritative).
220
+ if (status === 'pending') {
221
+ await da.patchRow('inbox', itemId, [
222
+ { op: 'replace', path: 'status', value: 'resolved' }, // BARE paths (G6)
223
+ { op: 'replace', path: 'answer', value: answer },
224
+ { op: 'replace', path: 'resolved_by', value: resolvedBy },
225
+ { op: 'replace', path: 'resolved_at', value: now.toISOString() },
226
+ ]);
227
+ }
228
+ // C2 (0004 review): the effective answer is ALWAYS the STORED (persisted) value — re-read
229
+ // from the row. On the already-resolved path the pre-read row carries the stored answer.
230
+ // On the first-resolve path, re-read after patching so we return what is durably recorded
231
+ // (guards against JSON-field round-trip canonicalization differences and concurrent resolvers).
232
+ // Both paths converge to: signal what is recorded, never the raw caller argument.
233
+ const resolvedInbox = status === 'pending' ? await da.getRow('inbox', itemId) : inbox;
234
+ const effectiveAnswer = resolvedInbox?.data.answer ?? answer;
235
+ // (4) ALWAYS-RUN STEP COMPLETION (the resumable part).
236
+ // Runs on every call once inbox is known-resolved.
237
+ const stepId = typeof inbox.data.step_id === 'string' ? inbox.data.step_id : '';
238
+ if (!stepId) {
239
+ // alert-kind / step-less item (including gate rows): inbox flip is the whole resolve.
240
+ return { status, answer: effectiveAnswer };
241
+ }
242
+ // Re-read the originating step and idempotently complete the unblock.
243
+ const step = await da.getRow('steps', stepId);
244
+ if (step && step.data.status === 'awaiting_approval') {
245
+ // Still parked → flip to ready carrying the stored answer. Bare paths (G6).
246
+ await da.patchRow('steps', stepId, [
247
+ { op: 'replace', path: 'status', value: 'ready' },
248
+ { op: 'replace', path: 'input', value: effectiveAnswer },
249
+ ]);
250
+ return { status, answer: effectiveAnswer };
251
+ }
252
+ // step already ready (done state) → clean NO-OP (no warn, no duplicate).
253
+ if (step && step.data.status === 'ready') {
254
+ return { status, answer: effectiveAnswer };
255
+ }
256
+ // step is null OR in an unexpected state (cancelled / re-driven / superseded) →
257
+ // RESURRECTION GUARD: do NOT flip it. Leave the inbox resolved and console.warn.
258
+ console.warn(`resolveInbox: step ${stepId} no longer awaiting_approval; skipping unblock`);
259
+ return { status, answer: effectiveAnswer };
260
+ }
261
+ //# sourceMappingURL=inbox.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inbox.js","sourceRoot":"","sources":["../../src/control-plane/inbox.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export { ControlPlaneError } from './errors.js';
2
+ export { createControlPlaneDataAccess, createControlPlaneDataAccessForTransport, } from './data-access.js';
3
+ export { runtimeTables } from './tables.js';
4
+ export { claimNextStep, startAttempt, writeResult, createSteps, failStep, recoverInFlight, fnv1a64Hex, } from './steps.js';
5
+ export { loadRole, loadModelProfile } from './definitions.js';
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/control-plane/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAA8B,MAAM,aAAa,CAAC;AAC5E,OAAO,EACL,4BAA4B,EAC5B,wCAAwC,GAQzC,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,aAAa,EAAqB,MAAM,aAAa,CAAC;AAC/D,OAAO,EACL,aAAa,EACb,YAAY,EACZ,WAAW,EACX,WAAW,EACX,QAAQ,EACR,eAAe,EACf,UAAU,GAKX,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAgC,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,68 @@
1
+ import { ControlPlaneError } from './errors.js';
2
+ const jsonFields = {
3
+ steps: ['input', 'output'],
4
+ events: ['payload'],
5
+ inbox: ['context', 'answer'],
6
+ };
7
+ function fieldsFor(table) {
8
+ return jsonFields[table] ?? [];
9
+ }
10
+ function pathRoot(path) {
11
+ return path.replace(/^\/+/, '').split(/[/.]/, 1)[0] ?? '';
12
+ }
13
+ function pathIsNestedJsonField(table, path) {
14
+ const normalized = path.replace(/^\/+/, '');
15
+ return fieldsFor(table).some((field) => normalized.startsWith(`${field}.`) || normalized.startsWith(`${field}/`));
16
+ }
17
+ export function serializeData(table, rowId, data) {
18
+ if (typeof data.id === 'string' && data.id !== rowId) {
19
+ throw new ControlPlaneError('VALIDATION_FAILURE', `data.id must match rowId for ${table}/${rowId}`, {
20
+ details: { table, rowId, field: 'id', value: data.id },
21
+ });
22
+ }
23
+ const serialized = {};
24
+ for (const [key, value] of Object.entries({ ...data, id: data.id ?? rowId })) {
25
+ if (value === undefined)
26
+ continue;
27
+ serialized[key] = fieldsFor(table).includes(key) ? JSON.stringify(value) : value;
28
+ }
29
+ return serialized;
30
+ }
31
+ export function deserializeData(table, rowId, data) {
32
+ const deserialized = { ...data };
33
+ for (const field of fieldsFor(table)) {
34
+ const value = deserialized[field];
35
+ if (value === undefined || value === '') {
36
+ deserialized[field] = null;
37
+ continue;
38
+ }
39
+ if (typeof value !== 'string') {
40
+ throw new ControlPlaneError('VALIDATION_FAILURE', `Expected serialized JSON string at ${table}/${rowId}.${field}`, {
41
+ details: { table, rowId, field, value },
42
+ });
43
+ }
44
+ try {
45
+ deserialized[field] = JSON.parse(value);
46
+ }
47
+ catch (error) {
48
+ throw new ControlPlaneError('VALIDATION_FAILURE', `Invalid stored JSON at ${table}/${rowId}.${field}`, {
49
+ details: { table, rowId, field, value, error },
50
+ });
51
+ }
52
+ }
53
+ return deserialized;
54
+ }
55
+ export function serializePatches(table, patches) {
56
+ return patches.map((patch) => {
57
+ const root = pathRoot(patch.path);
58
+ if (pathIsNestedJsonField(table, patch.path)) {
59
+ throw new ControlPlaneError('VALIDATION_FAILURE', `Nested JSON field patches are not supported: ${patch.path}`, {
60
+ details: { table, path: patch.path },
61
+ });
62
+ }
63
+ if (!fieldsFor(table).includes(root) || !('value' in patch) || patch.value === undefined)
64
+ return { ...patch };
65
+ return { ...patch, value: JSON.stringify(patch.value) };
66
+ });
67
+ }
68
+ //# sourceMappingURL=json-fields.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-fields.js","sourceRoot":"","sources":["../../src/control-plane/json-fields.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,75 @@
1
+ /**
2
+ * resolve-cwd.ts — shared B1 contract for resolving the working directory from a repo_ref.
3
+ *
4
+ * B1 CONTRACT — the resolved working directory MUST be an existing directory. We accept:
5
+ * • '' or '.' → base (the workspace cwd)
6
+ * • an ABSOLUTE existing dir → that dir (the external target repo — the MVP case)
7
+ * • a relative path under base → resolve(base, ref), if it stays under base AND exists
8
+ * We REJECT (throw a lesson-bearing error):
9
+ * • a non-existent path → never launch claude in a directory that isn't there
10
+ * • a path that is not a dir → never launch claude against a file
11
+ * • a relative '../…' escape → traversal guard (only relative refs are guarded; an ABSOLUTE
12
+ * ref is taken as the literal target repo, existence-checked).
13
+ */
14
+ import { resolve, isAbsolute, relative, sep } from 'node:path';
15
+ import { existsSync, realpathSync, statSync } from 'node:fs';
16
+ import { toStr } from './steps.js';
17
+ /** Resolve a repo_ref string to an absolute, existing directory path. */
18
+ export async function resolveRepoCwdFromRef(repoRef, base) {
19
+ if (repoRef === '' || repoRef === '.')
20
+ return base;
21
+ const resolved = isAbsolute(repoRef) ? resolve(repoRef) : resolve(base, repoRef);
22
+ if (!isAbsolute(repoRef) && resolved !== base && !resolved.startsWith(base + sep)) {
23
+ throw new Error(`resolveCwd: relative repo_ref ${JSON.stringify(repoRef)} escapes the workspace base ` +
24
+ `${JSON.stringify(base)} — refusing to launch`);
25
+ }
26
+ if (!existsSync(resolved) || !statSync(resolved).isDirectory()) {
27
+ throw new Error(`resolveCwd: repo path ${JSON.stringify(resolved)} does not exist or is not a directory ` +
28
+ `— refusing to launch claude`);
29
+ }
30
+ // Symlink escape guard: canonicalize via realpathSync and re-check containment.
31
+ // This prevents a symlink inside the allowed base from pointing outside it.
32
+ // Only applied when the ref is relative (absolute refs designate an external target repo
33
+ // and are accepted as-is — their real path IS the intended target).
34
+ if (!isAbsolute(repoRef)) {
35
+ let realTarget;
36
+ let realBase;
37
+ try {
38
+ realTarget = realpathSync(resolved);
39
+ realBase = realpathSync(base);
40
+ }
41
+ catch {
42
+ // realpathSync throws on non-existent paths — treat as non-existent (same as above check).
43
+ throw new Error(`resolveCwd: repo path ${JSON.stringify(resolved)} does not exist or is not a directory ` +
44
+ `— refusing to launch claude`);
45
+ }
46
+ const rel = relative(realBase, realTarget);
47
+ if (rel.startsWith('..') || isAbsolute(rel)) {
48
+ throw new Error(`resolveCwd: relative repo_ref ${JSON.stringify(repoRef)} escapes the workspace base ` +
49
+ `${JSON.stringify(base)} via symlink — refusing to launch`);
50
+ }
51
+ }
52
+ return resolved;
53
+ }
54
+ async function readRepoRef(da, taskId) {
55
+ const task = await da.getRow('tasks', taskId);
56
+ if (task === null) {
57
+ throw new Error(`resolveCwd: task ${taskId} not found — cannot resolve a working directory`);
58
+ }
59
+ return toStr(task.data.repo_ref);
60
+ }
61
+ /**
62
+ * STEP-level resolver (M3) — shape expected by claude runner: (step: Step) => Promise<string>.
63
+ * Reads tasks.repo_ref for the step's taskId via the data-access layer.
64
+ */
65
+ export function makeResolveCwd(da, base = process.cwd()) {
66
+ return async (step) => resolveRepoCwdFromRef(await readRepoRef(da, step.taskId), base);
67
+ }
68
+ /**
69
+ * TASK-level resolver (M3) — shape expected by the integrator + live preflight: (taskId: string) => Promise<string>.
70
+ * Uses the same core as makeResolveCwd over one shared readRepoRef.
71
+ */
72
+ export function makeResolveTaskCwd(da, base = process.cwd()) {
73
+ return async (taskId) => resolveRepoCwdFromRef(await readRepoRef(da, taskId), base);
74
+ }
75
+ //# sourceMappingURL=resolve-cwd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-cwd.js","sourceRoot":"","sources":["../../src/control-plane/resolve-cwd.ts"],"names":[],"mappings":"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"}