@voybio/ace-swarm 2.4.9 → 2.4.10

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 (185) hide show
  1. package/CHANGELOG.md +52 -14
  2. package/README.md +14 -10
  3. package/assets/.agents/ACE/ACE_coders/AGENTS.md +23 -30
  4. package/assets/.agents/ACE/ACE_coders/INSTRUCTIONS.md +21 -30
  5. package/assets/.agents/skills/landing-review-watcher/SKILL.md +1 -1
  6. package/assets/.agents/skills/learn-from-work/SKILL.md +41 -0
  7. package/assets/agent-state/ACE_WORKFLOW.md +25 -2
  8. package/assets/agent-state/MODULES/schemas/ACE_RUNTIME_PROFILE.schema.json +143 -2
  9. package/assets/agent-state/MODULES/schemas/RUNTIME_EXECUTOR_SESSION_REGISTRY.schema.json +2 -2
  10. package/assets/agent-state/MODULES/schemas/SWARM_HANDOFF.schema.json +10 -0
  11. package/assets/eval-battery/battery.json +1 -1
  12. package/assets/instructions/ACE_Coder.instructions.md +9 -13
  13. package/assets/scripts/ace-hook-dispatch.mjs +16 -411
  14. package/assets/tasks/SWARM_HANDOFF.example_ui_to_coders.json +1 -2
  15. package/assets/vendor/README.md +9 -0
  16. package/assets/vendor/vericify-reader.mjs +272 -0
  17. package/assets/vendor/vericify-reader.provenance.json +10 -0
  18. package/dist/ace-autonomy.d.ts +58 -0
  19. package/dist/ace-autonomy.js +211 -6
  20. package/dist/ace-internal-tools.d.ts +1 -0
  21. package/dist/ace-internal-tools.js +3 -3
  22. package/dist/ace-server-instructions.d.ts +3 -3
  23. package/dist/ace-server-instructions.js +46 -192
  24. package/dist/cli.js +44 -6
  25. package/dist/experience-reflector.d.ts +7 -0
  26. package/dist/experience-reflector.js +39 -0
  27. package/dist/goal-engine.d.ts +64 -7
  28. package/dist/goal-engine.js +197 -41
  29. package/dist/handoff-registry.d.ts +10 -0
  30. package/dist/handoff-registry.js +59 -0
  31. package/dist/helpers/artifacts.d.ts +6 -2
  32. package/dist/helpers/artifacts.js +8 -6
  33. package/dist/helpers/bootstrap.js +13 -20
  34. package/dist/helpers/path-utils.js +2 -0
  35. package/dist/helpers/store-resolution.js +18 -3
  36. package/dist/helpers/workspace-root.d.ts +2 -0
  37. package/dist/helpers/workspace-root.js +9 -0
  38. package/dist/helpers.d.ts +1 -0
  39. package/dist/helpers.js +1 -0
  40. package/dist/hermes/bridge-protocol.d.ts +1 -1
  41. package/dist/hermes/session-manager.d.ts +17 -0
  42. package/dist/hermes/session-manager.js +67 -14
  43. package/dist/host-goal-engine.d.ts +138 -0
  44. package/dist/host-goal-engine.js +793 -0
  45. package/dist/host-goal-status.d.ts +113 -0
  46. package/dist/host-goal-status.js +175 -0
  47. package/dist/host-learning.d.ts +68 -0
  48. package/dist/host-learning.js +115 -0
  49. package/dist/host-observation.d.ts +12 -0
  50. package/dist/host-observation.js +91 -0
  51. package/dist/host-plan-revision.d.ts +14 -0
  52. package/dist/host-plan-revision.js +101 -0
  53. package/dist/host-verification-job.d.ts +12 -0
  54. package/dist/host-verification-job.js +183 -0
  55. package/dist/host-work-context.d.ts +12 -0
  56. package/dist/host-work-context.js +8 -0
  57. package/dist/job-scheduler.d.ts +48 -14
  58. package/dist/job-scheduler.js +169 -23
  59. package/dist/job-worker.d.ts +3 -1
  60. package/dist/job-worker.js +10 -3
  61. package/dist/kanban.d.ts +4 -1
  62. package/dist/kanban.js +44 -16
  63. package/dist/learned-artifacts.d.ts +64 -0
  64. package/dist/learned-artifacts.js +321 -0
  65. package/dist/learning-controls.d.ts +142 -0
  66. package/dist/learning-controls.js +135 -0
  67. package/dist/learning-input-context.d.ts +9 -0
  68. package/dist/learning-input-context.js +62 -0
  69. package/dist/learning-lifecycle.d.ts +10 -0
  70. package/dist/learning-lifecycle.js +11 -0
  71. package/dist/learning-overlays.d.ts +3 -0
  72. package/dist/learning-overlays.js +17 -0
  73. package/dist/learning-policy.d.ts +40 -0
  74. package/dist/learning-policy.js +38 -0
  75. package/dist/learning-records.d.ts +35 -0
  76. package/dist/learning-records.js +17 -0
  77. package/dist/learning-scorecard.d.ts +82 -0
  78. package/dist/learning-scorecard.js +86 -0
  79. package/dist/learning-serving.d.ts +29 -0
  80. package/dist/learning-serving.js +134 -0
  81. package/dist/linked-work.d.ts +9 -0
  82. package/dist/linked-work.js +28 -0
  83. package/dist/local-model-runtime.js +1 -1
  84. package/dist/mcp-command-progress.d.ts +16 -0
  85. package/dist/mcp-command-progress.js +70 -0
  86. package/dist/model-bridge.d.ts +14 -13
  87. package/dist/model-bridge.js +73 -41
  88. package/dist/orchestrator-supervisor.d.ts +108 -6
  89. package/dist/orchestrator-supervisor.js +4 -32
  90. package/dist/plan-proposal.js +3 -3
  91. package/dist/playbook-curator.d.ts +1 -0
  92. package/dist/playbook-curator.js +11 -9
  93. package/dist/process-identity.d.ts +8 -0
  94. package/dist/process-identity.js +38 -0
  95. package/dist/progress-command.d.ts +34 -0
  96. package/dist/progress-command.js +153 -0
  97. package/dist/prompts.js +15 -15
  98. package/dist/resources.js +2 -2
  99. package/dist/runtime-command.d.ts +8 -1
  100. package/dist/runtime-command.js +48 -7
  101. package/dist/runtime-executor.js +33 -11
  102. package/dist/runtime-profile-syntax.d.ts +6 -0
  103. package/dist/runtime-profile-syntax.js +121 -0
  104. package/dist/runtime-profile.d.ts +37 -5
  105. package/dist/runtime-profile.js +36 -129
  106. package/dist/runtime-tool-specs.js +1 -1
  107. package/dist/safe-edit.d.ts +7 -3
  108. package/dist/safe-edit.js +40 -47
  109. package/dist/schemas.js +4 -2
  110. package/dist/server.d.ts +2 -16
  111. package/dist/server.js +14 -20
  112. package/dist/shared.d.ts +2 -0
  113. package/dist/shared.js +2 -0
  114. package/dist/skill-auditor.d.ts +19 -0
  115. package/dist/skill-auditor.js +32 -2
  116. package/dist/skill-catalog.d.ts +4 -1
  117. package/dist/skill-catalog.js +6 -5
  118. package/dist/store/ace-packed-store.d.ts +5 -1
  119. package/dist/store/ace-packed-store.js +33 -1
  120. package/dist/store/bootstrap-store.js +19 -12
  121. package/dist/store/cache-workspace.d.ts +8 -0
  122. package/dist/store/cache-workspace.js +94 -27
  123. package/dist/store/importer.js +11 -20
  124. package/dist/store/knowledge-bake.d.ts +3 -1
  125. package/dist/store/knowledge-bake.js +10 -1
  126. package/dist/store/legacy-vericify-posts.d.ts +8 -0
  127. package/dist/store/legacy-vericify-posts.js +76 -0
  128. package/dist/store/materializers/hook-context-materializer.d.ts +2 -1
  129. package/dist/store/materializers/hook-context-materializer.js +6 -2
  130. package/dist/store/materializers/host-file-materializer.js +2 -2
  131. package/dist/store/materializers/projection-manager.d.ts +4 -1
  132. package/dist/store/materializers/projection-manager.js +10 -3
  133. package/dist/store/materializers/todo-syncer.d.ts +39 -20
  134. package/dist/store/materializers/todo-syncer.js +208 -110
  135. package/dist/store/materializers/vericify-projector.js +15 -26
  136. package/dist/store/repositories/goal-repository.d.ts +51 -2
  137. package/dist/store/repositories/goal-repository.js +114 -48
  138. package/dist/store/repositories/playbook-repository.d.ts +41 -3
  139. package/dist/store/repositories/playbook-repository.js +61 -5
  140. package/dist/store/repositories/todo-repository.d.ts +8 -2
  141. package/dist/store/repositories/todo-repository.js +1 -1
  142. package/dist/store/repositories/vericify-repository.d.ts +4 -0
  143. package/dist/store/repositories/vericify-repository.js +46 -0
  144. package/dist/store/skills-install.d.ts +1 -0
  145. package/dist/store/skills-install.js +20 -16
  146. package/dist/store/source-preservation.d.ts +24 -0
  147. package/dist/store/source-preservation.js +79 -0
  148. package/dist/store/store-artifacts.js +8 -1
  149. package/dist/store/test-write-boundary.d.ts +16 -0
  150. package/dist/store/test-write-boundary.js +175 -0
  151. package/dist/store/types.d.ts +5 -1
  152. package/dist/store/write-coordinator.js +16 -0
  153. package/dist/task-plan-context.d.ts +11 -0
  154. package/dist/task-plan-context.js +59 -0
  155. package/dist/todo-state.d.ts +19 -9
  156. package/dist/todo-state.js +204 -321
  157. package/dist/tools-agent.d.ts +1 -1
  158. package/dist/tools-agent.js +38 -62
  159. package/dist/tools-files.d.ts +0 -3
  160. package/dist/tools-files.js +21 -11
  161. package/dist/tools-framework.js +16 -25
  162. package/dist/tools-goal.d.ts +2 -4
  163. package/dist/tools-goal.js +119 -16
  164. package/dist/tools-handoff.js +28 -4
  165. package/dist/tools-lifecycle.js +1 -1
  166. package/dist/tools-memory.d.ts +0 -3
  167. package/dist/tools-memory.js +51 -6
  168. package/dist/tools-scheduler.js +19 -17
  169. package/dist/tools-todo.js +160 -148
  170. package/dist/vericify-bridge.d.ts +8 -0
  171. package/dist/vericify-bridge.js +18 -0
  172. package/dist/vericify-context.d.ts +4 -0
  173. package/dist/vericify-context.js +26 -5
  174. package/dist/vericify-native.d.ts +101 -0
  175. package/dist/vericify-native.js +221 -0
  176. package/dist/verification-worker.d.ts +2 -0
  177. package/dist/verification-worker.js +31 -0
  178. package/docs/mcp-tool-reference.md +44 -9
  179. package/package.json +19 -6
  180. package/scripts/dev/run-tests-isolated.mjs +165 -0
  181. package/scripts/dev/run_hermes_crispr_live.mjs +1 -0
  182. package/scripts/dev/test-isolation-guard.mjs +20 -0
  183. package/scripts/dev/test-preload.mjs +153 -0
  184. package/scripts/dev/test-source-inventory.mjs +31 -0
  185. package/docs/development.md +0 -46
