@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,101 @@
1
+ import type { GoalEngineDeps } from './goal-engine.js';
2
+ export interface NativeVericifyConfig {
3
+ enabled: boolean;
4
+ executable?: string;
5
+ data_dir?: string;
6
+ workspace_root?: string;
7
+ workspace_id?: string;
8
+ incarnation?: string;
9
+ query_timeout_ms?: number;
10
+ /** Retained for old profiles; window queries never traverse pages. */
11
+ max_pages?: number;
12
+ max_bytes?: number;
13
+ retries?: number;
14
+ acceptance_wait_ms?: number;
15
+ }
16
+ export interface NativeObservationRef {
17
+ id: string;
18
+ attribution: 'Linked' | 'Inferred' | 'Ambiguous' | 'Unknown';
19
+ native_attribution: 'Linked' | 'Inferred' | 'Ambiguous' | 'Unknown';
20
+ kind: string;
21
+ path_display?: string;
22
+ path_display_truncated?: boolean;
23
+ change_kind?: string;
24
+ sequence: string;
25
+ workspace_id: string;
26
+ incarnation: string;
27
+ attempt_linked: false;
28
+ link_refs: string[];
29
+ evidence_refs: string[];
30
+ purpose_refs: Array<{
31
+ evidence_id: string;
32
+ basis: string;
33
+ uncertainty: string;
34
+ }>;
35
+ purpose_availability: string;
36
+ native_purpose_availability: string;
37
+ purpose_resolved: false;
38
+ capture_coverage: {
39
+ source: string;
40
+ stable_read: string;
41
+ gaps: string[];
42
+ omission_reason: string | null;
43
+ };
44
+ binding: {
45
+ protocol: string;
46
+ workspace_id: string;
47
+ incarnation: string;
48
+ sequence: string;
49
+ snapshot: string;
50
+ };
51
+ observed_from: string;
52
+ observed_to: string;
53
+ }
54
+ export interface NativeReadResult {
55
+ status: 'available' | 'pending' | 'unavailable';
56
+ records: NativeObservationRef[];
57
+ reason_code?: string;
58
+ query?: Record<string, unknown>;
59
+ coverage: {
60
+ /** Completeness of the selected retained history, never universal capture. */
61
+ complete: boolean;
62
+ expired_ranges: Array<{
63
+ first: string;
64
+ last: string;
65
+ }>;
66
+ from_sequence?: string;
67
+ through_sequence?: string;
68
+ native_scope?: string;
69
+ native_coverage?: NativeRecordRef[];
70
+ host_gaps?: NativeRecordRef[];
71
+ evidence_unavailable?: Array<{
72
+ id: string;
73
+ reason: string;
74
+ }>;
75
+ capture_complete?: false | null;
76
+ };
77
+ cursor?: string;
78
+ capabilities: {
79
+ command_pass: false;
80
+ ace_attempt_mapping: false;
81
+ };
82
+ }
83
+ interface NativeRecordRef {
84
+ id: string;
85
+ sequence: string;
86
+ kind: string;
87
+ }
88
+ export declare function getNativeVericifyContext(input: {
89
+ config: NativeVericifyConfig;
90
+ workspaceRoot: string;
91
+ since?: string;
92
+ window?: {
93
+ started_at: string;
94
+ ended_at?: string;
95
+ };
96
+ }): Promise<NativeReadResult>;
97
+ export declare function createNativeObservationAdapter(config: NativeVericifyConfig, workspaceRoot: string): NonNullable<GoalEngineDeps['observations']> & {
98
+ supported_requirements: readonly [];
99
+ };
100
+ export {};
101
+ //# sourceMappingURL=vericify-native.d.ts.map
@@ -0,0 +1,221 @@
1
+ /** Optional read-only native consumer. The vendored owner client selects history. */
2
+ import { realpathSync } from 'node:fs';
3
+ import { resolve } from 'node:path';
4
+ const protocol = 'vericify-readonly-adapter/v1';
5
+ const capabilities = { command_pass: false, ace_attempt_mapping: false };
6
+ const U64_MAX = 18446744073709551615n;
7
+ function decimal(value) {
8
+ if (typeof value !== 'string' || !/^(0|[1-9][0-9]{0,19})$/.test(value) || BigInt(value) > U64_MAX)
9
+ throw new ConsumerError('invalid_native_response');
10
+ return value;
11
+ }
12
+ function text(value) {
13
+ if (typeof value !== 'string' || value.length < 1 || value.length > 512 || /[\u0000-\u001f]/.test(value))
14
+ throw new ConsumerError('invalid_native_response');
15
+ return value;
16
+ }
17
+ class ConsumerError extends Error {
18
+ code;
19
+ constructor(code) {
20
+ super(code);
21
+ this.code = code;
22
+ }
23
+ }
24
+ function fail(status, reason_code) {
25
+ return { status, reason_code, records: [], coverage: { complete: false, expired_ranges: [] }, capabilities };
26
+ }
27
+ function bound(value, fallback, low, high) {
28
+ const result = value ?? fallback;
29
+ if (!Number.isSafeInteger(result) || result < low || result > high)
30
+ throw new ConsumerError('invalid_native_configuration');
31
+ return result;
32
+ }
33
+ function confidence(value) {
34
+ const result = String(value ?? '').toLowerCase();
35
+ return result === 'linked' ? 'Linked' : result === 'inferred' ? 'Inferred' : result === 'ambiguous' ? 'Ambiguous' : 'Unknown';
36
+ }
37
+ function identity(snapshot, config) {
38
+ if (snapshot.workspace_id !== config.workspace_id || (config.incarnation && snapshot.incarnation !== config.incarnation))
39
+ throw new ConsumerError('workspace_identity_mismatch');
40
+ text(snapshot.incarnation);
41
+ decimal(snapshot.high_water);
42
+ if (snapshot.record_profile !== 'vericify/records/v1')
43
+ throw new ConsumerError('incompatible_native_profile');
44
+ }
45
+ function cursorEncode(position) {
46
+ return 'vcn1:' + Buffer.from(JSON.stringify({ workspace_id: position.workspace_id, incarnation: position.incarnation, sequence: position.sequence })).toString('base64url');
47
+ }
48
+ function cursorDecode(cursor) {
49
+ try {
50
+ if (!cursor.startsWith('vcn1:') || cursor.length > 2048)
51
+ throw new Error();
52
+ const result = JSON.parse(Buffer.from(cursor.slice(5), 'base64url').toString('utf8'));
53
+ text(result.workspace_id);
54
+ text(result.incarnation);
55
+ decimal(result.sequence);
56
+ return result;
57
+ }
58
+ catch {
59
+ throw new ConsumerError('invalid_native_cursor');
60
+ }
61
+ }
62
+ /** Map references already selected/enriched by Vericify. No history scan or join lives here. */
63
+ function observationRef(item, snapshot) {
64
+ const record = item.record, data = record.payload.data;
65
+ const selected = item.latest_link?.payload.data ?? data;
66
+ const capture = data.coverage ?? {};
67
+ return {
68
+ id: record.id, attribution: 'Unknown', native_attribution: confidence(selected.attribution?.confidence), kind: 'file_change',
69
+ ...(typeof data.path?.display === 'string' ? { path_display: data.path.display.slice(0, 512), path_display_truncated: data.path.display.length > 512 } : {}),
70
+ ...(typeof data.kind === 'string' ? { change_kind: text(data.kind) } : {}),
71
+ sequence: record.seq, workspace_id: record.workspace_id, incarnation: record.incarnation, attempt_linked: false,
72
+ link_refs: item.latest_link ? [item.latest_link.id] : [],
73
+ evidence_refs: Array.isArray(selected.attribution?.evidence_refs) ? selected.attribution.evidence_refs.slice(0, 16).map(text) : [],
74
+ purpose_refs: Array.isArray(selected.purpose_refs) ? selected.purpose_refs.slice(0, 16).map((ref) => ({ evidence_id: text(ref.evidence_id), basis: text(ref.basis), uncertainty: text(ref.uncertainty) })) : [],
75
+ purpose_availability: 'unresolved', native_purpose_availability: typeof selected.purpose_availability === 'string' ? text(selected.purpose_availability) : 'unknown', purpose_resolved: false,
76
+ capture_coverage: {
77
+ source: typeof capture.source === 'string' ? text(capture.source) : 'unknown',
78
+ stable_read: typeof capture.stable_read === 'string' ? text(capture.stable_read) : 'unknown',
79
+ gaps: Array.isArray(capture.gaps) ? capture.gaps.slice(0, 16).map(text) : [],
80
+ omission_reason: typeof capture.omission_reason === 'string' ? text(capture.omission_reason) : null,
81
+ },
82
+ binding: { protocol, workspace_id: record.workspace_id, incarnation: record.incarnation, sequence: record.seq, snapshot: snapshot.binding },
83
+ observed_from: record.payload.data.observed_from, observed_to: record.payload.data.observed_to,
84
+ };
85
+ }
86
+ function recordRef(record) {
87
+ return { id: record.id, sequence: record.seq, kind: record.payload.record_type };
88
+ }
89
+ function nativeFailure(code) {
90
+ switch (code) {
91
+ case 'stale_snapshot': return fail('pending', 'native_stale_snapshot');
92
+ case 'capacity': return fail('pending', 'native_capacity');
93
+ case 'timeout': return fail('pending', 'query_timeout');
94
+ case 'missing_history': return fail('unavailable', 'native_history_missing');
95
+ case 'identity_mismatch': return fail('unavailable', 'workspace_identity_mismatch');
96
+ case 'incompatible_protocol': return fail('unavailable', 'incompatible_native_protocol');
97
+ case 'invalid_query': return fail('unavailable', 'invalid_native_query');
98
+ case 'expired': return fail('unavailable', 'history_expired');
99
+ default: return fail('unavailable', 'native_query_unavailable');
100
+ }
101
+ }
102
+ export async function getNativeVericifyContext(input) {
103
+ const config = input.config;
104
+ if (!config.enabled)
105
+ return fail('unavailable', 'native_disabled');
106
+ if (!config.workspace_root || !config.workspace_id)
107
+ return fail('unavailable', 'workspace_identity_unconfigured');
108
+ let workspaceRoot;
109
+ try {
110
+ workspaceRoot = realpathSync(input.workspaceRoot);
111
+ if (workspaceRoot !== realpathSync(resolve(input.workspaceRoot, config.workspace_root)))
112
+ return fail('unavailable', 'workspace_identity_mismatch');
113
+ }
114
+ catch {
115
+ return fail('unavailable', 'workspace_identity_unavailable');
116
+ }
117
+ if (!config.executable || !config.data_dir)
118
+ return fail('unavailable', 'native_configuration_incomplete');
119
+ let timeout, maxBytes, retries;
120
+ try {
121
+ timeout = bound(config.query_timeout_ms, 15000, 1000, 60000);
122
+ maxBytes = bound(config.max_bytes, 262144, 4096, 1048576);
123
+ retries = bound(config.retries, 1, 0, 3);
124
+ }
125
+ catch {
126
+ return fail('unavailable', 'invalid_native_configuration');
127
+ }
128
+ let since;
129
+ try {
130
+ if (input.since)
131
+ since = cursorDecode(input.since);
132
+ if (since && (since.workspace_id !== config.workspace_id || (config.incarnation && since.incarnation !== config.incarnation)))
133
+ throw new Error();
134
+ }
135
+ catch {
136
+ return fail('unavailable', 'invalid_native_cursor');
137
+ }
138
+ // No-window context/delta reads cover all epoch values without Number coercion.
139
+ let fromMs = '0', toMs = U64_MAX.toString();
140
+ if (input.window) {
141
+ const from = Date.parse(input.window.started_at), to = Date.parse(input.window.ended_at ?? new Date().toISOString());
142
+ if (!Number.isSafeInteger(from) || !Number.isSafeInteger(to) || from < 0 || to < from)
143
+ return fail('unavailable', 'invalid_observation_window');
144
+ fromMs = String(from);
145
+ toMs = String(to);
146
+ }
147
+ const after = since ?? (config.incarnation ? { workspace_id: config.workspace_id, incarnation: config.incarnation, sequence: '0' } : undefined);
148
+ const clientUrl = new URL('../assets/vendor/vericify-reader.mjs', import.meta.url).href;
149
+ let owner;
150
+ try {
151
+ owner = await import(clientUrl);
152
+ }
153
+ catch {
154
+ return fail('unavailable', 'native_consumer_unavailable');
155
+ }
156
+ const end = Date.now() + timeout;
157
+ for (let attempt = 0; attempt <= retries; attempt++) {
158
+ const remaining = attempt === 0 ? timeout : end - Date.now();
159
+ if (remaining < 1000)
160
+ return fail('pending', 'query_timeout');
161
+ try {
162
+ const client = owner.openVericifyHistory({ executable: resolve(input.workspaceRoot, config.executable), dataDir: resolve(input.workspaceRoot, config.data_dir), timeoutMs: Math.min(60000, remaining) });
163
+ if (typeof client.window !== 'function')
164
+ return fail('unavailable', 'incompatible_native_protocol');
165
+ // The owner resolves identity, history, latest links and evidence in one pinned native lifetime.
166
+ const result = await client.window({ workspaceRoot, fromMs, toMs, after, limit: 100, maxBytes });
167
+ identity(result.snapshot, config);
168
+ const status = result.state === 'pending' ? 'pending' : result.state === 'expired' ? 'unavailable' : 'available';
169
+ return {
170
+ status,
171
+ ...(result.state === 'pending' ? { reason_code: 'native_window_pending' } : result.state === 'expired' ? { reason_code: 'history_expired' } : {}),
172
+ records: result.changes.map((item) => observationRef(item, result.snapshot)),
173
+ query: {
174
+ protocol, workspace_id: result.snapshot.workspace_id, incarnation: result.snapshot.incarnation, snapshot: result.snapshot.binding,
175
+ ...(result.snapshot.generation !== undefined ? { generation: decimal(result.snapshot.generation) } : {}),
176
+ record_profile: result.snapshot.record_profile, high_water: result.high_water.sequence, since: result.query.after_sequence,
177
+ window: input.window ?? null, from_ms: fromMs, to_ms: toMs, bytes: Buffer.byteLength(JSON.stringify(result)), attempts: attempt + 1,
178
+ state: result.state, scan_complete: result.scan_complete, evidence_complete: result.evidence.complete,
179
+ },
180
+ coverage: {
181
+ complete: result.complete && result.state === 'complete', expired_ranges: result.expired_ranges,
182
+ from_sequence: result.query.after_sequence, through_sequence: result.scanned_through.sequence,
183
+ native_scope: result.coverage_scope,
184
+ native_coverage: result.coverage.map(recordRef), host_gaps: result.host_gaps.map(recordRef),
185
+ evidence_unavailable: result.evidence.unavailable.map((ref) => ({ id: ref.id, reason: ref.reason })),
186
+ capture_complete: result.host_gaps.length ? false : null,
187
+ },
188
+ // Only acknowledged progress can be resumed. Never substitute the snapshot high-water.
189
+ cursor: cursorEncode(result.next_after), capabilities,
190
+ };
191
+ }
192
+ catch (error) {
193
+ if (error instanceof ConsumerError)
194
+ return fail('unavailable', error.code);
195
+ if (typeof owner.NativeReadError === 'function' && error instanceof owner.NativeReadError) {
196
+ const native = error;
197
+ if (native.retryable && attempt < retries && end - Date.now() >= 1000)
198
+ continue;
199
+ return nativeFailure(native.code);
200
+ }
201
+ return fail('unavailable', 'invalid_native_response');
202
+ }
203
+ }
204
+ return fail('pending', 'native_query_unavailable');
205
+ }
206
+ export function createNativeObservationAdapter(config, workspaceRoot) {
207
+ return {
208
+ supported_requirements: [],
209
+ async readWindow(window) {
210
+ try {
211
+ if (realpathSync(window.workspace_id) !== realpathSync(workspaceRoot))
212
+ return fail('unavailable', 'workspace_identity_mismatch');
213
+ }
214
+ catch {
215
+ return fail('unavailable', 'workspace_identity_unavailable');
216
+ }
217
+ return getNativeVericifyContext({ config, workspaceRoot, window });
218
+ },
219
+ };
220
+ }
221
+ //# sourceMappingURL=vericify-native.js.map
@@ -0,0 +1,2 @@
1
+ export declare function runVerificationWorker(root: string, jobId: string): Promise<void>;
2
+ //# sourceMappingURL=verification-worker.d.ts.map
@@ -0,0 +1,31 @@
1
+ /** Detached execution process; the MCP server owns no long verification request. */
2
+ import { fileURLToPath } from 'node:url';
3
+ import { resolve } from 'node:path';
4
+ import { withWorkspaceRoot } from './helpers.js';
5
+ import { registerJobHandler, runJobWorkerOnce } from './job-worker.js';
6
+ import { readJobQueue } from './job-scheduler.js';
7
+ import { executeHostVerificationJob } from './host-verification-job.js';
8
+ export async function runVerificationWorker(root, jobId) {
9
+ await withWorkspaceRoot(root, async () => {
10
+ registerJobHandler('goal.verify', async ({ job }) => {
11
+ await executeHostVerificationJob(root, job.job_id);
12
+ return { success: true, evidence_ref: `goal:${String(job.payload.goal_id)}:verification:${job.job_id}` };
13
+ });
14
+ for (;;) {
15
+ const job = readJobQueue().jobs.find(j => j.job_id === jobId);
16
+ if (!job || ['done', 'failed', 'failed_terminal', 'canceled', 'unknown_recovery'].includes(job.status))
17
+ return;
18
+ const tick = await runJobWorkerOnce({ owner: 'capability-ops', job_ids: [jobId] });
19
+ if (tick.executed.length)
20
+ return;
21
+ await new Promise(resolve => setTimeout(resolve, 1000));
22
+ }
23
+ });
24
+ }
25
+ if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
26
+ const [root, jobId] = process.argv.slice(2);
27
+ if (!root || !jobId)
28
+ throw new Error('Verification worker requires workspace and job id.');
29
+ await runVerificationWorker(root, jobId).catch(error => { console.error(error); process.exitCode = 1; });
30
+ }
31
+ //# sourceMappingURL=verification-worker.js.map
@@ -1,8 +1,8 @@
1
1
  # MCP Tool Reference
