@voybio/ace-swarm 2.4.8 → 2.4.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (187) hide show
  1. package/CHANGELOG.md +52 -14
  2. package/README.md +14 -10
  3. package/assets/.agents/ACE/ACE_coders/AGENTS.md +23 -30
  4. package/assets/.agents/ACE/ACE_coders/INSTRUCTIONS.md +21 -30
  5. package/assets/.agents/skills/landing-review-watcher/SKILL.md +1 -1
  6. package/assets/.agents/skills/learn-from-work/SKILL.md +41 -0
  7. package/assets/agent-state/ACE_WORKFLOW.md +25 -2
  8. package/assets/agent-state/MODULES/schemas/ACE_RUNTIME_PROFILE.schema.json +143 -2
  9. package/assets/agent-state/MODULES/schemas/RUNTIME_EXECUTOR_SESSION_REGISTRY.schema.json +2 -2
  10. package/assets/agent-state/MODULES/schemas/SWARM_HANDOFF.schema.json +10 -0
  11. package/assets/eval-battery/battery.json +1 -1
  12. package/assets/instructions/ACE_Coder.instructions.md +9 -13
  13. package/assets/scripts/ace-hook-dispatch.mjs +16 -411
  14. package/assets/tasks/SWARM_HANDOFF.example_ui_to_coders.json +1 -2
  15. package/assets/vendor/README.md +9 -0
  16. package/assets/vendor/vericify-reader.mjs +272 -0
  17. package/assets/vendor/vericify-reader.provenance.json +10 -0
  18. package/dist/ace-autonomy.d.ts +58 -0
  19. package/dist/ace-autonomy.js +211 -6
  20. package/dist/ace-internal-tools.d.ts +1 -0
  21. package/dist/ace-internal-tools.js +3 -3
  22. package/dist/ace-server-instructions.d.ts +3 -3
  23. package/dist/ace-server-instructions.js +46 -192
  24. package/dist/cli.js +44 -6
  25. package/dist/experience-reflector.d.ts +7 -0
  26. package/dist/experience-reflector.js +39 -0
  27. package/dist/goal-engine.d.ts +64 -7
  28. package/dist/goal-engine.js +197 -41
  29. package/dist/handoff-registry.d.ts +10 -0
  30. package/dist/handoff-registry.js +59 -0
  31. package/dist/helpers/artifacts.d.ts +6 -2
  32. package/dist/helpers/artifacts.js +8 -6
  33. package/dist/helpers/bootstrap.js +13 -20
  34. package/dist/helpers/path-utils.js +2 -0
  35. package/dist/helpers/store-resolution.js +18 -3
  36. package/dist/helpers/workspace-root.d.ts +2 -0
  37. package/dist/helpers/workspace-root.js +9 -0
  38. package/dist/helpers.d.ts +1 -0
  39. package/dist/helpers.js +1 -0
  40. package/dist/hermes/bridge-protocol.d.ts +1 -1
  41. package/dist/hermes/session-manager.d.ts +17 -0
  42. package/dist/hermes/session-manager.js +67 -14
  43. package/dist/host-goal-engine.d.ts +138 -0
  44. package/dist/host-goal-engine.js +793 -0
  45. package/dist/host-goal-status.d.ts +113 -0
  46. package/dist/host-goal-status.js +175 -0
  47. package/dist/host-learning.d.ts +68 -0
  48. package/dist/host-learning.js +115 -0
  49. package/dist/host-observation.d.ts +12 -0
  50. package/dist/host-observation.js +91 -0
  51. package/dist/host-plan-revision.d.ts +14 -0
  52. package/dist/host-plan-revision.js +101 -0
  53. package/dist/host-verification-job.d.ts +12 -0
  54. package/dist/host-verification-job.js +183 -0
  55. package/dist/host-work-context.d.ts +12 -0
  56. package/dist/host-work-context.js +8 -0
  57. package/dist/job-scheduler.d.ts +58 -13
  58. package/dist/job-scheduler.js +298 -48
  59. package/dist/job-worker.d.ts +3 -1
  60. package/dist/job-worker.js +10 -3
  61. package/dist/kanban.d.ts +4 -1
  62. package/dist/kanban.js +44 -16
  63. package/dist/learned-artifacts.d.ts +64 -0
  64. package/dist/learned-artifacts.js +321 -0
  65. package/dist/learning-controls.d.ts +142 -0
  66. package/dist/learning-controls.js +135 -0
  67. package/dist/learning-input-context.d.ts +9 -0
  68. package/dist/learning-input-context.js +62 -0
  69. package/dist/learning-lifecycle.d.ts +10 -0
  70. package/dist/learning-lifecycle.js +11 -0
  71. package/dist/learning-overlays.d.ts +3 -0
  72. package/dist/learning-overlays.js +17 -0
  73. package/dist/learning-policy.d.ts +40 -0
  74. package/dist/learning-policy.js +38 -0
  75. package/dist/learning-records.d.ts +35 -0
  76. package/dist/learning-records.js +17 -0
  77. package/dist/learning-scorecard.d.ts +82 -0
  78. package/dist/learning-scorecard.js +86 -0
  79. package/dist/learning-serving.d.ts +29 -0
  80. package/dist/learning-serving.js +134 -0
  81. package/dist/linked-work.d.ts +9 -0
  82. package/dist/linked-work.js +28 -0
  83. package/dist/local-model-runtime.js +1 -1
  84. package/dist/mcp-command-progress.d.ts +16 -0
  85. package/dist/mcp-command-progress.js +70 -0
  86. package/dist/model-bridge.d.ts +14 -13
  87. package/dist/model-bridge.js +73 -41
  88. package/dist/orchestrator-supervisor.d.ts +111 -6
  89. package/dist/orchestrator-supervisor.js +67 -43
  90. package/dist/plan-proposal.js +3 -3
  91. package/dist/playbook-curator.d.ts +1 -0
  92. package/dist/playbook-curator.js +11 -9
  93. package/dist/process-identity.d.ts +8 -0
  94. package/dist/process-identity.js +38 -0
  95. package/dist/progress-command.d.ts +34 -0
  96. package/dist/progress-command.js +153 -0
  97. package/dist/prompts.js +15 -15
  98. package/dist/resources.js +2 -2
  99. package/dist/runtime-command.d.ts +8 -1
  100. package/dist/runtime-command.js +48 -7
  101. package/dist/runtime-executor.js +33 -11
  102. package/dist/runtime-profile-syntax.d.ts +6 -0
  103. package/dist/runtime-profile-syntax.js +121 -0
  104. package/dist/runtime-profile.d.ts +37 -5
  105. package/dist/runtime-profile.js +36 -129
  106. package/dist/runtime-tool-specs.js +1 -1
  107. package/dist/safe-edit.d.ts +7 -3
  108. package/dist/safe-edit.js +40 -47
  109. package/dist/schemas.js +4 -2
  110. package/dist/server.d.ts +2 -16
  111. package/dist/server.js +14 -20
  112. package/dist/shared.d.ts +2 -0
  113. package/dist/shared.js +2 -0
  114. package/dist/skill-auditor.d.ts +19 -0
  115. package/dist/skill-auditor.js +32 -2
  116. package/dist/skill-catalog.d.ts +4 -1
  117. package/dist/skill-catalog.js +6 -5
  118. package/dist/store/ace-packed-store.d.ts +5 -1
  119. package/dist/store/ace-packed-store.js +33 -1
  120. package/dist/store/bootstrap-store.js +19 -12
  121. package/dist/store/cache-workspace.d.ts +8 -0
  122. package/dist/store/cache-workspace.js +94 -27
  123. package/dist/store/importer.js +11 -20
  124. package/dist/store/knowledge-bake.d.ts +3 -1
  125. package/dist/store/knowledge-bake.js +10 -1
  126. package/dist/store/legacy-vericify-posts.d.ts +8 -0
  127. package/dist/store/legacy-vericify-posts.js +76 -0
  128. package/dist/store/materializers/hook-context-materializer.d.ts +2 -1
  129. package/dist/store/materializers/hook-context-materializer.js +6 -2
  130. package/dist/store/materializers/host-file-materializer.js +2 -2
  131. package/dist/store/materializers/projection-manager.d.ts +4 -1
  132. package/dist/store/materializers/projection-manager.js +10 -3
  133. package/dist/store/materializers/todo-syncer.d.ts +39 -20
  134. package/dist/store/materializers/todo-syncer.js +208 -110
  135. package/dist/store/materializers/vericify-projector.js +15 -26
  136. package/dist/store/repositories/goal-repository.d.ts +51 -2
  137. package/dist/store/repositories/goal-repository.js +114 -48
  138. package/dist/store/repositories/playbook-repository.d.ts +41 -3
  139. package/dist/store/repositories/playbook-repository.js +61 -5
  140. package/dist/store/repositories/todo-repository.d.ts +8 -2
  141. package/dist/store/repositories/todo-repository.js +1 -1
  142. package/dist/store/repositories/vericify-repository.d.ts +4 -0
  143. package/dist/store/repositories/vericify-repository.js +46 -0
  144. package/dist/store/skills-install.d.ts +1 -0
  145. package/dist/store/skills-install.js +20 -16
  146. package/dist/store/source-preservation.d.ts +24 -0
  147. package/dist/store/source-preservation.js +79 -0
  148. package/dist/store/store-artifacts.js +8 -1
  149. package/dist/store/test-write-boundary.d.ts +16 -0
  150. package/dist/store/test-write-boundary.js +175 -0
  151. package/dist/store/types.d.ts +5 -1
  152. package/dist/store/write-coordinator.js +16 -0
  153. package/dist/task-plan-context.d.ts +11 -0
  154. package/dist/task-plan-context.js +59 -0
  155. package/dist/todo-state.d.ts +19 -9
  156. package/dist/todo-state.js +204 -321
  157. package/dist/tools-agent.d.ts +1 -1
  158. package/dist/tools-agent.js +38 -62
  159. package/dist/tools-files.d.ts +0 -3
  160. package/dist/tools-files.js +21 -11
  161. package/dist/tools-framework.js +16 -25
  162. package/dist/tools-goal.d.ts +2 -4
  163. package/dist/tools-goal.js +119 -16
  164. package/dist/tools-handoff.d.ts +3 -2
  165. package/dist/tools-handoff.js +53 -23
  166. package/dist/tools-lifecycle.js +1 -1
  167. package/dist/tools-memory.d.ts +0 -3
  168. package/dist/tools-memory.js +51 -6
  169. package/dist/tools-scheduler.js +20 -17
  170. package/dist/tools-todo.js +160 -148
  171. package/dist/vericify-bridge.d.ts +8 -0
  172. package/dist/vericify-bridge.js +18 -0
  173. package/dist/vericify-context.d.ts +4 -0
  174. package/dist/vericify-context.js +26 -5
  175. package/dist/vericify-native.d.ts +101 -0
  176. package/dist/vericify-native.js +221 -0
  177. package/dist/verification-worker.d.ts +2 -0
  178. package/dist/verification-worker.js +31 -0
  179. package/docs/mcp-tool-reference.md +50 -10
  180. package/docs/troubleshooting.md +29 -0
  181. package/package.json +19 -6
  182. package/scripts/dev/run-tests-isolated.mjs +165 -0
  183. package/scripts/dev/run_hermes_crispr_live.mjs +1 -0
  184. package/scripts/dev/test-isolation-guard.mjs +20 -0
  185. package/scripts/dev/test-preload.mjs +153 -0
  186. package/scripts/dev/test-source-inventory.mjs +31 -0
  187. package/docs/development.md +0 -46
