@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.
- package/CHANGELOG.md +78 -0
- package/README.md +72 -532
- package/assets/agent-state/INTERFACE_REGISTRY.md +2 -2
- package/assets/agent-state/MODULES/gates/gate-autonomy.json +4 -2
- package/assets/agent-state/MODULES/gates/gate-completeness.json +4 -2
- package/assets/agent-state/MODULES/gates/gate-operability.json +4 -2
- package/assets/agent-state/MODULES/gates/gate-security.json +3 -1
- package/assets/agent-state/MODULES/registry.json +1 -4
- package/assets/agent-state/MODULES/roles/capability-build.json +1 -2
- package/assets/agent-state/MODULES/roles/capability-eval.json +1 -1
- package/assets/agent-state/MODULES/roles/capability-qa.json +1 -2
- package/assets/agent-state/QUALITY_GATES.md +25 -9
- package/assets/agent-state/TEAL_CONFIG.md +2 -11
- package/assets/eval-battery/battery.json +85 -0
- package/assets/eval-battery/fixtures/edit-target.js +3 -0
- package/assets/scripts/render-mcp-configs.sh +12 -0
- package/dist/ace-context.d.ts +18 -0
- package/dist/ace-context.js +161 -16
- package/dist/ace-server-instructions.js +36 -9
- package/dist/astgrep-index.d.ts +7 -1
- package/dist/astgrep-index.js +66 -39
- package/dist/bridge-trace.d.ts +17 -0
- package/dist/bridge-trace.js +23 -0
- package/dist/cli.d.ts +9 -1
- package/dist/cli.js +619 -10
- package/dist/discovery-runtime-wrappers.d.ts +16 -1
- package/dist/discovery-runtime-wrappers.js +65 -9
- package/dist/eval-harness.d.ts +110 -0
- package/dist/eval-harness.js +254 -0
- package/dist/experience-reflector.d.ts +13 -0
- package/dist/experience-reflector.js +62 -0
- package/dist/gate-contract.d.ts +63 -0
- package/dist/gate-contract.js +178 -0
- package/dist/goal-engine.d.ts +155 -0
- package/dist/goal-engine.js +795 -0
- package/dist/helpers/store-resolution.js +40 -39
- package/dist/hermes/session-manager.d.ts +10 -0
- package/dist/hermes/session-manager.js +130 -27
- package/dist/job-scheduler.d.ts +58 -0
- package/dist/job-scheduler.js +202 -2
- package/dist/job-worker.d.ts +87 -0
- package/dist/job-worker.js +182 -0
- package/dist/local-model-policy.d.ts +8 -2
- package/dist/local-model-policy.js +31 -17
- package/dist/local-model-runtime.d.ts +6 -0
- package/dist/local-model-runtime.js +130 -2
- package/dist/measured-classing.d.ts +66 -0
- package/dist/measured-classing.js +165 -0
- package/dist/model-bridge.d.ts +45 -0
- package/dist/model-bridge.js +502 -44
- package/dist/orchestrator-supervisor.js +1 -1
- package/dist/plan-proposal.d.ts +7 -0
- package/dist/plan-proposal.js +74 -4
- package/dist/playbook-adaptation.d.ts +37 -0
- package/dist/playbook-adaptation.js +159 -0
- package/dist/playbook-curator.d.ts +16 -0
- package/dist/playbook-curator.js +106 -0
- package/dist/playbook-llm-reflector.d.ts +26 -0
- package/dist/playbook-llm-reflector.js +157 -0
- package/dist/public-surface.d.ts +2 -0
- package/dist/public-surface.js +26 -14
- package/dist/runtime-executor.js +45 -2
- package/dist/runtime-tool-specs.d.ts +8 -1
- package/dist/runtime-tool-specs.js +19 -1
- package/dist/safe-edit.d.ts +20 -0
- package/dist/safe-edit.js +355 -78
- package/dist/schemas.js +16 -15
- package/dist/server.d.ts +11 -2
- package/dist/server.js +55 -5
- package/dist/shared.d.ts +12 -0
- package/dist/shared.js +13 -1
- package/dist/source-mutation-coordinator.d.ts +54 -0
- package/dist/source-mutation-coordinator.js +259 -0
- package/dist/status-events.d.ts +4 -1
- package/dist/status-events.js +41 -6
- package/dist/store/ace-packed-store.d.ts +28 -9
- package/dist/store/ace-packed-store.js +324 -57
- package/dist/store/bgzf.d.ts +4 -0
- package/dist/store/bgzf.js +94 -0
- package/dist/store/bootstrap-store.d.ts +4 -1
- package/dist/store/bootstrap-store.js +87 -9
- package/dist/store/cache-workspace.d.ts +8 -0
- package/dist/store/cache-workspace.js +67 -1
- package/dist/store/crc32.d.ts +2 -0
- package/dist/store/crc32.js +16 -0
- package/dist/store/fsck.d.ts +16 -0
- package/dist/store/fsck.js +240 -0
- package/dist/store/gate-contract-migration.d.ts +10 -0
- package/dist/store/gate-contract-migration.js +413 -0
- package/dist/store/materializers/context-snapshot-materializer.d.ts +1 -0
- package/dist/store/materializers/context-snapshot-materializer.js +4 -1
- package/dist/store/materializers/projection-manager.d.ts +7 -0
- package/dist/store/materializers/projection-manager.js +37 -0
- package/dist/store/materializers/scheduler-projection-materializer.js +10 -3
- package/dist/store/repositories/goal-repository.d.ts +112 -0
- package/dist/store/repositories/goal-repository.js +157 -0
- package/dist/store/repositories/playbook-repository.d.ts +107 -0
- package/dist/store/repositories/playbook-repository.js +314 -0
- package/dist/store/store-artifacts.js +44 -2
- package/dist/store/store-authority-audit.js +1 -0
- package/dist/store/store-cli.d.ts +30 -0
- package/dist/store/store-cli.js +242 -0
- package/dist/store/store-snapshot.d.ts +12 -0
- package/dist/store/store-snapshot.js +74 -11
- package/dist/store/types.d.ts +1 -0
- package/dist/store/write-coordinator.d.ts +7 -0
- package/dist/store/write-coordinator.js +11 -0
- package/dist/todo-state.d.ts +21 -0
- package/dist/todo-state.js +150 -53
- package/dist/tools-agent.js +86 -22
- package/dist/tools-discovery.js +5 -3
- package/dist/tools-files.js +107 -23
- package/dist/tools-framework.js +146 -40
- package/dist/tools-goal.d.ts +11 -0
- package/dist/tools-goal.js +108 -0
- package/dist/tools-handoff.js +7 -11
- package/dist/tools-lifecycle.js +2 -0
- package/dist/tools-scheduler.js +44 -1
- package/dist/tools-skills.js +3 -2
- package/dist/tools-todo.js +64 -3
- package/dist/tools.js +2 -0
- package/dist/tui/chat.js +4 -0
- package/dist/tui/index.js +4 -1
- package/dist/tui/ollama.d.ts +2 -0
- package/dist/tui/openai-compatible.d.ts +14 -0
- package/dist/tui/openai-compatible.js +15 -2
- package/dist/tui/provider-discovery.d.ts +1 -0
- package/dist/tui/provider-discovery.js +2 -2
- package/docs/cli-reference.md +125 -0
- package/docs/development.md +46 -0
- package/docs/evaluation-safety.md +27 -0
- package/docs/experience-playbook.md +35 -0
- package/docs/hermes.md +129 -0
- package/docs/library-api.md +31 -0
- package/docs/mcp-tool-reference.md +290 -0
- package/docs/provider-security.md +85 -0
- package/docs/state-model.md +113 -0
- package/docs/troubleshooting.md +95 -0
- package/docs/tui-operator-guide.md +75 -0
- package/package.json +5 -1
- package/scripts/dev/run_hermes_crispr_live.mjs +212 -0
- package/assets/agent-state/MODULES/gates/gate-correctness.json +0 -7
- package/assets/agent-state/MODULES/gates/gate-evaluation.json +0 -7
- package/assets/agent-state/MODULES/gates/gate-typescript-public-surface.json +0 -7
package/dist/todo-state.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
|
-
import { ACE_TASKS_ROOT_REL, resolveWorkspaceRoot, safeRead, safeWrite, wsPath } from "./helpers.js";
|
|
2
|
+
import { ACE_TASKS_ROOT_REL, resolveWorkspaceRoot, resolveWritableTaskPath, safeRead, safeWrite, safeWriteAsync, withFileLock, wsPath, } from "./helpers.js";
|
|
3
3
|
import { openStore } from "./store/ace-packed-store.js";
|
|
4
4
|
import { ProjectionManager } from "./store/materializers/projection-manager.js";
|
|
5
5
|
import { TodoRepository } from "./store/repositories/todo-repository.js";
|
|
@@ -385,6 +385,49 @@ function setTodoCheckbox(todoRaw, line, checked) {
|
|
|
385
385
|
lines[line - 1] = target.replace(/^- \[( |x|X)\]/, `- [${checked ? "x" : " "}]`);
|
|
386
386
|
return lines.join("\n");
|
|
387
387
|
}
|
|
388
|
+
function insertTodoItem(todoRaw, section, title, id, dependsOn) {
|
|
389
|
+
const metaParts = [`id=${id}`];
|
|
390
|
+
if (dependsOn.length > 0)
|
|
391
|
+
metaParts.push(`depends_on=${dependsOn.join(",")}`);
|
|
392
|
+
const newLine = `- [ ] ${title} <!-- ace:${metaParts.join("; ")} -->`;
|
|
393
|
+
const lines = todoRaw.length > 0 ? todoRaw.split(/\r?\n/) : [];
|
|
394
|
+
let sectionStart = -1;
|
|
395
|
+
let sectionEnd = lines.length;
|
|
396
|
+
for (let i = 0; i < lines.length; i++) {
|
|
397
|
+
const header = lines[i]?.match(/^##\s+(.+)$/);
|
|
398
|
+
if (!header?.[1])
|
|
399
|
+
continue;
|
|
400
|
+
if (header[1].trim().toLowerCase() === section.toLowerCase()) {
|
|
401
|
+
sectionStart = i;
|
|
402
|
+
let j = i + 1;
|
|
403
|
+
while (j < lines.length && !/^##\s+/.test(lines[j] ?? ""))
|
|
404
|
+
j++;
|
|
405
|
+
sectionEnd = j;
|
|
406
|
+
break;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
if (sectionStart === -1) {
|
|
410
|
+
const trimmed = todoRaw.replace(/\s+$/, "");
|
|
411
|
+
const prefix = trimmed.length > 0 ? `${trimmed}\n\n` : "";
|
|
412
|
+
return `${prefix}## ${section}\n${newLine}\n`;
|
|
413
|
+
}
|
|
414
|
+
let insertAt = sectionEnd;
|
|
415
|
+
while (insertAt > sectionStart + 1 && (lines[insertAt - 1] ?? "").trim() === "")
|
|
416
|
+
insertAt--;
|
|
417
|
+
lines.splice(insertAt, 0, newLine);
|
|
418
|
+
return lines.join("\n");
|
|
419
|
+
}
|
|
420
|
+
function uniqueTodoId(base, existingIds) {
|
|
421
|
+
if (!existingIds.has(base))
|
|
422
|
+
return base;
|
|
423
|
+
let n = 2;
|
|
424
|
+
let candidate = `${base}-${n}`;
|
|
425
|
+
while (existingIds.has(candidate)) {
|
|
426
|
+
n += 1;
|
|
427
|
+
candidate = `${base}-${n}`;
|
|
428
|
+
}
|
|
429
|
+
return candidate;
|
|
430
|
+
}
|
|
388
431
|
function getTodoPath() {
|
|
389
432
|
return wsPath(TODO_MARKDOWN_REL);
|
|
390
433
|
}
|
|
@@ -401,7 +444,10 @@ export function syncTodoState(todoContent) {
|
|
|
401
444
|
const path = writeState(state);
|
|
402
445
|
return { state, path };
|
|
403
446
|
}
|
|
404
|
-
|
|
447
|
+
// Lock-free core: callers that already hold the TODO_MARKDOWN_REL lock (e.g.
|
|
448
|
+
// appendTodoItemSafe) must call this directly instead of syncTodoStateSafe,
|
|
449
|
+
// since withFileLock is not reentrant and re-acquiring the same key deadlocks.
|
|
450
|
+
async function syncTodoStateLocked(todoContent) {
|
|
405
451
|
const root = workspaceRoot();
|
|
406
452
|
if (storeExistsSync(root)) {
|
|
407
453
|
const todoRaw = typeof todoContent === "string" ? todoContent : safeRead(TODO_MARKDOWN_REL);
|
|
@@ -418,6 +464,9 @@ export async function syncTodoStateSafe(todoContent) {
|
|
|
418
464
|
await waitForTodoStoreMirror();
|
|
419
465
|
return result;
|
|
420
466
|
}
|
|
467
|
+
export async function syncTodoStateSafe(todoContent) {
|
|
468
|
+
return withFileLock(TODO_MARKDOWN_REL, () => syncTodoStateLocked(todoContent));
|
|
469
|
+
}
|
|
421
470
|
export function readTodoState() {
|
|
422
471
|
if (storeExistsSync(workspaceRoot())) {
|
|
423
472
|
return readPersistedState();
|
|
@@ -495,73 +544,121 @@ export function transitionTodoNode(nodeId, targetStatus) {
|
|
|
495
544
|
};
|
|
496
545
|
}
|
|
497
546
|
export async function transitionTodoNodeSafe(nodeId, targetStatus) {
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
ok: false,
|
|
504
|
-
path: getTodoStatePath(),
|
|
505
|
-
todo_path: getTodoPath(),
|
|
506
|
-
error: `${TODO_MARKDOWN_REL} not found; cannot apply transition.`,
|
|
507
|
-
};
|
|
508
|
-
}
|
|
509
|
-
const synced = syncStateWithTodo(todoRaw, readPersistedState());
|
|
510
|
-
const node = synced.nodes[nodeId];
|
|
511
|
-
if (!node) {
|
|
512
|
-
return {
|
|
513
|
-
ok: false,
|
|
514
|
-
path: getTodoStatePath(),
|
|
515
|
-
todo_path: getTodoPath(),
|
|
516
|
-
error: `Unknown node_id: ${nodeId}`,
|
|
517
|
-
};
|
|
518
|
-
}
|
|
519
|
-
const from = node.status;
|
|
520
|
-
if (from !== targetStatus) {
|
|
521
|
-
const allowed = TRANSITIONS[from];
|
|
522
|
-
if (!allowed.includes(targetStatus)) {
|
|
547
|
+
return withFileLock(TODO_MARKDOWN_REL, async () => {
|
|
548
|
+
const root = workspaceRoot();
|
|
549
|
+
if (storeExistsSync(root)) {
|
|
550
|
+
const todoRaw = safeRead(TODO_MARKDOWN_REL);
|
|
551
|
+
if (isReadError(todoRaw)) {
|
|
523
552
|
return {
|
|
524
553
|
ok: false,
|
|
525
554
|
path: getTodoStatePath(),
|
|
526
555
|
todo_path: getTodoPath(),
|
|
527
|
-
|
|
528
|
-
from,
|
|
529
|
-
to: targetStatus,
|
|
530
|
-
allowed,
|
|
531
|
-
error: `Invalid transition ${from} -> ${targetStatus}`,
|
|
556
|
+
error: `${TODO_MARKDOWN_REL} not found; cannot apply transition.`,
|
|
532
557
|
};
|
|
533
558
|
}
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
targetStatus === "done") {
|
|
538
|
-
const unresolved = node.depends_on.filter((depId) => synced.nodes[depId]?.status !== "done");
|
|
539
|
-
if (unresolved.length > 0) {
|
|
559
|
+
const synced = syncStateWithTodo(todoRaw, readPersistedState());
|
|
560
|
+
const node = synced.nodes[nodeId];
|
|
561
|
+
if (!node) {
|
|
540
562
|
return {
|
|
541
563
|
ok: false,
|
|
542
564
|
path: getTodoStatePath(),
|
|
543
565
|
todo_path: getTodoPath(),
|
|
544
|
-
node_id: nodeId
|
|
545
|
-
from,
|
|
546
|
-
to: targetStatus,
|
|
547
|
-
unresolved_dependencies: unresolved,
|
|
548
|
-
error: `Dependencies not satisfied for ${nodeId}`,
|
|
566
|
+
error: `Unknown node_id: ${nodeId}`,
|
|
549
567
|
};
|
|
550
568
|
}
|
|
569
|
+
const from = node.status;
|
|
570
|
+
if (from !== targetStatus) {
|
|
571
|
+
const allowed = TRANSITIONS[from];
|
|
572
|
+
if (!allowed.includes(targetStatus)) {
|
|
573
|
+
return {
|
|
574
|
+
ok: false,
|
|
575
|
+
path: getTodoStatePath(),
|
|
576
|
+
todo_path: getTodoPath(),
|
|
577
|
+
node_id: nodeId,
|
|
578
|
+
from,
|
|
579
|
+
to: targetStatus,
|
|
580
|
+
allowed,
|
|
581
|
+
error: `Invalid transition ${from} -> ${targetStatus}`,
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
if (targetStatus === "in_progress" ||
|
|
586
|
+
targetStatus === "gated" ||
|
|
587
|
+
targetStatus === "done") {
|
|
588
|
+
const unresolved = node.depends_on.filter((depId) => synced.nodes[depId]?.status !== "done");
|
|
589
|
+
if (unresolved.length > 0) {
|
|
590
|
+
return {
|
|
591
|
+
ok: false,
|
|
592
|
+
path: getTodoStatePath(),
|
|
593
|
+
todo_path: getTodoPath(),
|
|
594
|
+
node_id: nodeId,
|
|
595
|
+
from,
|
|
596
|
+
to: targetStatus,
|
|
597
|
+
unresolved_dependencies: unresolved,
|
|
598
|
+
error: `Dependencies not satisfied for ${nodeId}`,
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
node.status = targetStatus;
|
|
603
|
+
const statePath = await writeStateStoreBacked(root, synced);
|
|
604
|
+
return {
|
|
605
|
+
ok: true,
|
|
606
|
+
path: statePath,
|
|
607
|
+
todo_path: getTodoPath(),
|
|
608
|
+
node_id: nodeId,
|
|
609
|
+
from,
|
|
610
|
+
to: targetStatus,
|
|
611
|
+
};
|
|
551
612
|
}
|
|
552
|
-
|
|
553
|
-
|
|
613
|
+
const result = transitionTodoNode(nodeId, targetStatus);
|
|
614
|
+
await waitForTodoStoreMirror();
|
|
615
|
+
return result;
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Atomically append a new TODO item under a section, without requiring the
|
|
620
|
+
* caller to read the whole board and resubmit it via update_todo(new_content).
|
|
621
|
+
* That read-modify-write dance is what let concurrent agents silently clobber
|
|
622
|
+
* each other's appended items: the read and write are separate MCP calls, so
|
|
623
|
+
* a lock around the write alone cannot close the race. Here the read, insert,
|
|
624
|
+
* and write all happen inside one TODO_MARKDOWN_REL lock acquisition.
|
|
625
|
+
*/
|
|
626
|
+
export async function appendTodoItemSafe(title, options) {
|
|
627
|
+
const trimmedTitle = title.trim();
|
|
628
|
+
if (!trimmedTitle) {
|
|
554
629
|
return {
|
|
555
|
-
ok:
|
|
556
|
-
path:
|
|
630
|
+
ok: false,
|
|
631
|
+
path: getTodoStatePath(),
|
|
557
632
|
todo_path: getTodoPath(),
|
|
558
|
-
|
|
559
|
-
from,
|
|
560
|
-
to: targetStatus,
|
|
633
|
+
error: "Todo item title must not be empty.",
|
|
561
634
|
};
|
|
562
635
|
}
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
636
|
+
return withFileLock(TODO_MARKDOWN_REL, async () => {
|
|
637
|
+
const existingRaw = safeRead(TODO_MARKDOWN_REL);
|
|
638
|
+
const baseRaw = isReadError(existingRaw) ? "# Tasks\n" : existingRaw;
|
|
639
|
+
const section = options?.section?.trim() || "Active Work";
|
|
640
|
+
const dependsOn = (options?.depends_on ?? []).map((dep) => dep.trim()).filter(Boolean);
|
|
641
|
+
const existingIds = new Set(parseTodoMarkdown(baseRaw).map((entry) => entry.id));
|
|
642
|
+
const requestedId = options?.id?.trim();
|
|
643
|
+
if (requestedId && existingIds.has(requestedId)) {
|
|
644
|
+
return {
|
|
645
|
+
ok: false,
|
|
646
|
+
path: getTodoStatePath(),
|
|
647
|
+
todo_path: getTodoPath(),
|
|
648
|
+
error: `Todo id already exists: ${requestedId}`,
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
const id = requestedId || uniqueTodoId(`todo-${slugify(trimmedTitle)}`, existingIds);
|
|
652
|
+
const updatedRaw = insertTodoItem(baseRaw, section, trimmedTitle, id, dependsOn);
|
|
653
|
+
const synced = await syncTodoStateLocked(updatedRaw);
|
|
654
|
+
const mdPath = await safeWriteAsync(resolveWritableTaskPath("todo"), updatedRaw);
|
|
655
|
+
return {
|
|
656
|
+
ok: true,
|
|
657
|
+
path: synced.path,
|
|
658
|
+
todo_path: mdPath,
|
|
659
|
+
node_id: id,
|
|
660
|
+
section,
|
|
661
|
+
};
|
|
662
|
+
});
|
|
566
663
|
}
|
|
567
664
|
//# sourceMappingURL=todo-state.js.map
|
package/dist/tools-agent.js
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
* Agent, skill, kernel, and task-pack tool registrations.
|
|
3
3
|
*/
|
|
4
4
|
import { z } from "zod";
|
|
5
|
-
import { ALL_AGENTS, COMPOSABLE_AGENTS, SWARM_AGENTS, SWARM_SUBAGENT_MAP, classifyPathSource, getAgentInstructionPath, getAgentManifestPath, getKernelArtifactPath, isSwarmRole, listAvailableSkills, readAgentInstructions, readAgentManifest, readKernelArtifact, readSkillInstructions, readTaskArtifact, resolveWorkspaceRoot,
|
|
5
|
+
import { ALL_AGENTS, COMPOSABLE_AGENTS, SWARM_AGENTS, SWARM_SUBAGENT_MAP, classifyPathSource, getAgentInstructionPath, getAgentManifestPath, getKernelArtifactPath, isSwarmRole, listAvailableSkills, readAgentInstructions, readAgentManifest, readKernelArtifact, readSkillInstructions, readTaskArtifact, resolveWorkspaceRoot, } from "./helpers.js";
|
|
6
6
|
import { loadRuntimeProfile, readRuntimePromptTemplate, readRuntimeProfileState, validateRuntimeProfileContent, } from "./runtime-profile.js";
|
|
7
7
|
import { getUnattendedSession, listUnattendedSessions, startUnattendedSession, stopUnattendedSession, validateRuntimeExecutorSessionRegistryContent, waitForUnattendedSession, } from "./runtime-executor.js";
|
|
8
|
+
import { materializePlaybookMarkdown, PlaybookRepository, renderPlaybookMarkdown, } from "./store/repositories/playbook-repository.js";
|
|
8
9
|
import { executeRuntimeTool, listRuntimeToolSpecs, loadRuntimeToolRegistry, validateRuntimeToolRegistryContent, } from "./runtime-tool-specs.js";
|
|
9
10
|
import { createWorkspaceSessionAsync, listWorkspaceSessions, removeWorkspaceSession, resolveRuntimeWorkspaceRoot, validateManagedWorkspacePath, } from "./workspace-manager.js";
|
|
10
11
|
import { getTrackerAdapter, listTrackerAdapterKinds, loadTrackerSnapshot, validateTrackerSnapshotContent, } from "./tracker-adapters.js";
|
|
@@ -350,8 +351,9 @@ function createToolOnlyBridgeResult(step, reason) {
|
|
|
350
351
|
return {
|
|
351
352
|
bridge_id: `tool-only-${step.step_id}-${Date.now()}`,
|
|
352
353
|
role: step.role,
|
|
353
|
-
status: "
|
|
354
|
-
|
|
354
|
+
status: "blocked",
|
|
355
|
+
reason_code: "no_model_runtime",
|
|
356
|
+
summary: `Step blocked — no model runtime available for ${step.step_id} (${step.role}): ${step.task}. ${reason} Run \`ace connect\` or \`ace doctor --scan\`, or pass provider/model to resolve.`,
|
|
355
357
|
turns: 0,
|
|
356
358
|
tool_calls: [],
|
|
357
359
|
child_results: [],
|
|
@@ -1369,7 +1371,11 @@ export function registerAgentTools(server) {
|
|
|
1369
1371
|
.string()
|
|
1370
1372
|
.optional()
|
|
1371
1373
|
.describe("Optional workspace root override; defaults to the active workspace"),
|
|
1372
|
-
|
|
1374
|
+
mode: z
|
|
1375
|
+
.enum(["execute", "plan_only"])
|
|
1376
|
+
.optional()
|
|
1377
|
+
.describe("'plan_only' compiles and validates the plan without executing any steps; 'execute' is the default"),
|
|
1378
|
+
}, async ({ task, steps, execution_mode, max_turns_per_step, provider, model, base_url, ollama_url, workspace_root, mode }, extra) => {
|
|
1373
1379
|
let runtime;
|
|
1374
1380
|
const runtimeWarnings = [];
|
|
1375
1381
|
try {
|
|
@@ -1406,6 +1412,32 @@ export function registerAgentTools(server) {
|
|
|
1406
1412
|
const bridge = runtime
|
|
1407
1413
|
? new ModelBridge(createDefaultModelBridgeClients(runtime))
|
|
1408
1414
|
: undefined;
|
|
1415
|
+
// plan_only mode: return the compiled plan without executing anything
|
|
1416
|
+
if (mode === "plan_only" || (!runtime && runtimeWarnings.length > 0)) {
|
|
1417
|
+
const planOnly = mode === "plan_only";
|
|
1418
|
+
const reasonCode = planOnly ? "plan_only_requested" : "no_model_runtime";
|
|
1419
|
+
return {
|
|
1420
|
+
content: [
|
|
1421
|
+
{
|
|
1422
|
+
type: "text",
|
|
1423
|
+
text: JSON.stringify({
|
|
1424
|
+
ok: planOnly,
|
|
1425
|
+
reason_code: reasonCode,
|
|
1426
|
+
execution_backend: "none",
|
|
1427
|
+
mode: planOnly ? "plan_only" : "execute",
|
|
1428
|
+
plan,
|
|
1429
|
+
runtime: runtime ?? null,
|
|
1430
|
+
runtime_warnings: runtimeWarnings,
|
|
1431
|
+
remediation: !runtime
|
|
1432
|
+
? "Run `ace connect` or `ace doctor --scan`, or pass provider/model. Use mode:\"plan_only\" explicitly to compile a plan without a runtime."
|
|
1433
|
+
: undefined,
|
|
1434
|
+
acceptance_trace_map: traceArtifactPath,
|
|
1435
|
+
plan_source: planSource,
|
|
1436
|
+
}, null, 2),
|
|
1437
|
+
},
|
|
1438
|
+
],
|
|
1439
|
+
};
|
|
1440
|
+
}
|
|
1409
1441
|
const fallbackHandoffPrefix = `LOCAL-${plan.plan_id}-`;
|
|
1410
1442
|
const vericifyWarnings = [];
|
|
1411
1443
|
const appendSessionPlanTransition = async (stepId, from, to, reason, reasonCode) => {
|
|
@@ -1426,6 +1458,22 @@ export function registerAgentTools(server) {
|
|
|
1426
1458
|
evidence_refs: step.tool_scope ?? [],
|
|
1427
1459
|
});
|
|
1428
1460
|
}).catch(() => undefined);
|
|
1461
|
+
// WS-C item 5: emit a PLAN_UPDATE payload on the existing status-event pipe so
|
|
1462
|
+
// consumers can follow "exactly one item in_progress" without a new transport.
|
|
1463
|
+
const inProgressStep = plan.steps.find((candidate) => candidate.status === "running");
|
|
1464
|
+
await executeAceInternalTool("emit_status_event", {
|
|
1465
|
+
source_module: "capability-ops",
|
|
1466
|
+
event_type: "PLAN_UPDATE",
|
|
1467
|
+
status: to === "done" ? "done" : to === "blocked" || to === "failed" ? "blocked" : "in_progress",
|
|
1468
|
+
summary: `Plan ${plan.plan_id} step ${stepId}: ${from} -> ${to}`,
|
|
1469
|
+
payload_json: JSON.stringify({
|
|
1470
|
+
plan_id: plan.plan_id,
|
|
1471
|
+
step_id: stepId,
|
|
1472
|
+
from,
|
|
1473
|
+
to,
|
|
1474
|
+
in_progress_step_id: inProgressStep?.step_id ?? null,
|
|
1475
|
+
}),
|
|
1476
|
+
}, sessionId).catch(() => undefined);
|
|
1429
1477
|
};
|
|
1430
1478
|
const supervised = await superviseTaskPlan(plan, {
|
|
1431
1479
|
async spawnStep(step) {
|
|
@@ -1439,6 +1487,8 @@ export function registerAgentTools(server) {
|
|
|
1439
1487
|
provider: runtime.provider,
|
|
1440
1488
|
model: runtime.model,
|
|
1441
1489
|
toolScope: step.tool_scope,
|
|
1490
|
+
expectedArtifacts: step.expected_artifacts,
|
|
1491
|
+
requiredEvidenceRefs: step.required_evidence_refs,
|
|
1442
1492
|
});
|
|
1443
1493
|
}
|
|
1444
1494
|
const reason = runtimeWarnings[0] ??
|
|
@@ -2287,31 +2337,45 @@ export function registerAgentTools(server) {
|
|
|
2287
2337
|
};
|
|
2288
2338
|
});
|
|
2289
2339
|
// ── Lessons ───────────────────────────────────────────────────────
|
|
2290
|
-
server.tool("get_lessons", "Read all accumulated lessons and guardrails", {}, async () =>
|
|
2291
|
-
|
|
2292
|
-
|
|
2340
|
+
server.tool("get_lessons", "Read all accumulated playbook lessons and guardrails", {}, async () => {
|
|
2341
|
+
const root = resolveWorkspaceRoot();
|
|
2342
|
+
const repo = new PlaybookRepository(root);
|
|
2343
|
+
const path = materializePlaybookMarkdown(root);
|
|
2344
|
+
return {
|
|
2345
|
+
content: [{ type: "text", text: `${renderPlaybookMarkdown(repo)}\nProjection: ${path}` }],
|
|
2346
|
+
};
|
|
2347
|
+
});
|
|
2293
2348
|
server.tool("add_lesson", "Record a new lesson/guardrail after a correction or failure", {
|
|
2294
2349
|
context: z.string().describe("What happened"),
|
|
2295
2350
|
failure_pattern: z.string().describe("What went wrong"),
|
|
2296
2351
|
guardrail: z.string().describe("Rule to prevent recurrence"),
|
|
2297
2352
|
verification_check: z.string().describe("How to verify compliance"),
|
|
2298
2353
|
}, async ({ context, failure_pattern, guardrail, verification_check }) => {
|
|
2299
|
-
const
|
|
2300
|
-
|
|
2301
|
-
"",
|
|
2302
|
-
|
|
2303
|
-
"",
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2354
|
+
const knownReasonCodes = [
|
|
2355
|
+
"false_completion_no_evidence",
|
|
2356
|
+
"forward_pacing_incomplete",
|
|
2357
|
+
"bridge_tool_doom_loop",
|
|
2358
|
+
"bridge_text_doom_loop",
|
|
2359
|
+
"context_overflow",
|
|
2360
|
+
"tool_timeout",
|
|
2361
|
+
"role_contract_violation",
|
|
2362
|
+
];
|
|
2363
|
+
const scope = {};
|
|
2364
|
+
const lowered = failure_pattern.toLowerCase();
|
|
2365
|
+
const reason = knownReasonCodes.find((code) => lowered.includes(code));
|
|
2366
|
+
if (reason)
|
|
2367
|
+
scope.reason_code = reason;
|
|
2368
|
+
const root = resolveWorkspaceRoot();
|
|
2369
|
+
const bullet = new PlaybookRepository(root).createBullet({
|
|
2370
|
+
section: "failure_modes",
|
|
2371
|
+
scope,
|
|
2372
|
+
content: `${guardrail} Verify: ${verification_check}`,
|
|
2373
|
+
evidence_refs: [],
|
|
2374
|
+
provenance: { source: "operator" },
|
|
2375
|
+
});
|
|
2376
|
+
const path = materializePlaybookMarkdown(root);
|
|
2313
2377
|
return {
|
|
2314
|
-
content: [{ type: "text", text: `✅ Lesson recorded in ${path}` }],
|
|
2378
|
+
content: [{ type: "text", text: `✅ Lesson recorded as ${bullet.bullet_id} (${bullet.status}) in ${path}. Context: ${context}` }],
|
|
2315
2379
|
};
|
|
2316
2380
|
});
|
|
2317
2381
|
// ── CLI Work Split ────────────────────────────────────────────────
|
package/dist/tools-discovery.js
CHANGED
|
@@ -217,7 +217,7 @@ export function registerDiscoveryTools(server) {
|
|
|
217
217
|
},
|
|
218
218
|
],
|
|
219
219
|
}));
|
|
220
|
-
server.tool("mcp_ingestion_probe", "
|
|
220
|
+
server.tool("mcp_ingestion_probe", "Connect to an external MCP server with a bounded initialize handshake, list tools/resources/templates/prompts, and persist the probe artifacts", {
|
|
221
221
|
name: z.string().min(1).describe("Logical MCP server name"),
|
|
222
222
|
transport: z.enum(["stdio", "http"]).describe("MCP transport class"),
|
|
223
223
|
command: z.string().optional().describe("Command for stdio MCP servers"),
|
|
@@ -231,8 +231,9 @@ export function registerDiscoveryTools(server) {
|
|
|
231
231
|
input_schema: z.unknown().optional(),
|
|
232
232
|
}))
|
|
233
233
|
.optional()
|
|
234
|
-
.describe("
|
|
235
|
-
|
|
234
|
+
.describe("Deprecated compatibility input; live server catalogs are authoritative"),
|
|
235
|
+
timeout_ms: z.number().int().min(250).max(60000).optional().describe("Bound for connect and each catalog request (default 10000ms)"),
|
|
236
|
+
}, async ({ name, transport, command, args, url, env, declared_tools, timeout_ms }) => ({
|
|
236
237
|
content: [
|
|
237
238
|
{
|
|
238
239
|
type: "text",
|
|
@@ -244,6 +245,7 @@ export function registerDiscoveryTools(server) {
|
|
|
244
245
|
url,
|
|
245
246
|
env,
|
|
246
247
|
declared_tools,
|
|
248
|
+
timeout_ms,
|
|
247
249
|
}), null, 2),
|
|
248
250
|
},
|
|
249
251
|
],
|