2
2
 
3
- > Companion to the [README](../README.md). The full MCP surface spans **119 registered tools** across 13 domain modules, plus resources and prompts. For premium hosts (Claude Code, Codex), the curated `ace mcp --profile host-min` exposes 22 high-value tools; the rest remain reachable via `run_orchestrator` delegation or `--profile full`.
3
+ > Companion to the [README](../README.md). The full MCP surface spans **121 registered tools** across 13 domain modules, plus resources and prompts. For premium hosts (Claude Code, Codex), `ace mcp --profile host-min` exposes 30 tools, including work-state and stop/resume operations. Optional `run_local_model`, `run_orchestrator`, `emit_status_event` and `append_vericify_process_post` are available through `--profile full`.
4
4
 
5
- ACE exposes tools through a domain-modular registration system (`src/tools.ts` delegates to 13 domain modules). Tools can be allow-listed at startup; `ace mcp-shadow` uses this to give Hermes a scoped tools-only subset. Public `ace mcp` always exposes the full prompts and resources surface; its visible tool schema depends on `--profile` (119 tools for `full`, 22 for `host-min`). `ace mcp-shadow` instead exposes only its explicitly allow-listed tools and omits prompts and resources.
5
+ ACE exposes tools through a domain-modular registration system (`src/tools.ts` delegates to 13 domain modules). Tools can be allow-listed at startup; `ace mcp-shadow` uses this to give Hermes a scoped tools-only subset. Public `ace mcp` always exposes the full prompts and resources surface; its visible tool schema depends on `--profile` (121 tools for `full`, 30 for `host-min`). `ace mcp-shadow` instead exposes only its explicitly allow-listed tools and omits prompts and resources.
6
6
 
