@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.
- package/CHANGELOG.md +52 -14
- package/README.md +14 -10
- package/assets/.agents/ACE/ACE_coders/AGENTS.md +23 -30
- package/assets/.agents/ACE/ACE_coders/INSTRUCTIONS.md +21 -30
- package/assets/.agents/skills/landing-review-watcher/SKILL.md +1 -1
- package/assets/.agents/skills/learn-from-work/SKILL.md +41 -0
- package/assets/agent-state/ACE_WORKFLOW.md +25 -2
- package/assets/agent-state/MODULES/schemas/ACE_RUNTIME_PROFILE.schema.json +143 -2
- package/assets/agent-state/MODULES/schemas/RUNTIME_EXECUTOR_SESSION_REGISTRY.schema.json +2 -2
- package/assets/agent-state/MODULES/schemas/SWARM_HANDOFF.schema.json +10 -0
- package/assets/eval-battery/battery.json +1 -1
- package/assets/instructions/ACE_Coder.instructions.md +9 -13
- package/assets/scripts/ace-hook-dispatch.mjs +16 -411
- package/assets/tasks/SWARM_HANDOFF.example_ui_to_coders.json +1 -2
- package/assets/vendor/README.md +9 -0
- package/assets/vendor/vericify-reader.mjs +272 -0
- package/assets/vendor/vericify-reader.provenance.json +10 -0
- package/dist/ace-autonomy.d.ts +58 -0
- package/dist/ace-autonomy.js +211 -6
- package/dist/ace-internal-tools.d.ts +1 -0
- package/dist/ace-internal-tools.js +3 -3
- package/dist/ace-server-instructions.d.ts +3 -3
- package/dist/ace-server-instructions.js +46 -192
- package/dist/cli.js +44 -6
- package/dist/experience-reflector.d.ts +7 -0
- package/dist/experience-reflector.js +39 -0
- package/dist/goal-engine.d.ts +64 -7
- package/dist/goal-engine.js +197 -41
- package/dist/handoff-registry.d.ts +10 -0
- package/dist/handoff-registry.js +59 -0
- package/dist/helpers/artifacts.d.ts +6 -2
- package/dist/helpers/artifacts.js +8 -6
- package/dist/helpers/bootstrap.js +13 -20
- package/dist/helpers/path-utils.js +2 -0
- package/dist/helpers/store-resolution.js +18 -3
- package/dist/helpers/workspace-root.d.ts +2 -0
- package/dist/helpers/workspace-root.js +9 -0
- package/dist/helpers.d.ts +1 -0
- package/dist/helpers.js +1 -0
- package/dist/hermes/bridge-protocol.d.ts +1 -1
- package/dist/hermes/session-manager.d.ts +17 -0
- package/dist/hermes/session-manager.js +67 -14
- package/dist/host-goal-engine.d.ts +138 -0
- package/dist/host-goal-engine.js +793 -0
- package/dist/host-goal-status.d.ts +113 -0
- package/dist/host-goal-status.js +175 -0
- package/dist/host-learning.d.ts +68 -0
- package/dist/host-learning.js +115 -0
- package/dist/host-observation.d.ts +12 -0
- package/dist/host-observation.js +91 -0
- package/dist/host-plan-revision.d.ts +14 -0
- package/dist/host-plan-revision.js +101 -0
- package/dist/host-verification-job.d.ts +12 -0
- package/dist/host-verification-job.js +183 -0
- package/dist/host-work-context.d.ts +12 -0
- package/dist/host-work-context.js +8 -0
- package/dist/job-scheduler.d.ts +48 -14
- package/dist/job-scheduler.js +169 -23
- package/dist/job-worker.d.ts +3 -1
- package/dist/job-worker.js +10 -3
- package/dist/kanban.d.ts +4 -1
- package/dist/kanban.js +44 -16
- package/dist/learned-artifacts.d.ts +64 -0
- package/dist/learned-artifacts.js +321 -0
- package/dist/learning-controls.d.ts +142 -0
- package/dist/learning-controls.js +135 -0
- package/dist/learning-input-context.d.ts +9 -0
- package/dist/learning-input-context.js +62 -0
- package/dist/learning-lifecycle.d.ts +10 -0
- package/dist/learning-lifecycle.js +11 -0
- package/dist/learning-overlays.d.ts +3 -0
- package/dist/learning-overlays.js +17 -0
- package/dist/learning-policy.d.ts +40 -0
- package/dist/learning-policy.js +38 -0
- package/dist/learning-records.d.ts +35 -0
- package/dist/learning-records.js +17 -0
- package/dist/learning-scorecard.d.ts +82 -0
- package/dist/learning-scorecard.js +86 -0
- package/dist/learning-serving.d.ts +29 -0
- package/dist/learning-serving.js +134 -0
- package/dist/linked-work.d.ts +9 -0
- package/dist/linked-work.js +28 -0
- package/dist/local-model-runtime.js +1 -1
- package/dist/mcp-command-progress.d.ts +16 -0
- package/dist/mcp-command-progress.js +70 -0
- package/dist/model-bridge.d.ts +14 -13
- package/dist/model-bridge.js +73 -41
- package/dist/orchestrator-supervisor.d.ts +108 -6
- package/dist/orchestrator-supervisor.js +4 -32
- package/dist/plan-proposal.js +3 -3
- package/dist/playbook-curator.d.ts +1 -0
- package/dist/playbook-curator.js +11 -9
- package/dist/process-identity.d.ts +8 -0
- package/dist/process-identity.js +38 -0
- package/dist/progress-command.d.ts +34 -0
- package/dist/progress-command.js +153 -0
- package/dist/prompts.js +15 -15
- package/dist/resources.js +2 -2
- package/dist/runtime-command.d.ts +8 -1
- package/dist/runtime-command.js +48 -7
- package/dist/runtime-executor.js +33 -11
- package/dist/runtime-profile-syntax.d.ts +6 -0
- package/dist/runtime-profile-syntax.js +121 -0
- package/dist/runtime-profile.d.ts +37 -5
- package/dist/runtime-profile.js +36 -129
- package/dist/runtime-tool-specs.js +1 -1
- package/dist/safe-edit.d.ts +7 -3
- package/dist/safe-edit.js +40 -47
- package/dist/schemas.js +4 -2
- package/dist/server.d.ts +2 -16
- package/dist/server.js +14 -20
- package/dist/shared.d.ts +2 -0
- package/dist/shared.js +2 -0
- package/dist/skill-auditor.d.ts +19 -0
- package/dist/skill-auditor.js +32 -2
- package/dist/skill-catalog.d.ts +4 -1
- package/dist/skill-catalog.js +6 -5
- package/dist/store/ace-packed-store.d.ts +5 -1
- package/dist/store/ace-packed-store.js +33 -1
- package/dist/store/bootstrap-store.js +19 -12
- package/dist/store/cache-workspace.d.ts +8 -0
- package/dist/store/cache-workspace.js +94 -27
- package/dist/store/importer.js +11 -20
- package/dist/store/knowledge-bake.d.ts +3 -1
- package/dist/store/knowledge-bake.js +10 -1
- package/dist/store/legacy-vericify-posts.d.ts +8 -0
- package/dist/store/legacy-vericify-posts.js +76 -0
- package/dist/store/materializers/hook-context-materializer.d.ts +2 -1
- package/dist/store/materializers/hook-context-materializer.js +6 -2
- package/dist/store/materializers/host-file-materializer.js +2 -2
- package/dist/store/materializers/projection-manager.d.ts +4 -1
- package/dist/store/materializers/projection-manager.js +10 -3
- package/dist/store/materializers/todo-syncer.d.ts +39 -20
- package/dist/store/materializers/todo-syncer.js +208 -110
- package/dist/store/materializers/vericify-projector.js +15 -26
- package/dist/store/repositories/goal-repository.d.ts +51 -2
- package/dist/store/repositories/goal-repository.js +114 -48
- package/dist/store/repositories/playbook-repository.d.ts +41 -3
- package/dist/store/repositories/playbook-repository.js +61 -5
- package/dist/store/repositories/todo-repository.d.ts +8 -2
- package/dist/store/repositories/todo-repository.js +1 -1
- package/dist/store/repositories/vericify-repository.d.ts +4 -0
- package/dist/store/repositories/vericify-repository.js +46 -0
- package/dist/store/skills-install.d.ts +1 -0
- package/dist/store/skills-install.js +20 -16
- package/dist/store/source-preservation.d.ts +24 -0
- package/dist/store/source-preservation.js +79 -0
- package/dist/store/store-artifacts.js +8 -1
- package/dist/store/test-write-boundary.d.ts +16 -0
- package/dist/store/test-write-boundary.js +175 -0
- package/dist/store/types.d.ts +5 -1
- package/dist/store/write-coordinator.js +16 -0
- package/dist/task-plan-context.d.ts +11 -0
- package/dist/task-plan-context.js +59 -0
- package/dist/todo-state.d.ts +19 -9
- package/dist/todo-state.js +204 -321
- package/dist/tools-agent.d.ts +1 -1
- package/dist/tools-agent.js +38 -62
- package/dist/tools-files.d.ts +0 -3
- package/dist/tools-files.js +21 -11
- package/dist/tools-framework.js +16 -25
- package/dist/tools-goal.d.ts +2 -4
- package/dist/tools-goal.js +119 -16
- package/dist/tools-handoff.js +28 -4
- package/dist/tools-lifecycle.js +1 -1
- package/dist/tools-memory.d.ts +0 -3
- package/dist/tools-memory.js +51 -6
- package/dist/tools-scheduler.js +19 -17
- package/dist/tools-todo.js +160 -148
- package/dist/vericify-bridge.d.ts +8 -0
- package/dist/vericify-bridge.js +18 -0
- package/dist/vericify-context.d.ts +4 -0
- package/dist/vericify-context.js +26 -5
- package/dist/vericify-native.d.ts +101 -0
- package/dist/vericify-native.js +221 -0
- package/dist/verification-worker.d.ts +2 -0
- package/dist/verification-worker.js +31 -0
- package/docs/mcp-tool-reference.md +44 -9
- package/package.json +19 -6
- package/scripts/dev/run-tests-isolated.mjs +165 -0
- package/scripts/dev/run_hermes_crispr_live.mjs +1 -0
- package/scripts/dev/test-isolation-guard.mjs +20 -0
- package/scripts/dev/test-preload.mjs +153 -0
- package/scripts/dev/test-source-inventory.mjs +31 -0
- package/docs/development.md +0 -46
package/dist/tools-goal.js
CHANGED
|
@@ -1,15 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ACE Goal Engine tool registrations (WS-F).
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* goal: they compile/prepare, then hand execution to a detached `ace goal run`
|
|
7
|
-
* child and return immediately. The host polls `get_goal_status`.
|
|
4
|
+
* Engine goals delegate to the existing detached runner. Host goals return
|
|
5
|
+
* assignments to the connected participant and never invoke that executor.
|
|
8
6
|
*/
|
|
9
7
|
import { spawn } from "node:child_process";
|
|
10
8
|
import { fileURLToPath } from "node:url";
|
|
11
9
|
import { z } from "zod";
|
|
12
|
-
import { getGoalStatus, listGoals, startGoal, stopGoal, } from "./goal-engine.js";
|
|
10
|
+
import { getGoalStatus, listGoals, startGoal, stopGoal, resumeGoal, beginStep, completeStep, } from "./goal-engine.js";
|
|
11
|
+
import { hostRefusal } from './host-goal-engine.js';
|
|
12
|
+
import { resolveCurrentWork } from './ace-autonomy.js';
|
|
13
|
+
import { resolveWorkspaceRoot } from './helpers.js';
|
|
14
|
+
import { bindHostWork, hostAgent } from './host-work-context.js';
|
|
15
|
+
import { getCompactGoalStatus } from './host-goal-status.js';
|
|
16
|
+
import { commandPolicySchema, withMcpCommandProgress } from './mcp-command-progress.js';
|
|
17
|
+
function hostResult(result) {
|
|
18
|
+
return { isError: !!result.isError, structuredContent: { ...result }, content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
19
|
+
}
|
|
20
|
+
const artifactSchema = z.object({ path: z.string().min(1), required: z.boolean().optional(), transient: z.boolean().optional() });
|
|
21
|
+
const hostStepSchema = z.object({ id: z.string().optional(), task: z.string().min(1), role: z.string().optional(), depends_on: z.array(z.string()).optional(), expected_artifacts: z.array(z.union([z.string().min(1), artifactSchema])).optional(), required_evidence_refs: z.array(z.string()).optional(), required_observation: z.enum(['action', 'command_pass']).optional() });
|
|
13
22
|
function asJsonBlock(value) {
|
|
14
23
|
return `\`\`\`json\n${JSON.stringify(value, null, 2)}\n\`\`\``;
|
|
15
24
|
}
|
|
@@ -29,14 +38,34 @@ function spawnDetachedRunner(goalId, verb) {
|
|
|
29
38
|
}
|
|
30
39
|
}
|
|
31
40
|
export function registerGoalTools(server) {
|
|
32
|
-
server.tool("start_goal", "
|
|
41
|
+
server.tool("start_goal", "Start a goal with an explicit executor choice: host accepts your steps and returns the first assignment; engine plans and runs through ACE's autonomous executor. Productive work has no default duration, tick, or total replan limit.", {
|
|
42
|
+
executor: z.enum(['engine', 'host']),
|
|
43
|
+
agent: z.string().min(1).optional(),
|
|
44
|
+
steps: z.array(hostStepSchema).min(1).optional(),
|
|
45
|
+
verify: z.array(z.union([z.string().min(1), commandPolicySchema.extend({ cmd: z.string().min(1) })])).optional(),
|
|
46
|
+
todo_ids: z.record(z.string(), z.string()).optional(),
|
|
33
47
|
objective: z.string().min(1).describe("What the goal should accomplish"),
|
|
34
|
-
budget_hours: z.number().positive().
|
|
35
|
-
max_replans: z.number().int().min(0).
|
|
48
|
+
budget_hours: z.number().positive().optional().describe("Explicit operator deadline measured from the original start; omit for unlimited work."),
|
|
49
|
+
max_replans: z.number().int().min(0).optional().describe("Explicit operator cap on corrections; omitted by default. Repeated unchanged failures are detected separately."),
|
|
36
50
|
plan_only: z.boolean().optional().describe("Compile + validate the plan but do not execute"),
|
|
37
51
|
detach: z.boolean().optional().describe("Run in a detached background child (default true when not plan_only)"),
|
|
38
|
-
}, async ({ objective, budget_hours, max_replans, plan_only, detach }) => {
|
|
39
|
-
const
|
|
52
|
+
}, async ({ objective, budget_hours, max_replans, plan_only, detach, executor, agent, steps, verify, todo_ids }, extra) => {
|
|
53
|
+
const participant = hostAgent(agent, extra.sessionId);
|
|
54
|
+
let started;
|
|
55
|
+
try {
|
|
56
|
+
if (budget_hours !== undefined && !Number.isFinite(budget_hours * 3600000))
|
|
57
|
+
throw new Error('budget_hours must be a finite positive allowance.');
|
|
58
|
+
started = await startGoal(objective, { budget_hours, max_replans, plan_only, executor, agent: participant, session_id: extra.sessionId, steps, verify_commands: verify, todo_ids });
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
if (executor !== 'host')
|
|
62
|
+
throw error;
|
|
63
|
+
return hostResult(hostRefusal(resolveWorkspaceRoot(), 'host_plan_invalid', error instanceof Error ? error.message : String(error)));
|
|
64
|
+
}
|
|
65
|
+
if (executor === 'host') {
|
|
66
|
+
bindHostWork({ agent: participant, goal_id: started.goal.goal_id, step_id: started.assignment?.step_id, attempt: started.assignment?.attempt }, extra.sessionId);
|
|
67
|
+
return { isError: !started.ok, structuredContent: { ...started }, content: [{ type: 'text', text: JSON.stringify(started, null, 2) }] };
|
|
68
|
+
}
|
|
40
69
|
let runnerPid;
|
|
41
70
|
if (started.ok && !plan_only && detach !== false) {
|
|
42
71
|
runnerPid = spawnDetachedRunner(started.goal.goal_id, "run");
|
|
@@ -59,25 +88,75 @@ export function registerGoalTools(server) {
|
|
|
59
88
|
],
|
|
60
89
|
};
|
|
61
90
|
});
|
|
62
|
-
server.tool(
|
|
91
|
+
server.tool('next_step', 'Receive or resume your current assignment from a host goal. Never executes work itself.', {
|
|
92
|
+
goal_id: z.string().optional(), step_id: z.string().optional(), agent: z.string().min(1).optional(),
|
|
93
|
+
}, async ({ goal_id, step_id, agent }, extra) => {
|
|
94
|
+
const participant = hostAgent(agent, extra.sessionId);
|
|
95
|
+
const work = resolveCurrentWork({ goal_id, agent: participant });
|
|
96
|
+
const id = goal_id ?? work.objective?.goal_id ?? (work.idle_learning?.includes('learning step available') ? 'G-learning' : undefined);
|
|
97
|
+
if (!id)
|
|
98
|
+
return hostResult({ ...hostRefusal(resolveWorkspaceRoot(), work.state === 'ambiguous' ? 'ambiguous_goal' : 'no_active_goal', 'Select a host goal_id, or create one with start_goal executor:host.'), candidates: work.other_goals });
|
|
99
|
+
try {
|
|
100
|
+
const result = await beginStep(id, { step_id, agent: participant, session_id: extra.sessionId });
|
|
101
|
+
if (result.assignment)
|
|
102
|
+
bindHostWork({ agent: participant, goal_id: id, step_id: result.assignment.step_id, attempt: result.assignment.attempt }, extra.sessionId);
|
|
103
|
+
return hostResult(result);
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
return hostResult(hostRefusal(resolveWorkspaceRoot(), 'next_step_failed', error instanceof Error ? error.message : String(error), id));
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
server.tool('complete_step', 'Record this attempt once, update linked state and optionally receive the next assignment. Identical retries return the original response.', {
|
|
110
|
+
goal_id: z.string(), step_id: z.string(), attempt: z.number().int().positive(), agent: z.string().min(1).optional(), continue: z.boolean().optional(),
|
|
111
|
+
outcome: z.object({ status: z.enum(['done', 'blocked', 'failed']), summary: z.string().min(1).max(2000), waiting_on: z.string().optional(), artifacts: z.array(z.string()).optional(),
|
|
112
|
+
feedback: z.array(z.object({ id: z.string(), helpful: z.boolean() })).optional(),
|
|
113
|
+
learned: z.object({
|
|
114
|
+
lessons: z.array(z.object({ op: z.enum(['add', 'edit', 'retire']), bullet_id: z.string().optional(), section: z.enum(['strategies', 'tool_usage', 'failure_modes', 'verification', 'env_facts']), content: z.string().max(400), scope: z.object({ role: z.string().optional(), paths: z.array(z.string()).optional(), task_tags: z.array(z.string()).optional(), failure_signature: z.string().optional() }), episodes: z.array(z.string()).min(1), retired_episodes: z.array(z.string()).optional() })).max(3).optional(),
|
|
115
|
+
skill: z.object({ op: z.enum(['add', 'edit']), slug: z.string(), skill_md: z.string(), references: z.record(z.string(), z.string()).optional(), episodes: z.array(z.string()).min(3), retired_episodes: z.array(z.string()).optional() }).optional(),
|
|
116
|
+
instruction: z.object({ role: z.string(), text: z.string().max(1200), episodes: z.array(z.string()).min(1) }).optional()
|
|
117
|
+
}).optional(),
|
|
118
|
+
requirement_updates: z.array(z.object({ step_id: z.string(), reason: z.string().min(1), task: z.string().min(1).optional(), expected_artifacts: z.array(artifactSchema).optional(), required_evidence_refs: z.array(z.string()).optional() })).optional(),
|
|
119
|
+
evidence: z.array(z.object({ ref: z.string(), kind: z.enum(['command', 'artifact', 'diff', 'note']), detail: z.string().optional() })).optional(),
|
|
120
|
+
follow_ups: z.array(z.object({ task: z.string().min(1), role: z.string().optional(), depends_on: z.array(z.string()).optional() })).optional() }),
|
|
121
|
+
}, async ({ goal_id, step_id, attempt, agent, outcome, continue: proceed }, extra) => {
|
|
122
|
+
const participant = hostAgent(agent, extra.sessionId);
|
|
123
|
+
try {
|
|
124
|
+
const result = await withMcpCommandProgress(extra, 'complete_step acceptance', () => completeStep(goal_id, step_id, outcome, { agent: participant, attempt, continue: proceed }));
|
|
125
|
+
if (result.ok)
|
|
126
|
+
bindHostWork({ agent: participant, goal_id, step_id: result.assignment?.step_id, attempt: result.assignment?.attempt }, extra.sessionId);
|
|
127
|
+
return hostResult(result);
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
return hostResult(hostRefusal(resolveWorkspaceRoot(), 'completion_failed', error instanceof Error ? error.message : String(error), goal_id));
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
server.tool("get_goal_status", "Get goal status. view:compact gives bounded verification, blockers and executable next actions; view:full (default) retains the complete plan and evidence. Omit goal_id to list goals.", {
|
|
63
134
|
goal_id: z.string().optional().describe("Goal id; omit to list all goals"),
|
|
64
|
-
|
|
135
|
+
view: z.enum(['compact', 'full']).optional(),
|
|
136
|
+
agent: z.string().min(1).optional(),
|
|
137
|
+
}, async ({ goal_id, view: mode, agent }, extra) => {
|
|
65
138
|
if (!goal_id) {
|
|
66
139
|
return {
|
|
67
140
|
content: [{ type: "text", text: asJsonBlock({ goals: listGoals() }) }],
|
|
68
141
|
};
|
|
69
142
|
}
|
|
143
|
+
if (mode === 'compact') {
|
|
144
|
+
const compact = getCompactGoalStatus(goal_id, hostAgent(agent, extra.sessionId));
|
|
145
|
+
if (!compact)
|
|
146
|
+
return hostResult(hostRefusal(resolveWorkspaceRoot(), 'goal_not_found', `Unknown goal: ${goal_id}`, goal_id));
|
|
147
|
+
return { structuredContent: { ...compact }, content: [{ type: 'text', text: JSON.stringify(compact) }] };
|
|
148
|
+
}
|
|
70
149
|
const view = getGoalStatus(goal_id);
|
|
71
150
|
if (!view) {
|
|
72
151
|
return { content: [{ type: "text", text: `❌ Unknown goal: ${goal_id}` }] };
|
|
73
152
|
}
|
|
74
|
-
return { content: [{ type: "text", text: asJsonBlock(view) }] };
|
|
153
|
+
return { structuredContent: { ...view }, content: [{ type: "text", text: asJsonBlock(view) }] };
|
|
75
154
|
});
|
|
76
155
|
server.tool("stop_goal", "Request that a running goal stop at its next tick boundary.", { goal_id: z.string().describe("Goal id to stop") }, async ({ goal_id }) => {
|
|
77
156
|
try {
|
|
78
|
-
const goal = stopGoal(goal_id);
|
|
157
|
+
const goal = await stopGoal(goal_id);
|
|
79
158
|
return {
|
|
80
|
-
content: [{ type: "text", text: `🛑 Stop requested for ${goal.goal_id} (status ${goal.status})
|
|
159
|
+
content: [{ type: "text", text: `🛑 Stop requested for ${goal.goal_id} (status ${goal.status}).\n` }],
|
|
81
160
|
};
|
|
82
161
|
}
|
|
83
162
|
catch (err) {
|
|
@@ -86,11 +165,35 @@ export function registerGoalTools(server) {
|
|
|
86
165
|
};
|
|
87
166
|
}
|
|
88
167
|
});
|
|
89
|
-
server.tool("resume_goal", "Resume a
|
|
168
|
+
server.tool("resume_goal", "Resume a goal. budget_hours/max_replans increase explicit total allowances only with operator approval; never execute a status proposal automatically. Accepted work and original start time are preserved. Engine goals resume in the detached runner.", { goal_id: z.string().describe("Goal id to resume"),
|
|
169
|
+
budget_hours: z.number().positive().optional().describe('Operator-approved increase to an explicit total deadline from the original start.'),
|
|
170
|
+
max_replans: z.number().int().nonnegative().optional().describe('Operator-approved increase admitting retained pending follow-ups.') }, async ({ goal_id, budget_hours, max_replans }, extra) => {
|
|
90
171
|
const view = getGoalStatus(goal_id);
|
|
91
172
|
if (!view) {
|
|
92
173
|
return { content: [{ type: "text", text: `❌ Unknown goal: ${goal_id}` }] };
|
|
93
174
|
}
|
|
175
|
+
if (view.goal.executor === 'host') {
|
|
176
|
+
try {
|
|
177
|
+
const resumed = await resumeGoal(goal_id, {}, { budget_hours, max_replans });
|
|
178
|
+
const status = getCompactGoalStatus(goal_id, hostAgent(undefined, extra.sessionId));
|
|
179
|
+
const result = { ...resumed, options: status?.options, operator_proposal: status?.operator_proposal, waiting_on: status?.waiting_on, now: status?.now };
|
|
180
|
+
return { structuredContent: result, content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
181
|
+
}
|
|
182
|
+
catch (error) {
|
|
183
|
+
return hostResult(hostRefusal(resolveWorkspaceRoot(), 'budget_recovery_rejected', error instanceof Error ? error.message : String(error), goal_id));
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
if (budget_hours !== undefined || max_replans !== undefined) {
|
|
187
|
+
try {
|
|
188
|
+
const { recoverEngineGoalBudgets } = await import('./goal-engine.js');
|
|
189
|
+
await recoverEngineGoalBudgets(goal_id, { budget_hours, max_replans });
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
const refusal = hostRefusal(resolveWorkspaceRoot(), 'budget_recovery_rejected', error instanceof Error ? error.message : String(error), goal_id);
|
|
193
|
+
refusal.options = [{ tool: 'get_goal_status', args: { goal_id, view: 'compact' }, why: 'Inspect the retained engine allowance and obtain operator approval for any sufficient increase.' }];
|
|
194
|
+
return hostResult(refusal);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
94
197
|
const runnerPid = spawnDetachedRunner(goal_id, "resume");
|
|
95
198
|
return {
|
|
96
199
|
content: [
|
package/dist/tools-handoff.js
CHANGED
|
@@ -9,9 +9,12 @@ import { ROLE_ENUM, HANDOFF_VALIDATION_MODE, HANDOFF_LIFECYCLE_STATUS_ENUM, } fr
|
|
|
9
9
|
import { validateHandoffPayload, validateSwarmHandoffPayload, lintHandoffPayload, } from "./schemas.js";
|
|
10
10
|
import { acknowledgeHandoffSafe, getHandoffRegistryPath, readHandoffRegistry, registerHandoffSafe, } from "./handoff-registry.js";
|
|
11
11
|
import { appendRunLedgerEntrySafe } from "./run-ledger.js";
|
|
12
|
-
import { refreshKanbanArtifacts } from "./kanban.js";
|
|
12
|
+
import { formatKanbanRefresh, refreshKanbanArtifacts } from "./kanban.js";
|
|
13
13
|
import { readJobQueue, syncHandoffLifecycleJob } from "./job-scheduler.js";
|
|
14
14
|
import { appendStatusEventSafe } from "./status-events.js";
|
|
15
|
+
import { hostAgent, bindHostWork } from './host-work-context.js';
|
|
16
|
+
import { GoalRepository } from './store/repositories/goal-repository.js';
|
|
17
|
+
import { resolveWorkspaceRoot } from './helpers.js';
|
|
15
18
|
function toSchedulerPriority(input) {
|
|
16
19
|
if (input === "P0" || input === "P1" || input === "P2" || input === "P3") {
|
|
17
20
|
return input;
|
|
@@ -61,6 +64,8 @@ async function recordHandoffSchedulerSyncEffects(record, sync) {
|
|
|
61
64
|
}
|
|
62
65
|
}
|
|
63
66
|
export async function syncHandoffIntoScheduler(record, options = {}) {
|
|
67
|
+
if (record.step_ref)
|
|
68
|
+
return { ok: true, job_id: '', enqueued: false, enqueue_reason: 'Host step ownership follows the handoff; no engine dispatch.', dispatch_performed: false, reconciliation_required: false };
|
|
64
69
|
const jobId = handoffSchedulerJobId(record.handoff_id);
|
|
65
70
|
const enqueue = await syncHandoffLifecycleJob({
|
|
66
71
|
now_iso: options.now_iso,
|
|
@@ -107,6 +112,7 @@ export async function backfillHandoffsIntoScheduler(options = {}) {
|
|
|
107
112
|
const existing = new Set(readJobQueue().jobs.filter((job) => job.kind === "handoff.lifecycle")
|
|
108
113
|
.map((job) => job.payload.handoff_id));
|
|
109
114
|
const active = Object.values(registry.handoffs)
|
|
115
|
+
.filter(record => !record.step_ref)
|
|
110
116
|
.filter((record) => record.status !== "completed" || existing.has(record.handoff_id))
|
|
111
117
|
.sort((a, b) => Date.parse(a.created_at) - Date.parse(b.created_at));
|
|
112
118
|
const result = {
|
|
@@ -161,11 +167,15 @@ export function registerHandoffTools(server) {
|
|
|
161
167
|
})
|
|
162
168
|
.optional()
|
|
163
169
|
.describe("File:line pointers for context"),
|
|
164
|
-
|
|
170
|
+
step_ref: z.object({ goal_id: z.string(), step_id: z.string() }).optional(),
|
|
171
|
+
agent: z.string().optional().describe('Submitting host label for a linked step'),
|
|
172
|
+
recipient_agent: z.string().optional().describe('Host label receiving a linked step; defaults to the target role label'),
|
|
173
|
+
}, async ({ from, to, title, task_type, priority, directive, acceptance_criteria, context_pointers, step_ref, agent, recipient_agent, }, extra) => {
|
|
165
174
|
const now = new Date();
|
|
166
175
|
const dateStr = now.toISOString().slice(0, 10).replace(/-/g, "");
|
|
167
176
|
const shortId = randomUUID().replace(/-/g, "").slice(0, 8);
|
|
168
177
|
const handoff = {
|
|
178
|
+
...(step_ref ? { step_ref, recipient_agent: recipient_agent ?? `ACE-${to.charAt(0).toUpperCase() + to.slice(1)}` } : {}),
|
|
169
179
|
handoff_id: `HND-${dateStr}-${shortId}`,
|
|
170
180
|
timestamp_utc: now.toISOString(),
|
|
171
181
|
router: {
|
|
@@ -252,6 +262,9 @@ export function registerHandoffTools(server) {
|
|
|
252
262
|
task_type: handoff.task.type,
|
|
253
263
|
priority: handoff.task.priority,
|
|
254
264
|
source_file: payloadPath,
|
|
265
|
+
step_ref,
|
|
266
|
+
actor: step_ref ? hostAgent(agent, extra.sessionId) : undefined,
|
|
267
|
+
...(step_ref && recipient_agent ? { to: recipient_agent } : {}),
|
|
255
268
|
});
|
|
256
269
|
const runLedger = await appendRunLedgerEntrySafe({
|
|
257
270
|
tool: "create_handoff",
|
|
@@ -271,6 +284,7 @@ export function registerHandoffTools(server) {
|
|
|
271
284
|
const schedulerSync = await syncHandoffIntoScheduler(registry.record);
|
|
272
285
|
const kanban = refreshKanbanArtifacts("tool:create_handoff");
|
|
273
286
|
return {
|
|
287
|
+
structuredContent: { handoff_id: handoff.handoff_id, record: registry.record },
|
|
274
288
|
content: [
|
|
275
289
|
{
|
|
276
290
|
type: "text",
|
|
@@ -290,7 +304,7 @@ export function registerHandoffTools(server) {
|
|
|
290
304
|
? `Scheduler enqueue detail: ${schedulerSync.enqueue_reason}`
|
|
291
305
|
: "",
|
|
292
306
|
"",
|
|
293
|
-
|
|
307
|
+
formatKanbanRefresh(kanban),
|
|
294
308
|
`Kanban counts: backlog=${kanban.counts.backlog}, blocked=${kanban.counts.blocked}, in_progress=${kanban.counts.in_progress}, gated=${kanban.counts.gated}, done=${kanban.counts.done}`,
|
|
295
309
|
"",
|
|
296
310
|
"```json",
|
|
@@ -411,14 +425,19 @@ export function registerHandoffTools(server) {
|
|
|
411
425
|
handoff_id: z.string().describe("Handoff identifier"),
|
|
412
426
|
status: HANDOFF_LIFECYCLE_STATUS_ENUM.describe("Lifecycle status"),
|
|
413
427
|
note: z.string().optional().describe("Optional status note"),
|
|
414
|
-
|
|
428
|
+
agent: z.string().optional().describe('Recipient host label for a linked step'),
|
|
429
|
+
}, async ({ handoff_id, status, note, agent }, extra) => {
|
|
430
|
+
const linked = readHandoffRegistry().handoffs[handoff_id]?.step_ref;
|
|
415
431
|
const ack = await acknowledgeHandoffSafe({
|
|
416
432
|
handoff_id,
|
|
417
433
|
status,
|
|
418
434
|
note,
|
|
435
|
+
actor: linked ? hostAgent(agent, extra.sessionId) : undefined,
|
|
419
436
|
});
|
|
420
437
|
if (!ack.ok) {
|
|
421
438
|
return {
|
|
439
|
+
...(linked ? { isError: true } : {}),
|
|
440
|
+
structuredContent: { ...ack },
|
|
422
441
|
content: [
|
|
423
442
|
{
|
|
424
443
|
type: "text",
|
|
@@ -438,6 +457,10 @@ export function registerHandoffTools(server) {
|
|
|
438
457
|
],
|
|
439
458
|
};
|
|
440
459
|
}
|
|
460
|
+
if (linked && ack.record) {
|
|
461
|
+
const step = new GoalRepository(resolveWorkspaceRoot()).getPlan(linked.goal_id)?.steps.find(s => s.step_id === linked.step_id);
|
|
462
|
+
bindHostWork({ agent: ack.record.to, goal_id: linked.goal_id, ...(status === 'accepted' ? { step_id: linked.step_id, attempt: step?.attempt } : {}) }, extra.sessionId);
|
|
463
|
+
}
|
|
441
464
|
const runLedger = await appendRunLedgerEntrySafe({
|
|
442
465
|
tool: "ack_handoff",
|
|
443
466
|
category: status === "blocked" ? "regression" : "major_update",
|
|
@@ -455,6 +478,7 @@ export function registerHandoffTools(server) {
|
|
|
455
478
|
: undefined;
|
|
456
479
|
const kanban = refreshKanbanArtifacts("tool:ack_handoff");
|
|
457
480
|
return {
|
|
481
|
+
structuredContent: { ...ack },
|
|
458
482
|
content: [
|
|
459
483
|
{
|
|
460
484
|
type: "text",
|
package/dist/tools-lifecycle.js
CHANGED
|
@@ -190,7 +190,7 @@ export function registerLifecycleTools(server) {
|
|
|
190
190
|
{
|
|
191
191
|
type: "text",
|
|
192
192
|
text: [
|
|
193
|
-
"# Kanban Updated",
|
|
193
|
+
snapshot.refresh_scheduled ? "# Kanban refresh scheduled" : "# Kanban Updated",
|
|
194
194
|
"",
|
|
195
195
|
`JSON: ${snapshot.json_path}`,
|
|
196
196
|
`HTML: ${snapshot.html_path}`,
|
package/dist/tools-memory.d.ts
CHANGED
package/dist/tools-memory.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { renderLearningTask } from './learning-input-context.js';
|
|
2
|
+
import { readLearningPolicy } from './learning-policy.js';
|
|
1
3
|
/**
|
|
2
4
|
* Memory tool registrations – context snapshots and recall.
|
|
3
5
|
*/
|
|
@@ -8,7 +10,10 @@ import { isReadError } from "./shared.js";
|
|
|
8
10
|
import { appendRunLedgerEntrySafe } from "./run-ledger.js";
|
|
9
11
|
import { appendStatusEventSafe } from "./status-events.js";
|
|
10
12
|
import { readRuntimeProfile } from "./runtime-profile.js";
|
|
11
|
-
import { buildAceContinuityPacket, formatAceContinuityPacketMarkdown, normalizeContinuityPolicy, } from "./ace-autonomy.js";
|
|
13
|
+
import { buildAceContinuityPacket, formatAceContinuityPacketMarkdown, normalizeContinuityPolicy, formatCurrentWork, readCurrentWorkContext, } from "./ace-autonomy.js";
|
|
14
|
+
import { GoalRepository } from "./store/repositories/goal-repository.js";
|
|
15
|
+
import { bindHostWork } from './host-work-context.js';
|
|
16
|
+
import { formatStepTaskForBridge, collectUpstreamOutputs } from "./task-plan-context.js";
|
|
12
17
|
import { openStore } from "./store/ace-packed-store.js";
|
|
13
18
|
import { ProjectionManager } from "./store/materializers/projection-manager.js";
|
|
14
19
|
import { ContextSnapshotRepository } from "./store/repositories/context-snapshot-repository.js";
|
|
@@ -89,6 +94,8 @@ export function registerMemoryTools(server) {
|
|
|
89
94
|
if (!isReadError(indexRaw)) {
|
|
90
95
|
try {
|
|
91
96
|
index = JSON.parse(indexRaw);
|
|
97
|
+
if (!Array.isArray(index.snapshots) || index.snapshots.some((s) => !s || typeof s.name !== "string" || typeof s.file !== "string" || typeof s.timestamp !== "string"))
|
|
98
|
+
throw new Error("Invalid snapshot index");
|
|
92
99
|
}
|
|
93
100
|
catch {
|
|
94
101
|
/* keep default */
|
|
@@ -207,16 +214,43 @@ export function registerMemoryTools(server) {
|
|
|
207
214
|
],
|
|
208
215
|
};
|
|
209
216
|
});
|
|
210
|
-
server.tool("recall_context", "
|
|
217
|
+
server.tool("recall_context", "Return current work and linked context; retrieve a named snapshot or explicitly list snapshots", {
|
|
211
218
|
name: z
|
|
212
219
|
.string()
|
|
213
220
|
.optional()
|
|
214
|
-
.describe("
|
|
215
|
-
|
|
216
|
-
|
|
221
|
+
.describe("Named snapshot to retrieve; omit for current work"),
|
|
222
|
+
list: z.boolean().optional().describe("List saved snapshots instead of current work"),
|
|
223
|
+
goal_id: z.string().optional(),
|
|
224
|
+
agent: z.string().optional(),
|
|
225
|
+
}, async ({ name, list, goal_id, agent }, extra) => {
|
|
226
|
+
if (!name && !list) {
|
|
227
|
+
const context = readCurrentWorkContext({ goal_id, agent });
|
|
228
|
+
const lines = [formatCurrentWork(context.work, "block")];
|
|
229
|
+
const goalId = goal_id ?? context.work.objective?.goal_id;
|
|
230
|
+
if (goalId) {
|
|
231
|
+
const plan = new GoalRepository(resolveWorkspaceRoot()).getPlan(goalId);
|
|
232
|
+
if (plan)
|
|
233
|
+
for (const step of plan.steps.filter((s) => s.status === "running")) {
|
|
234
|
+
if (agent && step.assignee === agent)
|
|
235
|
+
bindHostWork({ agent, goal_id: goalId, step_id: step.step_id, attempt: step.attempt }, extra.sessionId);
|
|
236
|
+
lines.push(`Assignment ${step.step_id} (attempt ${step.attempt ?? 'engine'}, assignee ${step.assignee ?? step.role}):\n${formatStepTaskForBridge({ ...step, task: renderLearningTask(resolveWorkspaceRoot(), goalId, step), upstream_outputs: collectUpstreamOutputs(plan, step) })}${readLearningPolicy(resolveWorkspaceRoot()).enabled && step.served_learning?.text ? '\n' + step.served_learning.text : ''}`);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
const failures = context.events.filter((e) => ["fail", "blocked"].includes(e.status));
|
|
240
|
+
const selected = [...new Map([...context.events.slice(-5), ...failures].map((e) => [e.event_id, e])).values()];
|
|
241
|
+
for (const event of selected.reverse())
|
|
242
|
+
lines.push(`[${event.event_id}] ${event.event_type}: ${event.payload.summary ?? ""}`);
|
|
243
|
+
for (const entry of context.entries.slice(-4).reverse())
|
|
244
|
+
lines.push(`[${entry.id}] ${entry.message}`);
|
|
245
|
+
if (context.events.length > selected.length || context.entries.length > 4)
|
|
246
|
+
lines.push("Additional linked detail: list_status_events / get_run_ledger.");
|
|
247
|
+
return { isError: false, content: [{ type: "text", text: lines.join("\n") }] };
|
|
248
|
+
}
|
|
249
|
+
const indexPath = `${CONTEXT_STORE_REL}/index.json`;
|
|
217
250
|
const indexRaw = safeRead(indexPath);
|
|
218
251
|
if (isReadError(indexRaw)) {
|
|
219
252
|
return {
|
|
253
|
+
isError: !!name,
|
|
220
254
|
content: [
|
|
221
255
|
{
|
|
222
256
|
type: "text",
|
|
@@ -228,9 +262,12 @@ export function registerMemoryTools(server) {
|
|
|
228
262
|
let index;
|
|
229
263
|
try {
|
|
230
264
|
index = JSON.parse(indexRaw);
|
|
265
|
+
if (!Array.isArray(index.snapshots) || index.snapshots.some((s) => !s || typeof s.name !== "string" || typeof s.file !== "string" || typeof s.timestamp !== "string"))
|
|
266
|
+
throw new Error("Invalid snapshot index");
|
|
231
267
|
}
|
|
232
268
|
catch {
|
|
233
269
|
return {
|
|
270
|
+
isError: true,
|
|
234
271
|
content: [
|
|
235
272
|
{
|
|
236
273
|
type: "text",
|
|
@@ -257,6 +294,7 @@ export function registerMemoryTools(server) {
|
|
|
257
294
|
const match = index.snapshots.find((s) => s.name.toLowerCase() === name.toLowerCase());
|
|
258
295
|
if (!match) {
|
|
259
296
|
return {
|
|
297
|
+
isError: true,
|
|
260
298
|
content: [
|
|
261
299
|
{
|
|
262
300
|
type: "text",
|
|
@@ -265,9 +303,10 @@ export function registerMemoryTools(server) {
|
|
|
265
303
|
],
|
|
266
304
|
};
|
|
267
305
|
}
|
|
268
|
-
const content = safeRead(
|
|
306
|
+
const content = safeRead(`${CONTEXT_STORE_REL}/${match.file}`);
|
|
269
307
|
if (isReadError(content)) {
|
|
270
308
|
return {
|
|
309
|
+
isError: true,
|
|
271
310
|
content: [
|
|
272
311
|
{
|
|
273
312
|
type: "text",
|
|
@@ -276,6 +315,12 @@ export function registerMemoryTools(server) {
|
|
|
276
315
|
],
|
|
277
316
|
};
|
|
278
317
|
}
|
|
318
|
+
try {
|
|
319
|
+
JSON.parse(content);
|
|
320
|
+
}
|
|
321
|
+
catch {
|
|
322
|
+
return { isError: true, content: [{ type: "text", text: `Snapshot ${match.name} is corrupt. Recover its source or select another named snapshot.` }] };
|
|
323
|
+
}
|
|
279
324
|
return {
|
|
280
325
|
content: [
|
|
281
326
|
{
|
package/dist/tools-scheduler.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Job scheduler tool registrations.
|
|
3
3
|
*/
|
|
4
4
|
import { z } from "zod";
|
|
5
|
+
import { resolveWorkspaceRoot } from "./helpers.js";
|
|
5
6
|
import { acknowledgeJob, completeJob, dispatchJobNow, dispatchJobs, enqueueJob, getJobLockTablePath, getJobQueuePath, getSchedulerLeasePath, heartbeatJob, reapStaleRunningJobsTick, readJobQueue, readJobLockTable, readSchedulerLease, } from "./job-scheduler.js";
|
|
6
7
|
// Event emission handled in core job-scheduler.ts — no wrapper duplication.
|
|
7
8
|
const PRIORITY_BAND_ENUM = z.enum(["P0", "P1", "P2", "P3"]);
|
|
@@ -22,6 +23,7 @@ function asJsonBlock(value) {
|
|
|
22
23
|
return `\`\`\`json\n${JSON.stringify(value, null, 2)}\n\`\`\``;
|
|
23
24
|
}
|
|
24
25
|
export function registerSchedulerTools(server) {
|
|
26
|
+
const workspaceRoot = resolveWorkspaceRoot();
|
|
25
27
|
server.tool("enqueue_job", "Add a handoff job to the scheduler queue with priority/dependency/resource metadata", {
|
|
26
28
|
kind: z.string().describe("Job kind/type identifier"),
|
|
27
29
|
payload: z
|
|
@@ -46,7 +48,7 @@ export function registerSchedulerTools(server) {
|
|
|
46
48
|
backoff_ms: z.number().int().min(1000).max(86_400_000).optional(),
|
|
47
49
|
job_id: z.string().optional().describe("Optional caller-provided job id"),
|
|
48
50
|
}, async (input) => {
|
|
49
|
-
const result = await enqueueJob(input);
|
|
51
|
+
const result = await enqueueJob(input, workspaceRoot);
|
|
50
52
|
return {
|
|
51
53
|
content: [
|
|
52
54
|
{
|
|
@@ -78,15 +80,15 @@ export function registerSchedulerTools(server) {
|
|
|
78
80
|
.describe("Include lock table payload (default true)"),
|
|
79
81
|
limit: z.number().int().min(1).max(1000).optional().describe("Max jobs returned"),
|
|
80
82
|
}, async ({ status, include_locks, limit }) => {
|
|
81
|
-
const queue = readJobQueue();
|
|
83
|
+
const queue = readJobQueue(workspaceRoot);
|
|
82
84
|
const filtered = queue.jobs.filter((job) => (status ? job.status === status : true));
|
|
83
85
|
const clipped = typeof limit === "number" ? filtered.slice(0, limit) : filtered;
|
|
84
|
-
const lockTable = readJobLockTable();
|
|
85
|
-
const lease = readSchedulerLease();
|
|
86
|
+
const lockTable = readJobLockTable(workspaceRoot);
|
|
87
|
+
const lease = readSchedulerLease(workspaceRoot);
|
|
86
88
|
const payload = {
|
|
87
|
-
queue_path: getJobQueuePath(),
|
|
88
|
-
lock_path: getJobLockTablePath(),
|
|
89
|
-
lease_path: getSchedulerLeasePath(),
|
|
89
|
+
queue_path: getJobQueuePath(workspaceRoot),
|
|
90
|
+
lock_path: getJobLockTablePath(workspaceRoot),
|
|
91
|
+
lease_path: getSchedulerLeasePath(workspaceRoot),
|
|
90
92
|
total_jobs: queue.jobs.length,
|
|
91
93
|
returned_jobs: clipped.length,
|
|
92
94
|
jobs: clipped,
|
|
@@ -109,7 +111,7 @@ export function registerSchedulerTools(server) {
|
|
|
109
111
|
action: z.enum(["accept", "reject", "cancel", "resume"]),
|
|
110
112
|
reason: z.string().optional(),
|
|
111
113
|
}, async ({ job_id, action, reason }) => {
|
|
112
|
-
const result = await acknowledgeJob({ job_id, action, reason });
|
|
114
|
+
const result = await acknowledgeJob({ job_id, action, reason }, workspaceRoot);
|
|
113
115
|
return {
|
|
114
116
|
content: [
|
|
115
117
|
{
|
|
@@ -135,7 +137,7 @@ export function registerSchedulerTools(server) {
|
|
|
135
137
|
lease_ttl_seconds: z.number().int().min(5).max(3600).optional(),
|
|
136
138
|
now_iso: z.string().optional().describe("Optional deterministic clock override"),
|
|
137
139
|
}, async (input) => {
|
|
138
|
-
const result = await dispatchJobs(input);
|
|
140
|
+
const result = await dispatchJobs(input, workspaceRoot);
|
|
139
141
|
return {
|
|
140
142
|
content: [
|
|
141
143
|
{
|
|
@@ -158,7 +160,7 @@ export function registerSchedulerTools(server) {
|
|
|
158
160
|
owner: z.string().optional(),
|
|
159
161
|
now_iso: z.string().optional(),
|
|
160
162
|
}, async ({ job_id, owner, now_iso }) => {
|
|
161
|
-
const result = await dispatchJobNow({ job_id, owner, now_iso });
|
|
163
|
+
const result = await dispatchJobNow({ job_id, owner, now_iso }, workspaceRoot);
|
|
162
164
|
return {
|
|
163
165
|
content: [
|
|
164
166
|
{
|
|
@@ -185,7 +187,7 @@ export function registerSchedulerTools(server) {
|
|
|
185
187
|
evidence_ref,
|
|
186
188
|
error,
|
|
187
189
|
now_iso,
|
|
188
|
-
});
|
|
190
|
+
}, workspaceRoot);
|
|
189
191
|
return {
|
|
190
192
|
content: [
|
|
191
193
|
{
|
|
@@ -203,7 +205,7 @@ export function registerSchedulerTools(server) {
|
|
|
203
205
|
lease_ttl_seconds: z.number().int().min(5).max(3600).optional(),
|
|
204
206
|
now_iso: z.string().optional().describe("Optional deterministic clock override"),
|
|
205
207
|
}, async ({ job_id, owner, lease_ttl_seconds, now_iso }) => {
|
|
206
|
-
const result = await heartbeatJob({ job_id, owner, lease_ttl_seconds, now_iso });
|
|
208
|
+
const result = await heartbeatJob({ job_id, owner, lease_ttl_seconds, now_iso }, workspaceRoot);
|
|
207
209
|
return {
|
|
208
210
|
content: [
|
|
209
211
|
{
|
|
@@ -225,7 +227,7 @@ export function registerSchedulerTools(server) {
|
|
|
225
227
|
.describe("Staleness window in seconds (default 120)"),
|
|
226
228
|
now_iso: z.string().optional().describe("Optional deterministic clock override"),
|
|
227
229
|
}, async ({ heartbeat_stale_seconds, now_iso }) => {
|
|
228
|
-
const result = await reapStaleRunningJobsTick({ heartbeat_stale_seconds, now_iso });
|
|
230
|
+
const result = await reapStaleRunningJobsTick({ heartbeat_stale_seconds, now_iso }, workspaceRoot);
|
|
229
231
|
return {
|
|
230
232
|
content: [
|
|
231
233
|
{
|
|
@@ -247,9 +249,9 @@ export function registerSchedulerTools(server) {
|
|
|
247
249
|
text: [
|
|
248
250
|
"# Lock Table",
|
|
249
251
|
"",
|
|
250
|
-
`Path: ${getJobLockTablePath()}`,
|
|
252
|
+
`Path: ${getJobLockTablePath(workspaceRoot)}`,
|
|
251
253
|
"",
|
|
252
|
-
asJsonBlock(readJobLockTable()),
|
|
254
|
+
asJsonBlock(readJobLockTable(workspaceRoot)),
|
|
253
255
|
].join("\n"),
|
|
254
256
|
},
|
|
255
257
|
],
|
|
@@ -261,9 +263,9 @@ export function registerSchedulerTools(server) {
|
|
|
261
263
|
text: [
|
|
262
264
|
"# Scheduler Lease",
|
|
263
265
|
"",
|
|
264
|
-
`Path: ${getSchedulerLeasePath()}`,
|
|
266
|
+
`Path: ${getSchedulerLeasePath(workspaceRoot)}`,
|
|
265
267
|
"",
|
|
266
|
-
asJsonBlock(readSchedulerLease() ?? null),
|
|
268
|
+
asJsonBlock(readSchedulerLease(workspaceRoot) ?? null),
|
|
267
269
|
].join("\n"),
|
|
268
270
|
},
|
|
269
271
|
],
|