@voybio/ace-swarm 2.4.8 → 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 (187) 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 +58 -13
  58. package/dist/job-scheduler.js +298 -48
  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 +111 -6
  89. package/dist/orchestrator-supervisor.js +67 -43
  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.d.ts +3 -2
  165. package/dist/tools-handoff.js +53 -23
  166. package/dist/tools-lifecycle.js +1 -1
  167. package/dist/tools-memory.d.ts +0 -3
  168. package/dist/tools-memory.js +51 -6
  169. package/dist/tools-scheduler.js +20 -17
  170. package/dist/tools-todo.js +160 -148
  171. package/dist/vericify-bridge.d.ts +8 -0
  172. package/dist/vericify-bridge.js +18 -0
  173. package/dist/vericify-context.d.ts +4 -0
  174. package/dist/vericify-context.js +26 -5
  175. package/dist/vericify-native.d.ts +101 -0
  176. package/dist/vericify-native.js +221 -0
  177. package/dist/verification-worker.d.ts +2 -0
  178. package/dist/verification-worker.js +31 -0
  179. package/docs/mcp-tool-reference.md +50 -10
  180. package/docs/troubleshooting.md +29 -0
  181. package/package.json +19 -6
  182. package/scripts/dev/run-tests-isolated.mjs +165 -0
  183. package/scripts/dev/run_hermes_crispr_live.mjs +1 -0
  184. package/scripts/dev/test-isolation-guard.mjs +20 -0
  185. package/scripts/dev/test-preload.mjs +153 -0
  186. package/scripts/dev/test-source-inventory.mjs +31 -0
  187. 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
  /**
@@ -103,8 +112,12 @@ export interface DispatchJobsInput {
103
112
  }
104
113
  export interface DispatchJobsResult {
105
114
  ok: boolean;
115
+ reason_code?: "scheduler_lease_held";
116
+ error?: string;
106
117
  owner: string;
107
118
  lease_acquired: boolean;
119
+ /** Whether execution still retains ownership after this tick. */
120
+ lease_retained: boolean;
108
121
  lease: SchedulerLeaseFile;
109
122
  queue_path: string;
110
123
  lock_path: string;
@@ -196,12 +209,12 @@ export interface AckJobResult {
196
209
  to?: JobStatus;
197
210
  error?: string;
198
211
  }
199
- export declare function getJobQueuePath(): string;
200
- export declare function getJobLockTablePath(): string;
201
- export declare function getSchedulerLeasePath(): string;
202
- export declare function readJobQueue(): JobQueueFile;
203
- export declare function readJobLockTable(): JobLockTableFile;
204
- 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;
205
218
  /**
206
219
  * Default window a running job may go without a worker heartbeat before the
207
220
  * watchdog presumes the worker is dead and reaps the job into
@@ -209,11 +222,43 @@ export declare function readSchedulerLease(): SchedulerLeaseFile | undefined;
209
222
  * `heartbeatJob` well within this window (see job-worker.ts).
210
223
  */
211
224
  export declare const DEFAULT_HEARTBEAT_STALE_MS = 120000;
212
- export declare function enqueueJob(input: EnqueueJobInput): Promise<EnqueueJobResult>;
213
- export declare function acknowledgeJob(input: AckJobInput): Promise<AckJobResult>;
214
- export declare function dispatchJobs(input?: DispatchJobsInput): Promise<DispatchJobsResult>;
215
- export declare function dispatchJobNow(input: DispatchJobNowInput): Promise<DispatchJobNowResult>;
216
- export declare function completeJob(input: CompleteJobInput): Promise<CompleteJobResult>;
225
+ export declare function enqueueJob(input: EnqueueJobInput, workspaceRoot?: string): Promise<EnqueueJobResult>;
226
+ /** Mirror handoff lifecycle without admitting or executing a job. */
227
+ export declare function syncHandoffLifecycleJob(input: EnqueueJobInput & {
228
+ job_id: string;
229
+ now_iso?: string;
230
+ }, workspaceRoot?: string): Promise<EnqueueJobResult & {
231
+ enqueued: boolean;
232
+ }>;
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>;
217
262
  /**
218
263
  * Prove liveness for a running job. A live in-process worker calls this
219
264
  * periodically while executing a job body so the heartbeat watchdog does not
@@ -222,12 +267,12 @@ export declare function completeJob(input: CompleteJobInput): Promise<CompleteJo
222
267
  * job's heartbeat — the dispatcher deliberately does not, so a missing
223
268
  * heartbeat reliably signals a dead worker.
224
269
  */
225
- export declare function heartbeatJob(input: HeartbeatJobInput): Promise<HeartbeatJobResult>;
270
+ export declare function heartbeatJob(input: HeartbeatJobInput, workspaceRoot?: string): Promise<HeartbeatJobResult>;
226
271
  /**
227
272
  * Standalone heartbeat-watchdog tick: reap stale running jobs without planning
228
273
  * or starting anything. Takes its own scheduler lock. Useful for a dedicated
229
274
  * watchdog cadence separate from dispatch. Does not require lease ownership —
230
275
  * reaping a dead worker's job must not depend on holding the lease.
231
276
  */
232
- export declare function reapStaleRunningJobsTick(input?: ReapStaleRunningJobsInput): Promise<ReapStaleRunningJobsResult>;
277
+ export declare function reapStaleRunningJobsTick(input?: ReapStaleRunningJobsInput, workspaceRoot?: string): Promise<ReapStaleRunningJobsResult>;
233
278
  //# sourceMappingURL=job-scheduler.d.ts.map