7
7
  ## Agent & Runtime
8
8
 
@@ -78,11 +78,16 @@ create_handoff {
78
78
  | `get_lock_table` | Read active resource locks |
79
79
  | `get_scheduler_lease` | Read the current scheduler lease |
80
80
 
81
+ Dispatch reserves resources and records running work; a worker executes the job
82
+ body. `handoff.lifecycle` is reserved for metadata synchronization and is never
83
+ dispatched. Empty ticks release ownership; `lease_retained` in the dispatch result
84
+ indicates whether execution still holds the lease afterward.
85
+
81
86
  Example — queuing a blocked job:
82
87
 
83
88
  ```text
84
89
  enqueue_job {
85
- "kind": "handoff.lifecycle",
90
+ "kind": "example.analysis",
86
91
  "priority": "P1",
87
92
  "dependencies": [{ "kind": "handoff", "ref": "HND-20260710-abc12345", "expected": "accepted" }],
88
93
  "resource_requirements": ["agent-state/handoff-registry.json"],
@@ -94,13 +99,33 @@ enqueue_job {
94
99
 
95
100
  | Tool | Purpose |
96
101
  |---|---|
97
- | `start_goal` | Compile an objective into a validated plan and open a goal record |
98
- | `get_goal_status` | Read goal status, tick/replan counts, ready steps, and escalations |
99
- | `resume_goal` | Resume a stopped or escalated goal from its persisted state |
102
+ | `start_goal` | Choose `executor:"host"` or `executor:"engine"` explicitly; host plans return their first assignment |
103
+ | `next_step` | Receive/resume a ready host assignment with dependency results; never takes another participant's running step |
104
+ | `complete_step` | Record a host outcome once, update linked views, optionally begin the successor; identical retries replay the original response |
105
+ | `get_goal_status` | `view:"compact"` returns bounded verification, blockers and executable next actions; default/`view:"full"` retains complete records |
106
+ | `resume_goal` | Resume an engine goal; for host goals, preserve running work and explicitly retry blocked work or interrupted verification |
100
107
  | `stop_goal` | Operator stop for a running goal |
101
108
 
102
109
  A goal reaches `done` only through evidence-gated success; it otherwise leaves `executing` through budget exhaustion, operator stop, or an escalated blocker. The same loop is available from the CLI as `ace goal start|status|run|resume|stop`.
103
110
 
111
+ Host loop: `start_goal({executor:"host", agent:"codex", steps:[{id:"S1", task:"Implement"}]})` returns an assignment. Do the work with the host's tools, then call `complete_step` with its `goal_id`, `step_id`, `attempt`, and `{status, summary}`. `continue:true` returns the next assignment in that response. Use a stable `agent` label across restarts; `recall_context` recovers it. The attached host performs the step work; ACE does not launch a model for it. Final acceptance runs in a detached scheduler worker, with no external observation capture.
112
+
113
+ Goal-end acceptance enqueues background verification and promptly returns an immutable `verification:"pending"` receipt with an executable `get_goal_status({goal_id, view:"compact", agent})` action. Follow it to confirm success, inspect a bounded failure preview, or receive `next_step` for corrective work. Identical completion retries return the entire original reply verbatim. Compact status targets 4 KiB of structured JSON and retains counts and a `view:"full"` locator when optional previews are shortened; full plans, attempts and failure output are preserved.
114
+ Requirement changes use optional `outcome.requirement_updates: [{step_id, reason, task?, expected_artifacts?, required_evidence_refs?}]`. ACE reopens only those steps and their dependents, retains earlier results, and charges a total correction allowance only if the operator explicitly supplied one. The completion response names reopened steps or changes pending admission; unrelated sibling outcomes stay intact.
115
+
116
+
117
+ `verify` declares goal-end shell checks as strings or `{cmd,stall_min?,kill_min?,deadline_min?}` objects. The command defaults flag a suspected stall after 10 minutes of silence and terminate its entire process group after 60 minutes of silence total (50 more minutes after the warning). `stall_min:0` disables silence detection, `kill_min:0` disables automatic stall termination, and a total deadline exists only when `deadline_min` is supplied. Compact status includes the verification job, worker identity, command duration, time since output and a bounded output tail; full logs live under `.ace-runtime/verification/<job_id>/`. A server restart retains live/uncertain work; explicit recovery reruns checks only after old processes are confirmed gone. The existing scheduler tracks both worker and command identities so stale heartbeats cannot release live work’s locks.
118
+
119
+ `run_tests` accepts the same three policy fields directly. `safe_edit_file`, `apply_patch`, structural rewrites and executable gates accept them in `command_policy`. These commands run asynchronously and emit MCP progress notifications when the request supplies a progress token. Clients must opt into `resetTimeoutOnProgress` to extend their request wait; an explicit `maxTotalTimeout` still bounds the client’s patience. This does not reset ACE’s command progress timestamps. A client wait timeout or connection loss does not cancel the work; explicit operator cancellation and declared command deadlines still do. Learned-payload validation can remain blocking and uses the same progress notifications. Native local-model and orchestrated child-step turn counts default to unlimited; external Hermes adapter and client settings are configured separately.
120
+
121
+ Three consecutive unchanged failure sets park a goal for review instead of exhausting a default correction count. Failure identity retains meaningful output, test names and evidence references; changed failures can continue beyond three corrections. Host contact/age is advisory and never authorizes automatic reassignment.
122
+
123
+ `verify` checks determine acceptance. Without shell checks, a permitted host goal completes as **done (not machine-verified)**. Required final `expected_artifacts` are rechecked at closure even without shell commands; missing deliverables create bounded corrective work. Existing string entries declare required final deliverables. Object entries such as `{path:"scratch.txt", transient:true}` declare outputs required at step completion but not at goal closure; `{path:"optional.txt", required:false}` remains optional. Requirement updates accept the same object metadata. Step `required_evidence_refs` and an optional `required_observation` still apply. Optional native observation is configured under `observability.vericify_bridge.native`; missing reads do not block unrelated work. Native records retain their own IDs/confidence, with ACE attempt attribution Unknown. The current native adapter cannot satisfy `action` or `command_pass` requirements and reports that capability gap explicitly. A capable adapter may use bounded late acceptance in the existing verification job; resume then collects evidence for the original attempt instead of repeating the edit or changing its receipt.
124
+
125
+ Host goals are unlimited unless started with explicit `budget_hours`; goals without the new marker, including legacy goals and `G-learning`, have no deadline. Explicit hours bound new assignments from the retained start and may exceed 24; engine goals also have no implicit deadline or 200-tick ceiling, and explicit deadlines may exceed 24 hours. In-flight outcomes and final acceptance survive expiry. A blocked host can recover through `resume_goal({goal_id,budget_hours:3})` or `resume_goal({goal_id,max_replans:3})`, using a sufficient nondecreasing total allowance. Hours do not restart the clock; a finite deadline cannot be imposed on an unlimited goal through resume. All provided changes are validated before either takes effect, and decreases are refused. Raised replan allowance admits pending dependency closures with their retained IDs; any remainder stays in `goal.pending_follow_ups`. Compact status retains sufficient recovery arguments in `operator_proposal` with `requires_operator_approval:true`, outside executable `options`. Deadline/replan exhaustion leaves `options:[]` and waits on `operator:budget`/`operator:replan_budget`. The host must obtain operator approval before submitting those arguments, including when it receives a proposal from plain `resume_goal`. Status and plain resume never raise an allowance. This is an instruction boundary: MCP does not authenticate whether the caller obtained approval. Clearing a legacy unmarked deadline blocker needs no increase and remains an executable option. Plain resume/restart changes no allowance, and completion receipts stay identical after recovery.
126
+
127
+ Link existing todos with `todo_ids: {S1:"todo-id"}`. Linked todo completion routes to the step. A handoff may carry `step_ref:{goal_id,step_id}`, the submitting `agent` and optional `recipient_agent`; `ack_handoff({status:"accepted",agent:recipient})` transfers ownership and advances the attempt. A sender's old completion is then stale. Standalone todos/handoffs keep their existing behavior.
128
+
104
129
  ## Files & Structural Editing
105
130
 
106
131
  | Tool | Purpose |
@@ -155,7 +180,7 @@ A goal reaches `done` only through evidence-gated success; it otherwise leaves `
155
180
  |---|---|
156
181
  | `context_snapshot` | Capture a structured context snapshot for the current session |
157
182
  | `build_continuity_packet` | Build a continuity packet for cross-session handoff |
158
- | `recall_context` | Recall an exact snapshot name, or list all snapshots when no name is supplied |
183
+ | `recall_context` | Return current work, a running assignment and linked context by default; `list: true` lists snapshots; `name` retrieves one |
159
184
 
160
185
  Example — before switching models mid-task:
161
186
 
@@ -192,11 +217,22 @@ emit_status_event {
192
217
 
193
218
  | Tool | Purpose |
194
219
  |---|---|
195
- | `get_todo` | Read the current todo surface |
196
- | `get_todo_state` | Read structured todo state from the store |
197
- | `update_todo` | Update one or more todo items |
220
+ | `get_todo` | Import changed native rows and read the todo board, with import warnings |
221
+ | `get_todo_state` | Import changed native rows and return authoritative task records |
222
+ | `update_todo` | Append, transition, or explicitly delete (`delete_id`) a task; `new_content` imports edits and retains omitted tasks |
198
223
  | `get_run_ledger` | Read the run ledger (history of major tool invocations) |
199
224
 
225
+ Todo statuses are `backlog`, `in_progress`, `blocked`, `gated`, `done`, and `cancelled`.
226
+ `pending` remains the repository spelling of backlog. A cancelled dependency does not satisfy
227
+ `done`. Native edits are imported on todo reads and mutations; malformed, duplicate or absent
228
+ rows produce `import_warnings` and retain canonical records. `get_todo` returns the canonical
229
+ board even when the native file is awaiting repair. Use `delete_id` for intentional removal;
230
+ `new_content` no longer deletes omitted tasks. Prefer append and transition operations for
231
+ concurrent work. Whole-content field edits do not provide a three-way merge.
232
+
233
+ Kanban distinguishes cancelled work from completion. During debounce, tools report a
234
+ scheduled refresh and retain the last actual generation timestamp; a trailing render follows.
235
+
200
236
  ## Vericify (Verification)
201
237
 
202
238
  | Tool | Purpose |
@@ -288,3 +324,7 @@ Prompts:
288
324
  `ace-astgrep`, `ace-codemunch`, `ace-coders`, `ace-codesnipe`, `ace-eval`, `ace-genesis`, `ace-handoff`, `ace-initiate`, `ace-lessons`, `ace-memory`, `ace-observability`, `ace-orchestrator`, `ace-release`, `ace-runtime-executor`, `ace-security`, `ace-skill`, `ace-skills-index`, `ace-ui`, and `ace-vos`.
289
325
 
290
326
  For schema-exact tool inputs, use MCP `tools/list`; for the release-bound schema files and their surface mappings, run `audit_public_surface`. This avoids maintaining a second hand-copied schema that can silently diverge from the registered Zod contracts.
327
+
328
+ Host learning uses optional `complete_step.outcome.learned` (up to three cited lessons, one skill and one instruction proposal). The response reports acceptance errors and a bounded correction option. Mechanical checks earn `checked`; only exact failing-command recovery earns `demonstrated`. Role instructions remain proposed until operator approval. `get_skill_instructions` is available in `host-min`; active skills under `learned/<slug>` appear in the normal catalog.
329
+
330
+ `ace learn list|show|approve|retire|export|report` provides operator control outside the normal step loop. `ACE_LEARN=0` disables automatic learning without deleting knowledge. Only explicit approval activates a proposed role instruction or distiller overlay. Configure bounded defaults through `learning:` in the existing `ACE_WORKFLOW.md`; instruction approval remains required. Exports with episode citations are reviewable source documents, not an automatic publication.
@@ -34,6 +34,35 @@ Store-first is normal. `ACE_STORE_FIRST=0` is a legacy diagnostic escape hatch.
34
34
 
35
35
  Run `ace fsck --json`. A `.corrupt-*` file is a preserved copy, not disposable litter. `ace repair` re-bakes known assets and projections; it does not reconstruct arbitrary corrupt bytes. Restore from a verified backup when structural checks cannot be repaired safely.
36
36
 
37
+ ## Scheduler
38
+
39
+ ### Jobs are recorded but dispatch reports a held lease
40
+
41
+ Enqueue and dispatch are separate operations. `get_scheduler_lease` identifies the
42
+ current owner and expiry; `dispatch_jobs` returns `scheduler_lease_held` when
43
+ another owner holds an unexpired lease protecting running work or active locks.
44
+ An idle lease is reclaimed during dispatch without waiting for expiry. Empty
45
+ or dependency-blocked ticks retain no lease, and completing the last running job
46
+ releases it. `lease_acquired` describes admission for the tick; `lease_retained`
47
+ says whether execution still holds ownership afterward. The returned `lease` is
48
+ the admission receipt, even if it was released at the end of the tick.
49
+
50
+ Handoff creation, acknowledgment, and startup backfill synchronize lifecycle
51
+ metadata without execution dispatch. `handoff.lifecycle` records cannot execute
52
+ through either dispatch tool. Legacy records that appeared to be running are
53
+ parked in `unknown_recovery` with their existing locks preserved until the handoff
54
+ completion receipt resolves them. Backfill also reconciles completed handoffs
55
+ that already have scheduler records; it preserves IDs and history.
56
+
57
+ Parallel-group promotion leaves undispatched steps planned and retains their job
58
+ IDs. Retrying promotion reuses the same jobs, including when retrying the original
59
+ plan. Resource contention can leave a step queued after successful lease acquisition.
60
+
61
+ Running work still requires its owning scheduler. Do not adopt another owner's
62
+ identity or delete locks to bypass contention. Check host-agent execution receipts
63
+ before retrying work already launched outside ACE. Ordinary watchdog recovery
64
+ leaves stale jobs parked; acquiring a released lease does not resume those jobs.
65
+
37
66
  ## Providers
38
67
 
39
68
  ### Ollama unreachable or model missing
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@voybio/ace-swarm",
3
- "version": "2.4.8",
4
- "description": "ACE Framework MCP server and CLI — single-file ACEPACK state, local-model serving, agent orchestration, and host compliance enforcement.",
3
+ "version": "2.4.10",
4
+ "description": "ACE Framework MCP server and CLI \u2014 single-file ACEPACK state, local-model serving, agent orchestration, and host compliance enforcement.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -23,8 +23,21 @@
23
23
  "dist/**/*.d.ts",
24
24
  "scripts/hermes_bridge_worker.py",
25
25
  "scripts/dev/run_hermes_crispr_live.mjs",
26
+ "scripts/dev/run-tests-isolated.mjs",
27
+ "scripts/dev/test-isolation-guard.mjs",
28
+ "scripts/dev/test-preload.mjs",
29
+ "scripts/dev/test-source-inventory.mjs",
26
30
  "assets",
27
- "docs",
31
+ "docs/cli-reference.md",
32
+ "docs/evaluation-safety.md",
33
+ "docs/experience-playbook.md",
34
+ "docs/hermes.md",
35
+ "docs/library-api.md",
36
+ "docs/mcp-tool-reference.md",
37
+ "docs/provider-security.md",
38
+ "docs/state-model.md",
39
+ "docs/troubleshooting.md",
40
+ "docs/tui-operator-guide.md",
28
41
  "README.md",
29
42
  "CHANGELOG.md"
30
43
  ],
@@ -38,14 +51,14 @@
38
51
  },
39
52
  "scripts": {
40
53
  "build": "tsc",
41
- "test": "npm run build && node --test 'test/*.test.mjs' 'test/**/*.test.mjs'",
42
- "test:real-astgrep": "npm run build && ACE_TEST_REAL_ASTGREP=1 node --test test/astgrep-real-cli.test.mjs",
54
+ "test": "npm run build && node scripts/dev/run-tests-isolated.mjs",
55
+ "test:real-astgrep": "npm run build && ACE_TEST_REAL_ASTGREP=1 node scripts/dev/run-tests-isolated.mjs test/astgrep-real-cli.test.mjs",
43
56
  "start": "node dist/cli.js mcp",
44
57
  "dev": "tsc && node dist/cli.js mcp",
45
58
  "init": "node dist/cli.js init",
46
59
  "turnkey": "node dist/cli.js turnkey",
47
60
  "mcp-config": "node dist/cli.js mcp-config",
48
- "prepublishOnly": "npm run build"
61
+ "prepack": "node scripts/dev/prepack.mjs"
49
62
  },
50
63
  "keywords": [
51
64
  "mcp",