@voybio/ace-swarm 2.4.3 → 2.4.6

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 (144) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/README.md +72 -532
  3. package/assets/agent-state/INTERFACE_REGISTRY.md +2 -2
  4. package/assets/agent-state/MODULES/gates/gate-autonomy.json +4 -2
  5. package/assets/agent-state/MODULES/gates/gate-completeness.json +4 -2
  6. package/assets/agent-state/MODULES/gates/gate-operability.json +4 -2
  7. package/assets/agent-state/MODULES/gates/gate-security.json +3 -1
  8. package/assets/agent-state/MODULES/registry.json +1 -4
  9. package/assets/agent-state/MODULES/roles/capability-build.json +1 -2
  10. package/assets/agent-state/MODULES/roles/capability-eval.json +1 -1
  11. package/assets/agent-state/MODULES/roles/capability-qa.json +1 -2
  12. package/assets/agent-state/QUALITY_GATES.md +25 -9
  13. package/assets/agent-state/TEAL_CONFIG.md +2 -11
  14. package/assets/eval-battery/battery.json +85 -0
  15. package/assets/eval-battery/fixtures/edit-target.js +3 -0
  16. package/assets/scripts/render-mcp-configs.sh +12 -0
  17. package/dist/ace-context.d.ts +18 -0
  18. package/dist/ace-context.js +161 -16
  19. package/dist/ace-server-instructions.js +36 -9
  20. package/dist/astgrep-index.d.ts +7 -1
  21. package/dist/astgrep-index.js +66 -39
  22. package/dist/bridge-trace.d.ts +17 -0
  23. package/dist/bridge-trace.js +23 -0
  24. package/dist/cli.d.ts +9 -1
  25. package/dist/cli.js +619 -10
  26. package/dist/discovery-runtime-wrappers.d.ts +16 -1
  27. package/dist/discovery-runtime-wrappers.js +65 -9
  28. package/dist/eval-harness.d.ts +110 -0
  29. package/dist/eval-harness.js +254 -0
  30. package/dist/experience-reflector.d.ts +13 -0
  31. package/dist/experience-reflector.js +62 -0
  32. package/dist/gate-contract.d.ts +63 -0
  33. package/dist/gate-contract.js +178 -0
  34. package/dist/goal-engine.d.ts +155 -0
  35. package/dist/goal-engine.js +795 -0
  36. package/dist/helpers/store-resolution.js +40 -39
  37. package/dist/hermes/session-manager.d.ts +10 -0
  38. package/dist/hermes/session-manager.js +130 -27
  39. package/dist/job-scheduler.d.ts +58 -0
  40. package/dist/job-scheduler.js +202 -2
  41. package/dist/job-worker.d.ts +87 -0
  42. package/dist/job-worker.js +182 -0
  43. package/dist/local-model-policy.d.ts +8 -2
  44. package/dist/local-model-policy.js +31 -17
  45. package/dist/local-model-runtime.d.ts +6 -0
  46. package/dist/local-model-runtime.js +130 -2
  47. package/dist/measured-classing.d.ts +66 -0
  48. package/dist/measured-classing.js +165 -0
  49. package/dist/model-bridge.d.ts +45 -0
  50. package/dist/model-bridge.js +502 -44
  51. package/dist/orchestrator-supervisor.js +1 -1
  52. package/dist/plan-proposal.d.ts +7 -0
  53. package/dist/plan-proposal.js +74 -4
  54. package/dist/playbook-adaptation.d.ts +37 -0
  55. package/dist/playbook-adaptation.js +159 -0
  56. package/dist/playbook-curator.d.ts +16 -0
  57. package/dist/playbook-curator.js +106 -0
  58. package/dist/playbook-llm-reflector.d.ts +26 -0
  59. package/dist/playbook-llm-reflector.js +157 -0
  60. package/dist/public-surface.d.ts +2 -0
  61. package/dist/public-surface.js +26 -14
  62. package/dist/runtime-executor.js +45 -2
  63. package/dist/runtime-tool-specs.d.ts +8 -1
  64. package/dist/runtime-tool-specs.js +19 -1
  65. package/dist/safe-edit.d.ts +20 -0
  66. package/dist/safe-edit.js +355 -78
  67. package/dist/schemas.js +16 -15
  68. package/dist/server.d.ts +11 -2
  69. package/dist/server.js +55 -5
  70. package/dist/shared.d.ts +12 -0
  71. package/dist/shared.js +13 -1
  72. package/dist/source-mutation-coordinator.d.ts +54 -0
  73. package/dist/source-mutation-coordinator.js +259 -0
  74. package/dist/status-events.d.ts +4 -1
  75. package/dist/status-events.js +41 -6
  76. package/dist/store/ace-packed-store.d.ts +28 -9
  77. package/dist/store/ace-packed-store.js +324 -57
  78. package/dist/store/bgzf.d.ts +4 -0
  79. package/dist/store/bgzf.js +94 -0
  80. package/dist/store/bootstrap-store.d.ts +4 -1
  81. package/dist/store/bootstrap-store.js +87 -9
  82. package/dist/store/cache-workspace.d.ts +8 -0
  83. package/dist/store/cache-workspace.js +67 -1
  84. package/dist/store/crc32.d.ts +2 -0
  85. package/dist/store/crc32.js +16 -0
  86. package/dist/store/fsck.d.ts +16 -0
  87. package/dist/store/fsck.js +240 -0
  88. package/dist/store/gate-contract-migration.d.ts +10 -0
  89. package/dist/store/gate-contract-migration.js +413 -0
  90. package/dist/store/materializers/context-snapshot-materializer.d.ts +1 -0
  91. package/dist/store/materializers/context-snapshot-materializer.js +4 -1
  92. package/dist/store/materializers/projection-manager.d.ts +7 -0
  93. package/dist/store/materializers/projection-manager.js +37 -0
  94. package/dist/store/materializers/scheduler-projection-materializer.js +10 -3
  95. package/dist/store/repositories/goal-repository.d.ts +112 -0
  96. package/dist/store/repositories/goal-repository.js +157 -0
  97. package/dist/store/repositories/playbook-repository.d.ts +107 -0
  98. package/dist/store/repositories/playbook-repository.js +314 -0
  99. package/dist/store/store-artifacts.js +44 -2
  100. package/dist/store/store-authority-audit.js +1 -0
  101. package/dist/store/store-cli.d.ts +30 -0
  102. package/dist/store/store-cli.js +242 -0
  103. package/dist/store/store-snapshot.d.ts +12 -0
  104. package/dist/store/store-snapshot.js +74 -11
  105. package/dist/store/types.d.ts +1 -0
  106. package/dist/store/write-coordinator.d.ts +7 -0
  107. package/dist/store/write-coordinator.js +11 -0
  108. package/dist/todo-state.d.ts +21 -0
  109. package/dist/todo-state.js +150 -53
  110. package/dist/tools-agent.js +86 -22
  111. package/dist/tools-discovery.js +5 -3
  112. package/dist/tools-files.js +107 -23
  113. package/dist/tools-framework.js +146 -40
  114. package/dist/tools-goal.d.ts +11 -0
  115. package/dist/tools-goal.js +108 -0
  116. package/dist/tools-handoff.js +7 -11
  117. package/dist/tools-lifecycle.js +2 -0
  118. package/dist/tools-scheduler.js +44 -1
  119. package/dist/tools-skills.js +3 -2
  120. package/dist/tools-todo.js +64 -3
  121. package/dist/tools.js +2 -0
  122. package/dist/tui/chat.js +4 -0
  123. package/dist/tui/index.js +4 -1
  124. package/dist/tui/ollama.d.ts +2 -0
  125. package/dist/tui/openai-compatible.d.ts +14 -0
  126. package/dist/tui/openai-compatible.js +15 -2
  127. package/dist/tui/provider-discovery.d.ts +1 -0
  128. package/dist/tui/provider-discovery.js +2 -2
  129. package/docs/cli-reference.md +125 -0
  130. package/docs/development.md +46 -0
  131. package/docs/evaluation-safety.md +27 -0
  132. package/docs/experience-playbook.md +35 -0
  133. package/docs/hermes.md +129 -0
  134. package/docs/library-api.md +31 -0
  135. package/docs/mcp-tool-reference.md +290 -0
  136. package/docs/provider-security.md +85 -0
  137. package/docs/state-model.md +113 -0
  138. package/docs/troubleshooting.md +95 -0
  139. package/docs/tui-operator-guide.md +75 -0
  140. package/package.json +5 -1
  141. package/scripts/dev/run_hermes_crispr_live.mjs +212 -0
  142. package/assets/agent-state/MODULES/gates/gate-correctness.json +0 -7
  143. package/assets/agent-state/MODULES/gates/gate-evaluation.json +0 -7
  144. package/assets/agent-state/MODULES/gates/gate-typescript-public-surface.json +0 -7
