@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.
- package/CHANGELOG.md +52 -14
- package/README.md +14 -10
- package/assets/.agents/ACE/ACE_coders/AGENTS.md +23 -30
- package/assets/.agents/ACE/ACE_coders/INSTRUCTIONS.md +21 -30
- package/assets/.agents/skills/landing-review-watcher/SKILL.md +1 -1
- package/assets/.agents/skills/learn-from-work/SKILL.md +41 -0
- package/assets/agent-state/ACE_WORKFLOW.md +25 -2
- package/assets/agent-state/MODULES/schemas/ACE_RUNTIME_PROFILE.schema.json +143 -2
- package/assets/agent-state/MODULES/schemas/RUNTIME_EXECUTOR_SESSION_REGISTRY.schema.json +2 -2
- package/assets/agent-state/MODULES/schemas/SWARM_HANDOFF.schema.json +10 -0
- package/assets/eval-battery/battery.json +1 -1
- package/assets/instructions/ACE_Coder.instructions.md +9 -13
- package/assets/scripts/ace-hook-dispatch.mjs +16 -411
- package/assets/tasks/SWARM_HANDOFF.example_ui_to_coders.json +1 -2
- package/assets/vendor/README.md +9 -0
- package/assets/vendor/vericify-reader.mjs +272 -0
- package/assets/vendor/vericify-reader.provenance.json +10 -0
- package/dist/ace-autonomy.d.ts +58 -0
- package/dist/ace-autonomy.js +211 -6
- package/dist/ace-internal-tools.d.ts +1 -0
- package/dist/ace-internal-tools.js +3 -3
- package/dist/ace-server-instructions.d.ts +3 -3
- package/dist/ace-server-instructions.js +46 -192
- package/dist/cli.js +44 -6
- package/dist/experience-reflector.d.ts +7 -0
- package/dist/experience-reflector.js +39 -0
- package/dist/goal-engine.d.ts +64 -7
- package/dist/goal-engine.js +197 -41
- package/dist/handoff-registry.d.ts +10 -0
- package/dist/handoff-registry.js +59 -0
- package/dist/helpers/artifacts.d.ts +6 -2
- package/dist/helpers/artifacts.js +8 -6
- package/dist/helpers/bootstrap.js +13 -20
- package/dist/helpers/path-utils.js +2 -0
- package/dist/helpers/store-resolution.js +18 -3
- package/dist/helpers/workspace-root.d.ts +2 -0
- package/dist/helpers/workspace-root.js +9 -0
- package/dist/helpers.d.ts +1 -0
- package/dist/helpers.js +1 -0
- package/dist/hermes/bridge-protocol.d.ts +1 -1
- package/dist/hermes/session-manager.d.ts +17 -0
- package/dist/hermes/session-manager.js +67 -14
- package/dist/host-goal-engine.d.ts +138 -0
- package/dist/host-goal-engine.js +793 -0
- package/dist/host-goal-status.d.ts +113 -0
- package/dist/host-goal-status.js +175 -0
- package/dist/host-learning.d.ts +68 -0
- package/dist/host-learning.js +115 -0
- package/dist/host-observation.d.ts +12 -0
- package/dist/host-observation.js +91 -0
- package/dist/host-plan-revision.d.ts +14 -0
- package/dist/host-plan-revision.js +101 -0
- package/dist/host-verification-job.d.ts +12 -0
- package/dist/host-verification-job.js +183 -0
- package/dist/host-work-context.d.ts +12 -0
- package/dist/host-work-context.js +8 -0
- package/dist/job-scheduler.d.ts +58 -13
- package/dist/job-scheduler.js +298 -48
- package/dist/job-worker.d.ts +3 -1
- package/dist/job-worker.js +10 -3
- package/dist/kanban.d.ts +4 -1
- package/dist/kanban.js +44 -16
- package/dist/learned-artifacts.d.ts +64 -0
- package/dist/learned-artifacts.js +321 -0
- package/dist/learning-controls.d.ts +142 -0
- package/dist/learning-controls.js +135 -0
- package/dist/learning-input-context.d.ts +9 -0
- package/dist/learning-input-context.js +62 -0
- package/dist/learning-lifecycle.d.ts +10 -0
- package/dist/learning-lifecycle.js +11 -0
- package/dist/learning-overlays.d.ts +3 -0
- package/dist/learning-overlays.js +17 -0
- package/dist/learning-policy.d.ts +40 -0
- package/dist/learning-policy.js +38 -0
- package/dist/learning-records.d.ts +35 -0
- package/dist/learning-records.js +17 -0
- package/dist/learning-scorecard.d.ts +82 -0
- package/dist/learning-scorecard.js +86 -0
- package/dist/learning-serving.d.ts +29 -0
- package/dist/learning-serving.js +134 -0
- package/dist/linked-work.d.ts +9 -0
- package/dist/linked-work.js +28 -0
- package/dist/local-model-runtime.js +1 -1
- package/dist/mcp-command-progress.d.ts +16 -0
- package/dist/mcp-command-progress.js +70 -0
- package/dist/model-bridge.d.ts +14 -13
- package/dist/model-bridge.js +73 -41
- package/dist/orchestrator-supervisor.d.ts +111 -6
- package/dist/orchestrator-supervisor.js +67 -43
- package/dist/plan-proposal.js +3 -3
- package/dist/playbook-curator.d.ts +1 -0
- package/dist/playbook-curator.js +11 -9
- package/dist/process-identity.d.ts +8 -0
- package/dist/process-identity.js +38 -0
- package/dist/progress-command.d.ts +34 -0
- package/dist/progress-command.js +153 -0
- package/dist/prompts.js +15 -15
- package/dist/resources.js +2 -2
- package/dist/runtime-command.d.ts +8 -1
- package/dist/runtime-command.js +48 -7
- package/dist/runtime-executor.js +33 -11
- package/dist/runtime-profile-syntax.d.ts +6 -0
- package/dist/runtime-profile-syntax.js +121 -0
- package/dist/runtime-profile.d.ts +37 -5
- package/dist/runtime-profile.js +36 -129
- package/dist/runtime-tool-specs.js +1 -1
- package/dist/safe-edit.d.ts +7 -3
- package/dist/safe-edit.js +40 -47
- package/dist/schemas.js +4 -2
- package/dist/server.d.ts +2 -16
- package/dist/server.js +14 -20
- package/dist/shared.d.ts +2 -0
- package/dist/shared.js +2 -0
- package/dist/skill-auditor.d.ts +19 -0
- package/dist/skill-auditor.js +32 -2
- package/dist/skill-catalog.d.ts +4 -1
- package/dist/skill-catalog.js +6 -5
- package/dist/store/ace-packed-store.d.ts +5 -1
- package/dist/store/ace-packed-store.js +33 -1
- package/dist/store/bootstrap-store.js +19 -12
- package/dist/store/cache-workspace.d.ts +8 -0
- package/dist/store/cache-workspace.js +94 -27
- package/dist/store/importer.js +11 -20
- package/dist/store/knowledge-bake.d.ts +3 -1
- package/dist/store/knowledge-bake.js +10 -1
- package/dist/store/legacy-vericify-posts.d.ts +8 -0
- package/dist/store/legacy-vericify-posts.js +76 -0
- package/dist/store/materializers/hook-context-materializer.d.ts +2 -1
- package/dist/store/materializers/hook-context-materializer.js +6 -2
- package/dist/store/materializers/host-file-materializer.js +2 -2
- package/dist/store/materializers/projection-manager.d.ts +4 -1
- package/dist/store/materializers/projection-manager.js +10 -3
- package/dist/store/materializers/todo-syncer.d.ts +39 -20
- package/dist/store/materializers/todo-syncer.js +208 -110
- package/dist/store/materializers/vericify-projector.js +15 -26
- package/dist/store/repositories/goal-repository.d.ts +51 -2
- package/dist/store/repositories/goal-repository.js +114 -48
- package/dist/store/repositories/playbook-repository.d.ts +41 -3
- package/dist/store/repositories/playbook-repository.js +61 -5
- package/dist/store/repositories/todo-repository.d.ts +8 -2
- package/dist/store/repositories/todo-repository.js +1 -1
- package/dist/store/repositories/vericify-repository.d.ts +4 -0
- package/dist/store/repositories/vericify-repository.js +46 -0
- package/dist/store/skills-install.d.ts +1 -0
- package/dist/store/skills-install.js +20 -16
- package/dist/store/source-preservation.d.ts +24 -0
- package/dist/store/source-preservation.js +79 -0
- package/dist/store/store-artifacts.js +8 -1
- package/dist/store/test-write-boundary.d.ts +16 -0
- package/dist/store/test-write-boundary.js +175 -0
- package/dist/store/types.d.ts +5 -1
- package/dist/store/write-coordinator.js +16 -0
- package/dist/task-plan-context.d.ts +11 -0
- package/dist/task-plan-context.js +59 -0
- package/dist/todo-state.d.ts +19 -9
- package/dist/todo-state.js +204 -321
- package/dist/tools-agent.d.ts +1 -1
- package/dist/tools-agent.js +38 -62
- package/dist/tools-files.d.ts +0 -3
- package/dist/tools-files.js +21 -11
- package/dist/tools-framework.js +16 -25
- package/dist/tools-goal.d.ts +2 -4
- package/dist/tools-goal.js +119 -16
- package/dist/tools-handoff.d.ts +3 -2
- package/dist/tools-handoff.js +53 -23
- package/dist/tools-lifecycle.js +1 -1
- package/dist/tools-memory.d.ts +0 -3
- package/dist/tools-memory.js +51 -6
- package/dist/tools-scheduler.js +20 -17
- package/dist/tools-todo.js +160 -148
- package/dist/vericify-bridge.d.ts +8 -0
- package/dist/vericify-bridge.js +18 -0
- package/dist/vericify-context.d.ts +4 -0
- package/dist/vericify-context.js +26 -5
- package/dist/vericify-native.d.ts +101 -0
- package/dist/vericify-native.js +221 -0
- package/dist/verification-worker.d.ts +2 -0
- package/dist/verification-worker.js +31 -0
- package/docs/mcp-tool-reference.md +50 -10
- package/docs/troubleshooting.md +29 -0
- package/package.json +19 -6
- package/scripts/dev/run-tests-isolated.mjs +165 -0
- package/scripts/dev/run_hermes_crispr_live.mjs +1 -0
- package/scripts/dev/test-isolation-guard.mjs +20 -0
- package/scripts/dev/test-preload.mjs +153 -0
- package/scripts/dev/test-source-inventory.mjs +31 -0
- package/docs/development.md +0 -46
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
// Optional ACE consumer: all history selection remains owned by the Rust reader.
|
|
2
|
+
// No ACE state, process lifecycle, manifest parser or writable store is involved.
|
|
3
|
+
import { execFile } from 'node:child_process';
|
|
4
|
+
import { promisify } from 'node:util';
|
|
5
|
+
import { resolve } from 'node:path';
|
|
6
|
+
|
|
7
|
+
const execute = promisify(execFile);
|
|
8
|
+
const PROTOCOL = 'vericify-readonly-adapter/v1';
|
|
9
|
+
const U64_MAX = 18446744073709551615n;
|
|
10
|
+
const FAILURE_MESSAGES = Object.freeze({
|
|
11
|
+
stale_snapshot: 'The native snapshot changed; explicitly begin a new query.',
|
|
12
|
+
missing_history: 'Native history is missing.',
|
|
13
|
+
incompatible_protocol: 'Unsupported native adapter protocol or profile.',
|
|
14
|
+
capacity: 'The native query exceeded its work or output limit.',
|
|
15
|
+
timeout: 'The native query exceeded its elapsed-time limit.',
|
|
16
|
+
invalid_query: 'The native query is invalid.',
|
|
17
|
+
identity_mismatch: 'The native workspace identity does not match the query.',
|
|
18
|
+
expired: 'The selected native history has expired.',
|
|
19
|
+
unavailable: 'The native read-only query is unavailable.',
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
/** Safe failure metadata only. Raw native output and process errors are discarded. */
|
|
23
|
+
export class NativeReadError extends Error {
|
|
24
|
+
constructor(code, retryable = false, exitCode) {
|
|
25
|
+
const known = Object.hasOwn(FAILURE_MESSAGES, code) ? code : 'unavailable';
|
|
26
|
+
const suffix = Number.isInteger(exitCode) ? ` (exit ${exitCode})` : '';
|
|
27
|
+
super(`Native read-only query failed${suffix}: ${FAILURE_MESSAGES[known]}`);
|
|
28
|
+
this.name = 'NativeReadError';
|
|
29
|
+
this.code = known;
|
|
30
|
+
this.retryable = retryable === true;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function wireFailure(result, exitCode) {
|
|
35
|
+
if (result?.protocol !== PROTOCOL || !result?.error || !Object.hasOwn(FAILURE_MESSAGES, result.error.code)
|
|
36
|
+
|| typeof result.error.retryable !== 'boolean') return null;
|
|
37
|
+
return new NativeReadError(result.error.code, result.error.retryable, exitCode);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function requireValue(condition, message) {
|
|
41
|
+
if (!condition) {
|
|
42
|
+
const error = new NativeReadError('unavailable');
|
|
43
|
+
// Messages at this boundary are fixed validation text, never native values.
|
|
44
|
+
error.message += ` ${message}`;
|
|
45
|
+
throw error;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function bounded(value, lower, upper, name) {
|
|
49
|
+
requireValue(Number.isSafeInteger(value) && value >= lower && value <= upper, `${name} is outside its supported bounds`);
|
|
50
|
+
return String(value);
|
|
51
|
+
}
|
|
52
|
+
function decimal(value) {
|
|
53
|
+
requireValue(typeof value === 'string' && /^(0|[1-9][0-9]{0,19})$/.test(value) && BigInt(value) <= U64_MAX,
|
|
54
|
+
'Native reader returned an invalid decimal sequence');
|
|
55
|
+
return BigInt(value);
|
|
56
|
+
}
|
|
57
|
+
function binding(snapshot) {
|
|
58
|
+
const value = typeof snapshot === 'string' ? snapshot : snapshot?.binding;
|
|
59
|
+
requireValue(typeof value === 'string' && /^[0-9a-f]{64}$/.test(value), 'Provide the native snapshot binding from info');
|
|
60
|
+
return value;
|
|
61
|
+
}
|
|
62
|
+
function identifier(value) {
|
|
63
|
+
requireValue(typeof value === 'string' && value.length > 0 && value.length <= 512 && !/[\u0000-\u001f\u007f]/.test(value),
|
|
64
|
+
'Invalid native identity');
|
|
65
|
+
return value;
|
|
66
|
+
}
|
|
67
|
+
function position(value, snapshot) {
|
|
68
|
+
requireValue(value?.workspace_id === snapshot.workspace_id && value?.incarnation === snapshot.incarnation,
|
|
69
|
+
'Native window identity differs from its snapshot');
|
|
70
|
+
return decimal(value.sequence);
|
|
71
|
+
}
|
|
72
|
+
function canonicalRecord(value, snapshot, type) {
|
|
73
|
+
identifier(value?.id);
|
|
74
|
+
if (value.schema_version !== '1') throw new NativeReadError('incompatible_protocol');
|
|
75
|
+
requireValue(value.workspace_id === snapshot.workspace_id && value.incarnation === snapshot.incarnation,
|
|
76
|
+
'Native record identity differs from its snapshot');
|
|
77
|
+
const seq = decimal(value.seq);
|
|
78
|
+
requireValue(seq > 0n && seq <= decimal(snapshot.high_water), 'Native record sequence exceeds its snapshot');
|
|
79
|
+
requireValue(value.id === `${snapshot.incarnation}:${value.seq}`, 'Native canonical identity differs from its sequence');
|
|
80
|
+
if (type) requireValue(value.payload?.record_type === type, 'Native window record type differs from its collection');
|
|
81
|
+
return seq;
|
|
82
|
+
}
|
|
83
|
+
function decoded(result, maxBytes) {
|
|
84
|
+
requireValue(result.encoding === 'hex' && typeof result.bytes_hex === 'string' &&
|
|
85
|
+
result.bytes_hex.length <= maxBytes * 2 && /^(?:[0-9a-f]{2})*$/.test(result.bytes_hex),
|
|
86
|
+
'Native object exceeds its byte/encoding bound');
|
|
87
|
+
requireValue(decimal(result.blob.raw_length) === BigInt(result.bytes_hex.length / 2), 'Native object length differs from its canonical reference');
|
|
88
|
+
return { ...result, bytes: Buffer.from(result.bytes_hex, 'hex') };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Uses the installed native executable; ACE needs Node only for this optional client. */
|
|
92
|
+
export function openVericifyHistory({ executable, dataDir, timeoutMs = 15000 }) {
|
|
93
|
+
requireValue(typeof executable === 'string' && typeof dataDir === 'string', 'Provide an executable and a history directory');
|
|
94
|
+
bounded(timeoutMs, 1000, 60000, 'timeoutMs');
|
|
95
|
+
const binary = resolve(executable), directory = resolve(dataDir);
|
|
96
|
+
|
|
97
|
+
async function query(action, words = [], expected, outputCap) {
|
|
98
|
+
let stdout;
|
|
99
|
+
try {
|
|
100
|
+
({ stdout } = await execute(binary, ['adapter', action, '--data-dir', directory, ...words], {
|
|
101
|
+
encoding: 'utf8', timeout: timeoutMs,
|
|
102
|
+
maxBuffer: outputCap ?? (action === 'info' ? 16 : action === 'page' ? 1 : 4) * 1024 * 1024 + 65536,
|
|
103
|
+
windowsHide: true, killSignal: 'SIGKILL',
|
|
104
|
+
}));
|
|
105
|
+
} catch (error) {
|
|
106
|
+
if (error.code === 'ERR_CHILD_PROCESS_STDIO_MAXBUFFER') throw new NativeReadError('capacity', true);
|
|
107
|
+
if (error.killed || error.code === 'ETIMEDOUT') throw new NativeReadError('timeout', true);
|
|
108
|
+
let result;
|
|
109
|
+
try { result = JSON.parse(error.stdout); } catch { /* Discard private native text. */ }
|
|
110
|
+
if (result?.protocol !== undefined && result.protocol !== PROTOCOL) throw new NativeReadError('incompatible_protocol');
|
|
111
|
+
throw wireFailure(result, error.code) ?? new NativeReadError('unavailable', false, error.code);
|
|
112
|
+
}
|
|
113
|
+
let result;
|
|
114
|
+
try { result = JSON.parse(stdout); } catch { throw new NativeReadError('unavailable'); }
|
|
115
|
+
if (result?.protocol !== PROTOCOL) throw new NativeReadError('incompatible_protocol');
|
|
116
|
+
if (result.error) throw wireFailure(result) ?? new NativeReadError('unavailable');
|
|
117
|
+
const actual = binding(result.snapshot);
|
|
118
|
+
if (expected) requireValue(actual === expected, 'Native reader mixed history snapshots');
|
|
119
|
+
decimal(result.snapshot.high_water);
|
|
120
|
+
return result;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return Object.freeze({
|
|
124
|
+
async info() {
|
|
125
|
+
return query('info');
|
|
126
|
+
},
|
|
127
|
+
/** One native lifetime resolves root identity, canonical changes and later evidence. */
|
|
128
|
+
async window({ workspaceRoot, fromMs, toMs, after, limit = 100, maxBytes = 262144 }) {
|
|
129
|
+
requireValue(typeof workspaceRoot === 'string' && workspaceRoot.length > 0 && !workspaceRoot.includes('\0'),
|
|
130
|
+
'Provide a workspace root for the native window');
|
|
131
|
+
const from = decimal(fromMs), to = decimal(toMs);
|
|
132
|
+
requireValue(from <= to, 'Invalid native window time bounds');
|
|
133
|
+
const words = ['--workspace-root', resolve(workspaceRoot), '--from-ms', fromMs, '--to-ms', toMs,
|
|
134
|
+
'--limit', bounded(limit, 1, 100, 'limit'), '--max-bytes', bounded(maxBytes, 4096, 1024 * 1024, 'maxBytes')];
|
|
135
|
+
let start = 0n;
|
|
136
|
+
if (after !== undefined && after !== null) {
|
|
137
|
+
identifier(after.workspace_id); identifier(after.incarnation);
|
|
138
|
+
start = decimal(after.sequence);
|
|
139
|
+
words.push('--after-sequence', after.sequence, '--workspace-id', after.workspace_id, '--incarnation', after.incarnation);
|
|
140
|
+
}
|
|
141
|
+
const result = await query('window', words, undefined, maxBytes);
|
|
142
|
+
if (result.snapshot.record_profile !== 'vericify/records/v1') throw new NativeReadError('incompatible_protocol');
|
|
143
|
+
identifier(result.snapshot.workspace_id); identifier(result.snapshot.incarnation);
|
|
144
|
+
const high = position(result.high_water, result.snapshot), next = position(result.next_after, result.snapshot);
|
|
145
|
+
const scanned = position(result.scanned_through, result.snapshot);
|
|
146
|
+
requireValue(high === decimal(result.snapshot.high_water) && start <= next && next <= scanned && scanned <= high,
|
|
147
|
+
'Native window sequence bounds disagree');
|
|
148
|
+
const scope = result.query;
|
|
149
|
+
requireValue(scope?.workspace_id === result.snapshot.workspace_id && scope?.incarnation === result.snapshot.incarnation
|
|
150
|
+
&& (!after || (after.workspace_id === scope.workspace_id && after.incarnation === scope.incarnation)),
|
|
151
|
+
'Native window query identity differs from its snapshot');
|
|
152
|
+
requireValue(scope.from_ms === fromMs && scope.to_ms === toMs && decimal(scope.after_sequence) === start,
|
|
153
|
+
'Native window query differs from the requested window');
|
|
154
|
+
requireValue(Array.isArray(result.changes) && result.changes.length <= limit && Array.isArray(result.coverage)
|
|
155
|
+
&& Array.isArray(result.host_gaps)
|
|
156
|
+
&& Array.isArray(result.expired_ranges) && Array.isArray(result.evidence?.links)
|
|
157
|
+
&& Array.isArray(result.evidence.host) && Array.isArray(result.evidence.purposes)
|
|
158
|
+
&& Array.isArray(result.evidence.unavailable), 'Invalid native window collections');
|
|
159
|
+
requireValue(typeof result.scan_complete === 'boolean' && typeof result.evidence.complete === 'boolean'
|
|
160
|
+
&& result.scan_complete === (scanned === high)
|
|
161
|
+
&& (result.evidence.complete ? next === scanned : next === start)
|
|
162
|
+
&& result.complete === (result.scan_complete && result.evidence.complete),
|
|
163
|
+
'Native window completion disagrees with its progress or evidence');
|
|
164
|
+
const expired = result.expired_ranges.length > 0 || result.evidence.unavailable.some(item => item?.reason === 'expired');
|
|
165
|
+
requireValue(['complete', 'pending', 'expired'].includes(result.state)
|
|
166
|
+
&& (result.state !== 'complete' || (result.complete && !expired))
|
|
167
|
+
&& (result.state !== 'pending' || !result.complete)
|
|
168
|
+
&& (result.state !== 'expired' || (result.complete && expired)), 'Invalid native window state');
|
|
169
|
+
const changes = new Map();
|
|
170
|
+
for (const item of result.changes) {
|
|
171
|
+
canonicalRecord(item.record, result.snapshot, 'observed_file_change');
|
|
172
|
+
requireValue(!changes.has(item.record.id), 'Native window repeated a change identity');
|
|
173
|
+
const data = item.record.payload.data;
|
|
174
|
+
const observedFrom = decimal(data?.observed_from), observedTo = decimal(data?.observed_to);
|
|
175
|
+
requireValue(observedFrom <= observedTo && observedFrom <= to && observedTo >= from,
|
|
176
|
+
'Native change is outside the requested window');
|
|
177
|
+
if (item.latest_link !== null) {
|
|
178
|
+
const seq = canonicalRecord(item.latest_link, result.snapshot, 'change_evidence_link');
|
|
179
|
+
requireValue(item.latest_link.payload.data?.change_id === item.record.id && seq > decimal(item.record.seq),
|
|
180
|
+
'Native enrichment does not name its observed change');
|
|
181
|
+
}
|
|
182
|
+
changes.set(item.record.id, item.latest_link);
|
|
183
|
+
}
|
|
184
|
+
const links = new Map();
|
|
185
|
+
for (const link of result.evidence.links) {
|
|
186
|
+
canonicalRecord(link, result.snapshot, 'change_evidence_link');
|
|
187
|
+
requireValue(changes.has(link.payload.data?.change_id) && !links.has(link.id), 'Native evidence contains unrelated enrichment');
|
|
188
|
+
links.set(link.id, link);
|
|
189
|
+
}
|
|
190
|
+
for (const link of changes.values()) if (link !== null) {
|
|
191
|
+
requireValue(links.has(link.id) && JSON.stringify(links.get(link.id)) === JSON.stringify(link),
|
|
192
|
+
'Native enrichment differs from its canonical evidence');
|
|
193
|
+
}
|
|
194
|
+
for (const [records, type] of [[result.coverage, 'coverage_event'], [result.evidence.host, 'host_evidence'],
|
|
195
|
+
[result.evidence.purposes, 'purpose_evidence']]) {
|
|
196
|
+
for (const record of records) canonicalRecord(record, result.snapshot, type);
|
|
197
|
+
}
|
|
198
|
+
for (const gap of result.host_gaps) {
|
|
199
|
+
canonicalRecord(gap, result.snapshot, 'host_evidence');
|
|
200
|
+
const observedAt = decimal(gap.payload.data?.observed_at);
|
|
201
|
+
requireValue(gap.payload.data?.kind === 'observation_gap' && observedAt >= from && observedAt <= to,
|
|
202
|
+
'Native host gap is outside its kind or requested time window');
|
|
203
|
+
}
|
|
204
|
+
for (const missing of result.evidence.unavailable) { identifier(missing?.id); identifier(missing?.reason); }
|
|
205
|
+
let previous = 0n;
|
|
206
|
+
for (const range of result.expired_ranges) {
|
|
207
|
+
const first = decimal(range.first), last = decimal(range.last);
|
|
208
|
+
requireValue(first > previous && first <= last && last <= high, 'Native window expiry sequence bounds disagree');
|
|
209
|
+
previous = last;
|
|
210
|
+
}
|
|
211
|
+
return result;
|
|
212
|
+
},
|
|
213
|
+
async page({ snapshot, cursor, limit = 128, maxBytes = 262144 }) {
|
|
214
|
+
const expected = binding(snapshot);
|
|
215
|
+
const words = ['--snapshot', expected, '--limit', bounded(limit, 1, 1024, 'limit'),
|
|
216
|
+
'--max-bytes', bounded(maxBytes, 4096, 1024 * 1024, 'maxBytes')];
|
|
217
|
+
if (cursor !== undefined && cursor !== null) {
|
|
218
|
+
requireValue(typeof cursor === 'string' && cursor.length > 0 && cursor.length <= 4096, 'Invalid native cursor');
|
|
219
|
+
words.push('--cursor', cursor);
|
|
220
|
+
}
|
|
221
|
+
const result = await query('page', words, expected);
|
|
222
|
+
requireValue(Array.isArray(result.records) && Array.isArray(result.expired_ranges), 'Invalid native page');
|
|
223
|
+
const start = decimal(result.after_sequence), end = decimal(result.through_sequence);
|
|
224
|
+
requireValue(start <= end && end <= decimal(result.snapshot.high_water), 'Native page sequence bounds disagree');
|
|
225
|
+
let previous = start;
|
|
226
|
+
for (const record of result.records) {
|
|
227
|
+
const sequence = decimal(record.seq);
|
|
228
|
+
requireValue(sequence > previous && sequence <= end, 'Native records lost their global sequence order');
|
|
229
|
+
requireValue(record.workspace_id === result.snapshot.workspace_id && record.incarnation === result.snapshot.incarnation,
|
|
230
|
+
'Native record belongs to another history');
|
|
231
|
+
previous = sequence;
|
|
232
|
+
}
|
|
233
|
+
requireValue(typeof result.complete === 'boolean', 'Native page omitted completion state');
|
|
234
|
+
requireValue(result.complete === (end === decimal(result.snapshot.high_water)), 'Native completion does not match its high-water sequence');
|
|
235
|
+
const coverage = [...result.records.map(record => ({ first: decimal(record.seq), last: decimal(record.seq) })),
|
|
236
|
+
...result.expired_ranges.map(range => ({ first: decimal(range.first), last: decimal(range.last) }))]
|
|
237
|
+
.sort((a, b) => a.first < b.first ? -1 : 1);
|
|
238
|
+
requireValue(coverage.length <= limit, 'Native page exceeds its entry bound');
|
|
239
|
+
let through = start;
|
|
240
|
+
for (const entry of coverage) {
|
|
241
|
+
requireValue(entry.first === through + 1n && entry.last >= entry.first && entry.last <= end,
|
|
242
|
+
'Native page contains an overlapping or unexplained history gap');
|
|
243
|
+
through = entry.last;
|
|
244
|
+
}
|
|
245
|
+
requireValue(through === end && (result.complete || end > start), 'Native page omitted coverage or progress');
|
|
246
|
+
requireValue(result.complete ? result.next_cursor === null : typeof result.next_cursor === 'string' && result.next_cursor.length > 0,
|
|
247
|
+
'Native page omitted its continuation');
|
|
248
|
+
return result;
|
|
249
|
+
},
|
|
250
|
+
async object({ snapshot, changeId, side = 'after', maxBytes = 2 * 1024 * 1024 }) {
|
|
251
|
+
const expected = binding(snapshot);
|
|
252
|
+
requireValue(typeof changeId === 'string' && changeId.length > 0 && changeId.length <= 512, 'Provide a canonical change ID');
|
|
253
|
+
requireValue(['before', 'after', 'patch'].includes(side), 'Unsupported retained object side');
|
|
254
|
+
const result = await query('object', ['--snapshot', expected, '--change-id', changeId, '--side', side,
|
|
255
|
+
'--max-bytes', bounded(maxBytes, 1, 2 * 1024 * 1024, 'maxBytes')], expected);
|
|
256
|
+
requireValue(result.record_id === changeId && result.side === side && result.encoding === 'hex', 'Native object does not bind this selection');
|
|
257
|
+
return decoded(result, maxBytes);
|
|
258
|
+
},
|
|
259
|
+
async blob({ snapshot, recordId, digest, maxBytes = 2 * 1024 * 1024 }) {
|
|
260
|
+
const expected = binding(snapshot);
|
|
261
|
+
requireValue(typeof recordId === 'string' && recordId.length > 0 && recordId.length <= 512, 'Provide a canonical record ID');
|
|
262
|
+
requireValue(typeof digest === 'string' && /^[0-9a-f]{64}$/.test(digest), 'Provide a canonical BLAKE3 digest');
|
|
263
|
+
const result = await query('object', ['--snapshot', expected, '--record-id', recordId, '--blob-digest', digest,
|
|
264
|
+
'--max-bytes', bounded(maxBytes, 1, 2 * 1024 * 1024, 'maxBytes')], expected);
|
|
265
|
+
requireValue(result.record_id === recordId && result.side === null && result.blob?.digest === digest,
|
|
266
|
+
'Native blob does not bind this retained record');
|
|
267
|
+
return decoded(result, maxBytes);
|
|
268
|
+
},
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
export { PROTOCOL };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"source": "vericify/integrations/ace/vericify-reader.mjs",
|
|
3
|
+
"protocol": "vericify-readonly-adapter/v1",
|
|
4
|
+
"sha256": "e7d8e7b864e42426d89a7a5c418f2e75f5cdd6c7fd6af4d3114e00cbe1f8ed69",
|
|
5
|
+
"vendor_policy": "Exact maintained read-only client bytes; update only by explicit source comparison. No sibling checkout at runtime.",
|
|
6
|
+
"license": "Apache-2.0",
|
|
7
|
+
"source_commit": "2cc636ceb19e48c01891957afac446db131ba16f",
|
|
8
|
+
"license_file": "LICENSE",
|
|
9
|
+
"license_authorization": "Authorized by the Vericify owner on 2026-10-21 for distribution of this reader file only, inside @voybio/ace-swarm under the package Apache-2.0 LICENSE. No other Vericify source, binary or package is licensed by this record."
|
|
10
|
+
}
|
package/dist/ace-autonomy.d.ts
CHANGED
|
@@ -1,3 +1,61 @@
|
|
|
1
|
+
import type { GoalRecord } from './store/repositories/goal-repository.js';
|
|
2
|
+
import type { TaskPlan } from './orchestrator-supervisor.js';
|
|
3
|
+
export interface CurrentWorkItem {
|
|
4
|
+
kind: "goal_step" | "todo" | "handoff";
|
|
5
|
+
id: string;
|
|
6
|
+
title: string;
|
|
7
|
+
goal_id?: string;
|
|
8
|
+
assignee?: string;
|
|
9
|
+
since?: string;
|
|
10
|
+
last_ace_call_at?: string;
|
|
11
|
+
reason?: string;
|
|
12
|
+
job_id?: string;
|
|
13
|
+
handoff_id?: string;
|
|
14
|
+
trace_id?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface CurrentWork {
|
|
17
|
+
state: "active" | "waiting" | "blocked" | "ready" | "completed" | "none" | "ambiguous";
|
|
18
|
+
objective?: {
|
|
19
|
+
text: string;
|
|
20
|
+
source: "goal" | "task_md";
|
|
21
|
+
goal_id?: string;
|
|
22
|
+
};
|
|
23
|
+
doing: CurrentWorkItem[];
|
|
24
|
+
next: CurrentWorkItem[];
|
|
25
|
+
blocked: CurrentWorkItem[];
|
|
26
|
+
waiting: CurrentWorkItem[];
|
|
27
|
+
other_goals: {
|
|
28
|
+
goal_id: string;
|
|
29
|
+
objective: string;
|
|
30
|
+
updated_at: string;
|
|
31
|
+
}[];
|
|
32
|
+
last_completed?: {
|
|
33
|
+
text: string;
|
|
34
|
+
at?: string;
|
|
35
|
+
};
|
|
36
|
+
sources: string[];
|
|
37
|
+
issues?: string[];
|
|
38
|
+
idle_learning?: string;
|
|
39
|
+
pending_learning_approval?: number;
|
|
40
|
+
}
|
|
41
|
+
export interface CurrentWorkInput {
|
|
42
|
+
workspaceRoot?: string;
|
|
43
|
+
goal_id?: string;
|
|
44
|
+
agent?: string;
|
|
45
|
+
goal_snapshot?: {
|
|
46
|
+
goal: GoalRecord;
|
|
47
|
+
plan: TaskPlan;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/** Canonical records only; never initializes state or writes a projection. */
|
|
51
|
+
export declare function resolveCurrentWork(input?: CurrentWorkInput): CurrentWork;
|
|
52
|
+
export declare function formatCurrentWork(work: CurrentWork, mode: "line" | "block"): string;
|
|
53
|
+
/** Select related events by identifiers; source records are never changed. */
|
|
54
|
+
export declare function readCurrentWorkContext(input?: CurrentWorkInput): {
|
|
55
|
+
work: CurrentWork;
|
|
56
|
+
events: import("./status-events.js").StatusEventRecord[];
|
|
57
|
+
entries: import("./run-ledger.js").RunLedgerEntry[];
|
|
58
|
+
};
|
|
1
59
|
export declare const ACE_TASK_CONTRACT_FILES: readonly ["agent-state/TASK.md", "agent-state/SCOPE.md", "agent-state/QUALITY_GATES.md", "agent-state/HANDOFF.json"];
|
|
2
60
|
export declare const ACE_DEFAULT_STATE_SOURCES: readonly ["agent-state/TASK.md", "agent-state/SCOPE.md", "agent-state/QUALITY_GATES.md", "agent-state/STATUS.md", "agent-state/HANDOFF.json", "agent-state/EVIDENCE_LOG.md"];
|
|
3
61
|
export declare const ACE_DEFAULT_CONTINUITY_SOURCES: readonly ["agent-state/TASK.md", "agent-state/SCOPE.md", "agent-state/QUALITY_GATES.md", "agent-state/HANDOFF.json", "agent-state/STATUS.md", "agent-state/DECISIONS.md", "agent-state/RISKS.md", "agent-state/EVIDENCE_LOG.md"];
|
package/dist/ace-autonomy.js
CHANGED
|
@@ -1,8 +1,210 @@
|
|
|
1
|
+
import { readLearningPolicy } from './learning-policy.js';
|
|
1
2
|
import { resolveWorkspaceRoot, safeRead, wsPath } from "./helpers.js";
|
|
2
3
|
import { readRunLedger } from "./run-ledger.js";
|
|
3
4
|
import { isReadError } from "./shared.js";
|
|
4
5
|
import { readStatusEvents } from "./status-events.js";
|
|
5
|
-
import { getWorkspaceStorePath, readStoreJsonSync, storeExistsSync, toVirtualStorePath, } from "./store/store-snapshot.js";
|
|
6
|
+
import { getWorkspaceStorePath, readStoreJsonSync, listStoreKeysSync, storeExistsSync, toVirtualStorePath, } from "./store/store-snapshot.js";
|
|
7
|
+
import { readStoreBlobSync } from "./store/store-snapshot.js";
|
|
8
|
+
import { readAceLogicalFile } from "./ace-state-resolver.js";
|
|
9
|
+
import { GoalRepository } from "./store/repositories/goal-repository.js";
|
|
10
|
+
import { getReadyTaskSteps } from "./task-plan-context.js";
|
|
11
|
+
import { readHandoffRegistry } from "./handoff-registry.js";
|
|
12
|
+
import { withWorkspaceRoot } from "./helpers/workspace-root.js";
|
|
13
|
+
import { createHash } from "node:crypto";
|
|
14
|
+
import { deriveLinkedTodo } from './linked-work.js';
|
|
15
|
+
function workText(value, length = 120) {
|
|
16
|
+
const text = value.replace(/\s+/g, " ").trim();
|
|
17
|
+
if (text.length <= length)
|
|
18
|
+
return text;
|
|
19
|
+
const prefix = text.slice(0, length - 1);
|
|
20
|
+
return `${prefix.slice(0, Math.max(0, prefix.lastIndexOf(" ")))}…`;
|
|
21
|
+
}
|
|
22
|
+
/** Canonical records only; never initializes state or writes a projection. */
|
|
23
|
+
export function resolveCurrentWork(input = {}) {
|
|
24
|
+
const root = input.workspaceRoot ?? resolveWorkspaceRoot();
|
|
25
|
+
return withWorkspaceRoot(root, () => {
|
|
26
|
+
const work = { state: "none", doing: [], next: [], blocked: [], waiting: [], other_goals: [], sources: [] };
|
|
27
|
+
const repo = new GoalRepository(root);
|
|
28
|
+
const goals = repo.listGoals();
|
|
29
|
+
if (input.goal_snapshot) {
|
|
30
|
+
const g = input.goal_snapshot.goal;
|
|
31
|
+
const index = goals.findIndex(row => row.goal_id === g.goal_id);
|
|
32
|
+
const entry = { goal_id: g.goal_id, status: g.status, objective: g.objective, updated_at: g.updated_at };
|
|
33
|
+
if (index < 0)
|
|
34
|
+
goals.push(entry);
|
|
35
|
+
else
|
|
36
|
+
goals[index] = entry;
|
|
37
|
+
}
|
|
38
|
+
const active = goals.filter((g) => g.goal_id !== "G-learning" && ["executing", "verifying", "blocked"].includes(g.status));
|
|
39
|
+
const stopped = goals.filter(g => g.goal_id !== 'G-learning' && g.status === 'stopped');
|
|
40
|
+
const stoppedIds = new Set(stopped.map(g => g.goal_id));
|
|
41
|
+
const matched = input.agent ? active.filter((g) => repo.getPlan(g.goal_id)?.steps.some((s) => s.status === "running" && s.assignee === input.agent)) : [];
|
|
42
|
+
const selected = input.goal_id && input.goal_id !== "G-learning" ? goals.find((g) => g.goal_id === input.goal_id) : matched.length === 1 ? matched[0] : active.length === 1 ? active[0] : active.length === 0 && stopped.length === 1 ? stopped[0] : undefined;
|
|
43
|
+
const ambiguous = !selected && active.length > 1;
|
|
44
|
+
work.other_goals = active.filter((g) => g.goal_id !== selected?.goal_id).map((g) => ({ goal_id: g.goal_id, objective: g.objective, updated_at: g.updated_at }));
|
|
45
|
+
if (selected?.status === "done") {
|
|
46
|
+
work.last_completed = { text: selected.objective, at: selected.updated_at };
|
|
47
|
+
}
|
|
48
|
+
else if (selected) {
|
|
49
|
+
work.objective = { text: selected.objective, source: "goal", goal_id: selected.goal_id };
|
|
50
|
+
work.sources.push(`goal:${selected.goal_id}@${selected.updated_at}`);
|
|
51
|
+
const plan = input.goal_snapshot?.goal.goal_id === selected.goal_id ? input.goal_snapshot.plan : repo.getPlan(selected.goal_id);
|
|
52
|
+
if (!plan)
|
|
53
|
+
work.issues = [`Plan unavailable for ${selected.goal_id}`];
|
|
54
|
+
const ready = new Set(plan ? getReadyTaskSteps(plan).map((s) => s.step_id) : []);
|
|
55
|
+
for (const step of plan?.steps ?? []) {
|
|
56
|
+
if (selected.status === 'stopped')
|
|
57
|
+
continue;
|
|
58
|
+
const extra = step;
|
|
59
|
+
const item = { kind: "goal_step", id: step.step_id, title: workText(step.task.split(/\r?\n/)[0]), goal_id: selected.goal_id, assignee: extra.assignee, since: extra.started_at, reason: extra.waiting_on ?? step.blocked_reason, job_id: step.job_id, handoff_id: step.handoff_id, trace_id: extra.trace_id };
|
|
60
|
+
if (step.status === "running") {
|
|
61
|
+
item.last_ace_call_at = step.last_ace_call_at ?? step.started_at;
|
|
62
|
+
work.doing.push(item);
|
|
63
|
+
}
|
|
64
|
+
else if (step.status === "blocked" || step.status === "failed")
|
|
65
|
+
(extra.waiting_on ? work.waiting : work.blocked).push(item);
|
|
66
|
+
else if (ready.has(step.step_id))
|
|
67
|
+
work.next.push(item);
|
|
68
|
+
}
|
|
69
|
+
if (selected.status === "blocked" && work.blocked.length === 0)
|
|
70
|
+
work.blocked.push({ kind: "goal_step", id: selected.goal_id, goal_id: selected.goal_id, title: selected.objective, reason: repo.getGoal(selected.goal_id)?.reason_code ?? "Goal is blocked" });
|
|
71
|
+
}
|
|
72
|
+
else if (input.goal_id && input.goal_id !== "G-learning") {
|
|
73
|
+
work.issues = [`Unknown goal: ${input.goal_id}`];
|
|
74
|
+
}
|
|
75
|
+
for (const goal of stopped)
|
|
76
|
+
work.waiting.push({ kind: 'goal_step', id: goal.goal_id, goal_id: goal.goal_id,
|
|
77
|
+
title: `stopped ${goal.goal_id}`, reason: 'user:resume' });
|
|
78
|
+
const registry = readHandoffRegistry();
|
|
79
|
+
const hasHandoffRecords = listStoreKeysSync(root, "state/handoffs/").some((key) => key !== "state/handoffs/index");
|
|
80
|
+
const handoffs = hasHandoffRecords ? registry.handoffs : readStoreJsonSync(root, "state/artifacts/agent-state/handoff-registry.json")?.handoffs ?? registry.handoffs;
|
|
81
|
+
if (Object.keys(handoffs).length)
|
|
82
|
+
work.sources.push("state/handoffs");
|
|
83
|
+
for (const handoff of Object.values(handoffs)) {
|
|
84
|
+
if (handoff.step_ref && stoppedIds.has(handoff.step_ref.goal_id))
|
|
85
|
+
continue;
|
|
86
|
+
const item = { kind: "handoff", id: handoff.handoff_id, title: workText(handoff.title), assignee: handoff.to, since: handoff.updated_at };
|
|
87
|
+
if (handoff.status === "accepted")
|
|
88
|
+
work.doing.push(item);
|
|
89
|
+
else if (handoff.status === "open")
|
|
90
|
+
work.waiting.push(item);
|
|
91
|
+
else if (handoff.status === "blocked")
|
|
92
|
+
work.blocked.push({ ...item, reason: handoff.history?.at(-1)?.note });
|
|
93
|
+
}
|
|
94
|
+
const ids = readStoreJsonSync(root, "state/todo/index");
|
|
95
|
+
const legacyTodos = ids === undefined ? readStoreJsonSync(root, "state/artifacts/agent-state/todo-state.json") : undefined;
|
|
96
|
+
const todos = (ids ? ids.map((id) => readStoreJsonSync(root, `state/todo/${id}`)).filter((t) => !!t) : Object.values(legacyTodos?.nodes ?? {})).map(t => deriveLinkedTodo(t, root, input.goal_snapshot));
|
|
97
|
+
if (todos.length)
|
|
98
|
+
work.sources.push("state/todo");
|
|
99
|
+
const completed = new Set(todos.filter((t) => t.status === "done").map((t) => t.id));
|
|
100
|
+
for (const todo of todos) {
|
|
101
|
+
if (todo.step_ref && stoppedIds.has(todo.step_ref.goal_id))
|
|
102
|
+
continue;
|
|
103
|
+
const item = { kind: "todo", id: todo.id, title: workText(todo.title), assignee: todo.assigned_to, reason: todo.notes };
|
|
104
|
+
if (todo.status === "in_progress")
|
|
105
|
+
work.doing.push(item);
|
|
106
|
+
else if (["blocked", "gated"].includes(todo.status))
|
|
107
|
+
work.blocked.push(item);
|
|
108
|
+
else if (["pending", "backlog"].includes(todo.status) && (todo.depends_on ?? []).every((id) => completed.has(id)))
|
|
109
|
+
work.next.push(item);
|
|
110
|
+
}
|
|
111
|
+
if (active.length === 0 && !input.goal_id && !selected) {
|
|
112
|
+
const task = readStoreBlobSync(root, "state/artifacts/agent-state/TASK.md") ?? readAceLogicalFile(root, "agent-state/TASK.md");
|
|
113
|
+
if (task?.trim()) {
|
|
114
|
+
const objective = task.match(/^##\s+Objective[^\n]*\n+([^#\n][^\n]*)/im)?.[1]?.trim() ?? task.split(/\r?\n/).map((line) => line.trim()).find((line) => line && !line.startsWith("#") && !/^-\s+\[/.test(line)) ?? task.match(/^#\s+(.+)$/m)?.[1]?.trim() ?? "Workspace task";
|
|
115
|
+
const criteria = [...task.matchAll(/^\s*-\s+\[([ xX])\]/gm)];
|
|
116
|
+
work.sources.push(`task_md:sha256:${createHash("sha256").update(task).digest("hex")}`);
|
|
117
|
+
if (criteria.length > 0 && criteria.every((m) => m[1].toLowerCase() === "x")) {
|
|
118
|
+
const status = readStoreBlobSync(root, "state/artifacts/agent-state/STATUS.md") ?? readAceLogicalFile(root, "agent-state/STATUS.md") ?? "";
|
|
119
|
+
work.last_completed = { text: objective, at: status.split("\n").find((l) => /complet|release|verif/i.test(l))?.match(/\d{4}-\d{2}-\d{2}/)?.[0] };
|
|
120
|
+
}
|
|
121
|
+
else
|
|
122
|
+
work.objective = { text: objective, source: "task_md" };
|
|
123
|
+
}
|
|
124
|
+
if (!work.last_completed && !work.objective) {
|
|
125
|
+
const done = goals.find((g) => g.goal_id !== "G-learning" && g.status === "done");
|
|
126
|
+
if (done)
|
|
127
|
+
work.last_completed = { text: done.objective, at: done.updated_at };
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
const learningPolicy = readLearningPolicy(root);
|
|
131
|
+
if (learningPolicy.issue && !work.doing.length && !work.next.length)
|
|
132
|
+
work.idle_learning = `idle · learning unavailable: ${workText(learningPolicy.issue, 120)}`;
|
|
133
|
+
if (learningPolicy.enabled && !work.doing.length && !work.next.length) {
|
|
134
|
+
const pending = repo.getPlan('G-learning')?.steps.find(s => ['planned', 'running'].includes(s.status));
|
|
135
|
+
const proposed = listStoreKeysSync(root, 'knowledge/').filter(k => k.endsWith('/LEARNED.md') || /\/references\/proposals\/[^/]+\.md$/.test(k)).filter(k => /^status: proposed$/m.test(readStoreBlobSync(root, k) ?? ''));
|
|
136
|
+
if (proposed.length)
|
|
137
|
+
work.pending_learning_approval = proposed.length;
|
|
138
|
+
const learningGoal = repo.getGoal('G-learning');
|
|
139
|
+
if (learningGoal?.status === 'blocked' && !pending)
|
|
140
|
+
work.idle_learning = `idle · learning blocked: ${workText(learningGoal.reason_code ?? 'inspect G-learning', 100)}`;
|
|
141
|
+
if (pending)
|
|
142
|
+
work.idle_learning = `idle · 1 learning step available (${pending.step_id}: ${workText(pending.learning_scope ?? pending.task, 70)})`;
|
|
143
|
+
}
|
|
144
|
+
work.state = ambiguous ? "ambiguous" : work.issues?.length ? "blocked" : work.doing.length ? "active" : work.next.length ? "ready" : work.blocked.length ? "blocked" : work.waiting.length ? "waiting" : work.objective ? "ready" : work.last_completed ? "completed" : "none";
|
|
145
|
+
return work;
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
export function formatCurrentWork(work, mode) {
|
|
149
|
+
if (mode === "line") {
|
|
150
|
+
const current = work.state === "ambiguous" ? `${work.other_goals.length} active goals (${work.other_goals.map((g) => g.goal_id).join(", ")}); choose goal_id` : work.objective ? `${work.objective.goal_id ?? ""} ${work.objective.text}`.trim() : work.doing.length || work.next.length || work.waiting.length || work.blocked.length ? "workspace work" : "no active work";
|
|
151
|
+
// Bound the objective independently so it cannot erase the current step.
|
|
152
|
+
const subject = work.doing.length || work.next.length || work.waiting.length ? workText(current, 110) : current;
|
|
153
|
+
const wait = work.waiting[0];
|
|
154
|
+
const waitTitle = wait?.reason === 'user:resume' && wait.goal_id === work.objective?.goal_id ? 'stopped' : wait?.title;
|
|
155
|
+
return workText(`ACE now: ${subject}${work.doing[0] ? ` · doing ${work.doing[0].id} ${workText(work.doing[0].title, 70)}` : ""}${work.next[0] ? ` · next ${work.next[0].id} ${work.next[0].title}` : ""}${work.blocked.length ? ` · ${work.blocked.length} blocked` : ""}${wait ? ` · waiting ${waitTitle}${wait.reason === 'user:resume' ? ' [user:resume]' : ''}` : ""}${work.issues?.length ? ` · ${work.issues.join("; ")}` : ""}${work.idle_learning ? ` · ${work.idle_learning}` : ""}${work.last_completed ? ` · last objective completed${work.last_completed.at ? ` ${work.last_completed.at}` : ""} (${work.last_completed.text})` : ""}`, 220);
|
|
156
|
+
}
|
|
157
|
+
const lines = [formatCurrentWork(work, "line")];
|
|
158
|
+
if (work.idle_learning)
|
|
159
|
+
lines.push(work.idle_learning);
|
|
160
|
+
if (work.pending_learning_approval)
|
|
161
|
+
lines.push(`${work.pending_learning_approval} learned instruction/overlay awaiting operator approval`);
|
|
162
|
+
if (work.last_completed)
|
|
163
|
+
lines.push(`Last objective completed${work.last_completed.at ? ` ${work.last_completed.at}` : ""}: ${work.last_completed.text}`);
|
|
164
|
+
if (work.objective)
|
|
165
|
+
lines.push(`Objective: ${work.objective.text}`);
|
|
166
|
+
for (const [label, items] of [["Doing", work.doing.slice(0, 3)], ["Next", work.next.slice(0, 3)], ["Blocked", work.blocked.slice(0, 5)], ["Waiting", work.waiting.slice(0, 5)]]) {
|
|
167
|
+
for (const item of items)
|
|
168
|
+
lines.push(`${label}: ${item.id} ${item.title}${item.assignee ? ` (${item.assignee})` : ""}${item.since ? ` [last touched ${item.since}${Date.now() - Date.parse(item.since) > 86400000 ? '; stale' : ''}]` : ""}${item.last_ace_call_at ? ` [step age ${Math.max(0, Math.floor((Date.now() - Date.parse(item.since ?? item.last_ace_call_at)) / 60000))}m; last ACE call ${item.last_ace_call_at}; host progress unobserved]` : ''}${item.reason ? ` — ${workText(item.reason, 220)}` : ""}`);
|
|
169
|
+
}
|
|
170
|
+
if (work.other_goals.length)
|
|
171
|
+
lines.push(`Other goals: ${work.other_goals.map((g) => `${g.goal_id} ${g.objective}`).join("; ")}`);
|
|
172
|
+
for (const issue of work.issues ?? [])
|
|
173
|
+
lines.push(`Context incomplete: ${issue}`);
|
|
174
|
+
return lines.join("\n");
|
|
175
|
+
}
|
|
176
|
+
/** Select related events by identifiers; source records are never changed. */
|
|
177
|
+
export function readCurrentWorkContext(input = {}) {
|
|
178
|
+
const root = input.workspaceRoot ?? resolveWorkspaceRoot();
|
|
179
|
+
return withWorkspaceRoot(root, () => {
|
|
180
|
+
const work = resolveCurrentWork({ ...input, workspaceRoot: root });
|
|
181
|
+
const items = [...work.doing, ...work.next, ...work.blocked, ...work.waiting];
|
|
182
|
+
const goalIds = new Set(work.objective?.goal_id ? [work.objective.goal_id] : work.other_goals.map((g) => g.goal_id));
|
|
183
|
+
const ids = new Set(items.flatMap((i) => [i.id, i.job_id, i.handoff_id, i.trace_id]).filter((v) => !!v));
|
|
184
|
+
const active = goalIds.size > 0 || items.length > 0;
|
|
185
|
+
const related = (row) => {
|
|
186
|
+
const record = row;
|
|
187
|
+
const fields = { ...record, ...(coerceObject(record.payload) ?? {}), ...(coerceObject(record.metadata) ?? {}) };
|
|
188
|
+
const linkedGoal = readString(fields.goal_id) ?? readString(fields.objective_id);
|
|
189
|
+
// Step names are only unique within a goal. A sibling goal's S1 is not our S1.
|
|
190
|
+
if (linkedGoal && goalIds.size && !goalIds.has(linkedGoal))
|
|
191
|
+
return false;
|
|
192
|
+
if (linkedGoal && goalIds.has(linkedGoal))
|
|
193
|
+
return true;
|
|
194
|
+
const directLink = ["handoff_id", "todo_id", "job_id", "trace_id"].some((key) => typeof fields[key] === "string" && ids.has(fields[key]));
|
|
195
|
+
if (directLink)
|
|
196
|
+
return true;
|
|
197
|
+
if (active)
|
|
198
|
+
return false;
|
|
199
|
+
// With no active work, show recent state transitions with work identity.
|
|
200
|
+
return ["step_id", "todo_id", "handoff_id"].some((key) => typeof fields[key] === "string") &&
|
|
201
|
+
["status", "from_status", "to_status", "previous_status", "next_status", "from", "to"].some((key) => typeof fields[key] === "string");
|
|
202
|
+
};
|
|
203
|
+
const events = readStatusEvents(Number.MAX_SAFE_INTEGER).filter(related);
|
|
204
|
+
const entries = readRunLedger().entries.filter(related);
|
|
205
|
+
return { work, events, entries };
|
|
206
|
+
});
|
|
207
|
+
}
|
|
6
208
|
export const ACE_TASK_CONTRACT_FILES = [
|
|
7
209
|
"agent-state/TASK.md",
|
|
8
210
|
"agent-state/SCOPE.md",
|
|
@@ -369,7 +571,9 @@ export function buildAceContinuityPacket(input) {
|
|
|
369
571
|
});
|
|
370
572
|
usedChars += excerpt.length;
|
|
371
573
|
}
|
|
574
|
+
const context = readCurrentWorkContext();
|
|
372
575
|
const highlights = [
|
|
576
|
+
formatCurrentWork(context.work, "block"),
|
|
373
577
|
recall.task_contract.summary,
|
|
374
578
|
...(recall.task_contract.blockers.length > 0
|
|
375
579
|
? recall.task_contract.blockers
|
|
@@ -382,16 +586,17 @@ export function buildAceContinuityPacket(input) {
|
|
|
382
586
|
? [`Missing ${missingSources.length} configured state sources.`]
|
|
383
587
|
: []),
|
|
384
588
|
];
|
|
385
|
-
const
|
|
589
|
+
const linkedFailures = context.events.filter((event) => ["fail", "blocked"].includes(event.status));
|
|
590
|
+
const visibleEvents = [...new Map([...context.events.slice(-policy.max_status_events), ...linkedFailures].map((event) => [event.event_id, event])).values()];
|
|
591
|
+
const recentStatusEvents = visibleEvents.reverse().map((event) => ({
|
|
386
592
|
timestamp: event.timestamp,
|
|
387
593
|
source_module: event.source_module,
|
|
388
594
|
event_type: event.event_type,
|
|
389
595
|
status: event.status,
|
|
390
596
|
summary: readString(coerceObject(event.payload)?.summary) ?? event.event_type,
|
|
391
597
|
}));
|
|
392
|
-
const
|
|
393
|
-
|
|
394
|
-
.slice(Math.max(0, ledger.entries.length - policy.max_run_ledger_entries))
|
|
598
|
+
const recentRunLedger = context.entries
|
|
599
|
+
.slice(-policy.max_run_ledger_entries).reverse()
|
|
395
600
|
.map((entry) => ({
|
|
396
601
|
timestamp: entry.timestamp_utc,
|
|
397
602
|
tool: entry.tool,
|
|
@@ -410,7 +615,7 @@ export function buildAceContinuityPacket(input) {
|
|
|
410
615
|
}
|
|
411
616
|
return {
|
|
412
617
|
generated_at: new Date().toISOString(),
|
|
413
|
-
summary:
|
|
618
|
+
summary: formatCurrentWork(context.work, "line"),
|
|
414
619
|
highlights,
|
|
415
620
|
task_contract_ok: recall.task_contract.ok,
|
|
416
621
|
blockers: [...recall.task_contract.blockers],
|
|
@@ -6,5 +6,6 @@ export interface AceInternalToolCatalogEntry {
|
|
|
6
6
|
export declare function listAceInternalToolCatalog(): AceInternalToolCatalogEntry[];
|
|
7
7
|
export declare function executeAceInternalTool(name: string, input: Record<string, unknown> | undefined, sessionId?: string, options?: {
|
|
8
8
|
workspace_path?: string;
|
|
9
|
+
signal?: AbortSignal;
|
|
9
10
|
}): Promise<any>;
|
|
10
11
|
//# sourceMappingURL=ace-internal-tools.d.ts.map
|
|
@@ -23,9 +23,9 @@ function summarizeSchemaKeys(schema) {
|
|
|
23
23
|
}
|
|
24
24
|
return [];
|
|
25
25
|
}
|
|
26
|
-
function buildRequestExtra(sessionId) {
|
|
26
|
+
function buildRequestExtra(sessionId, signal) {
|
|
27
27
|
return {
|
|
28
|
-
signal: new AbortController().signal,
|
|
28
|
+
signal: signal ?? new AbortController().signal,
|
|
29
29
|
sessionId,
|
|
30
30
|
requestId: randomUUID(),
|
|
31
31
|
sendNotification: async () => undefined,
|
|
@@ -75,7 +75,7 @@ export async function executeAceInternalTool(name, input, sessionId, options = {
|
|
|
75
75
|
isError: true,
|
|
76
76
|
};
|
|
77
77
|
}
|
|
78
|
-
const extra = buildRequestExtra(sessionId);
|
|
78
|
+
const extra = buildRequestExtra(sessionId, options.signal);
|
|
79
79
|
if (tool.inputSchema) {
|
|
80
80
|
return tool.handler(parsedInput.data, extra);
|
|
81
81
|
}
|
|
@@ -3,9 +3,9 @@ export declare function buildAceDigest(workspaceRoot: string): string;
|
|
|
3
3
|
export declare function buildServerInstructions(workspaceRoot: string): string;
|
|
4
4
|
export declare function buildHostInstructionText(host: "claude" | "copilot" | "cursor" | "codex", workspaceRoot: string): string;
|
|
5
5
|
export declare function buildBootstrapNudge(workspaceRoot: string): string;
|
|
6
|
-
export declare function buildBootstrapGuidance(workspaceRoot: string,
|
|
7
|
-
export declare function buildMutationGuidance(workspaceRoot: string,
|
|
8
|
-
export declare function buildRefreshGuidance(workspaceRoot: string,
|
|
6
|
+
export declare function buildBootstrapGuidance(workspaceRoot: string, _toolName: string): string;
|
|
7
|
+
export declare function buildMutationGuidance(workspaceRoot: string, _toolName: string): string;
|
|
8
|
+
export declare function buildRefreshGuidance(workspaceRoot: string, _toolName: string): string;
|
|
9
9
|
export declare function installAceToolGovernance(server: McpServer, workspaceRoot: string): void;
|
|
10
10
|
export declare function buildHookAdditionalContext(workspaceRoot: string): string;
|
|
11
11
|
export declare function buildScopeReminder(workspaceRoot: string): string | undefined;
|