@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,135 @@
1
+ import { noopWorktreeManager } from './worktree-manager.js';
2
+ import { REVO_RESULT_CONTRACT, parseTransportEnvelope, extractAgentResult, normalizeNextSteps, } from './result-envelope.js';
3
+ const DEFAULT_TIMEOUT_MS = 600_000;
4
+ const DEFAULT_COMMAND = 'claude';
5
+ const ERROR_TAIL = 2_000;
6
+ function tail(text) {
7
+ const trimmed = text.trim();
8
+ return trimmed.length > ERROR_TAIL ? trimmed.slice(-ERROR_TAIL) : trimmed;
9
+ }
10
+ /** Read a positive number from a parsed model_profiles.params object, trying camel + snake keys. */
11
+ function readParamNum(params, ...keys) {
12
+ if (!params || typeof params !== 'object' || Array.isArray(params))
13
+ return undefined;
14
+ const rec = params;
15
+ for (const k of keys) {
16
+ const v = rec[k];
17
+ const n = typeof v === 'number' ? v : typeof v === 'string' ? Number(v) : NaN;
18
+ if (Number.isFinite(n) && n > 0)
19
+ return n;
20
+ }
21
+ return undefined;
22
+ }
23
+ // CLI invocation — the only place flags are assembled. EXACT flag set is confirmed by the manual
24
+ // Step-6 smoke before --runner defaults to auto. The permission mode keeps the headless run
25
+ // non-interactive: a tool not in allowedTools is auto-denied (no TTY can prompt in -p mode).
26
+ // 0008 #5: permissionMode is now per-role DATA (was hardcoded 'default'); model_profiles.params
27
+ // (previously unused "{}") is threaded in — a configured maxTurns maps to claude's --max-turns.
28
+ function buildArgs(modelId, allowedTools, permissionMode, params) {
29
+ const args = ['-p', '--model', modelId, '--output-format', 'json', '--permission-mode', permissionMode];
30
+ // Empty list → pass NO tools (most restrictive; text/plan-only). Never widen beyond role.allowedTools.
31
+ if (allowedTools.length > 0) {
32
+ args.push('--allowedTools', allowedTools.join(','));
33
+ }
34
+ const maxTurns = readParamNum(params, 'maxTurns', 'max_turns');
35
+ if (maxTurns !== undefined) {
36
+ args.push('--max-turns', String(Math.trunc(maxTurns)));
37
+ }
38
+ return args;
39
+ }
40
+ // Prompt order (design decision 6): context → attemptId line → REVO_RESULT_CONTRACT. Appending the
41
+ // contract HERE — not in build-context.ts, not in the role system_prompt — guarantees the agent is
42
+ // told how to emit on EVERY attempt, including retries.
43
+ function buildPrompt(context, attemptId) {
44
+ const idempotencyLine = `Attempt-Id: ${attemptId} — idempotency key. Reference it on any external effect you create.`;
45
+ return [context, idempotencyLine, REVO_RESULT_CONTRACT].join('\n');
46
+ }
47
+ // One CostRecord from the transport envelope. Prefer the CLI-reported USD; else compute from tokens.
48
+ // Zero tokens with no reported cost → empty costs (keeps zero-cost paths truly free).
49
+ function buildCosts(step, profile, env) {
50
+ const inputTokens = env.inputTokens ?? 0;
51
+ const outputTokens = env.outputTokens ?? 0;
52
+ const reportedUsd = (typeof env.costUsd === 'number' && Number.isFinite(env.costUsd)) ? env.costUsd : undefined;
53
+ if (inputTokens === 0 && outputTokens === 0 && reportedUsd === undefined)
54
+ return [];
55
+ const computed = (inputTokens / 1_000_000) * profile.costPerInput +
56
+ (outputTokens / 1_000_000) * profile.costPerOutput;
57
+ return [
58
+ {
59
+ modelProfile: step.modelProfile,
60
+ inputTokens,
61
+ outputTokens,
62
+ costAmount: reportedUsd ?? computed,
63
+ currency: 'USD',
64
+ },
65
+ ];
66
+ }
67
+ export function createClaudeCodeRunner(deps) {
68
+ const fallbackTimeoutMs = deps.timeoutMs ?? DEFAULT_TIMEOUT_MS;
69
+ const command = deps.command ?? DEFAULT_COMMAND;
70
+ return async ({ role, profile, context, attemptId, step }) => {
71
+ // attemptId is already minted by startAttempt (loop) — consumed here, never re-minted.
72
+ const baseCwd = await deps.resolveCwd(step);
73
+ const manager = deps.worktreeManager ?? noopWorktreeManager;
74
+ const cwd = await manager.create(step.id, baseCwd);
75
+ // 0008 #5: per-role data overrides the hardcoded defaults (timeout + permission mode).
76
+ const timeoutMs = role.timeoutMs ?? fallbackTimeoutMs;
77
+ const permissionMode = role.permissionMode ?? 'default';
78
+ try {
79
+ const req = {
80
+ command,
81
+ args: buildArgs(profile.modelId, role.allowedTools, permissionMode, profile.params),
82
+ cwd,
83
+ timeoutMs,
84
+ input: buildPrompt(context, attemptId),
85
+ };
86
+ const result = await deps.executor(req);
87
+ // Timeout / process failure → throw; the loop's catch → failStep returns the step to ready
88
+ // (backoff) or dead at the attempt cap. No loop change needed.
89
+ if (result.timedOut) {
90
+ throw new Error(`claude-code runner exceeded ${timeoutMs}ms`);
91
+ }
92
+ if (result.code !== 0) {
93
+ throw new Error(`claude-code runner exited with code ${String(result.code)}: ${tail(result.stderr || result.stdout)}`);
94
+ }
95
+ const transport = parseTransportEnvelope(result.stdout);
96
+ if (transport.isError) {
97
+ throw new Error(`claude-code runner reported is_error: ${tail(transport.text)}`);
98
+ }
99
+ const agent = extractAgentResult(transport.text);
100
+ const costs = buildCosts(step, profile, transport);
101
+ // needsHuman: do NOT write nextSteps — the loop parks via the existing awaiting_approval path.
102
+ if (agent.needsHuman) {
103
+ const parked = {
104
+ output: agent.output,
105
+ nextSteps: [],
106
+ costs,
107
+ needsHuman: true,
108
+ lesson: agent.lesson,
109
+ };
110
+ return parked;
111
+ }
112
+ const success = {
113
+ output: agent.output,
114
+ artifacts: agent.artifacts,
115
+ nextSteps: normalizeNextSteps(agent.nextSteps, step),
116
+ costs,
117
+ needsHuman: false,
118
+ lesson: agent.lesson,
119
+ };
120
+ return success;
121
+ }
122
+ finally {
123
+ try {
124
+ await manager.release(cwd);
125
+ }
126
+ catch (err) {
127
+ console.warn(`Warning: worktree release failed for ${cwd}: ${String(err)}`);
128
+ }
129
+ }
130
+ };
131
+ }
132
+ // Idempotency seam (this slice does NO external create): attemptId is minted before the run and
133
+ // threaded into the prompt so artifacts/effects can reference it. The "create-only-if-key-unused"
134
+ // guard for real commits/PRs is Plan 0011 — the runner itself performs no external write.
135
+ //# sourceMappingURL=claude-code-runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-code-runner.js","sourceRoot":"","sources":["../../src/worker/claude-code-runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,84 @@
1
+ import { execFileSync } from 'node:child_process';
2
+ import { existsSync, lstatSync, rmSync, symlinkSync } from 'node:fs';
3
+ import { basename, dirname, isAbsolute, join, resolve } from 'node:path';
4
+ function git(args, cwd) {
5
+ return execFileSync('git', args, { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }).trim();
6
+ }
7
+ function branchName(stepId) {
8
+ return `run/${stepId}`;
9
+ }
10
+ function stepIdFromWorktreePath(worktreePath) {
11
+ return basename(worktreePath);
12
+ }
13
+ function gitRootFromExistingPath(path) {
14
+ return git(['rev-parse', '--show-toplevel'], path);
15
+ }
16
+ function gitCommonRootFromExistingPath(path) {
17
+ const commonDir = git(['rev-parse', '--git-common-dir'], path);
18
+ const absoluteCommonDir = isAbsolute(commonDir) ? commonDir : resolve(path, commonDir);
19
+ return dirname(absoluteCommonDir);
20
+ }
21
+ function gitRootFromWorktreePath(worktreePath) {
22
+ if (existsSync(worktreePath)) {
23
+ return gitCommonRootFromExistingPath(worktreePath);
24
+ }
25
+ const worktreesDir = dirname(worktreePath);
26
+ if (basename(worktreesDir) !== '.worktrees')
27
+ return null;
28
+ const candidate = dirname(worktreesDir);
29
+ return existsSync(join(candidate, '.git')) ? candidate : null;
30
+ }
31
+ function deleteBranchIfPresent(gitRoot, stepId) {
32
+ try {
33
+ git(['branch', '-D', branchName(stepId)], gitRoot);
34
+ }
35
+ catch {
36
+ // Missing branches are expected after partial cleanup or manual recovery.
37
+ }
38
+ }
39
+ function pruneWorktrees(gitRoot) {
40
+ try {
41
+ git(['worktree', 'prune'], gitRoot);
42
+ }
43
+ catch {
44
+ console.warn(`Warning: git worktree prune failed in ${gitRoot}`);
45
+ }
46
+ }
47
+ export class GitWorktreeManager {
48
+ async create(stepId, baseDir) {
49
+ const gitRoot = gitRootFromExistingPath(baseDir);
50
+ const worktreePath = join(gitRoot, '.worktrees', stepId);
51
+ await this.release(worktreePath);
52
+ git(['worktree', 'add', '-b', branchName(stepId), worktreePath, 'HEAD'], gitRoot);
53
+ const sourceNodeModules = join(gitRoot, 'node_modules');
54
+ const targetNodeModules = join(worktreePath, 'node_modules');
55
+ if (existsSync(sourceNodeModules)) {
56
+ if (existsSync(targetNodeModules) || lstatSync(targetNodeModules, { throwIfNoEntry: false }) !== undefined) {
57
+ rmSync(targetNodeModules, { recursive: true, force: true });
58
+ }
59
+ symlinkSync(sourceNodeModules, targetNodeModules, 'dir');
60
+ }
61
+ return worktreePath;
62
+ }
63
+ async release(worktreePath) {
64
+ const gitRoot = gitRootFromWorktreePath(worktreePath);
65
+ if (gitRoot === null)
66
+ return;
67
+ if (existsSync(worktreePath)) {
68
+ try {
69
+ git(['worktree', 'remove', '--force', worktreePath], gitRoot);
70
+ }
71
+ catch {
72
+ console.warn(`Warning: git worktree remove failed for ${worktreePath}; deleting directory directly`);
73
+ rmSync(worktreePath, { recursive: true, force: true });
74
+ }
75
+ }
76
+ if (existsSync(worktreePath)) {
77
+ console.warn(`Warning: worktree path still exists after git removal: ${worktreePath}; deleting directory directly`);
78
+ rmSync(worktreePath, { recursive: true, force: true });
79
+ }
80
+ pruneWorktrees(gitRoot);
81
+ deleteBranchIfPresent(gitRoot, stepIdFromWorktreePath(worktreePath));
82
+ }
83
+ }
84
+ //# sourceMappingURL=git-worktree-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git-worktree-manager.js","sourceRoot":"","sources":["../../src/worker/git-worktree-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACrE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGzE,SAAS,GAAG,CAAC,IAAc,EAAE,GAAW;IACtC,OAAO,YAAY,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;AACxG,CAAC;AAED,SAAS,UAAU,CAAC,MAAc;IAChC,OAAO,OAAO,MAAM,EAAE,CAAC;AACzB,CAAC;AAED,SAAS,sBAAsB,CAAC,YAAoB;IAClD,OAAO,QAAQ,CAAC,YAAY,CAAC,CAAC;AAChC,CAAC;AAED,SAAS,uBAAuB,CAAC,IAAY;IAC3C,OAAO,GAAG,CAAC,CAAC,WAAW,EAAE,iBAAiB,CAAC,EAAE,IAAI,CAAC,CAAC;AACrD,CAAC;AAED,SAAS,6BAA6B,CAAC,IAAY;IACjD,MAAM,SAAS,GAAG,GAAG,CAAC,CAAC,WAAW,EAAE,kBAAkB,CAAC,EAAE,IAAI,CAAC,CAAC;IAC/D,MAAM,iBAAiB,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IACvF,OAAO,OAAO,CAAC,iBAAiB,CAAC,CAAC;AACpC,CAAC;AAED,SAAS,uBAAuB,CAAC,YAAoB;IACnD,IAAI,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QAC7B,OAAO,6BAA6B,CAAC,YAAY,CAAC,CAAC;IACrD,CAAC;IACD,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IAC3C,IAAI,QAAQ,CAAC,YAAY,CAAC,KAAK,YAAY;QAAE,OAAO,IAAI,CAAC;IACzD,MAAM,SAAS,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IACxC,OAAO,UAAU,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC;AAChE,CAAC;AAED,SAAS,qBAAqB,CAAC,OAAe,EAAE,MAAc;IAC5D,IAAI,CAAC;QACH,GAAG,CAAC,CAAC,QAAQ,EAAE,IAAI,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;IACrD,CAAC;IAAC,MAAM,CAAC;QACP,0EAA0E;IAC5E,CAAC;AACH,CAAC;AAED,SAAS,cAAc,CAAC,OAAe;IACrC,IAAI,CAAC;QACH,GAAG,CAAC,CAAC,UAAU,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,IAAI,CAAC,yCAAyC,OAAO,EAAE,CAAC,CAAC;IACnE,CAAC;AACH,CAAC;AAED,MAAM,OAAO,kBAAkB;IAC7B,KAAK,CAAC,MAAM,CAAC,MAAc,EAAE,OAAe;QAC1C,MAAM,OAAO,GAAG,uBAAuB,CAAC,OAAO,CAAC,CAAC;QACjD,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,EAAE,YAAY,EAAE,MAAM,CAAC,CAAC;QACzD,MAAM,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QAEjC,GAAG,CAAC,CAAC,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,CAAC,MAAM,CAAC,EAAE,YAAY,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC;QAElF,MAAM,iBAAiB,GAAG,IAAI,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;QACxD,MAAM,iBAAiB,GAAG,IAAI,CAAC,YAAY,EAAE,cAAc,CAAC,CAAC;QAC7D,IAAI,UAAU,CAAC,iBAAiB,CAAC,EAAE,CAAC;YAClC,IAAI,UAAU,CAAC,iBAAiB,CAAC,IAAI,SAAS,CAAC,iBAAiB,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC,KAAK,SAAS,EAAE,CAAC;gBAC3G,MAAM,CAAC,iBAAiB,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YAC9D,CAAC;YACD,WAAW,CAAC,iBAAiB,EAAE,iBAAiB,EAAE,KAAK,CAAC,CAAC;QAC3D,CAAC;QAED,OAAO,YAAY,CAAC;IACtB,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,YAAoB;QAChC,MAAM,OAAO,GAAG,uBAAuB,CAAC,YAAY,CAAC,CAAC;QACtD,IAAI,OAAO,KAAK,IAAI;YAAE,OAAO;QAE7B,IAAI,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;YAC7B,IAAI,CAAC;gBACH,GAAG,CAAC,CAAC,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,YAAY,CAAC,EAAE,OAAO,CAAC,CAAC;YAChE,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,CAAC,IAAI,CAAC,2CAA2C,YAAY,+BAA+B,CAAC,CAAC;gBACrG,MAAM,CAAC,YAAY,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YACzD,CAAC;QACH,CAAC;QAED,IAAI,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;YAC7B,OAAO,CAAC,IAAI,CAAC,0DAA0D,YAAY,+BAA+B,CAAC,CAAC;YACpH,MAAM,CAAC,YAAY,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACzD,CAAC;QAED,cAAc,CAAC,OAAO,CAAC,CAAC;QACxB,qBAAqB,CAAC,OAAO,EAAE,sBAAsB,CAAC,YAAY,CAAC,CAAC,CAAC;IACvE,CAAC;CACF"}
@@ -0,0 +1,122 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { claimNextStep, startAttempt, writeResult, createSteps, failStep, recoverInFlight, compactStamp, } from '../control-plane/steps.js';
3
+ import { buildContext } from './build-context.js';
4
+ async function processClaimedStep(deps, workerId, step) {
5
+ const { da, loadRole, loadModelProfile, runAgent } = deps;
6
+ const role = await loadRole(step.role);
7
+ const profile = await loadModelProfile(role.modelLevel);
8
+ const context = await buildContext(da, step, role);
9
+ const { attemptId } = await startAttempt(da, step, { workerId, modelProfile: profile.modelId });
10
+ try {
11
+ const result = await runAgent({ role, profile, context, attemptId, step });
12
+ return { attemptId, result };
13
+ }
14
+ catch (err) {
15
+ await failStep(da, step, attemptId, {
16
+ lesson: err instanceof Error ? err.message : String(err),
17
+ error: err instanceof Error ? (err.stack ?? err.message) : String(err),
18
+ });
19
+ return null;
20
+ }
21
+ }
22
+ async function handleResult(da, step, attemptId, result) {
23
+ if (result.needsHuman) {
24
+ await parkForHuman(da, step, attemptId, result);
25
+ return;
26
+ }
27
+ const nextSteps = result.nextSteps.map((ns) => ({ ...ns, runId: step.runId }));
28
+ // createSteps runs BEFORE writeResult makes the parent terminal. Child IDs are a bounded,
29
+ // deterministic hash of (parent id + index), so a retry regenerates the same IDs and
30
+ // createSteps' idempotency check skips existing rows.
31
+ //
32
+ // createSteps failures (e.g. Revisium rejecting an invalid next step) are pre-terminal and
33
+ // SAFE to fail gracefully: nothing has been finalized yet, so failStep lets the step back off
34
+ // / go dead instead of crashing the worker. writeResult is DIFFERENT — it mutates several rows
35
+ // (attempt close, event, cost, then the step), so a partial failure must NOT be routed through
36
+ // failStep (that would mark a half-written step failed). We let writeResult errors propagate:
37
+ // the step stays 'running' → recoverInFlight resets it on the next start → idempotent retry.
38
+ try {
39
+ await createSteps(da, nextSteps, { parentStepId: step.id });
40
+ }
41
+ catch (err) {
42
+ await failStep(da, step, attemptId, {
43
+ lesson: err instanceof Error ? err.message : String(err),
44
+ error: err instanceof Error ? (err.stack ?? err.message) : String(err),
45
+ });
46
+ return;
47
+ }
48
+ await writeResult(da, step, attemptId, result.output, result.costs);
49
+ }
50
+ export function sleep(ms, signal) {
51
+ return new Promise((resolve) => {
52
+ const onAbort = () => { clearTimeout(timer); resolve(); };
53
+ const timer = setTimeout(() => {
54
+ signal?.removeEventListener('abort', onAbort);
55
+ resolve();
56
+ }, ms);
57
+ signal?.addEventListener('abort', onAbort, { once: true });
58
+ });
59
+ }
60
+ async function parkForHuman(da, step, attemptId, result) {
61
+ const now = new Date();
62
+ const nowIso = now.toISOString();
63
+ const st = compactStamp(now);
64
+ const sfx = randomUUID().replaceAll('-', '').slice(0, 8);
65
+ // Close the attempt so it is no longer 'running' while the step waits for a human.
66
+ await da.patchRow('attempts', attemptId, [
67
+ { op: 'replace', path: 'status', value: 'paused' },
68
+ { op: 'replace', path: 'finished_at', value: nowIso },
69
+ ]);
70
+ // Minimal inbox parking: mark step awaiting_approval, clear lease, append event.
71
+ // Full pushInbox (inbox row creation + resolution workflow) is deferred.
72
+ await da.patchRow('steps', step.id, [
73
+ { op: 'replace', path: 'status', value: 'awaiting_approval' },
74
+ { op: 'replace', path: 'lease_owner', value: '' },
75
+ { op: 'replace', path: 'lease_expires_at', value: '' },
76
+ { op: 'replace', path: 'updated_at', value: nowIso },
77
+ ]);
78
+ await da.createRow('events', `event_${st}_step-needs-human_${sfx}`, {
79
+ id: `event_${st}_step-needs-human_${sfx}`,
80
+ run_id: step.runId,
81
+ task_id: step.taskId,
82
+ step_id: step.id,
83
+ type: 'step_needs_human',
84
+ payload: { attempt_id: attemptId, lesson: result.lesson },
85
+ actor: 'orchestrator',
86
+ created_at: nowIso,
87
+ });
88
+ }
89
+ async function runNextStep(deps, workerId, roles) {
90
+ const { da } = deps;
91
+ const step = await claimNextStep(da, workerId, roles);
92
+ if (!step)
93
+ return 'idle';
94
+ const processed = await processClaimedStep(deps, workerId, step);
95
+ if (!processed)
96
+ return 'failed';
97
+ await handleResult(da, step, processed.attemptId, processed.result);
98
+ return 'completed';
99
+ }
100
+ export async function runWorker(deps, opts) {
101
+ const { da } = deps;
102
+ const { workerId, roles, once, idleSleepMs = 5000, maxCycles, signal } = opts;
103
+ if (!Number.isFinite(idleSleepMs) || idleSleepMs < 0) {
104
+ throw new Error(`idleSleepMs must be a non-negative finite number, got: ${String(idleSleepMs)}`);
105
+ }
106
+ await recoverInFlight(da, workerId);
107
+ let cycles = 0;
108
+ while (true) {
109
+ if (signal?.aborted)
110
+ break;
111
+ if (maxCycles !== undefined && cycles >= maxCycles)
112
+ break;
113
+ const outcome = await runNextStep(deps, workerId, roles);
114
+ if (outcome !== 'idle')
115
+ cycles++;
116
+ if (once)
117
+ break;
118
+ if (outcome === 'idle')
119
+ await sleep(idleSleepMs, signal);
120
+ }
121
+ }
122
+ //# sourceMappingURL=loop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loop.js","sourceRoot":"","sources":["../../src/worker/loop.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ import { spawn } from 'node:child_process';
2
+ // Kill the whole process GROUP, not just the leader: `claude` spawns subprocesses, and only a
3
+ // negative-PID signal reaches them. `detached: true` (below) made the child its own group leader,
4
+ // so process.kill(-pid) targets the group. Fall back to a direct kill if the group send throws.
5
+ function killGroup(child) {
6
+ const pid = child.pid;
7
+ if (pid === undefined) {
8
+ child.kill('SIGKILL');
9
+ return;
10
+ }
11
+ try {
12
+ process.kill(-pid, 'SIGKILL');
13
+ }
14
+ catch {
15
+ child.kill('SIGKILL');
16
+ }
17
+ }
18
+ // Real implementation, used only in production wiring (never in unit tests).
19
+ export const spawnExecutor = (req) => new Promise((resolve, reject) => {
20
+ const child = spawn(req.command, req.args, {
21
+ cwd: req.cwd,
22
+ env: req.env ? { ...process.env, ...req.env } : process.env,
23
+ // Own process group → a negative-PID SIGKILL reaps `claude` AND its children on timeout.
24
+ // Do NOT unref(): the parent must stay attached to collect stdout/stderr and observe exit.
25
+ detached: true,
26
+ stdio: ['pipe', 'pipe', 'pipe'],
27
+ });
28
+ let stdout = '';
29
+ let stderr = '';
30
+ let timedOut = false;
31
+ let settled = false;
32
+ child.stdout?.setEncoding('utf8');
33
+ child.stderr?.setEncoding('utf8');
34
+ child.stdout?.on('data', (chunk) => { stdout += chunk; });
35
+ child.stderr?.on('data', (chunk) => { stderr += chunk; });
36
+ const timer = setTimeout(() => {
37
+ timedOut = true;
38
+ killGroup(child);
39
+ }, req.timeoutMs);
40
+ // Spawn-level errors (binary missing) reject — the runner converts that to a lesson.
41
+ child.on('error', (err) => {
42
+ if (settled)
43
+ return;
44
+ settled = true;
45
+ clearTimeout(timer);
46
+ reject(err);
47
+ });
48
+ // Never reject for a non-zero exit; return the result and let the runner decide how to map it.
49
+ child.on('close', (code) => {
50
+ if (settled)
51
+ return;
52
+ settled = true;
53
+ clearTimeout(timer);
54
+ resolve({ code, stdout, stderr, timedOut });
55
+ });
56
+ // Swallow EPIPE: the child may exit before draining stdin; the 'close' handler reports the result.
57
+ child.stdin?.on('error', () => { });
58
+ if (req.input !== undefined)
59
+ child.stdin?.write(req.input);
60
+ child.stdin?.end();
61
+ });
62
+ //# sourceMappingURL=process-executor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-executor.js","sourceRoot":"","sources":["../../src/worker/process-executor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,134 @@
1
+ // The EXACT contract between the agent and the runner. Two layers:
2
+ // A. transport envelope — `claude -p --output-format json` stdout (final text + cost/usage)
3
+ // B. agent result envelope — a sentineled REVO_RESULT block the agent emits inside that final text
4
+ //
5
+ // The instruction below is the SINGLE SOURCE OF TRUTH. The runner (claude-code-runner.ts) appends it
6
+ // to EVERY prompt so the agent is always told how to emit; the parser here keys on the same markers.
7
+ // It lives next to the parser — not in build-context.ts (the stub shares that), not in role prompts.
8
+ export const REVO_RESULT_CONTRACT = `
9
+ You MUST end your reply with a single result block in EXACTLY this form — the markers on their own lines,
10
+ valid JSON between them, and NOTHING after the closing marker:
11
+
12
+ <<<REVO_RESULT
13
+ {
14
+ "output": <any JSON — a short human-readable summary or structured result>,
15
+ "artifacts": <any JSON, optional — e.g. { "planPath": "docs/plans/00xx.md" }; omit if none>,
16
+ "nextSteps": [
17
+ { "role": "developer", "kind": "implement", "input": { "from": "<this step>" },
18
+ "modelProfile"?: "standard", "taskId"?: "<defaults to the current step's task>",
19
+ "priority"?: 0, "maxAttempts"?: 3, "dependsOn"?: [], "runAfter"?: "" }
20
+ ],
21
+ "needsHuman": false,
22
+ "lesson": null
23
+ }
24
+ REVO_RESULT>>>
25
+
26
+ If you have no follow-up work, return "nextSteps": []. If you are blocked and need a human, set
27
+ "needsHuman": true and "nextSteps": []. Emit the block exactly once.
28
+ `;
29
+ // Markers the parser keys on — must stay identical to those embedded in REVO_RESULT_CONTRACT.
30
+ // The marker-sync unit test guards that invariant.
31
+ const OPEN_MARKER = '<<<REVO_RESULT';
32
+ const CLOSE_MARKER = 'REVO_RESULT>>>';
33
+ function readNumber(value) {
34
+ return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
35
+ }
36
+ function readFinalText(obj) {
37
+ if (typeof obj.result === 'string')
38
+ return obj.result;
39
+ if (typeof obj.text === 'string')
40
+ return obj.text;
41
+ return '';
42
+ }
43
+ // Layer A. Parse defensively; field names are read only inside this module, so a CLI drift is a
44
+ // one-file change. Throws only when stdout is not parseable JSON.
45
+ export function parseTransportEnvelope(stdout) {
46
+ let parsed;
47
+ try {
48
+ parsed = JSON.parse(stdout);
49
+ }
50
+ catch {
51
+ throw new Error('claude -p did not return parseable JSON (transport envelope)');
52
+ }
53
+ if (parsed === null || typeof parsed !== 'object') {
54
+ throw new Error('claude -p did not return parseable JSON (transport envelope)');
55
+ }
56
+ const obj = parsed;
57
+ const usage = obj.usage !== null && typeof obj.usage === 'object'
58
+ ? obj.usage
59
+ : undefined;
60
+ return {
61
+ text: readFinalText(obj),
62
+ isError: Boolean(obj.is_error),
63
+ costUsd: readNumber(obj.total_cost_usd) ?? readNumber(obj.cost_usd),
64
+ inputTokens: readNumber(usage?.input_tokens),
65
+ outputTokens: readNumber(usage?.output_tokens),
66
+ };
67
+ }
68
+ // Layer B. Extract the substring between the markers and JSON.parse it. Absent or unparseable →
69
+ // the documented lesson-bearing error (the corrective is the contract the runner re-appends, not this).
70
+ export function extractAgentResult(text) {
71
+ const start = text.indexOf(OPEN_MARKER);
72
+ const end = start === -1 ? -1 : text.indexOf(CLOSE_MARKER, start + OPEN_MARKER.length);
73
+ if (start === -1 || end === -1) {
74
+ throw new Error('agent did not emit a parseable REVO_RESULT envelope');
75
+ }
76
+ const jsonSlice = text.slice(start + OPEN_MARKER.length, end);
77
+ let parsed;
78
+ try {
79
+ parsed = JSON.parse(jsonSlice);
80
+ }
81
+ catch {
82
+ throw new Error('agent did not emit a parseable REVO_RESULT envelope');
83
+ }
84
+ if (parsed === null || typeof parsed !== 'object') {
85
+ throw new Error('agent did not emit a parseable REVO_RESULT envelope');
86
+ }
87
+ const obj = parsed;
88
+ return {
89
+ output: obj.output,
90
+ artifacts: obj.artifacts,
91
+ nextSteps: Array.isArray(obj.nextSteps) ? obj.nextSteps : [],
92
+ needsHuman: Boolean(obj.needsHuman),
93
+ lesson: typeof obj.lesson === 'string' ? obj.lesson : undefined,
94
+ };
95
+ }
96
+ // Map each raw nextSteps entry to NewStepSpec. Require role/kind/input; default taskId and
97
+ // modelProfile from the current step so the agent never needs to know IDs. Throw a lesson-bearing
98
+ // error (naming the index) on a malformed entry.
99
+ export function normalizeNextSteps(raw, step) {
100
+ return raw.map((entry, i) => {
101
+ if (entry === null || typeof entry !== 'object') {
102
+ throw new Error(`REVO_RESULT nextSteps[${i}] is not an object`);
103
+ }
104
+ const e = entry;
105
+ if (typeof e.role !== 'string' || e.role.length === 0) {
106
+ throw new Error(`REVO_RESULT nextSteps[${i}] missing required "role"`);
107
+ }
108
+ if (typeof e.kind !== 'string' || e.kind.length === 0) {
109
+ throw new Error(`REVO_RESULT nextSteps[${i}] missing required "kind"`);
110
+ }
111
+ if (!('input' in e)) {
112
+ throw new Error(`REVO_RESULT nextSteps[${i}] missing required "input"`);
113
+ }
114
+ const spec = {
115
+ taskId: typeof e.taskId === 'string' && e.taskId.length > 0 ? e.taskId : step.taskId,
116
+ role: e.role,
117
+ kind: e.kind,
118
+ input: e.input,
119
+ modelProfile: typeof e.modelProfile === 'string' && e.modelProfile.length > 0
120
+ ? e.modelProfile
121
+ : step.modelProfile,
122
+ };
123
+ if (typeof e.priority === 'number')
124
+ spec.priority = e.priority;
125
+ if (typeof e.maxAttempts === 'number')
126
+ spec.maxAttempts = e.maxAttempts;
127
+ if (Array.isArray(e.dependsOn))
128
+ spec.dependsOn = e.dependsOn.map(String);
129
+ if (typeof e.runAfter === 'string')
130
+ spec.runAfter = e.runAfter;
131
+ return spec;
132
+ });
133
+ }
134
+ //# sourceMappingURL=result-envelope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"result-envelope.js","sourceRoot":"","sources":["../../src/worker/result-envelope.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ // The single RunAgent the loop injects. All runner choice hides here, dispatched on role.runner —
2
+ // the loop never learns about runner kinds. A not-implemented runner throws (routing through
3
+ // failStep to a clear lesson); it never silently no-ops.
4
+ export function createRunAgent(deps) {
5
+ return async (args) => {
6
+ switch (args.role.runner) {
7
+ case 'claude-code':
8
+ return deps.claudeCode(args);
9
+ case 'codex':
10
+ throw new Error('RUNNER_NOT_IMPLEMENTED: codex runner is a later plan');
11
+ case 'script':
12
+ if (!deps.script)
13
+ throw new Error('RUNNER_NOT_IMPLEMENTED: script runner not wired');
14
+ return deps.script(args);
15
+ default:
16
+ throw new Error(`RUNNER_NOT_IMPLEMENTED: unknown runner "${String(args.role.runner)}"`);
17
+ }
18
+ };
19
+ }
20
+ //# sourceMappingURL=runner-dispatch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner-dispatch.js","sourceRoot":"","sources":["../../src/worker/runner-dispatch.ts"],"names":[],"mappings":"AAEA,kGAAkG;AAClG,6FAA6F;AAC7F,yDAAyD;AACzD,MAAM,UAAU,cAAc,CAAC,IAAiD;IAC9E,OAAO,KAAK,EAAE,IAAI,EAAE,EAAE;QACpB,QAAQ,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YACzB,KAAK,aAAa;gBAChB,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;YAC/B,KAAK,OAAO;gBACV,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;YAC1E,KAAK,QAAQ;gBACX,IAAI,CAAC,IAAI,CAAC,MAAM;oBAAE,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;gBACrF,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAC3B;gBACE,MAAM,IAAI,KAAK,CAAC,2CAA2C,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC5F,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.js","sourceRoot":"","sources":["../../src/worker/runner.ts"],"names":[],"mappings":""}
@@ -0,0 +1,28 @@
1
+ const DEFAULT_TIMEOUT_MS = 120_000;
2
+ /** Wraps registered script modules as a RunAgent with a configurable JS-level timeout. */
3
+ export function createScriptRunner(deps) {
4
+ const timeoutMs = deps.timeoutMs ?? DEFAULT_TIMEOUT_MS;
5
+ return async ({ role, step }) => {
6
+ const module = deps.scripts[role.name];
7
+ if (!module) {
8
+ throw new Error(`SCRIPT_NOT_FOUND: no script registered for role "${role.name}"`);
9
+ }
10
+ const parsedInput = typeof step.input === 'string'
11
+ ? JSON.parse(step.input || '{}')
12
+ : (step.input ?? {});
13
+ let timer;
14
+ const timeout = new Promise((_, reject) => {
15
+ timer = setTimeout(() => reject(new Error(`script runner exceeded ${timeoutMs}ms for role "${role.name}"`)), timeoutMs);
16
+ });
17
+ try {
18
+ // For sync script bodies (execFileSync in defaultExecGh) the OS-level exec timeout is the real guard; this timer covers async bodies.
19
+ return await Promise.race([module.run(parsedInput, step), timeout]);
20
+ }
21
+ finally {
22
+ // Clear the timer on every exit (success OR failure); an uncleared setTimeout keeps the
23
+ // event loop alive ~120s and hangs `--once` / tests.
24
+ clearTimeout(timer);
25
+ }
26
+ };
27
+ }
28
+ //# sourceMappingURL=script-runner.js.map