@@ -3,7 +3,8 @@
3
3
  */
4
4
  import { randomUUID } from "node:crypto";
5
5
  import { z } from "zod";
6
- import { ACE_TASKS_ROOT_REL, normalizePathForValidation, readTaskArtifact, safeWriteAsync, wsPath, WORKSPACE_ROOT, } from "./helpers.js";
6
+ import { ACE_TASKS_ROOT_REL, readTaskArtifact, WORKSPACE_ROOT, } from "./helpers.js";
7
+ import { writeStoreBlobQueued } from "./store/store-artifacts.js";
7
8
  import { ROLE_ENUM, HANDOFF_VALIDATION_MODE, HANDOFF_LIFECYCLE_STATUS_ENUM, } from "./shared.js";
8
9
  import { validateHandoffPayload, validateSwarmHandoffPayload, lintHandoffPayload, } from "./schemas.js";
9
10
  import { acknowledgeHandoffSafe, getHandoffRegistryPath, readHandoffRegistry, registerHandoffSafe, } from "./handoff-registry.js";
@@ -241,13 +242,8 @@ export function registerHandoffTools(server) {
241
242
  ],
242
243
  };
243
244
  }
244
- const timeStr = now
245
- .toISOString()
246
- .replace(/[-:]/g, "")
247
- .replace("T", "-")
248
- .slice(0, 15);
249
- const filePath = wsPath("tasks", `SWARM_HANDOFF.${from}_to_${to}_${timeStr}_${shortId}.json`);
250
- await safeWriteAsync(filePath, JSON.stringify(handoff, null, 2));
245
+ const payloadStoreKey = `state/handoffs/payloads/${handoff.handoff_id}.json`;
246
+ const payloadPath = await writeStoreBlobQueued(WORKSPACE_ROOT, payloadStoreKey, JSON.stringify(handoff, null, 2), { operation_label: "create_handoff.payload" });
251
247
  const registry = await registerHandoffSafe({
252
248
  handoff_id: handoff.handoff_id,
253
249
  from: handoff.router.from,
@@ -255,14 +251,14 @@ export function registerHandoffTools(server) {
255
251
  title: handoff.task.title,
256
252
  task_type: handoff.task.type,
257
253
  priority: handoff.task.priority,
258
- source_file: normalizePathForValidation(filePath),
254
+ source_file: payloadPath,
259
255
  });
260
256
  const runLedger = await appendRunLedgerEntrySafe({
261
257
  tool: "create_handoff",
262
258
  category: "major_update",
263
259
  message: `Created handoff ${handoff.handoff_id} (${handoff.router.from} -> ${handoff.router.to})`,
264
260
  artifacts: [
265
- normalizePathForValidation(filePath),
261
+ payloadPath,
266
262
  "agent-state/handoff-registry.json",
267
263
  ],
268
264
  metadata: {
@@ -282,7 +278,7 @@ export function registerHandoffTools(server) {
282
278
  "# Handoff Created",
283
279
  "",
284
280
  `Schema: ${validation.schema}`,
285
- `Saved to: ${filePath}`,
281
+ `Saved to: ${payloadPath}`,
286
282
  `Handoff registry: ${registry.path}`,
287
283
  `Run ledger: ${runLedger.path} (${runLedger.entry.id})`,
288
284
  `Scheduler job: ${schedulerSync.job_id} (${schedulerSync.enqueued ? "enqueued" : "already_exists"})`,
@@ -194,6 +194,8 @@ export function registerLifecycleTools(server) {
194
194
  "",
195
195
  `JSON: ${snapshot.json_path}`,
196
196
  `HTML: ${snapshot.html_path}`,
197
+ `Read: ace read agent-state/kanban.json`,
198
+ `Open: ace read agent-state/kanban.html --out /tmp/ace-kanban.html && open /tmp/ace-kanban.html`,
197
199
  `Updated: ${snapshot.generated_at}`,
198
200
  `Counts: backlog=${snapshot.counts.backlog}, blocked=${snapshot.counts.blocked}, in_progress=${snapshot.counts.in_progress}, gated=${snapshot.counts.gated}, done=${snapshot.counts.done}`,
199
201
  ].join("\n"),
@@ -2,7 +2,7 @@
2
2
  * Job scheduler tool registrations.
3
3
  */
4
4
  import { z } from "zod";
5
- import { acknowledgeJob, completeJob, dispatchJobNow, dispatchJobs, enqueueJob, getJobLockTablePath, getJobQueuePath, getSchedulerLeasePath, readJobQueue, readJobLockTable, readSchedulerLease, } from "./job-scheduler.js";
5
+ import { acknowledgeJob, completeJob, dispatchJobNow, dispatchJobs, enqueueJob, getJobLockTablePath, getJobQueuePath, getSchedulerLeasePath, heartbeatJob, reapStaleRunningJobsTick, readJobQueue, readJobLockTable, readSchedulerLease, } from "./job-scheduler.js";
6
6
  // Event emission handled in core job-scheduler.ts — no wrapper duplication.
7
7
  const PRIORITY_BAND_ENUM = z.enum(["P0", "P1", "P2", "P3"]);
8
8
  const JOB_STATUS_ENUM = z.enum([
@@ -196,6 +196,49 @@ export function registerSchedulerTools(server) {
196
196
  ],
197
197
  };
198
198
  });
199
+ server.tool("heartbeat_job", "Prove liveness for a running job so the heartbeat watchdog does not reap it; renews the owner lease", {
200
+ job_id: z.string(),
201
+ owner: z.string().optional().describe("Lease owner identity (defaults to capability-ops)"),
202
+ lease_ttl_seconds: z.number().int().min(5).max(3600).optional(),
203
+ now_iso: z.string().optional().describe("Optional deterministic clock override"),
204
+ }, async ({ job_id, owner, lease_ttl_seconds, now_iso }) => {
205
+ const result = await heartbeatJob({ job_id, owner, lease_ttl_seconds, now_iso });
206
+ return {
207
+ content: [
208
+ {
209
+ type: "text",
210
+ text: result.ok
211
+ ? ["✅ Job heartbeat recorded", asJsonBlock(result)].join("\n")
212
+ : ["❌ heartbeat_job failed", result.error ?? "Unknown error", asJsonBlock(result)].join("\n"),
213
+ },
214
+ ],
215
+ };
216
+ });
217
+ server.tool("reap_stale_jobs", "Run the heartbeat watchdog once: move running jobs with a stale worker heartbeat into unknown_recovery and release their locks", {
218
+ heartbeat_stale_seconds: z
219
+ .number()
220
+ .int()
221
+ .min(1)
222
+ .max(86_400)
223
+ .optional()
224
+ .describe("Staleness window in seconds (default 120)"),
225
+ now_iso: z.string().optional().describe("Optional deterministic clock override"),
226
+ }, async ({ heartbeat_stale_seconds, now_iso }) => {
227
+ const result = await reapStaleRunningJobsTick({ heartbeat_stale_seconds, now_iso });
228
+ return {
229
+ content: [
230
+ {
231
+ type: "text",
232
+ text: [
233
+ result.reaped_jobs > 0
234
+ ? `🩺 Watchdog reaped ${result.reaped_jobs} stale job(s)`
235
+ : "🩺 Watchdog found no stale running jobs",
236
+ asJsonBlock(result),
237
+ ].join("\n"),
238
+ },
239
+ ],
240
+ };
241
+ });
199
242
  server.tool("get_lock_table", "Read the scheduler lock table", {}, async () => ({
200
243
  content: [
201
244
  {
@@ -49,9 +49,10 @@ export function registerSkillTools(server) {
49
49
  .describe("Optional workspace-relative path for the audit report"),
50
50
  }, async ({ write_artifact, artifact_path }) => {
51
51
  const result = auditSkillCatalog();
52
+ const artifactPath = artifact_path || "agent-state/SKILL_AUDIT_REPORT.md";
52
53
  const writtenPath = write_artifact === false
53
54
  ? undefined
54
- : writeSkillAuditReport(result, artifact_path || "agent-state/SKILL_AUDIT_REPORT.md");
55
+ : writeSkillAuditReport(result, artifactPath);
55
56
  const failCount = result.entries.filter((entry) => entry.status === "fail").length;
56
57
  const warnCount = result.entries.filter((entry) => entry.status === "warn").length;
57
58
  return {
@@ -62,7 +63,7 @@ export function registerSkillTools(server) {
62
63
  `Portable-contract-ready skills: ${result.summary.portable_contract_ready_skills}/${result.summary.total_skills}`,
63
64
  `Fail/Warn: ${failCount}/${warnCount}`,
64
65
  ...(result.rollout_backlog.slice(0, 5).map((line) => `- ${line}`)),
65
- ...(writtenPath ? [`- Artifact written: ${writtenPath}`] : []),
66
+ ...(writtenPath ? [`- Artifact: ${artifactPath}`, `- Read: ace read ${artifactPath}`, `- Store path: ${writtenPath}`] : []),
66
67
  ].join("\n"),
67
68
  },
68
69
  ],
@@ -4,7 +4,7 @@
4
4
  import { z } from "zod";
5
5
  import { ACE_TASKS_ROOT_REL, readTaskArtifact, resolveWritableTaskPath, safeWriteAsync, } from "./helpers.js";
6
6
  import { TODO_STATUS_ENUM } from "./shared.js";
7
- import { getTodoStatePath, readTodoState, syncTodoStateSafe, transitionTodoNodeSafe, } from "./todo-state.js";
7
+ import { appendTodoItemSafe, getTodoStatePath, readTodoState, syncTodoStateSafe, transitionTodoNodeSafe, } from "./todo-state.js";
8
8
  import { appendRunLedgerEntrySafe, getRunLedgerPath, readRunLedger, } from "./run-ledger.js";
9
9
  import { refreshKanbanArtifacts } from "./kanban.js";
10
10
  export function registerTodoTools(server) {
@@ -19,7 +19,7 @@ export function registerTodoTools(server) {
19
19
  },
20
20
  ],
21
21
  }));
22
- server.tool("update_todo", "Replace TODO board content or transition a TODO node status with gate validation", {
22
+ server.tool("update_todo", "Replace TODO board content, append a new TODO item, or transition a TODO node status with gate validation", {
23
23
  new_content: z
24
24
  .string()
25
25
  .optional()
@@ -30,7 +30,68 @@ export function registerTodoTools(server) {
30
30
  .describe("Todo node id to transition (transition mode)"),
31
31
  status: TODO_STATUS_ENUM.optional().describe("Target status for transition mode"),
32
32
  note: z.string().optional().describe("Optional transition context note"),
33
- }, async ({ new_content, node_id, status, note }) => {
33
+ append_title: z
34
+ .string()
35
+ .optional()
36
+ .describe("Title for a new TODO item to append atomically (append mode). Prefer this over " +
37
+ "new_content when adding a single item — it is safe under concurrent callers, " +
38
+ "unlike a read-then-full-replace round trip."),
39
+ append_section: z
40
+ .string()
41
+ .optional()
42
+ .describe("Section heading to append the new item under (default: Active Work)"),
43
+ append_depends_on: z
44
+ .array(z.string())
45
+ .optional()
46
+ .describe("Node ids the new append-mode item depends on"),
47
+ append_id: z
48
+ .string()
49
+ .optional()
50
+ .describe("Optional explicit node id for the new append-mode item"),
51
+ }, async ({ new_content, node_id, status, note, append_title, append_section, append_depends_on, append_id, }) => {
52
+ if (typeof append_title === "string") {
53
+ const appended = await appendTodoItemSafe(append_title, {
54
+ section: append_section,
55
+ depends_on: append_depends_on,
56
+ id: append_id,
57
+ });
58
+ if (!appended.ok) {
59
+ return {
60
+ content: [
61
+ { type: "text", text: `❌ ${appended.error ?? "Failed to append TODO item."}` },
62
+ ],
63
+ };
64
+ }
65
+ const runLedger = await appendRunLedgerEntrySafe({
66
+ tool: "update_todo",
67
+ category: "info",
68
+ message: `Appended TODO item ${appended.node_id}`,
69
+ artifacts: [`${ACE_TASKS_ROOT_REL}/todo.md`, "agent-state/todo-state.json"],
70
+ metadata: {
71
+ mode: "append",
72
+ node_id: appended.node_id,
73
+ section: appended.section,
74
+ },
75
+ });
76
+ const kanban = refreshKanbanArtifacts("tool:update_todo");
77
+ const counts = kanban.counts;
78
+ return {
79
+ content: [
80
+ {
81
+ type: "text",
82
+ text: [
83
+ `✅ TODO item appended: ${appended.node_id}`,
84
+ `Section: ${appended.section}`,
85
+ `TODO state: ${appended.path}`,
86
+ `TODO markdown: ${appended.todo_path}`,
87
+ `Run ledger: ${runLedger.path} (${runLedger.entry.id})`,
88
+ `Kanban updated: ${kanban.html_path}`,
89
+ `Kanban counts: backlog=${counts.backlog}, blocked=${counts.blocked}, in_progress=${counts.in_progress}, gated=${counts.gated}, done=${counts.done}`,
90
+ ].join("\n"),
91
+ },
92
+ ],
93
+ };
94
+ }
34
95
  if (typeof new_content === "string") {
35
96
  const synced = await syncTodoStateSafe(new_content);
36
97
  const path = await safeWriteAsync(resolveWritableTaskPath("todo"), new_content);
package/dist/tools.js CHANGED
@@ -15,6 +15,7 @@ import { registerGitTools } from "./tools-git.js";
15
15
  import { registerMemoryTools } from "./tools-memory.js";
16
16
  import { registerDriftTools } from "./tools-drift.js";
17
17
  import { registerSchedulerTools } from "./tools-scheduler.js";
18
+ import { registerGoalTools } from "./tools-goal.js";
18
19
  import { registerSkillTools } from "./tools-skills.js";
19
20
  const TOOL_REGISTRARS = [
20
21
  registerAgentTools,
@@ -28,6 +29,7 @@ const TOOL_REGISTRARS = [
28
29
  registerMemoryTools,
29
30
  registerDriftTools,
30
31
  registerSchedulerTools,
32
+ registerGoalTools,
31
33
  registerSkillTools,
32
34
  ];
33
35
  function createAllowlistedServer(server, allowed) {
package/dist/tui/chat.js CHANGED
@@ -243,6 +243,10 @@ export class ChatSession extends EventEmitter {
243
243
  const activeProvider = this.activeStreamProvider ?? this.provider;
244
244
  this.streaming = false;
245
245
  if (this.activeAceBridge) {
246
+ if (this.executionEngine === "hermes_local") {
247
+ this.hermesExecutor?.interrupt?.();
248
+ return;
249
+ }
246
250
  this.aceBridge.interrupt();
247
251
  return;
248
252
  }
package/dist/tui/index.js CHANGED
@@ -190,7 +190,7 @@ export class AceTui {
190
190
  });
191
191
  // Handle signals
192
192
  process.on("SIGINT", () => {
193
- // Ctrl+C during streaming → interrupt; otherwise offer quit
193
+ // Ctrl+C interrupts an active stream; while idle it is deliberately a no-op.
194
194
  if (this.activeChatSession?.isStreaming()) {
195
195
  this.activeChatSession.interrupt();
196
196
  }
@@ -332,6 +332,9 @@ export class AceTui {
332
332
  case "w":
333
333
  this.closeCurrentTab();
334
334
  return;
335
+ case "z":
336
+ this.suspend();
337
+ return;
335
338
  }
336
339
  }
337
340
  // Alt+number → switch tab
@@ -36,6 +36,8 @@ export interface ChatRequest {
36
36
  model: string;
37
37
  messages: ChatMessage[];
38
38
  stream?: boolean;
39
+ /** "json" for generic JSON mode; a JSON Schema object for schema-constrained generation (Ollama ≥ 0.5). */
40
+ format?: "json" | Record<string, unknown> | string;
39
41
  options?: {
40
42
  temperature?: number;
41
43
  top_p?: number;
@@ -15,6 +15,19 @@ export interface OpenAICompatibleChatRequest {
15
15
  messages: OpenAICompatibleChatMessage[];
16
16
  temperature?: number;
17
17
  topP?: number;
18
+ maxTokens?: number;
19
+ responseFormat?: {
20
+ type: "json_object";
21
+ } | {
22
+ type: "json_schema";
23
+ json_schema: {
24
+ name: string;
25
+ schema: Record<string, unknown>;
26
+ strict?: boolean;
27
+ };
28
+ } | {
29
+ type: string;
30
+ };
18
31
  onProviderEvent?: (event: OpenAICompatibleProviderEvent) => void;
19
32
  }
20
33
  export interface OpenAICompatibleChatChunk {
@@ -66,6 +79,7 @@ export declare class OpenAICompatibleClient {
66
79
  private resolveProviderConfig;
67
80
  private streamSseData;
68
81
  }
82
+ export declare const ANTHROPIC_OPENAI_COMPATIBLE_BASE_URL = "https://api.anthropic.com/v1";
69
83
  /**
70
84
  * Resolve a GitHub / Copilot token from the environment or `gh` CLI.
71
85
  * Checks: GITHUB_TOKEN → GH_TOKEN → `gh auth token` (sync exec).
@@ -172,6 +172,8 @@ export class OpenAICompatibleClient {
172
172
  stream: true,
173
173
  temperature: request.temperature,
174
174
  top_p: request.topP,
175
+ max_tokens: request.maxTokens,
176
+ ...(request.responseFormat ? { response_format: request.responseFormat } : {}),
175
177
  };
176
178
  const streamRes = await fetch(`${config.baseUrl}/chat/completions`, {
177
179
  method: "POST",
@@ -263,6 +265,8 @@ export class OpenAICompatibleClient {
263
265
  stream: false,
264
266
  temperature: request.temperature,
265
267
  top_p: request.topP,
268
+ max_tokens: request.maxTokens,
269
+ ...(request.responseFormat ? { response_format: request.responseFormat } : {}),
266
270
  };
267
271
  const res = await fetch(`${config.baseUrl}/chat/completions`, {
268
272
  method: "POST",
@@ -348,6 +352,8 @@ export class OpenAICompatibleClient {
348
352
  stream: true,
349
353
  temperature: request.temperature,
350
354
  top_p: request.topP,
355
+ max_tokens: request.maxTokens,
356
+ ...(request.responseFormat ? { response_format: request.responseFormat } : {}),
351
357
  };
352
358
  // llama.cpp rejects custom stream_options; only include for non-llama providers
353
359
  if (provider !== 'llama.cpp') {
@@ -485,7 +491,10 @@ export class OpenAICompatibleClient {
485
491
  const apiKey = override?.apiKey || providerKey || genericKey || process.env.CODEX_API_KEY?.trim();
486
492
  return { provider, baseUrl, apiKey, headers: override?.headers };
487
493
  }
488
- const resolvedBaseUrl = override?.baseUrl || providerBase || genericBase || "";
494
+ const resolvedBaseUrl = override?.baseUrl ||
495
+ providerBase ||
496
+ genericBase ||
497
+ (provider === "claude" ? ANTHROPIC_OPENAI_COMPATIBLE_BASE_URL : "");
489
498
  const baseUrl = provider === "llama.cpp"
490
499
  ? buildOpenAiCompatibleBaseUrl(resolvedBaseUrl)
491
500
  : stripTrailingSlash(resolvedBaseUrl);
@@ -559,6 +568,7 @@ function providerEnvPrefix(provider) {
559
568
  return provider === "llama.cpp" ? "LLAMA_CPP" : provider.toUpperCase();
560
569
  }
561
570
  const GITHUB_MODELS_BASE_URL = "https://models.github.com/v1";
571
+ export const ANTHROPIC_OPENAI_COMPATIBLE_BASE_URL = "https://api.anthropic.com/v1";
562
572
  /**
563
573
  * Resolve a GitHub / Copilot token from the environment or `gh` CLI.
564
574
  * Checks: GITHUB_TOKEN → GH_TOKEN → `gh auth token` (sync exec).
@@ -612,7 +622,10 @@ export function diagnoseProviderRuntimeConfig(providerInput, override) {
612
622
  message: "Missing API key for provider 'codex'. Set CODEX_API_KEY or OPENAI_API_KEY before sending chat messages.",
613
623
  };
614
624
  }
615
- const baseUrl = stripTrailingSlash(override?.baseUrl || providerBase || genericBase || "");
625
+ const baseUrl = stripTrailingSlash(override?.baseUrl ||
626
+ providerBase ||
627
+ genericBase ||
628
+ (provider === "claude" ? ANTHROPIC_OPENAI_COMPATIBLE_BASE_URL : ""));
616
629
  const apiKey = override?.apiKey || providerKey || genericKey || "";
617
630
  const missing = [];
618
631
  if (!baseUrl) {
@@ -14,6 +14,7 @@
14
14
  */
15
15
  export declare const DEFAULT_OLLAMA_MODEL = "llama3.1:8b";
16
16
  export declare const DEFAULT_LLAMA_CPP_MODEL = "local-model";
17
+ export declare const DEFAULT_HOSTED_MODELS: Readonly<Record<"codex" | "claude" | "gemini" | "copilot", string>>;
17
18
  export declare const LOCAL_LLM_PROVIDERS: readonly ["ollama", "llama.cpp"];
18
19
  export type LocalLlmProvider = (typeof LOCAL_LLM_PROVIDERS)[number];
19
20
  export interface ProviderDiscoveryOptions {
@@ -18,9 +18,9 @@ import { join, resolve } from "node:path";
18
18
  import { readStoreJsonSync } from "../store/store-snapshot.js";
19
19
  export const DEFAULT_OLLAMA_MODEL = "llama3.1:8b";
20
20
  export const DEFAULT_LLAMA_CPP_MODEL = "local-model";
21
- const DEFAULT_HOSTED_MODELS = {
21
+ export const DEFAULT_HOSTED_MODELS = {
22
22
  codex: "gpt-5",
23
- claude: "claude-3-7-sonnet",
23
+ claude: "claude-opus-4-8",
24
24
  gemini: "gemini-2.5-pro",
25
25
  copilot: "copilot/gpt-5-mini",
26
26
  };
@@ -0,0 +1,125 @@
1
+ # CLI Reference
2
+
3
+ > Companion to the [Quick Start in the README](../README.md#quick-start).
4
+
5
+ The bare `ace` commands below assume a global install (`npm install -g @voybio/ace-swarm`); without one, prefix each with `npx -y @voybio/ace-swarm` — e.g. `npx -y @voybio/ace-swarm mcp`.
6
+
7
+ ## Commands
8
+
9
+ ```text
10
+ +--------------------------------------------+-------------------------------------------------------+
11
+ | Command | Purpose |
12
+ +--------------------------------------------+-------------------------------------------------------+
13
+ | `ace mcp [--profile host-min|full]` / `ace serve` | Start MCP server over stdio |
14
+ | `ace mcp-shadow --tools <csv>` | Start filtered shadow MCP server (Hermes use) |
15
+ | `ace tui [options]` | Open the terminal operator surface |
16
+ | `ace init [options]` | Seed the ACE store and optional host stubs |
17
+ | `ace connect [provider] [options]` | Register a live local runtime or hosted provider |
18
+ | `ace turnkey [options]` | Project store-backed host surface stubs |
19
+ | `ace doctor [options]` | Validate ACE runtime + MCP readiness |
20
+ | `ace goal <start|status|run|resume|stop>` | Drive a self-paced, evidence-gated goal loop |
21
+ | `ace ls [prefix] [--keys] [--json]` | List store-backed artifacts by logical path |
22
+ | `ace read <path> [--out f] [--source] [--json] [--prefer disk|store]` | Read a store/workspace file (aliases: get, cat) |
23
+ | `ace fsck [--json] [--repair]` | Validate ACEPACK structure, CRCs, orphans |
24
+ | `ace cache [options]` | DEPRECATED legacy ingest/clean command; it is not an alias for compaction |
25
+ | `ace compact [--no-sweep]` | Compact the store and sweep loose generated artifacts |
26
+ | `ace migrate [store-path]` | Import a legacy file workspace into the store |
27
+ | `ace repair` | Re-materialize host files and repair store assets |
28
+ | `ace add skills [name|all]` | Install packaged skill packs into the store |
29
+ | `ace playbook <list|show|add|tag|gc>` | Inspect and curate the experience playbook |
30
+ | `ace eval [--adapt] [options]` | Run the eval harness / adaptation cycle |
31
+ | `ace mcp-config [--client <name>|--all]` | Print baked client snippets for optional install |
32
+ | `ace preconfig` | Write .mcp-config/ bundle for all clients |
33
+ | `ace paths` | Show resolved package and workspace paths |
34
+ | `ace help` | Show help |
35
+ +--------------------------------------------+-------------------------------------------------------+
36
+ ```
37
+
38
+ ## Examples
39
+
40
+ ```bash
41
+ # Bootstrap a workspace with a hosted provider
42
+ ace init --project "My App" --llm claude --client-config-bundle
43
+
44
+ # Bootstrap with a local Ollama model and write VS Code MCP config
45
+ ace init --project "My App" --llm ollama --model qwen3:8b \
46
+ --base-url http://localhost:11434 --mcp-config
47
+
48
+ # Connect to a running llama.cpp server from a Hugging Face model
49
+ ace connect llama-server --hf bartowski/gemma-3-4b-it-GGUF \
50
+ --base-url http://localhost:8080
51
+
52
+ # Run doctor checks with auto-scan of common local endpoints
53
+ ace doctor --scan
54
+
55
+ # Run doctor checks for a specific provider + model
56
+ ace doctor --llm ollama --model qwen3:8b --base-url http://localhost:11434
57
+
58
+ # Run optional Hermes checks with user-supplied Hermes/Python
59
+ ACE_HERMES_ROOT=/path/to/hermes-agent HERMES_PYTHON=/path/to/python \
60
+ ace doctor --llm llama.cpp --model gemma-3-4b-it --base-url http://localhost:8080 --hermes
61
+
62
+ # Repair a missing Ollama model and re-probe runtime readiness
63
+ ace doctor --llm ollama --model qwen3:8b --repair-ollama
64
+
65
+ # Print VS Code MCP config snippet to stdout
66
+ ace mcp-config --client vscode
67
+
68
+ # Write config bundles for every supported client at workspace root
69
+ ace preconfig
70
+
71
+ # Launch TUI with optional Hermes engine against a local llama.cpp server
72
+ ACE_HERMES_ROOT=/path/to/hermes-agent HERMES_PYTHON=/path/to/python \
73
+ ace tui --provider llama.cpp --base-url http://localhost:8080 --model gemma-3-4b-it --hermes
74
+
75
+ # Compact the store and sweep loose generated artifacts
76
+ ace compact
77
+ ace compact --no-sweep
78
+
79
+ # List and read store-backed artifacts a human otherwise can't see on disk
80
+ ace ls agent-state
81
+ ace read agent-state/kanban.json
82
+ ace read agent-state/STORE_AUTHORITY_AUDIT.md --out /tmp/audit.md
83
+
84
+ # Validate ACEPACK structure/CRCs/orphans, optionally repairing safe issues
85
+ ace fsck
86
+ ace fsck --repair
87
+
88
+ # Start a self-paced goal, watch it, stop it if needed.
89
+ # The objective needs at least one machine-checkable obligation -- a declared
90
+ # artifact or a `verify: <command>` criterion -- or `start` refuses it with
91
+ # `contract_uncheckable` rather than completing on the model's own say-so.
92
+ ace goal start "Ship the auth module. verify: npm test" --budget-hours 2 --detach
93
+ ace goal status
94
+ ace goal stop <goal_id>
95
+
96
+ # Experimental: list and inspect the learned experience playbook
97
+ ace playbook list
98
+ ace playbook show pb-1234abcd
99
+ ace playbook add --section verification --content "Require evidence refs before completion."
100
+ ace playbook add --section failure_modes --reason-code false_completion_no_evidence \
101
+ --content "Require evidence refs before completion."
102
+ ace playbook tag pb-1234abcd helpful
103
+
104
+ # Experimental: run an eval adaptation cycle
105
+ ace eval --adapt --models qwen3:8b,gemma3:4b --epochs 2 \
106
+ --battery ./my-battery.json --out agent-state/eval-reports
107
+ ```
108
+
109
+ Playbook sections are `strategies`, `tool_usage`, `failure_modes`, `verification`,
110
+ and `env_facts`; list statuses are `active`, `quarantined`, and `retired`.
111
+ Lessons from every producer without evidence are stored as `quarantined` and
112
+ are not prompt-injected.
113
+
114
+ ## Selected options
115
+
116
+ - `ace mcp --profile <host-min|full>` selects the visible tool schema; `full` is the default.
117
+ - `ace turnkey --no-client-config-bundle` skips the host-facing config stubs created by default.
118
+ - `ace playbook add --reason-code <code>` scopes a lesson to a matching failure class.
119
+ - `ace eval` accepts `--model`/`--models`, `--battery <path>`, `--out <dir>`, and `--adapt`; `--epochs <n>` applies only to adaptation runs.
120
+ - `ace read` accepts `--source` and `--json` in addition to `--out` and `--prefer`.
121
+
122
+ Eval is non-destructive by default: every task, model, baseline, and adapted
123
+ epoch runs in a new disposable workspace seeded only from the battery's adjacent
124
+ `fixtures/` directory. Expected artifacts are scored inside that workspace and
125
+ recorded by SHA-256 in the scorecard, so stale project files cannot pass a run.
@@ -0,0 +1,46 @@
1
+ # Development
2
+
3
+ > Companion to the [README](../README.md).
4
+
5
+ ```bash
6
+ cd ace-mcp-server
7
+ npm install
8
+ npm run build
9
+ npm test
10
+ npm run start
11
+ ```
12
+
13
+ Run the Hermes live driver from a source checkout. The script is included in the npm package as of 2.4.6, but it imports the checkout/package `dist/` tree and is a diagnostic, not a supported production command. It exits nonzero if setup, protocol, or artifact checks fail.
14
+
15
+ ```bash
16
+ npm run build
17
+ # with a local llama.cpp or Ollama runtime running:
18
+ ACE_WORKSPACE_ROOT=$PWD \
19
+ ACE_HERMES_ROOT=/path/to/hermes-agent \
20
+ HERMES_PYTHON=/path/to/hermes-python \
21
+ ACE_LIVE_PROVIDERS=llama.cpp \
22
+ ACE_LIVE_LLAMA_CPP_BASE_URL=http://127.0.0.1:8080/v1 \
23
+ ACE_LIVE_LLAMA_CPP_MODEL=gemma-3-4b-it \
24
+ ACE_LIVE_HERMES_MAX_TURNS=3 \
25
+ node scripts/dev/run_hermes_crispr_live.mjs
26
+ ```
27
+
28
+ `ACE_LIVE_OUTPUT_ONLY=1` deliberately bypasses tool writing and is only for inspecting raw model output; it cannot satisfy the driver’s evidence-gated job check.
29
+
30
+ ## Key source files
31
+
32
+ | File | Role |
33
+ |---|---|
34
+ | `src/index.ts` | Package entry point |
35
+ | `src/cli.ts` | CLI command dispatch |
36
+ | `src/server.ts` | MCP server setup and stdio transport |
37
+ | `src/tools.ts` | Tool registration hub (delegates to domain modules) |
38
+ | `src/hermes/session-manager.ts` | `HermesSubprocessExecutor` — Hermes subprocess lifecycle |
39
+ | `src/hermes/bridge-protocol.ts` | Frame decoder for the Hermes bridge protocol |
40
+ | `src/model-bridge.ts` | Model bridge interface and process orchestration |
41
+ | `src/goal-engine.ts` | Goal engine — self-paced, evidence-gated objective loop |
42
+ | `src/job-worker.ts` | In-process job worker for the scheduler execution loop |
43
+ | `src/public-surface.ts` | Public surface audit and schema-bound entry map |
44
+ | `src/store/ace-packed-store.ts` | ACEPACK binary store read/write |
45
+ | `src/local-model-policy.ts` | Operator allow/block policy for local models |
46
+ | `scripts/hermes_bridge_worker.py` | Packaged Python worker for the Hermes bridge |
@@ -0,0 +1,27 @@
1
+ # Evaluation Safety and Reproducibility
2
+
3
+ `ace eval` executes tasks that may create and modify files. Packaged battery runs are therefore isolated from the caller's checkout.
4
+
5
+ ## Isolation contract
6
+
7
+ - Each model, epoch, and task receives a fresh temporary workspace.
8
+ - Battery fixtures are copied into that workspace before execution.
9
+ - A task cannot inherit artifacts from an earlier task or epoch.
10
+ - Temporary workspaces are removed after the result and artifact provenance have been recorded.
11
+
12
+ Do not treat an evaluation task as safe merely because its prompt sounds read-only. Review custom batteries before running them and never configure an evaluation workspace to point at a valuable checkout.
13
+
14
+ ## Provenance
15
+
16
+ Evaluation results record the task identity, isolated workspace identity, and hashes for produced artifacts. Hashes establish what bytes were evaluated after the temporary workspace is removed; they do not prove that an artifact is semantically correct. Correctness remains the responsibility of the task verifier and its evidence requirements.
17
+
18
+ For reproducible comparisons, pin the battery, model identifier, provider configuration, ACE version, and relevant environment variables. A provider-backed result may still vary when the remote model or serving stack changes behind a stable name.
19
+
20
+ ## Failure interpretation
21
+
22
+ - A missing required artifact is a failed task, not a zero-score success.
23
+ - An artifact left by a previous task must never satisfy a later task.
24
+ - Provider launch, timeout, and protocol failures are execution failures and must remain distinguishable from verifier failures.
25
+ - Preserve the result record when diagnosing a failure; do not rely on a temporary workspace surviving cleanup.
26
+
27
+ See [CLI Reference](cli-reference.md) for command syntax and [Troubleshooting](troubleshooting.md) for common failure paths.
@@ -0,0 +1,35 @@
1
+ # Experience Playbook (experimental)
2
+
3
+ > Companion to the [README](../README.md). This feature is experimental and off by default for prompt injection.
4
+
5
+ The playbook is an evidence-gated lessons store under `agent-state/playbook/`. Evidence-free lessons from every producer are retained as `quarantined`; only active, scope-matched lessons can be reused across runs.
6
+
7
+ Prompt injection is off by default. Set `ACE_PLAYBOOK=1` to inject active, scope-matched lessons into the stable system prompt block. Deterministic reflection can run at direct-lane run end while injection is off; disable it with `ACE_PLAYBOOK_REFLECT=0`.
8
+
9
+ The playbook is conservative by design. Use `ace eval --adapt` to compare baseline and adapted scorecards for your own models before changing defaults. `agent-state/PLAYBOOK.md` is a derived projection and can be regenerated with `ace playbook list`, `add`, `tag`, or `gc`.
10
+
11
+ The legacy `get_lessons` / `add_lesson` MCP tools read and write playbook bullets.
12
+ `add_lesson` currently creates a quarantined note because its legacy schema has no evidence field.
13
+
14
+ ## CLI
15
+
16
+ ```bash
17
+ # List and inspect lessons
18
+ ace playbook list
19
+ ace playbook show pb-1234abcd
20
+ ace playbook add --section verification --content "Require evidence refs before completion."
21
+ ace playbook tag pb-1234abcd helpful
22
+ ace playbook gc
23
+
24
+ # Run an eval adaptation cycle and compare scorecards
25
+ ace eval --adapt --model qwen3:8b
26
+ ```
27
+
28
+ The packaged battery ships at `assets/eval-battery/battery.json` and is used by
29
+ default; pass `--battery <path>` to run your own. Each task/model/epoch runs in a
30
+ fresh disposable workspace seeded from the `fixtures/` directory adjacent to
31
+ the battery. Scorecards hash the artifacts created in that run; the caller's
32
+ project and stale artifacts are never used for scoring.
33
+
34
+ The packaged fixture includes `fixtures/edit-target.js`, used by both
35
+ `edit-1-single` and `env-4-tool`.