@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
@@ -1,7 +1,7 @@
1
1
  import { calculateJaccard, getShingles } from "./semantic-hash.js";
2
2
  import { appendStatusEventSafe } from "./status-events.js";
3
3
  function scopeEqualOrBroader(candidate, incoming) {
4
- return Object.entries(candidate).every(([key, value]) => value === undefined || incoming[key] === value);
4
+ return Object.entries(candidate).every(([key, value]) => value === undefined || JSON.stringify(incoming[key]) === JSON.stringify(value));
5
5
  }
6
6
  function mergeEvidence(a, b) {
7
7
  return [...new Set([...a, ...b])].slice(0, 5);
@@ -43,7 +43,7 @@ export async function curate(repo, ops, config = {}) {
43
43
  }
44
44
  const incomingShingles = getShingles(op.content, 3);
45
45
  const match = repo
46
- .listBullets({ section: op.section, status: "active" })
46
+ .listBullets({ section: op.section, ...(op.status === "quarantined" ? {} : { status: "active" }) })
47
47
  .filter((bullet) => scopeEqualOrBroader(bullet.scope, op.scope))
48
48
  .map((bullet) => ({
49
49
  bullet,
@@ -55,6 +55,7 @@ export async function curate(repo, ops, config = {}) {
55
55
  ...match.bullet,
56
56
  observed_count: match.bullet.observed_count + 1,
57
57
  evidence_refs: mergeEvidence(match.bullet.evidence_refs, op.evidence_refs),
58
+ evidence_snapshots: [...new Map([...(match.bullet.evidence_snapshots ?? []), ...(op.evidence_snapshots ?? [])].map(e => [e.id, e])).values()],
58
59
  });
59
60
  result.merged += 1;
60
61
  }
@@ -69,13 +70,14 @@ export async function curate(repo, ops, config = {}) {
69
70
  }
70
71
  result.pruned += pruneOverflow(repo, config);
71
72
  const eventWorkspaceRoot = config.workspaceRoot ?? repo.root;
72
- await appendStatusEventSafe({
73
- event_type: "PLAYBOOK_CURATED",
74
- source_module: "capability-build",
75
- status: "done",
76
- summary: `Playbook curated: added=${result.added} merged=${result.merged} tagged=${result.tagged} quarantined=${result.quarantined} pruned=${result.pruned}`,
77
- payload: { ...result },
78
- }, eventWorkspaceRoot ? { workspaceRoot: eventWorkspaceRoot } : {});
73
+ if (config.emitEvent !== false && Object.values(result).some(n => n > 0))
74
+ await appendStatusEventSafe({
75
+ event_type: "PLAYBOOK_CURATED",
76
+ source_module: "capability-build",
77
+ status: "done",
78
+ summary: `Playbook curated: added=${result.added} merged=${result.merged} tagged=${result.tagged} quarantined=${result.quarantined} pruned=${result.pruned}`,
79
+ payload: { ...result },
80
+ }, eventWorkspaceRoot ? { workspaceRoot: eventWorkspaceRoot } : {});
79
81
  return result;
80
82
  }
81
83
  function pruneOverflow(repo, config) {
@@ -0,0 +1,8 @@
1
+ export interface ProcessIdentity {
2
+ pid: number;
3
+ started_at: string;
4
+ hostname: string;
5
+ }
6
+ export declare function readProcessIdentity(pid: number): ProcessIdentity | undefined;
7
+ export declare function probeProcess(identity: ProcessIdentity): 'alive' | 'dead' | 'unknown';
8
+ //# sourceMappingURL=process-identity.d.ts.map
@@ -0,0 +1,38 @@
1
+ /** OS identity, not just a PID: do not signal a reused or unverifiable process. */
2
+ import { execFileSync } from 'node:child_process';
3
+ import { hostname } from 'node:os';
4
+ export function readProcessIdentity(pid) {
5
+ if (!Number.isSafeInteger(pid) || pid <= 1)
6
+ return undefined;
7
+ try {
8
+ const row = execFileSync('ps', ['-p', String(pid), '-o', 'lstart=', '-o', 'stat='], { encoding: 'utf8', timeout: 2000 }).trim();
9
+ const match = row.match(/^(.+?)\s+(\S+)$/);
10
+ if (!match || /^[ZX]/.test(match[2]))
11
+ return undefined;
12
+ return { pid, started_at: match[1].trim().replace(/\s+/g, ' '), hostname: hostname() };
13
+ }
14
+ catch {
15
+ return undefined;
16
+ }
17
+ }
18
+ export function probeProcess(identity) {
19
+ if (identity.hostname !== hostname())
20
+ return 'unknown';
21
+ const observed = readProcessIdentity(identity.pid);
22
+ if (observed)
23
+ return observed.started_at === identity.started_at ? 'alive' : 'dead';
24
+ try {
25
+ const stat = execFileSync('ps', ['-p', String(identity.pid), '-o', 'stat='], { encoding: 'utf8', timeout: 2000 }).trim();
26
+ if (/^[ZX]/.test(stat))
27
+ return 'dead';
28
+ }
29
+ catch { /* Permission and transient OS errors are not proof of death. */ }
30
+ try {
31
+ process.kill(identity.pid, 0);
32
+ return 'unknown';
33
+ }
34
+ catch (error) {
35
+ return error.code === 'ESRCH' ? 'dead' : 'unknown';
36
+ }
37
+ }
38
+ //# sourceMappingURL=process-identity.js.map
@@ -0,0 +1,34 @@
1
+ import { type ProcessIdentity } from './process-identity.js';
2
+ export interface CommandPolicy {
3
+ stall_min?: number;
4
+ kill_min?: number;
5
+ deadline_min?: number;
6
+ }
7
+ export interface CommandProgress {
8
+ pid?: number;
9
+ started_at: string;
10
+ last_output_at?: string;
11
+ last_progress_at: string;
12
+ elapsed_ms: number;
13
+ silence_ms: number;
14
+ output_tail: string;
15
+ status: 'running' | 'suspected_stall' | 'terminating' | 'done' | 'failed';
16
+ reason_code?: string;
17
+ }
18
+ export interface CommandOptions extends CommandPolicy {
19
+ signal?: AbortSignal;
20
+ log_path?: string;
21
+ poll_interval_ms?: number;
22
+ onProgress?: (progress: CommandProgress) => void | Promise<void>;
23
+ onSpawn?: (identity: ProcessIdentity) => void | Promise<void>;
24
+ }
25
+ export interface CommandResult {
26
+ ok: boolean;
27
+ output: string;
28
+ exit_code: number | null;
29
+ duration_ms: number;
30
+ reason_code?: string;
31
+ }
32
+ export declare function resolveCommandPolicy(options?: CommandPolicy): Required<Omit<CommandPolicy, 'deadline_min'>> & Pick<CommandPolicy, 'deadline_min'>;
33
+ export declare function runProgressCommand(command: string, root: string, options?: CommandOptions): Promise<CommandResult>;
34
+ //# sourceMappingURL=progress-command.d.ts.map
@@ -0,0 +1,153 @@
1
+ /** One process-group command runner shared by verification, tests and staged checks. */
2
+ import { spawn } from 'node:child_process';
3
+ import { appendFileSync, mkdirSync } from 'node:fs';
4
+ import { dirname } from 'node:path';
5
+ import { readProcessIdentity } from './process-identity.js';
6
+ export function resolveCommandPolicy(options = {}) {
7
+ const stall = options.stall_min ?? 10;
8
+ const kill = stall === 0 ? 0 : options.kill_min ?? Math.max(60, stall);
9
+ for (const [key, value] of Object.entries({ stall_min: stall, kill_min: kill, deadline_min: options.deadline_min })) {
10
+ if (value !== undefined && (!Number.isFinite(value) || value < 0 || (key === 'deadline_min' && value === 0)))
11
+ throw new Error(`${key} must be finite and ${key === 'deadline_min' ? 'positive' : 'nonnegative'}.`);
12
+ }
13
+ if (stall > 0 && kill > 0 && kill < stall)
14
+ throw new Error('The kill silence window must not precede the stall warning.');
15
+ return { stall_min: stall, kill_min: kill, ...(options.deadline_min !== undefined ? { deadline_min: options.deadline_min } : {}) };
16
+ }
17
+ export async function runProgressCommand(command, root, options = {}) {
18
+ const policy = resolveCommandPolicy(options);
19
+ if (options.signal?.aborted)
20
+ return { ok: false, output: '', exit_code: null, duration_ms: 0, reason_code: 'command_canceled' };
21
+ const started = Date.now();
22
+ let lastOutput = started;
23
+ let outputAt;
24
+ let output = '';
25
+ let stopping;
26
+ let closed = false;
27
+ let force;
28
+ if (options.log_path)
29
+ mkdirSync(dirname(options.log_path), { recursive: true });
30
+ // Register process ownership before releasing the command. If the runner
31
+ // dies during registration, EOF exits the waiting shell without side effects.
32
+ const child = spawn('sh', ['-c', 'IFS= read -r ace_start || exit 125; exec sh -c "$1"', 'ace-command', command], { cwd: root, detached: process.platform !== 'win32', stdio: ['pipe', 'pipe', 'pipe'] });
33
+ const notify = (final) => {
34
+ const now = Date.now(), silence = now - lastOutput;
35
+ const value = { pid: child.pid, started_at: new Date(started).toISOString(), last_output_at: outputAt,
36
+ last_progress_at: new Date(lastOutput).toISOString(), elapsed_ms: now - started, silence_ms: silence, output_tail: output.slice(-2000),
37
+ status: final ?? (stopping ? 'terminating' : policy.stall_min > 0 && silence >= policy.stall_min * 60000 ? 'suspected_stall' : 'running'),
38
+ ...(stopping ? { reason_code: stopping } : {}) };
39
+ try {
40
+ Promise.resolve(options.onProgress?.(value)).catch(() => { });
41
+ }
42
+ catch { /* Observer failure cannot kill work. */ }
43
+ };
44
+ const signalGroup = (signal) => {
45
+ if (!child.pid)
46
+ return;
47
+ try {
48
+ if (process.platform === 'win32')
49
+ child.kill(signal);
50
+ else
51
+ process.kill(-child.pid, signal);
52
+ }
53
+ catch (error) {
54
+ if (error.code !== 'ESRCH')
55
+ output = `${output}\nUnable to signal process group: ${String(error)}`.slice(-64000);
56
+ }
57
+ };
58
+ const stop = (reason) => {
59
+ if (closed || stopping)
60
+ return;
61
+ stopping = reason;
62
+ signalGroup('SIGTERM');
63
+ notify();
64
+ force = setTimeout(() => signalGroup('SIGKILL'), 1000);
65
+ };
66
+ const abort = () => stop('command_canceled');
67
+ options.signal?.addEventListener('abort', abort, { once: true });
68
+ const capture = (chunk) => {
69
+ const value = chunk.toString();
70
+ output = (output + value).slice(-64000);
71
+ lastOutput = Date.now();
72
+ outputAt = new Date(lastOutput).toISOString();
73
+ if (options.log_path) {
74
+ try {
75
+ appendFileSync(options.log_path, value);
76
+ }
77
+ catch (error) {
78
+ stop('command_log_failed');
79
+ }
80
+ }
81
+ notify();
82
+ };
83
+ child.stdout.on('data', capture);
84
+ child.stderr.on('data', capture);
85
+ child.once('spawn', async () => {
86
+ const identity = child.pid && readProcessIdentity(child.pid);
87
+ try {
88
+ if (options.onSpawn && !identity)
89
+ throw new Error('Command process identity unavailable.');
90
+ if (identity)
91
+ await options.onSpawn?.(identity);
92
+ child.stdin.end(closed || stopping ? '' : 'run\n');
93
+ }
94
+ catch (error) {
95
+ output = `${output}\n${String(error)}`.trim();
96
+ stop('command_ownership_failed');
97
+ child.stdin.end();
98
+ }
99
+ notify();
100
+ if (options.signal?.aborted)
101
+ abort();
102
+ });
103
+ child.stdin.on('error', () => { });
104
+ const timer = setInterval(() => {
105
+ const now = Date.now();
106
+ if (policy.deadline_min !== undefined && now - started >= policy.deadline_min * 60000)
107
+ stop('command_deadline_exceeded');
108
+ else if (policy.kill_min > 0 && now - lastOutput >= policy.kill_min * 60000)
109
+ stop('command_stalled');
110
+ else
111
+ notify();
112
+ }, options.poll_interval_ms ?? 1000);
113
+ let deadlineTimer;
114
+ const armDeadline = () => {
115
+ if (policy.deadline_min === undefined || closed)
116
+ return;
117
+ const remaining = policy.deadline_min * 60000 - (Date.now() - started);
118
+ if (remaining <= 0) {
119
+ stop('command_deadline_exceeded');
120
+ return;
121
+ }
122
+ // Node clamps overflowing timers to 1ms. Long explicit deadlines must not
123
+ // accidentally become immediate cancellations.
124
+ deadlineTimer = setTimeout(armDeadline, Math.min(remaining, 2147483647));
125
+ };
126
+ armDeadline();
127
+ return await new Promise(resolve => {
128
+ const finish = (code, error) => {
129
+ if (closed)
130
+ return;
131
+ closed = true;
132
+ clearInterval(timer);
133
+ if (force)
134
+ clearTimeout(force);
135
+ if (deadlineTimer)
136
+ clearTimeout(deadlineTimer);
137
+ options.signal?.removeEventListener('abort', abort);
138
+ if (error)
139
+ output = `${output}\n${error.message}`.trim();
140
+ // Descendants may have ignored TERM and closed inherited output. Ensure
141
+ // cancellation cleans the entire original group before reporting terminal.
142
+ if (stopping)
143
+ signalGroup('SIGKILL');
144
+ const ok = !stopping && !error && code === 0;
145
+ notify(ok ? 'done' : 'failed');
146
+ resolve({ ok, output, exit_code: code, duration_ms: Date.now() - started,
147
+ ...(stopping ? { reason_code: stopping } : error ? { reason_code: 'command_spawn_failed' } : {}) });
148
+ };
149
+ child.once('error', error => finish(null, error));
150
+ child.once('close', code => finish(code));
151
+ });
152
+ }
153
+ //# sourceMappingURL=progress-command.js.map
package/dist/prompts.js CHANGED
@@ -89,7 +89,7 @@ function renderRuntimeRecallSections(input) {
89
89
  export function registerPrompts(server) {
90
90
  // ── Token-budget mode ─────────────────────────────────────────────
91
91
  const BRIEF_SCHEMA = z
92
- .boolean()
92
+ .union([z.boolean(), z.enum(['true', 'false']).transform(value => value === 'true')])
93
93
  .optional()
94
94
  .describe("Token-budget mode: true = role instructions only (no kernel/task-packs). Default: false (full context).");
95
95
  // ── 1. Per-agent activation prompts ─────────────────────────────────
@@ -171,7 +171,7 @@ export function registerPrompts(server) {
171
171
  "You are now operating as ACE-Orchestrator.",
172
172
  "",
173
173
  "## Orchestrator Instructions",
174
- readAgentInstructions("orchestrator"),
174
+ readAgentInstructions("orchestrator", { includeLearned: true }),
175
175
  "",
176
176
  ...(brief ? ["## ACE Preflight", formatAceRecallMarkdown(buildAceRecallContext())] : [formatRecallSlices(task)]),
177
177
  ...(brief
@@ -214,7 +214,7 @@ export function registerPrompts(server) {
214
214
  "You are now operating as ACE-VOS.",
215
215
  "",
216
216
  "## VOS Instructions",
217
- readAgentInstructions("vos"),
217
+ readAgentInstructions("vos", { includeLearned: true }),
218
218
  ...(brief
219
219
  ? []
220
220
  : [
@@ -249,7 +249,7 @@ export function registerPrompts(server) {
249
249
  "You are now operating as ACE-UI.",
250
250
  "",
251
251
  "## UI Instructions",
252
- readAgentInstructions("ui"),
252
+ readAgentInstructions("ui", { includeLearned: true }),
253
253
  ...(brief
254
254
  ? []
255
255
  : [
@@ -284,7 +284,7 @@ export function registerPrompts(server) {
284
284
  "You are now operating as ACE-Coders.",
285
285
  "",
286
286
  "## Coders Instructions",
287
- readAgentInstructions("coders"),
287
+ readAgentInstructions("coders", { includeLearned: true }),
288
288
  ...(brief
289
289
  ? []
290
290
  : [
@@ -316,10 +316,10 @@ export function registerPrompts(server) {
316
316
  "You are now operating as ACE-ASTGrep.",
317
317
  "",
318
318
  "## ASTGrep Agent Instructions",
319
- readAgentInstructions("astgrep"),
319
+ readAgentInstructions("astgrep", { includeLearned: true }),
320
320
  "",
321
321
  "## AST-grep Command Pack",
322
- readSkillInstructions("astgrep-index"),
322
+ readSkillInstructions("astgrep-index", { includeLearned: true }),
323
323
  "",
324
324
  task
325
325
  ? `## Current Task\n${task}`
@@ -341,7 +341,7 @@ export function registerPrompts(server) {
341
341
  "You are now operating as ACE-Memory.",
342
342
  "",
343
343
  "## Memory Agent Instructions",
344
- readAgentInstructions("memory"),
344
+ readAgentInstructions("memory", { includeLearned: true }),
345
345
  "",
346
346
  "## Shared Kernel",
347
347
  readKernelArtifact("directive_kernel"),
@@ -366,7 +366,7 @@ export function registerPrompts(server) {
366
366
  "You are now operating as ACE-Security.",
367
367
  "",
368
368
  "## Security Agent Instructions",
369
- readAgentInstructions("security"),
369
+ readAgentInstructions("security", { includeLearned: true }),
370
370
  "",
371
371
  "## Shared Kernel",
372
372
  readKernelArtifact("directive_kernel"),
@@ -391,7 +391,7 @@ export function registerPrompts(server) {
391
391
  "You are now operating as ACE-Observability.",
392
392
  "",
393
393
  "## Observability Agent Instructions",
394
- readAgentInstructions("observability"),
394
+ readAgentInstructions("observability", { includeLearned: true }),
395
395
  "",
396
396
  "## Shared Kernel",
397
397
  readKernelArtifact("directive_kernel"),
@@ -416,7 +416,7 @@ export function registerPrompts(server) {
416
416
  "You are now operating as ACE-Eval.",
417
417
  "",
418
418
  "## Eval Agent Instructions",
419
- readAgentInstructions("eval"),
419
+ readAgentInstructions("eval", { includeLearned: true }),
420
420
  "",
421
421
  "## Shared Kernel",
422
422
  readKernelArtifact("directive_kernel"),
@@ -441,7 +441,7 @@ export function registerPrompts(server) {
441
441
  "You are now operating as ACE-Release.",
442
442
  "",
443
443
  "## Release Agent Instructions",
444
- readAgentInstructions("release"),
444
+ readAgentInstructions("release", { includeLearned: true }),
445
445
  "",
446
446
  "## Shared Kernel",
447
447
  readKernelArtifact("directive_kernel"),
@@ -563,7 +563,7 @@ export function registerPrompts(server) {
563
563
  text: [
564
564
  `# ACE Skill Activation: ${skill}`,
565
565
  "",
566
- readSkillInstructions(skill),
566
+ readSkillInstructions(skill, { includeLearned: true }),
567
567
  "",
568
568
  task ? `## Task\n${task}` : "## Task\nApply this skill to the current workspace objective.",
569
569
  ].join("\n"),
@@ -580,7 +580,7 @@ export function registerPrompts(server) {
580
580
  text: [
581
581
  "# CodeMunch Activation",
582
582
  "",
583
- readSkillInstructions("codemunch"),
583
+ readSkillInstructions("codemunch", { includeLearned: true }),
584
584
  "",
585
585
  task
586
586
  ? `## Objective\n${task}`
@@ -599,7 +599,7 @@ export function registerPrompts(server) {
599
599
  text: [
600
600
  "# CodeSnipe Activation",
601
601
  "",
602
- readSkillInstructions("codesnipe"),
602
+ readSkillInstructions("codesnipe", { includeLearned: true }),
603
603
  "",
604
604
  task
605
605
  ? `## Objective\n${task}`
package/dist/resources.js CHANGED
@@ -26,7 +26,7 @@ export function registerResources(server) {
26
26
  contents: [
27
27
  {
28
28
  uri: `ace://agents/${role}/instructions`,
29
- text: readAgentInstructions(role),
29
+ text: readAgentInstructions(role, { includeLearned: true }),
30
30
  mimeType: "text/markdown",
31
31
  },
32
32
  ],
@@ -154,7 +154,7 @@ export function registerResources(server) {
154
154
  contents: [
155
155
  {
156
156
  uri: uri.href,
157
- text: readSkillInstructions(name),
157
+ text: readSkillInstructions(name, { includeLearned: true }),
158
158
  mimeType: "text/markdown",
159
159
  },
160
160
  ],
@@ -16,8 +16,15 @@ export interface ShellCommandProgressEvent {
16
16
  export interface RunShellCommandOptions {
17
17
  cwd: string;
18
18
  env?: NodeJS.ProcessEnv;
19
- timeout_ms: number;
19
+ timeout_ms?: number;
20
+ /** Final silence threshold; 0 disables stall detection. */
20
21
  stall_timeout_ms?: number;
22
+ stall_warning_ms?: number;
23
+ on_stall?: (event: {
24
+ at: number;
25
+ last_progress_at: number;
26
+ silence_ms: number;
27
+ }) => void;
21
28
  max_output_bytes?: number;
22
29
  on_spawn?: (child: ChildProcess) => void;
23
30
  on_progress?: (event: ShellCommandProgressEvent) => void;
@@ -9,6 +9,12 @@ function appendChunk(buffer, chunk, maxBytes) {
9
9
  return clipped.subarray(0, maxBytes).toString("utf8");
10
10
  }
11
11
  export async function runShellCommand(command, options) {
12
+ for (const [name, value] of Object.entries({ timeout_ms: options.timeout_ms, stall_timeout_ms: options.stall_timeout_ms, stall_warning_ms: options.stall_warning_ms })) {
13
+ if (value !== undefined && (!Number.isFinite(value) || value < 0))
14
+ throw new Error(`${name} must be finite and nonnegative; 0 disables the limit.`);
15
+ }
16
+ if (options.stall_timeout_ms && options.stall_warning_ms && options.stall_warning_ms > options.stall_timeout_ms)
17
+ throw new Error("The stall warning must not follow the final silence limit.");
12
18
  const startedAt = Date.now();
13
19
  const maxOutputBytes = Math.max(4 * 1024, options.max_output_bytes ?? 256 * 1024);
14
20
  return new Promise((resolve, reject) => {
@@ -19,6 +25,7 @@ export async function runShellCommand(command, options) {
19
25
  let finished = false;
20
26
  let lastProgressAt = startedAt;
21
27
  let stallTimer;
28
+ let warningTimer;
22
29
  let forcedKillTimer;
23
30
  let child;
24
31
  try {
@@ -26,6 +33,7 @@ export async function runShellCommand(command, options) {
26
33
  cwd: options.cwd,
27
34
  env: options.env,
28
35
  stdio: ["ignore", "pipe", "pipe"],
36
+ detached: process.platform !== "win32",
29
37
  });
30
38
  }
31
39
  catch (error) {
@@ -33,23 +41,48 @@ export async function runShellCommand(command, options) {
33
41
  return;
34
42
  }
35
43
  options.on_spawn?.(child);
44
+ const signalGroup = (signal) => {
45
+ if (!child.pid)
46
+ return;
47
+ try {
48
+ if (process.platform === "win32")
49
+ child.kill(signal);
50
+ else
51
+ process.kill(-child.pid, signal);
52
+ }
53
+ catch (error) {
54
+ if (error.code !== "ESRCH")
55
+ stderr = appendChunk(stderr, String(error), maxOutputBytes);
56
+ }
57
+ };
36
58
  const requestTimeout = (reason) => {
37
59
  if (finished || timedOut)
38
60
  return;
39
61
  timedOut = true;
40
62
  timeoutReason = reason;
41
- child.kill("SIGTERM");
63
+ signalGroup("SIGTERM");
42
64
  forcedKillTimer = setTimeout(() => {
43
65
  if (!finished)
44
- child.kill("SIGKILL");
66
+ signalGroup("SIGKILL");
45
67
  }, 500);
46
68
  };
47
69
  const resetStallTimer = () => {
48
70
  if (stallTimer)
49
71
  clearTimeout(stallTimer);
50
- const stallTimeoutMs = options.stall_timeout_ms;
72
+ if (warningTimer)
73
+ clearTimeout(warningTimer);
74
+ const stallTimeoutMs = options.stall_timeout_ms ?? 60 * 60_000;
51
75
  if (!stallTimeoutMs || stallTimeoutMs <= 0)
52
76
  return;
77
+ const warningMs = options.stall_warning_ms ?? Math.min(10 * 60_000, stallTimeoutMs);
78
+ if (warningMs > 0)
79
+ warningTimer = setTimeout(() => {
80
+ const at = Date.now();
81
+ try {
82
+ options.on_stall?.({ at, last_progress_at: lastProgressAt, silence_ms: at - lastProgressAt });
83
+ }
84
+ catch { /* Warning observers do not own execution. */ }
85
+ }, warningMs);
53
86
  stallTimer = setTimeout(() => requestTimeout("stall"), stallTimeoutMs + STALL_TIMEOUT_SCHEDULER_GRACE_MS);
54
87
  };
55
88
  const recordProgress = (source) => {
@@ -65,11 +98,15 @@ export async function runShellCommand(command, options) {
65
98
  clearTimeout(stallTimer);
66
99
  if (forcedKillTimer)
67
100
  clearTimeout(forcedKillTimer);
101
+ if (warningTimer)
102
+ clearTimeout(warningTimer);
103
+ if (timedOut)
104
+ signalGroup("SIGKILL");
68
105
  resolve(result);
69
106
  };
70
- const timeout = setTimeout(() => {
107
+ const timeout = options.timeout_ms && options.timeout_ms > 0 ? setTimeout(() => {
71
108
  requestTimeout("turn_budget");
72
- }, options.timeout_ms);
109
+ }, options.timeout_ms) : undefined;
73
110
  recordProgress("spawn");
74
111
  child.stdout?.setEncoding("utf8");
75
112
  child.stdout?.on("data", (chunk) => {
@@ -82,16 +119,20 @@ export async function runShellCommand(command, options) {
82
119
  recordProgress("stderr");
83
120
  });
84
121
  child.on("error", (error) => {
85
- clearTimeout(timeout);
122
+ if (timeout)
123
+ clearTimeout(timeout);
86
124
  if (stallTimer)
87
125
  clearTimeout(stallTimer);
88
126
  if (forcedKillTimer)
89
127
  clearTimeout(forcedKillTimer);
128
+ if (warningTimer)
129
+ clearTimeout(warningTimer);
90
130
  if (!finished)
91
131
  reject(error);
92
132
  });
93
133
  child.on("close", (code, signal) => {
94
- clearTimeout(timeout);
134
+ if (timeout)
135
+ clearTimeout(timeout);
95
136
  finish({
96
137
  exit_code: code,
97
138
  signal,