@@ -0,0 +1,101 @@
1
+ import { isDeepStrictEqual } from "node:util";
2
+ import { amendTaskPlan } from "./orchestrator-supervisor.js";
3
+ /** Prepare a requirement change; the caller owns persistence and execution. */
4
+ export function reviseHostRequirements(plan, updates) {
5
+ const steps = new Map();
6
+ const children = new Map();
7
+ const remaining = new Map();
8
+ for (const step of plan.steps) {
9
+ if (!step.step_id || steps.has(step.step_id)) {
10
+ throw new Error(`Invalid or duplicate step: ${step.step_id}`);
11
+ }
12
+ steps.set(step.step_id, step);
13
+ children.set(step.step_id, []);
14
+ remaining.set(step.step_id, step.depends_on?.length ?? 0);
15
+ }
16
+ for (const step of plan.steps) {
17
+ for (const dependency of step.depends_on ?? []) {
18
+ if (!steps.has(dependency))
19
+ throw new Error(`Missing dependency: ${dependency}`);
20
+ children.get(dependency).push(step.step_id);
21
+ }
22
+ }
23
+ const ready = [...remaining].filter(([, count]) => count === 0).map(([id]) => id);
24
+ for (let index = 0; index < ready.length; index++) {
25
+ for (const child of children.get(ready[index])) {
26
+ const count = remaining.get(child) - 1;
27
+ remaining.set(child, count);
28
+ if (count === 0)
29
+ ready.push(child);
30
+ }
31
+ }
32
+ if (ready.length !== steps.size)
33
+ throw new Error("Invalid task graph: dependency cycle");
34
+ const named = new Map();
35
+ for (const update of updates) {
36
+ if (!steps.has(update.step_id))
37
+ throw new Error(`Missing step: ${update.step_id}`);
38
+ if (typeof update.reason !== "string" || !update.reason.trim()) {
39
+ throw new Error(`Missing change reason: ${update.step_id}`);
40
+ }
41
+ // Compare only the public requirement fields, independent of object key order.
42
+ const normalized = {
43
+ step_id: update.step_id,
44
+ reason: update.reason,
45
+ task: update.task,
46
+ expected_artifacts: update.expected_artifacts,
47
+ required_evidence_refs: update.required_evidence_refs,
48
+ };
49
+ const previous = named.get(update.step_id);
50
+ if (previous && !isDeepStrictEqual(previous, normalized)) {
51
+ throw new Error(`Conflicting updates: ${update.step_id}`);
52
+ }
53
+ named.set(update.step_id, normalized);
54
+ }
55
+ const reasons = new Map();
56
+ for (const update of named.values()) {
57
+ const pending = [update.step_id];
58
+ const visited = new Set();
59
+ for (let index = 0; index < pending.length; index++) {
60
+ const id = pending[index];
61
+ if (visited.has(id))
62
+ continue;
63
+ visited.add(id);
64
+ if (!reasons.has(id))
65
+ reasons.set(id, new Set());
66
+ reasons.get(id).add(update.reason);
67
+ pending.push(...children.get(id));
68
+ }
69
+ }
70
+ // Detach all nested data too, so later edits to the prepared plan cannot mutate
71
+ // its source or the caller's update objects.
72
+ const copy = structuredClone(plan);
73
+ const reopened = copy.steps.filter(step => reasons.has(step.step_id)).map(step => step.step_id);
74
+ if (!reopened.length)
75
+ return { plan: copy, reopened };
76
+ const updateSteps = copy.steps.filter(step => reasons.has(step.step_id)).map(step => {
77
+ const update = named.get(step.step_id);
78
+ const { attempt_history: history = [], ...receipt } = step;
79
+ const patch = {
80
+ status: "planned",
81
+ blocked_reason: `Requirement changed: ${[...reasons.get(step.step_id)].join("; ")}`,
82
+ attempt_history: [...history, structuredClone(receipt)],
83
+ assignee: undefined,
84
+ started_at: undefined,
85
+ job_id: undefined,
86
+ handoff_id: undefined,
87
+ waiting_on: undefined,
88
+ result_digest: undefined,
89
+ result_response: undefined,
90
+ };
91
+ if (update?.task !== undefined)
92
+ patch.task = update.task;
93
+ if (update?.expected_artifacts !== undefined)
94
+ patch.expected_artifacts = structuredClone(update.expected_artifacts);
95
+ if (update?.required_evidence_refs !== undefined)
96
+ patch.required_evidence_refs = [...update.required_evidence_refs];
97
+ return { step_id: step.step_id, patch };
98
+ });
99
+ return { plan: amendTaskPlan(copy, { update_steps: updateSteps }), reopened };
100
+ }
101
+ //# sourceMappingURL=host-plan-revision.js.map
@@ -0,0 +1,12 @@
1
+ import { type GoalRecord } from './store/repositories/goal-repository.js';
2
+ import { type GoalEngineDeps } from './goal-engine.js';
3
+ type Snapshot = {
4
+ goal: GoalRecord;
5
+ planDigest: string;
6
+ };
7
+ /** Unknown identity is retained; it is never permission to rerun a check. */
8
+ export declare function hostVerificationActive(root: string, goalId: string): boolean;
9
+ export declare function scheduleHostVerification(goalId: string, snapshot: Snapshot, deps?: GoalEngineDeps): Promise<void>;
10
+ export declare function executeHostVerificationJob(root: string, jobId: string): Promise<void>;
11
+ export {};
12
+ //# sourceMappingURL=host-verification-job.d.ts.map
@@ -0,0 +1,183 @@
1
+ /** Durable host verification over the existing scheduler and goal coordinator. */
2
+ import { spawn } from 'node:child_process';
3
+ import { openSync, closeSync, mkdirSync } from 'node:fs';
4
+ import { join } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+ import { randomUUID } from 'node:crypto';
7
+ import { GoalRepository } from './store/repositories/goal-repository.js';
8
+ import { resolveGoalDeps } from './goal-engine.js';
9
+ import { withStoreWriteCoordinator } from './store/write-coordinator.js';
10
+ import { enqueueJob, readJobQueue, claimJobExecution, recordJobExecutionChild } from './job-scheduler.js';
11
+ import { withWorkspaceRoot } from './helpers.js';
12
+ import { readProcessIdentity, probeProcess } from './process-identity.js';
13
+ import { runProgressCommand } from './progress-command.js';
14
+ const injected = new Map();
15
+ const key = (root, goalId) => `${root}\0${goalId}`;
16
+ /** Unknown identity is retained; it is never permission to rerun a check. */
17
+ export function hostVerificationActive(root, goalId) {
18
+ if (injected.has(key(root, goalId)))
19
+ return true;
20
+ const run = new GoalRepository(root).getGoal(goalId)?.verification_run;
21
+ if (!run || ['done', 'failed', 'interrupted'].includes(run.status))
22
+ return false;
23
+ if (run.worker && probeProcess(run.worker) !== 'dead')
24
+ return true;
25
+ if (run.command?.process && probeProcess(run.command.process) !== 'dead')
26
+ return true;
27
+ // An OS identity unavailable at launch is ambiguous, not a dead execution.
28
+ return run.status === 'uncertain';
29
+ }
30
+ export async function scheduleHostVerification(goalId, snapshot, deps = {}) {
31
+ const d = resolveGoalDeps(deps), token = key(d.workspaceRoot, goalId);
32
+ // Injected verifiers cannot be serialized to a process. They still run after
33
+ // the receipt returns; production always uses the durable scheduler worker.
34
+ if (deps.runVerification || deps.artifactExists || deps.now || deps.observations) {
35
+ if (injected.has(token))
36
+ return;
37
+ const work = new Promise(resolve => setImmediate(resolve)).then(async () => {
38
+ const { finishHostVerification } = await import('./host-goal-engine.js');
39
+ await finishHostVerification(goalId, snapshot, deps);
40
+ }).finally(() => { if (injected.get(token) === work)
41
+ injected.delete(token); });
42
+ injected.set(token, work);
43
+ void work.catch(async (error) => {
44
+ await withStoreWriteCoordinator(d.repo.planPath(goalId), async () => {
45
+ const goal = d.repo.getGoal(goalId);
46
+ if (goal && goal.verification_attempt === snapshot.goal.verification_attempt) {
47
+ goal.status = 'blocked';
48
+ goal.reason_code = 'verification_interrupted';
49
+ goal.result_summary = String(error);
50
+ d.repo.saveGoal(goal);
51
+ }
52
+ });
53
+ });
54
+ return;
55
+ }
56
+ await withStoreWriteCoordinator(d.repo.planPath(goalId), async () => {
57
+ const goal = d.repo.getGoal(goalId);
58
+ if (!goal || goal.status !== 'verifying' || goal.verification_attempt !== snapshot.goal.verification_attempt || goal.stop_requested)
59
+ return;
60
+ if (hostVerificationActive(d.workspaceRoot, goalId))
61
+ return;
62
+ if (goal.verification_run) {
63
+ const previous = withWorkspaceRoot(d.workspaceRoot, () => readJobQueue()).jobs.find(j => j.job_id === goal.verification_run.job_id);
64
+ if (previous?.status === 'running' && previous.execution) {
65
+ const recovered = await claimJobExecution({ job_id: previous.job_id, owner: 'capability-ops' }, d.workspaceRoot);
66
+ if (recovered.reason_code !== 'execution_dead') {
67
+ goal.verification_run.status = 'uncertain';
68
+ goal.verification_run.error = 'Previous scheduler execution has not been confirmed dead. Retain its resource locks.';
69
+ d.repo.saveGoal(goal);
70
+ return;
71
+ }
72
+ }
73
+ }
74
+ const jobId = `goal-verify-${randomUUID()}`;
75
+ goal.verification_run = { job_id: jobId, attempt: goal.verification_attempt, enqueued_at: new Date(d.now()).toISOString(), status: 'queued' };
76
+ // Reserve the generation before enqueue/spawn. Recovery uses this retained
77
+ // obligation if the launching server dies between these operations.
78
+ d.repo.saveGoal(goal);
79
+ const queued = await withWorkspaceRoot(d.workspaceRoot, () => enqueueJob({ job_id: jobId, kind: 'goal.verify',
80
+ payload: { goal_id: goalId, attempt: goal.verification_attempt, plan_digest: snapshot.planDigest },
81
+ resource_requirements: [`goal:${goalId}:verification`], max_attempts: 1 }));
82
+ if (!queued.ok) {
83
+ goal.verification_run.status = 'interrupted';
84
+ goal.verification_run.error = queued.nack?.reason;
85
+ d.repo.saveGoal(goal);
86
+ return;
87
+ }
88
+ const dir = join(d.workspaceRoot, '.ace-runtime', 'verification', jobId);
89
+ mkdirSync(dir, { recursive: true });
90
+ const log = openSync(join(dir, 'worker.log'), 'a');
91
+ try {
92
+ const child = spawn(process.execPath, [fileURLToPath(new URL('./verification-worker.js', import.meta.url)), d.workspaceRoot, jobId], {
93
+ cwd: d.workspaceRoot, env: { ...process.env, ACE_WORKSPACE_ROOT: d.workspaceRoot }, detached: true, stdio: ['ignore', log, log],
94
+ });
95
+ await new Promise((resolve, reject) => { child.once('spawn', resolve); child.once('error', reject); });
96
+ const identity = child.pid && readProcessIdentity(child.pid);
97
+ if (identity)
98
+ goal.verification_run.worker = identity;
99
+ else {
100
+ goal.verification_run.status = 'uncertain';
101
+ goal.verification_run.error = 'Worker launched but its OS identity could not be verified. Inspect it before resuming.';
102
+ }
103
+ child.unref();
104
+ d.repo.saveGoal(goal);
105
+ }
106
+ catch (error) {
107
+ goal.verification_run.status = 'interrupted';
108
+ goal.verification_run.error = String(error);
109
+ d.repo.saveGoal(goal);
110
+ }
111
+ finally {
112
+ closeSync(log);
113
+ }
114
+ }, { operation_label: 'host_verification_enqueue' });
115
+ }
116
+ export async function executeHostVerificationJob(root, jobId) {
117
+ const d = resolveGoalDeps({ workspaceRoot: root });
118
+ const job = withWorkspaceRoot(root, () => readJobQueue()).jobs.find(j => j.job_id === jobId);
119
+ if (!job)
120
+ throw new Error(`Verification job not found: ${jobId}`);
121
+ const goalId = String(job.payload.goal_id), attempt = Number(job.payload.attempt);
122
+ const initial = await withStoreWriteCoordinator(d.repo.planPath(goalId), async () => {
123
+ const goal = d.repo.getGoal(goalId);
124
+ if (!goal || goal.verification_attempt !== attempt || goal.verification_run?.job_id !== jobId || goal.stop_requested)
125
+ return undefined;
126
+ const identity = readProcessIdentity(process.pid);
127
+ if (!identity) {
128
+ goal.verification_run.status = 'uncertain';
129
+ goal.verification_run.error = 'Worker OS identity unavailable; no checks were started.';
130
+ d.repo.saveGoal(goal);
131
+ return undefined;
132
+ }
133
+ goal.verification_run.worker = identity;
134
+ goal.verification_run.status = 'running';
135
+ d.repo.saveGoal(goal);
136
+ return goal;
137
+ }, { operation_label: 'host_verification_worker_start' });
138
+ if (!initial)
139
+ return;
140
+ const snapshot = { goal: initial, planDigest: String(job.payload.plan_digest) };
141
+ let index = 0;
142
+ const { finishHostVerification } = await import('./host-goal-engine.js');
143
+ await finishHostVerification(goalId, snapshot, { workspaceRoot: root, runVerification: async (command, commandRoot, policy) => {
144
+ const controller = new AbortController();
145
+ const logPath = join(root, '.ace-runtime', 'verification', jobId, `command-${++index}.log`);
146
+ let identity;
147
+ let latest, writing;
148
+ const persist = () => {
149
+ if (writing)
150
+ return writing;
151
+ writing = (async () => {
152
+ while (latest) {
153
+ const progress = latest;
154
+ latest = undefined;
155
+ await withStoreWriteCoordinator(d.repo.planPath(goalId), async () => {
156
+ const goal = d.repo.getGoal(goalId);
157
+ if (!goal || goal.verification_attempt !== attempt || goal.verification_run?.job_id !== jobId) {
158
+ controller.abort();
159
+ return;
160
+ }
161
+ if (goal.stop_requested || goal.status === 'stopped')
162
+ controller.abort();
163
+ goal.verification_run.status = goal.stop_requested ? 'stopped' : 'running';
164
+ goal.verification_run.command = { ...progress, index, command, log_path: logPath, ...(identity ? { process: identity } : {}) };
165
+ d.repo.saveGoal(goal);
166
+ }, { operation_label: 'host_verification_progress' });
167
+ }
168
+ })().finally(() => { writing = undefined; });
169
+ return writing;
170
+ };
171
+ const result = await runProgressCommand(command, commandRoot, { ...policy, log_path: logPath, signal: controller.signal,
172
+ onSpawn: async (process) => {
173
+ identity = process;
174
+ const registered = await recordJobExecutionChild({ job_id: jobId, token: job.execution?.token, identity: process }, root);
175
+ if (!registered.ok)
176
+ controller.abort(new Error('Unable to retain command process identity in the scheduler.'));
177
+ },
178
+ onProgress: progress => { latest = progress; return persist(); } });
179
+ await persist();
180
+ return { ok: result.ok, output: result.reason_code ? `${result.output}\n[${result.reason_code}]` : result.output };
181
+ } });
182
+ }
183
+ //# sourceMappingURL=host-verification-job.js.map
@@ -0,0 +1,12 @@
1
+ export interface HostWorkBinding {
2
+ agent: string;
3
+ goal_id?: string;
4
+ step_id?: string;
5
+ attempt?: number;
6
+ }
7
+ type Binding = HostWorkBinding;
8
+ export declare function getHostBinding(session?: string): HostWorkBinding | undefined;
9
+ export declare function bindHostWork(binding: Binding, session?: string): void;
10
+ export declare function hostAgent(agent?: string, session?: string): string;
11
+ export {};
12
+ //# sourceMappingURL=host-work-context.d.ts.map
@@ -0,0 +1,8 @@
1
+ /** Process/session bindings are convenience only; durable ownership lives on TaskStep. */
2
+ import { resolveWorkspaceRoot } from './helpers.js';
3
+ const bindings = new Map();
4
+ function key(session) { return `${resolveWorkspaceRoot()}\0${session ?? 'process'}`; }
5
+ export function getHostBinding(session) { return bindings.get(key(session)); }
6
+ export function bindHostWork(binding, session) { bindings.set(key(session), binding); }
7
+ export function hostAgent(agent, session) { return agent ?? getHostBinding(session)?.agent ?? `host-${session ?? process.pid}`; }
8
+ //# sourceMappingURL=host-work-context.js.map
@@ -1,3 +1,4 @@
1
+ import { type ProcessIdentity } from "./process-identity.js";
1
2
  export declare const JOB_QUEUE_REL = "agent-state/job-queue.json";