@@ -0,0 +1,86 @@
1
+ import { learningScopeKey } from './learning-policy.js';
2
+ const ratio = (n, d) => d ? n / d : null;
3
+ const phase = () => ({ done: 0, steps: 0, rate: null });
4
+ function match(scope, e) {
5
+ return (scope.role === e.role ? 2 : 0) +
6
+ (scope.paths?.some(p => e.paths.some(q => q === p || q.startsWith(p + '/') || p.startsWith(q + '/'))) ? 3 : 0) +
7
+ (scope.task_tags?.filter(t => e.tags.includes(t)).length ?? 0) +
8
+ (scope.failure_signature && scope.failure_signature === e.failure_signature ? 5 : 0);
9
+ }
10
+ export function buildLearningScorecard(steps, episodes, lessons, artifacts, now = Date.now()) {
11
+ const skills = artifacts.filter(a => a.kind === 'skill');
12
+ const known = [...lessons, ...skills];
13
+ const byId = new Map(known.map(a => ['bullet_id' in a ? a.bullet_id : a.id, a]));
14
+ const work = new Map(steps.map(s => [`${s.goal}:${s.step.step_id}`, s.step]));
15
+ const signatures = new Set(), recurring = new Set();
16
+ const servedSizes = [];
17
+ let missingContext = 0;
18
+ for (const ep of episodes) {
19
+ const step = work.get(`${ep.goal_id}:${ep.step_id}`);
20
+ const attempt = ep.attempt ?? Number(ep.id.split(':').at(-1));
21
+ const fallback = step && step.attempt === attempt && step.served_learning ? {
22
+ items: step.served_learning.items,
23
+ rendered_bytes: Buffer.byteLength(step.served_learning.text),
24
+ } : undefined;
25
+ const context = ep.served_context ?? fallback;
26
+ if (!context) {
27
+ if (ep.served?.length)
28
+ missingContext++;
29
+ continue;
30
+ }
31
+ if (context.items.length)
32
+ servedSizes.push({ episode_id: ep.id, utf8_bytes: context.rendered_bytes, token_upper_bound: context.rendered_bytes });
33
+ for (const item of context.items.filter(i => i.kind === 'lesson' && i.scope.failure_signature)) {
34
+ signatures.add(item.scope.failure_signature);
35
+ if (ep.failure_signature === item.scope.failure_signature)
36
+ recurring.add(item.scope.failure_signature);
37
+ }
38
+ }
39
+ const scopes = new Map();
40
+ let missingFirst = 0, reconstructedScopes = 0;
41
+ const episodeMap = new Map(episodes.map(e => [`${e.goal_id}:${e.step_id}:${e.attempt ?? Number(e.id.split(':').at(-1))}`, e]));
42
+ for (const { goal, step } of steps) {
43
+ const first = episodeMap.get(`${goal}:${step.step_id}:1`);
44
+ if (!first) {
45
+ missingFirst++;
46
+ continue;
47
+ }
48
+ if (!first.scope_snapshot)
49
+ reconstructedScopes++;
50
+ const key = learningScopeKey({ role: first.role, paths: first.paths, task_tags: first.tags });
51
+ const group = scopes.get(key) ?? { scope: key, before: phase(), after: phase(), unclassified: phase(), unexposed: phase() };
52
+ const relevant = known.filter(a => a.ever_active !== false && (a.status === 'active' || a.ever_active) && match(a.scope, first) >= 3);
53
+ const dates = relevant.flatMap(a => a.activated_at && Number.isFinite(Date.parse(a.activated_at)) ? [Date.parse(a.activated_at)] : []);
54
+ const kind = !Number.isFinite(Date.parse(first.at)) ? 'unclassified' : !relevant.length ? 'unexposed' : dates.length !== relevant.length ? 'unclassified' : Date.parse(first.at) < Math.min(...dates) ? 'before' : 'after';
55
+ group[kind].steps++;
56
+ if (first.status === 'done')
57
+ group[kind].done++;
58
+ for (const p of [group.before, group.after, group.unclassified, group.unexposed])
59
+ p.rate = ratio(p.done, p.steps);
60
+ scopes.set(key, group);
61
+ }
62
+ const learning = steps.filter(({ step }) => !!step.learning_scope);
63
+ const submissions = steps.filter(({ step }) => !!step.learning_payload && !!step.learning_result);
64
+ const checked = submissions.filter(({ step }) => step.learning_result.ok);
65
+ const helpful = known.reduce((n, a) => n + a.helpful_count, 0), harmful = known.reduce((n, a) => n + a.harmful_count, 0);
66
+ const since = now - 30 * 86400000;
67
+ const recentActive = known.filter(a => a.status === 'active' || (a.ever_active && !!a.last_active_at && Date.parse(a.last_active_at) >= since));
68
+ const retired = recentActive.filter(a => a.status === 'retired' && !!a.retired_at && Date.parse(a.retired_at) >= since);
69
+ const unknownHistory = known.filter(a => a.status !== 'active' && (a.ever_active === undefined || (a.ever_active && !a.last_active_at)));
70
+ return {
71
+ active_learned_artifacts: { lessons: lessons.filter(a => a.status === 'active').length, skills: skills.filter(a => a.status === 'active').length, approved_instructions: artifacts.filter(a => a.kind === 'instruction' && a.status === 'active').length },
72
+ repeat_failure_rate: { signatures_served: signatures.size, signatures_recurred: recurring.size, rate: ratio(recurring.size, signatures.size), missing_context_attempts: missingContext, interpretation: 'Recurrence after serving is correlational; it is not proof the item was used or caused the outcome.' },
73
+ first_attempt_success_by_scope: [...scopes.values()],
74
+ first_attempt_coverage: { missing_first_attempts: missingFirst, reconstructed_scopes: reconstructedScopes, interpretation: 'Before/after uses known activation timestamps and retained first attempts. Unknown activation history stays unclassified.' },
75
+ served_helpfulness: { helpful, harmful, ratio: ratio(helpful, helpful + harmful), counter_scope: 'Existing artifact counters; legacy producer history is not retrospectively re-attributed.' },
76
+ distill_acceptance: { checked: checked.length, submitted: submissions.length, rate: ratio(checked.length, submissions.length), demonstrated_current_artifacts: known.filter(a => a.status === 'active' && a.label === 'demonstrated').length },
77
+ learning_overhead: { learning_steps: learning.length, all_steps: steps.length, ratio: ratio(learning.length, steps.length), opportunistic_payload_steps: submissions.filter(({ step }) => !step.learning_scope).length, assignments: servedSizes, max_served_token_upper_bound: servedSizes.length ? Math.max(...servedSizes.map(s => s.token_upper_bound)) : null, token_method: 'UTF-8 byte count is a conservative upper bound; no provider tokenizer count is claimed.' },
78
+ retirement_health: { window_days: 30, retired: retired.length, ever_active: recentActive.length, ratio: ratio(retired.length, recentActive.length), unknown_history: unknownHistory.length },
79
+ distillation_steps: submissions.map(({ goal, step }) => {
80
+ const eligible = (step.learning_result.ids ?? []).flatMap(id => { const a = byId.get(id); return a && (a.served_count ?? 0) >= 20 && a.helpful_count + a.harmful_count > 0 ? [a.helpful_count / (a.helpful_count + a.harmful_count)] : []; });
81
+ return { goal_id: goal, step_id: step.step_id, checked: step.learning_result.ok, items_after_20_serves: eligible.length, mean_strong_helpfulness: eligible.length ? eligible.reduce((sum, v) => sum + v, 0) / eligible.length : null };
82
+ }),
83
+ coverage: 'Retained plans, pinned episodes and existing artifacts only; missing history and insufficient samples are not treated as success.',
84
+ };
85
+ }
86
+ //# sourceMappingURL=learning-scorecard.js.map
@@ -0,0 +1,29 @@
1
+ /** Match learning at assignment time; score only consequence with evidence of use. */
2
+ import type { TaskPlan, TaskStep } from './orchestrator-supervisor.js';
3
+ import type { GoalRecord } from './store/repositories/goal-repository.js';
4
+ import { type PlaybookScope } from './store/repositories/playbook-repository.js';
5
+ import type { StepOutcome } from './host-goal-engine.js';
6
+ export interface ServedLearningItem {
7
+ id: string;
8
+ kind: 'lesson' | 'skill' | 'packaged';
9
+ scope: PlaybookScope;
10
+ text: string;
11
+ check?: string;
12
+ checks?: string[];
13
+ helpful: number;
14
+ harmful: number;
15
+ score: number;
16
+ }
17
+ export interface ServedLearning {
18
+ text: string;
19
+ items: ServedLearningItem[];
20
+ }
21
+ export declare function selectLearning(root: string, plan: TaskPlan, step: TaskStep): ServedLearning;
22
+ export declare function recordSkillUse(root: string, ref: {
23
+ goal_id: string;
24
+ step_id: string;
25
+ attempt: number;
26
+ agent: string;
27
+ }, id: string): Promise<void>;
28
+ export declare function scoreLearning(root: string, goal: GoalRecord, step: TaskStep, outcome: StepOutcome): Promise<void>;
29
+ //# sourceMappingURL=learning-serving.d.ts.map
@@ -0,0 +1,134 @@
1
+ import { readLearningPolicy } from './learning-policy.js';
2
+ import { GoalRepository } from './store/repositories/goal-repository.js';
3
+ import { PlaybookRepository, withPlaybookTransaction } from './store/repositories/playbook-repository.js';
4
+ import { listLearnedArtifacts, updateLearnedArtifact, ensureLearningStep } from './learned-artifacts.js';
5
+ import { listSkillCatalogEntries } from './skill-catalog.js';
6
+ import { withWorkspaceRoot } from './helpers/workspace-root.js';
7
+ import { withStoreWriteCoordinator } from './store/write-coordinator.js';
8
+ import { failureSignature, learningTaskText } from './experience-reflector.js';
9
+ import { curate } from './playbook-curator.js';
10
+ const tags = ['test', 'build', 'store', 'todo', 'handoff', 'scheduler', 'migration', 'docs', 'release', 'mcp', 'hook', 'schema'];
11
+ export function selectLearning(root, plan, step) {
12
+ const policy = readLearningPolicy(root);
13
+ if (!policy.enabled || step.learning_scope)
14
+ return { text: '', items: [] };
15
+ const all = [step, ...plan.steps.filter(s => step.depends_on?.includes(s.step_id))];
16
+ const signatures = new Set(all.flatMap(s => s.attempts_log?.flatMap(a => a.failure_signature ? [a.failure_signature] : []) ?? []));
17
+ const paths = all.flatMap(s => [...(s.expected_artifacts ?? []).map(a => a.path), ...(s.evidence_refs ?? [])]).map(p => p.replace(/\\/g, '/').replace(/^\.\//, ''));
18
+ const taskTags = tags.filter(tag => learningTaskText(step).toLowerCase().includes(tag));
19
+ const score = (scope, helpful, harmful) => (scope.failure_signature && signatures.has(scope.failure_signature) ? 5 : 0) + (scope.paths?.some(p => paths.some(q => q === p || q.startsWith(p + '/') || p.startsWith(q + '/'))) ? 3 : 0) + (scope.role === step.role ? 2 : 0) + (scope.task_tags?.filter(t => taskTags.includes(t)).length ?? 0) - (harmful > helpful ? 3 : 0);
20
+ const candidates = new PlaybookRepository(root).listBulletsReadOnly({ status: 'active' }).map(b => ({ id: b.bullet_id, kind: 'lesson', scope: b.scope, text: `[${b.bullet_id}] (learned, ${b.label ?? 'checked'}) ${b.content} (helped ${b.helpful_count}×; harmful ${b.harmful_count}×)`, check: b.content.match(/Check:\s*(.+)$/)?.[1]?.trim(), helpful: b.helpful_count, harmful: b.harmful_count, score: score(b.scope, b.helpful_count, b.harmful_count) }));
21
+ for (const a of listLearnedArtifacts(root).filter(a => a.kind === 'skill' && a.status === 'active'))
22
+ candidates.push({ id: a.id, kind: 'skill', scope: a.scope, text: `[${a.id}] (${a.label}) ${typeof a.description === 'string' ? a.description : 'Use for ' + JSON.stringify(a.scope)}`, checks: [...(a.body.match(/^## Validation[^\n]*\n([\s\S]*?)(?=^## |$(?![\s\S]))/m)?.[1] ?? '').matchAll(/```bash\s*\n([\s\S]*?)```/g)].map(m => m[1].replace(/\n$/, '')), helpful: a.helpful_count, harmful: a.harmful_count, score: score(a.scope, a.helpful_count, a.harmful_count) });
23
+ const packaged = withWorkspaceRoot(root, () => listSkillCatalogEntries()).filter(s => s.source === 'package-defaults');
24
+ for (const skill of packaged) {
25
+ const scope = { task_tags: tags.filter(t => skill.description.toLowerCase().includes(t)) };
26
+ candidates.push({ id: skill.name, kind: 'packaged', scope, text: `[${skill.name}] ${skill.description} — load with get_skill_instructions`, helpful: 0, harmful: 0, score: score(scope, 0, 0) });
27
+ }
28
+ const picked = [];
29
+ let text = '';
30
+ for (const item of candidates.filter(c => c.score >= 3).sort((a, b) => b.score - a.score || b.helpful - a.helpful || a.id.localeCompare(b.id))) {
31
+ if (picked.filter(i => i.kind === 'lesson').length >= policy.serve_max_lessons && item.kind === 'lesson')
32
+ continue;
33
+ if (picked.filter(i => i.kind !== 'lesson').length >= policy.serve_max_skills && item.kind !== 'lesson')
34
+ continue;
35
+ const rendered = text + (text ? '\n' : 'Known pitfalls and relevant skills (load when useful):\n') + item.text;
36
+ if (Buffer.byteLength(rendered, 'utf8') > policy.serve_max_tokens)
37
+ continue;
38
+ picked.push(item);
39
+ text = rendered;
40
+ }
41
+ return { text, items: picked };
42
+ }
43
+ export async function recordSkillUse(root, ref, id) {
44
+ if (!readLearningPolicy(root).enabled)
45
+ return;
46
+ const repo = new GoalRepository(root);
47
+ await withStoreWriteCoordinator(repo.planPath(ref.goal_id), async () => {
48
+ const p = repo.getPlan(ref.goal_id), s = p?.steps.find(s => s.step_id === ref.step_id);
49
+ if (!p || !s || s.status !== 'running' || s.attempt !== ref.attempt || s.assignee !== ref.agent || !s.served_learning?.items.some(i => i.id === id && i.kind !== 'lesson'))
50
+ return;
51
+ s.loaded_learning ??= [];
52
+ if (!s.loaded_learning.some(i => i.id === id)) {
53
+ s.loaded_learning.push({ id, at: new Date().toISOString() });
54
+ repo.savePlan(ref.goal_id, p);
55
+ }
56
+ }, { operation_label: 'served_skill_use' });
57
+ }
58
+ export async function scoreLearning(root, goal, step, outcome) {
59
+ const policy = readLearningPolicy(root);
60
+ if (!policy.enabled || !step.served_learning?.items.length)
61
+ return;
62
+ const episode = step.attempts_log?.at(-1);
63
+ if (!episode || episode.attempt !== step.attempt)
64
+ return;
65
+ const key = `${goal.goal_id}:${step.step_id}:${episode.attempt}`, runs = episode.observed_runs ?? [], forces = [];
66
+ for (const served of step.served_learning.items) {
67
+ if (served.kind === 'packaged')
68
+ continue;
69
+ const feedback = step.learning_feedback?.find(f => f.id === served.id);
70
+ const checkRuns = runs.filter(r => r.attribution === 'Linked' && (r.command === served.check || served.checks?.includes(r.command)));
71
+ const loaded = step.loaded_learning?.find(i => i.id === served.id);
72
+ const used = feedback !== undefined || !!loaded || (served.kind === 'lesson' && checkRuns.length > 0);
73
+ const usedAt = loaded?.at ?? checkRuns[0]?.at;
74
+ const recurrence = !!served.scope.failure_signature && (runs.some(r => r.attribution === 'Linked' && r.outcome === 'failed' && failureSignature(r.output_tail ?? '') === served.scope.failure_signature && (!usedAt || r.at >= usedAt)) || (!runs.length && episode.status !== 'done' && episode.failure_signature === served.scope.failure_signature));
75
+ const commands = served.check ? [served.check] : (served.checks ?? []);
76
+ const passed = episode.status === 'done' && commands.length > 0 && commands.every(command => checkRuns.some(r => r.command === command && r.outcome === 'ok' && r.exit_code === 0));
77
+ const tag = feedback ? (feedback.helpful ? 'helpful' : 'harmful') : used && recurrence ? 'harmful' : used && passed ? 'helpful' : undefined;
78
+ const weak = !used ? (episode.status === 'done' && episode.attempt === 1 ? 'done_first_attempt' : recurrence ? 'failed_same_signature' : 'other') : undefined;
79
+ if (served.kind === 'lesson') {
80
+ await withPlaybookTransaction(root, async (repo) => {
81
+ let b = repo.getBullet(served.id);
82
+ if (!b || b.scored_attempts?.includes(key))
83
+ return;
84
+ if (tag) {
85
+ await curate(repo, [{ type: 'TAG', bullet_id: b.bullet_id, tag }], { quarantine_harmful_min: 2, emitEvent: false });
86
+ b = repo.getBullet(served.id);
87
+ }
88
+ const counts = b.served_outcomes ?? { done_first_attempt: 0, failed_same_signature: 0, other: 0 };
89
+ if (weak)
90
+ counts[weak]++;
91
+ const next = { ...b, served_count: (b.served_count ?? 0) + 1, used_count: (b.used_count ?? 0) + (used ? 1 : 0), served_outcomes: counts, scored_attempts: [...(b.scored_attempts ?? []), key], used_goals: used ? [...new Set([...(b.used_goals ?? []), goal.goal_id])] : b.used_goals };
92
+ if (next.served_count >= policy.retire_unhelpful_after_serves && !next.used_count) {
93
+ next.status = 'retired';
94
+ next.retired_reason = 'Served at least 40 times without evidence of use.';
95
+ }
96
+ repo.saveBullet(next);
97
+ if (tag === 'harmful' && recurrence)
98
+ forces.push(b.scope);
99
+ });
100
+ }
101
+ else {
102
+ const found = listLearnedArtifacts(root).find(a => a.id === served.id && a.kind === 'skill');
103
+ if (!found)
104
+ continue;
105
+ await updateLearnedArtifact(root, found.key, a => {
106
+ if (a.scored_attempts?.includes(key))
107
+ return;
108
+ a.served_count = (a.served_count ?? 0) + 1;
109
+ a.used_count = (a.used_count ?? 0) + (used ? 1 : 0);
110
+ a.scored_attempts = [...(a.scored_attempts ?? []), key];
111
+ a.used_goals = used ? [...new Set([...(a.used_goals ?? []), goal.goal_id])] : a.used_goals;
112
+ if (tag === 'helpful')
113
+ a.helpful_count++;
114
+ if (tag === 'harmful')
115
+ a.harmful_count++;
116
+ a.served_outcomes ??= { done_first_attempt: 0, failed_same_signature: 0, other: 0 };
117
+ if (weak)
118
+ a.served_outcomes[weak]++;
119
+ if (a.harmful_count >= 2 && a.harmful_count > a.helpful_count)
120
+ a.status = 'quarantined';
121
+ if (a.served_count >= policy.retire_unhelpful_after_serves && !a.used_count) {
122
+ a.status = 'retired';
123
+ a.retired_reason = 'Served at least 40 times without evidence of use.';
124
+ }
125
+ if (tag === 'harmful' && recurrence)
126
+ forces.push(a.scope);
127
+ return a;
128
+ });
129
+ }
130
+ }
131
+ for (const scope of forces)
132
+ await ensureLearningStep(root, { scope, reason: 'An applied artifact\'s own failure recurred. Compare the served attempts and repair the advice.' });
133
+ }
134
+ //# sourceMappingURL=learning-serving.js.map
@@ -0,0 +1,9 @@
1
+ import type { TodoNode } from './store/repositories/todo-repository.js';
2
+ import type { TaskPlan } from './orchestrator-supervisor.js';
3
+ export declare function deriveLinkedTodo<T extends TodoNode>(todo: T, root: string, snapshot?: {
4
+ goal: {
5
+ goal_id: string;
6
+ };
7
+ plan: TaskPlan;
8
+ }): T;
9
+ //# sourceMappingURL=linked-work.d.ts.map
@@ -0,0 +1,28 @@
1
+ /** Read-only derivation: a linked todo is a view of its existing goal step. */
2
+ import { GoalRepository } from './store/repositories/goal-repository.js';
3
+ export function deriveLinkedTodo(todo, root, snapshot) {
4
+ const repo = new GoalRepository(root);
5
+ let ref = todo.step_ref;
6
+ if (!ref) {
7
+ const current = snapshot?.plan.steps.find(s => s.todo_id === todo.id);
8
+ if (current)
9
+ ref = { goal_id: snapshot.goal.goal_id, step_id: current.step_id };
10
+ else
11
+ for (const goal of repo.listGoals()) {
12
+ const linked = repo.getPlan(goal.goal_id)?.steps.find(s => s.todo_id === todo.id);
13
+ if (linked) {
14
+ ref = { goal_id: goal.goal_id, step_id: linked.step_id };
15
+ break;
16
+ }
17
+ }
18
+ }
19
+ if (!ref)
20
+ return todo;
21
+ const plan = snapshot?.goal.goal_id === ref.goal_id ? snapshot.plan : repo.getPlan(ref.goal_id);
22
+ const step = plan?.steps.find(s => s.step_id === ref.step_id);
23
+ if (!step)
24
+ return todo;
25
+ const status = step.status === 'running' ? 'in_progress' : step.status === 'done' || step.status === 'skipped' ? 'done' : step.status === 'failed' || step.status === 'blocked' ? 'blocked' : 'pending';
26
+ return { ...todo, step_ref: ref, status, ...(step.assignee ? { assigned_to: step.assignee } : {}) };
27
+ }
28
+ //# sourceMappingURL=linked-work.js.map
@@ -159,7 +159,7 @@ export async function runLocalModelTask(options) {
159
159
  requested_tier: options.tier,
160
160
  requested_model_class: options.modelClass,
161
161
  });
162
- const maxTurns = options.maxTurns ?? defaultTurnsForPolicy(role, policy);
162
+ const maxTurns = options.maxTurns ?? (runtime.executionEngine === 'hermes_local' ? defaultTurnsForPolicy(role, policy) : 0);
163
163
  const requestedToolScope = options.toolScope ?? defaultToolScopeForPolicy(role, policy);
164
164
  const filteredToolCatalog = buildFilteredToolCatalog(policy.model_class, requestedToolScope, {
165
165
  mutation_lane: policy.mutation_lane,
@@ -0,0 +1,16 @@
1
+ import { z } from "zod";
2
+ import type { CommandProgress } from "./progress-command.js";
3
+ /** MCP liveness messages keep opted-in requests alive without claiming output progress. */
4
+ export declare function withMcpCommandProgress<T>(extra: {
5
+ _meta?: {
6
+ progressToken?: string | number;
7
+ };
8
+ signal?: AbortSignal;
9
+ sendNotification?: (notification: any) => Promise<unknown>;
10
+ } | undefined, label: string, work: (onProgress: (progress: CommandProgress) => void, signal: AbortSignal) => Promise<T>, connectionIsOpen?: () => boolean): Promise<T>;
11
+ export declare const commandPolicySchema: z.ZodObject<{
12
+ stall_min: z.ZodOptional<z.ZodNumber>;
13
+ kill_min: z.ZodOptional<z.ZodNumber>;
14
+ deadline_min: z.ZodOptional<z.ZodNumber>;
15
+ }, z.core.$strip>;
16
+ //# sourceMappingURL=mcp-command-progress.d.ts.map
@@ -0,0 +1,70 @@
1
+ import { z } from "zod";
2
+ /** MCP liveness messages keep opted-in requests alive without claiming output progress. */
3
+ export async function withMcpCommandProgress(extra, label, work, connectionIsOpen) {
4
+ // The SDK cancels a request when its client stops waiting. That cancellation
5
+ // owns the response channel, not the command's lifetime. Explicit operator
6
+ // cancellation and ACE's explicitly configured tool deadlines still propagate.
7
+ const sourceSignal = extra?.signal;
8
+ const workController = new AbortController();
9
+ const beganConnected = connectionIsOpen?.() ?? false;
10
+ const forwardCancellation = () => {
11
+ const reason = sourceSignal?.reason;
12
+ const text = String(reason ?? "");
13
+ const code = reason && typeof reason === "object" && "code" in reason ? reason.code : undefined;
14
+ const clientWaitExpired = code === -32001 || /(?:McpError|MCP error).*Request timed out/i.test(text);
15
+ const transportClosed = /(?:connection|transport|stream)(?: was)? closed|\bdisconnected\b|ECONNRESET|EPIPE/i.test(text);
16
+ if (clientWaitExpired || transportClosed)
17
+ return;
18
+ const genericAbort = (reason instanceof Error && reason.name === "AbortError") || /^AbortError:/i.test(text);
19
+ if (genericAbort && beganConnected) {
20
+ // SDK _onclose aborts handlers before clearing its transport. Check after
21
+ // that synchronous close sequence; an operator abort leaves transport open.
22
+ queueMicrotask(() => {
23
+ if (connectionIsOpen?.() === false)
24
+ return;
25
+ workController.abort(reason);
26
+ });
27
+ }
28
+ else
29
+ workController.abort(reason);
30
+ };
31
+ if (sourceSignal?.aborted) {
32
+ forwardCancellation();
33
+ await Promise.resolve();
34
+ }
35
+ else
36
+ sourceSignal?.addEventListener("abort", forwardCancellation, { once: true });
37
+ const token = extra?._meta?.progressToken;
38
+ let sequence = 0;
39
+ let latest = "running";
40
+ const notify = () => {
41
+ if (token === undefined || !extra?.sendNotification)
42
+ return;
43
+ try {
44
+ void Promise.resolve(extra.sendNotification({
45
+ method: "notifications/progress",
46
+ params: { progressToken: token, progress: ++sequence, message: `${label}: ${latest}` },
47
+ })).catch(() => undefined);
48
+ }
49
+ catch { /* A closed notification channel must not change command outcomes. */ }
50
+ };
51
+ notify();
52
+ const heartbeat = setInterval(notify, 15_000);
53
+ heartbeat.unref?.();
54
+ try {
55
+ return await work((progress) => {
56
+ latest = `${progress.status}; running ${Math.floor(progress.elapsed_ms / 1000)}s, last output ${Math.floor(progress.silence_ms / 1000)}s ago`;
57
+ notify();
58
+ }, workController.signal);
59
+ }
60
+ finally {
61
+ clearInterval(heartbeat);
62
+ sourceSignal?.removeEventListener("abort", forwardCancellation);
63
+ }
64
+ }
65
+ export const commandPolicySchema = z.object({
66
+ stall_min: z.number().nonnegative().finite().optional().describe("Silence before suspected stall; default 10 minutes, 0 disables stall detection"),
67
+ kill_min: z.number().nonnegative().finite().optional().describe("Total silence before terminating stalled process group; default 60 minutes"),
68
+ deadline_min: z.number().positive().finite().optional().describe("Explicit overall deadline in minutes; omitted means no overall deadline"),
69
+ });
70
+ //# sourceMappingURL=mcp-command-progress.js.map
@@ -10,7 +10,7 @@ export interface BridgeToolResult {
10
10
  ok: boolean;
11
11
  summary: string;
12
12
  isError: boolean;
13
- /** Set when a tool fails for a classified reason (e.g. "tool_timeout"). */
13
+ /** Classified failure or nonterminal state (for example "still_running"). */
14
14
  reason_code?: string;
15
15
  }
16
16
  export interface BridgeUsageTotals {
@@ -83,20 +83,20 @@ export declare function verifyCompletionArtifacts(result: BridgeResult, context:
83
83
  requiredEvidenceRefs?: string[];
84
84
  forceCompletionVerification?: boolean;
85
85
  }): Promise<BridgeResult>;
86
- /**
87
- * Per-tool-call deadline (E12). A hung validation/test subprocess must never
88
- * wedge a turn indefinitely — and under Promise.all one stuck call also holds
89
- * every parallel sibling's feedback hostage. On timeout we resolve a classified
90
- * `tool_timeout` failure that then feeds the WS-C doom-loop brake.
91
- */
92
- export declare function executeToolWithDeadline(tool: string, args: Record<string, unknown>, sessionId: string, opts: {
93
- workspace_path: string;
94
- }, deadlineMs: number, exec?: (tool: string, args: Record<string, unknown>, sessionId: string, opts: {
86
+ type InternalToolOptions = {
95
87
  workspace_path: string;
96
- }) => Promise<unknown>): Promise<{
88
+ signal?: AbortSignal;
89
+ };
90
+ type ToolDeadlineOutcome = {
97
91
  rawToolResult: unknown;
98
- timedOut: boolean;
99
- }>;
92
+ timedOut: false;
93
+ stillRunning?: boolean;
94
+ operationId?: string;
95
+ };
96
+ /** No implicit cutoff. An explicit deadline requests cancellation; unconfirmed
97
+ * cancellation is reported as still running. Identical retries collect the same
98
+ * operation instead of launching a duplicate side effect. */
99
+ export declare function executeToolWithDeadline(tool: string, args: Record<string, unknown>, sessionId: string, opts: InternalToolOptions, deadlineMs?: number, exec?: (tool: string, args: Record<string, unknown>, sessionId: string, opts: InternalToolOptions) => Promise<unknown>): Promise<ToolDeadlineOutcome>;
100
100
  export declare class ModelBridge {
101
101
  private clients;
102
102
  private activeProviderClient;
@@ -108,4 +108,5 @@ export declare class ModelBridge {
108
108
  private selectToolScope;
109
109
  run(options: ModelBridgeRunOptions): Promise<BridgeResult>;
110
110
  }
111
+ export {};
111
112
  //# sourceMappingURL=model-bridge.d.ts.map