2
3
  export declare const JOB_LOCK_TABLE_REL = "agent-state/job-locks.json";
3
4
  export declare const SCHEDULER_LEASE_REL = "agent-state/scheduler-lease.json";
@@ -31,6 +32,12 @@ export interface JobRecord {
31
32
  evidence_ref?: string;
32
33
  error?: string;
33
34
  reason_code?: string;
35
+ /** Durable handler claim: owner strings alone do not identify an execution. */
36
+ execution?: ProcessIdentity & {
37
+ token: string;
38
+ };
39
+ /** Commands can outlive their worker; retain their process identities too. */
40
+ execution_children?: ProcessIdentity[];
34
41
  }
35
42
  export interface JobQueueFile {
36
43
  version: 1;
@@ -92,6 +99,8 @@ export interface EnqueueJobResult {
92
99
  }
93
100
  export interface DispatchJobsInput {
94
101
  owner?: string;
102
+ /** Restrict admission, recovery and watchdog changes to these jobs. */
103
+ job_ids?: string[];
95
104
  lease_ttl_seconds?: number;
96
105
  now_iso?: string;
97
106
  /**
@@ -200,12 +209,12 @@ export interface AckJobResult {
200
209
  to?: JobStatus;
201
210
  error?: string;
202
211
  }
203
- export declare function getJobQueuePath(): string;
204
- export declare function getJobLockTablePath(): string;
205
- export declare function getSchedulerLeasePath(): string;
206
- export declare function readJobQueue(): JobQueueFile;
207
- export declare function readJobLockTable(): JobLockTableFile;
208
- export declare function readSchedulerLease(): SchedulerLeaseFile | undefined;
212
+ export declare function getJobQueuePath(workspaceRoot?: string): string;
213
+ export declare function getJobLockTablePath(workspaceRoot?: string): string;
214
+ export declare function getSchedulerLeasePath(workspaceRoot?: string): string;
215
+ export declare function readJobQueue(workspaceRoot?: string): JobQueueFile;
216
+ export declare function readJobLockTable(workspaceRoot?: string): JobLockTableFile;
217
+ export declare function readSchedulerLease(workspaceRoot?: string): SchedulerLeaseFile | undefined;
209
218
  /**
210
219
  * Default window a running job may go without a worker heartbeat before the
211
220
  * watchdog presumes the worker is dead and reaps the job into
@@ -213,18 +222,43 @@ export declare function readSchedulerLease(): SchedulerLeaseFile | undefined;
213
222
  * `heartbeatJob` well within this window (see job-worker.ts).
214
223
  */
215
224
  export declare const DEFAULT_HEARTBEAT_STALE_MS = 120000;
216
- export declare function enqueueJob(input: EnqueueJobInput): Promise<EnqueueJobResult>;
225
+ export declare function enqueueJob(input: EnqueueJobInput, workspaceRoot?: string): Promise<EnqueueJobResult>;
217
226
  /** Mirror handoff lifecycle without admitting or executing a job. */
218
227
  export declare function syncHandoffLifecycleJob(input: EnqueueJobInput & {
219
228
  job_id: string;
220
229
  now_iso?: string;
221
- }): Promise<EnqueueJobResult & {
230
+ }, workspaceRoot?: string): Promise<EnqueueJobResult & {
222
231
  enqueued: boolean;
223
232
  }>;
224
- export declare function acknowledgeJob(input: AckJobInput): Promise<AckJobResult>;
225
- export declare function dispatchJobs(input?: DispatchJobsInput): Promise<DispatchJobsResult>;
226
- export declare function dispatchJobNow(input: DispatchJobNowInput): Promise<DispatchJobNowResult>;
227
- export declare function completeJob(input: CompleteJobInput): Promise<CompleteJobResult>;
233
+ export declare function acknowledgeJob(input: AckJobInput, workspaceRoot?: string): Promise<AckJobResult>;
234
+ export declare function dispatchJobs(input?: DispatchJobsInput, workspaceRoot?: string): Promise<DispatchJobsResult>;
235
+ export declare function dispatchJobNow(input: DispatchJobNowInput, workspaceRoot?: string): Promise<DispatchJobNowResult>;
236
+ export interface ClaimJobExecutionInput {
237
+ job_id: string;
238
+ owner?: string;
239
+ now_iso?: string;
240
+ }
241
+ export interface ClaimJobExecutionResult {
242
+ ok: boolean;
243
+ reason_code?: string;
244
+ error?: string;
245
+ token?: string;
246
+ job?: JobRecord;
247
+ }
248
+ /** Atomically claim the side-effecting handler, independently of scheduler owner. */
249
+ export declare function claimJobExecution(input: ClaimJobExecutionInput, workspaceRoot?: string): Promise<ClaimJobExecutionResult>;
250
+ export interface RecordJobExecutionChildInput {
251
+ job_id: string;
252
+ token?: string;
253
+ identity: ProcessIdentity;
254
+ }
255
+ /** Register a command before waiting on it; only its claimed attempt may do so. */
256
+ export declare function recordJobExecutionChild(input: RecordJobExecutionChildInput, workspaceRoot?: string): Promise<{
257
+ ok: boolean;
258
+ reason_code?: string;
259
+ error?: string;
260
+ }>;
261
+ export declare function completeJob(input: CompleteJobInput, workspaceRoot?: string): Promise<CompleteJobResult>;
228
262
  /**
229
263
  * Prove liveness for a running job. A live in-process worker calls this
230
264
  * periodically while executing a job body so the heartbeat watchdog does not
@@ -233,12 +267,12 @@ export declare function completeJob(input: CompleteJobInput): Promise<CompleteJo
233
267
  * job's heartbeat — the dispatcher deliberately does not, so a missing
234
268
  * heartbeat reliably signals a dead worker.
235
269
  */
236
- export declare function heartbeatJob(input: HeartbeatJobInput): Promise<HeartbeatJobResult>;
270
+ export declare function heartbeatJob(input: HeartbeatJobInput, workspaceRoot?: string): Promise<HeartbeatJobResult>;
237
271
  /**
238
272
  * Standalone heartbeat-watchdog tick: reap stale running jobs without planning
239
273
  * or starting anything. Takes its own scheduler lock. Useful for a dedicated
240
274
  * watchdog cadence separate from dispatch. Does not require lease ownership —
241
275
  * reaping a dead worker's job must not depend on holding the lease.
242
276
  */
243
- export declare function reapStaleRunningJobsTick(input?: ReapStaleRunningJobsInput): Promise<ReapStaleRunningJobsResult>;
277
+ export declare function reapStaleRunningJobsTick(input?: ReapStaleRunningJobsInput, workspaceRoot?: string): Promise<ReapStaleRunningJobsResult>;
244
278
  //# sourceMappingURL=job-scheduler.d.ts.map