aiden-runtime 4.6.1 → 4.8.0
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/README.md +499 -265
- package/dist/cli/v4/aidenCLI.js +44 -5
- package/dist/cli/v4/callbacks.js +52 -31
- package/dist/cli/v4/chatSession.js +46 -1
- package/dist/cli/v4/commands/help.js +22 -11
- package/dist/cli/v4/commands/runs.js +42 -24
- package/dist/cli/v4/commands/skills.js +15 -17
- package/dist/cli/v4/commands/usage.js +17 -5
- package/dist/cli/v4/daemonAgentBuilder.js +13 -4
- package/dist/cli/v4/design/tokens.js +265 -0
- package/dist/cli/v4/display/framedPanel.js +116 -0
- package/dist/cli/v4/display/toolTrail.js +2 -2
- package/dist/cli/v4/display.js +446 -164
- package/dist/cli/v4/onboarding/disclaimer.js +42 -10
- package/dist/cli/v4/onboarding/loading.js +24 -1
- package/dist/cli/v4/onboarding/successScreen.js +17 -8
- package/dist/cli/v4/replyRenderer.js +74 -58
- package/dist/cli/v4/setupWizard.js +19 -2
- package/dist/cli/v4/skinEngine.js +13 -0
- package/dist/cli/v4/table.js +65 -8
- package/dist/core/v4/aidenAgent.js +42 -14
- package/dist/core/v4/auxiliaryClient.js +46 -13
- package/dist/core/v4/daemon/dispatcher/realAgentRunner.js +13 -8
- package/dist/core/v4/promptBuilder.js +45 -0
- package/dist/core/v4/sandboxFs.js +1 -1
- package/dist/core/v4/subagent/childBuilder.js +13 -4
- package/dist/core/v4/subagent/spawnSubAgent.js +7 -1
- package/dist/core/v4/ui/banner.js +16 -16
- package/dist/core/version.js +1 -1
- package/dist/moat/approvalEngine.js +14 -0
- package/dist/moat/honestyEnforcement.js +143 -241
- package/dist/tools/v4/index.js +54 -0
- package/dist/tools/v4/subagent/spawnSubAgentTool.js +23 -0
- package/package.json +10 -4
|
@@ -6,111 +6,92 @@
|
|
|
6
6
|
* Aiden — local-first agent.
|
|
7
7
|
*/
|
|
8
8
|
/**
|
|
9
|
-
* moat/honestyEnforcement.ts — Aiden v4.0.
|
|
9
|
+
* moat/honestyEnforcement.ts — Aiden v4.7.0 (Phase 2.3 — outcome-based verifier)
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* says no tool fired (or fired and failed verification), Honesty refuses
|
|
15
|
-
* the claim and rewrites the response.
|
|
11
|
+
* The regex-based natural-language claim scanner (deleted in Phase 2.2)
|
|
12
|
+
* has been replaced with a deterministic outcome recorder that consumes
|
|
13
|
+
* `toolCallTrace` structurally. Two failure modes are recorded:
|
|
16
14
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* - "I searched the web" → no web_search call
|
|
22
|
-
* - "I ran X" → no shell_exec call
|
|
15
|
+
* 1. mutation_errored — a tool tagged `mutates: true` (in the
|
|
16
|
+
* registry, stamped onto trace entries at dispatch time via
|
|
17
|
+
* `handlerMutates`) returned an `error` envelope. Path is
|
|
18
|
+
* extracted from `result.path` when present.
|
|
23
19
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* enforce — DEFAULT. Rewrites failed claims into honest text that lists
|
|
29
|
-
* the actual trace summary.
|
|
20
|
+
* 2. memory_unverified — a memory_* tool's result carries
|
|
21
|
+
* `verified === false` (per Phase 9 MemoryGuard). This was
|
|
22
|
+
* the v3 C20/C21 lying surface and remains the only memory-
|
|
23
|
+
* specific check the verifier performs.
|
|
30
24
|
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
25
|
+
* Modes:
|
|
26
|
+
* off — bypass entirely. No events recorded.
|
|
27
|
+
* detect — Record events; never user-visible. `findings` populated;
|
|
28
|
+
* no `footer`.
|
|
29
|
+
* enforce — DEFAULT. Record events + append a short footer to the
|
|
30
|
+
* assistant reply summarising the unverified outcomes.
|
|
31
|
+
* The footer is APPEND-ONLY — the assistant's text is
|
|
32
|
+
* never rewritten. (This is the key behaviour change vs
|
|
33
|
+
* v4.6.x — append-only, never an in-place edit.)
|
|
36
34
|
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
35
|
+
* What the verifier intentionally does NOT do (delta vs the deleted
|
|
36
|
+
* scanner):
|
|
37
|
+
* - It does not look at the assistant's natural-language text at all.
|
|
38
|
+
* There's no regex matching of English verbs to tool names.
|
|
39
|
+
* - It does not emit `no_tool_call` findings. The previous "model
|
|
40
|
+
* claimed X but no tool fired" failure mode is gone — that was
|
|
41
|
+
* the false-refusal class. We only record OUTCOMES that ran.
|
|
42
|
+
* - It does not mutate `loopResult.messages`. The caller appends
|
|
43
|
+
* the footer to its own `finalContent` string variable.
|
|
44
44
|
*/
|
|
45
45
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
|
-
exports.
|
|
47
|
-
/**
|
|
48
|
-
*
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
{
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
label: 'memory_add',
|
|
96
|
-
kind: 'memory',
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
pattern: new RegExp(`${I_PREFIX}(?:forgot(?:ten)?|removed)\\b[^.]*\\bmemory\\b`, 'i'),
|
|
100
|
-
tools: ['memory_remove', 'memory_forget'],
|
|
101
|
-
label: 'memory_remove',
|
|
102
|
-
kind: 'memory',
|
|
103
|
-
},
|
|
104
|
-
// ── Model switch ───────────────────────────────────────────────
|
|
105
|
-
{
|
|
106
|
-
pattern: new RegExp(`${I_PREFIX}(?:switched\\s+to|changed\\s+(?:to|model\\s+to)|am\\s+now\\s+using)\\s+\\S+`, 'i'),
|
|
107
|
-
tools: ['model_switch'],
|
|
108
|
-
label: 'model_switch',
|
|
109
|
-
},
|
|
110
|
-
];
|
|
111
|
-
/** Negation patterns. If matched at the start of a sentence containing
|
|
112
|
-
* the claim, the claim is NOT flagged. */
|
|
113
|
-
const NEGATION_RE = /\b(?:couldn'?t|cannot|can'?t|wasn'?t\s+able|unable\s+to|failed\s+to|did\s+not|didn'?t|won'?t|will\s+not)\b/i;
|
|
46
|
+
exports.HonestyEnforcement = void 0;
|
|
47
|
+
/**
|
|
48
|
+
* Memory tools whose results carry the `verified` flag set by
|
|
49
|
+
* MemoryGuard. The list is closed — adding a new memory_* tool
|
|
50
|
+
* means extending this set.
|
|
51
|
+
*/
|
|
52
|
+
const MEMORY_TOOLS = new Set([
|
|
53
|
+
'memory_add',
|
|
54
|
+
'memory_replace',
|
|
55
|
+
'memory_remove',
|
|
56
|
+
]);
|
|
57
|
+
/**
|
|
58
|
+
* Read `result.path` when present (file_* tools' result envelopes
|
|
59
|
+
* carry it). Returns undefined otherwise. Used only for cosmetic
|
|
60
|
+
* footer detail — never affects pass/fail outcome.
|
|
61
|
+
*/
|
|
62
|
+
function extractPath(result) {
|
|
63
|
+
if (result && typeof result === 'object' && 'path' in result) {
|
|
64
|
+
const p = result.path;
|
|
65
|
+
if (typeof p === 'string')
|
|
66
|
+
return p;
|
|
67
|
+
}
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Translate a `HonestyEvent` to the legacy `HonestyFinding` shape so
|
|
72
|
+
* existing downstream consumers (chatSession, telemetry) keep working.
|
|
73
|
+
* The fine-grained kind is preserved via `reason`.
|
|
74
|
+
*/
|
|
75
|
+
function toFinding(event) {
|
|
76
|
+
switch (event.kind) {
|
|
77
|
+
case 'mutation_errored':
|
|
78
|
+
return {
|
|
79
|
+
claim: event.tool,
|
|
80
|
+
expectedTool: event.tool,
|
|
81
|
+
found: false,
|
|
82
|
+
confidence: 1,
|
|
83
|
+
reason: 'tool_errored',
|
|
84
|
+
};
|
|
85
|
+
case 'memory_unverified':
|
|
86
|
+
return {
|
|
87
|
+
claim: event.tool,
|
|
88
|
+
expectedTool: event.tool,
|
|
89
|
+
found: false,
|
|
90
|
+
confidence: 1,
|
|
91
|
+
reason: 'memory_verified_false',
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
114
95
|
class HonestyEnforcement {
|
|
115
96
|
constructor(mode = 'enforce', llmAdapter, logger) {
|
|
116
97
|
this.llmAdapter = llmAdapter;
|
|
@@ -124,20 +105,63 @@ class HonestyEnforcement {
|
|
|
124
105
|
return this.mode;
|
|
125
106
|
}
|
|
126
107
|
/**
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
* use `correctedResponse` or `originalResponse` based on `passed`.
|
|
108
|
+
* v4.7.0 Phase 2.3 — record deterministic unverified outcomes from
|
|
109
|
+
* the per-turn tool trace. Pure function; no I/O, no side effects.
|
|
130
110
|
*/
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
111
|
+
recordOutcomes(trace) {
|
|
112
|
+
const events = [];
|
|
113
|
+
for (const t of trace) {
|
|
114
|
+
if (t.error && t.handlerMutates === true) {
|
|
115
|
+
events.push({
|
|
116
|
+
kind: 'mutation_errored',
|
|
117
|
+
tool: t.name,
|
|
118
|
+
reason: t.error,
|
|
119
|
+
path: extractPath(t.result),
|
|
120
|
+
});
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
if (MEMORY_TOOLS.has(t.name) && t.verified === false) {
|
|
124
|
+
events.push({
|
|
125
|
+
kind: 'memory_unverified',
|
|
126
|
+
tool: t.name,
|
|
127
|
+
reason: 'verification failed',
|
|
128
|
+
});
|
|
129
|
+
}
|
|
139
130
|
}
|
|
140
|
-
|
|
131
|
+
return events;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* v4.7.0 Phase 2.3 — render the append-only footer used in enforce
|
|
135
|
+
* mode. Caller concatenates with a blank line; we own the lines
|
|
136
|
+
* inside. Format: one summary line + one row per event.
|
|
137
|
+
*/
|
|
138
|
+
buildFooter(events) {
|
|
139
|
+
const lines = [];
|
|
140
|
+
lines.push(`⚠️ Verifier: ${events.length} tool outcome(s) not verified this turn.`);
|
|
141
|
+
for (const e of events) {
|
|
142
|
+
if (e.kind === 'mutation_errored') {
|
|
143
|
+
const where = e.path ? ` (path: ${e.path})` : '';
|
|
144
|
+
lines.push(`- ${e.tool}${where}: errored — ${e.reason}`);
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
lines.push(`- ${e.tool}: not verified`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return lines.join('\n');
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* v4.7.0 Phase 2.3 — entry point. Records outcome events from the
|
|
154
|
+
* trace, converts to legacy `HonestyFinding[]` for downstream
|
|
155
|
+
* consumers, and renders an append-only footer in enforce mode.
|
|
156
|
+
*
|
|
157
|
+
* NEVER rewrites `response`. The returned `footer` is what the
|
|
158
|
+
* caller appends; the original text is preserved verbatim.
|
|
159
|
+
*
|
|
160
|
+
* Off mode short-circuits without touching the trace — minimal cost
|
|
161
|
+
* for users who opt out.
|
|
162
|
+
*/
|
|
163
|
+
async check(response, _messages, trace) {
|
|
164
|
+
if (this.mode === 'off') {
|
|
141
165
|
return {
|
|
142
166
|
passed: true,
|
|
143
167
|
findings: [],
|
|
@@ -145,145 +169,23 @@ class HonestyEnforcement {
|
|
|
145
169
|
originalResponse: response,
|
|
146
170
|
};
|
|
147
171
|
}
|
|
148
|
-
const
|
|
149
|
-
const
|
|
150
|
-
const passed =
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
findings.length;
|
|
155
|
-
if (this.mode === 'detect') {
|
|
156
|
-
this.logger?.('info', `[HonestyEnforcement] detect mode: ${findings.length} findings (${failed.length} failed)`);
|
|
157
|
-
return {
|
|
158
|
-
passed,
|
|
159
|
-
findings,
|
|
160
|
-
confidence,
|
|
161
|
-
originalResponse: response,
|
|
162
|
-
};
|
|
172
|
+
const events = this.recordOutcomes(trace);
|
|
173
|
+
const findings = events.map(toFinding);
|
|
174
|
+
const passed = findings.length === 0;
|
|
175
|
+
let footer;
|
|
176
|
+
if (this.mode === 'enforce' && !passed) {
|
|
177
|
+
footer = this.buildFooter(events);
|
|
163
178
|
}
|
|
164
|
-
// enforce mode
|
|
165
|
-
let correctedResponse;
|
|
166
179
|
if (!passed) {
|
|
167
|
-
|
|
168
|
-
this.logger?.('warn', `[HonestyEnforcement] enforce: rewrote response (${failed.length} failed claims)`);
|
|
180
|
+
this.logger?.('info', `honesty: ${events.length} unverified outcome(s) this turn`);
|
|
169
181
|
}
|
|
170
182
|
return {
|
|
171
183
|
passed,
|
|
172
184
|
findings,
|
|
173
|
-
confidence,
|
|
185
|
+
confidence: 1,
|
|
174
186
|
originalResponse: response,
|
|
175
|
-
|
|
187
|
+
footer,
|
|
176
188
|
};
|
|
177
189
|
}
|
|
178
|
-
// ─────────────────────────────────────────────────────────────────────
|
|
179
|
-
// pattern detection
|
|
180
|
-
// ─────────────────────────────────────────────────────────────────────
|
|
181
|
-
detectClaimsPattern(response, trace) {
|
|
182
|
-
const findings = [];
|
|
183
|
-
const sentences = splitSentences(response);
|
|
184
|
-
for (const sentence of sentences) {
|
|
185
|
-
// Skip negated sentences entirely.
|
|
186
|
-
if (NEGATION_RE.test(sentence))
|
|
187
|
-
continue;
|
|
188
|
-
for (const pat of PATTERNS) {
|
|
189
|
-
if (!pat.pattern.test(sentence))
|
|
190
|
-
continue;
|
|
191
|
-
const matched = sentence.match(pat.pattern);
|
|
192
|
-
const claimText = matched?.[0] ?? sentence.trim();
|
|
193
|
-
const found = this.traceSatisfies(pat, trace);
|
|
194
|
-
let reason;
|
|
195
|
-
if (!found) {
|
|
196
|
-
if (pat.kind === 'memory' && memoryFiredButUnverified(pat, trace)) {
|
|
197
|
-
reason = 'memory_verified_false';
|
|
198
|
-
}
|
|
199
|
-
else if (toolFiredButErrored(pat, trace)) {
|
|
200
|
-
reason = 'tool_errored';
|
|
201
|
-
}
|
|
202
|
-
else {
|
|
203
|
-
reason = 'no_tool_call';
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
findings.push({
|
|
207
|
-
claim: claimText.trim(),
|
|
208
|
-
expectedTool: pat.tools.length === 1 ? pat.tools[0] : pat.tools,
|
|
209
|
-
found,
|
|
210
|
-
confidence: 0.8,
|
|
211
|
-
reason,
|
|
212
|
-
});
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
return findings;
|
|
216
|
-
}
|
|
217
|
-
traceSatisfies(pat, trace) {
|
|
218
|
-
const matching = trace.filter((t) => pat.tools.includes(t.name) && !t.error);
|
|
219
|
-
if (matching.length === 0)
|
|
220
|
-
return false;
|
|
221
|
-
if (pat.kind === 'memory') {
|
|
222
|
-
// verified must be explicitly true
|
|
223
|
-
return matching.some((m) => m.verified === true);
|
|
224
|
-
}
|
|
225
|
-
return true;
|
|
226
|
-
}
|
|
227
|
-
// ─────────────────────────────────────────────────────────────────────
|
|
228
|
-
// correction builder
|
|
229
|
-
// ─────────────────────────────────────────────────────────────────────
|
|
230
|
-
buildCorrection(_original, failed, trace) {
|
|
231
|
-
const lines = [];
|
|
232
|
-
lines.push("I shouldn't claim actions I didn't take. Honest summary of what I actually did:");
|
|
233
|
-
lines.push('');
|
|
234
|
-
if (trace.length === 0) {
|
|
235
|
-
lines.push('- No tools were called this turn.');
|
|
236
|
-
}
|
|
237
|
-
else {
|
|
238
|
-
for (const entry of trace) {
|
|
239
|
-
const status = entry.error ? `errored (${entry.error})` : 'succeeded';
|
|
240
|
-
const verified = entry.verified === false
|
|
241
|
-
? ' (NOT VERIFIED)'
|
|
242
|
-
: entry.verified === true
|
|
243
|
-
? ' (verified)'
|
|
244
|
-
: '';
|
|
245
|
-
lines.push(`- ${entry.name}: ${status}${verified}`);
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
lines.push('');
|
|
249
|
-
lines.push('Refused claims:');
|
|
250
|
-
for (const f of failed) {
|
|
251
|
-
const tool = Array.isArray(f.expectedTool)
|
|
252
|
-
? f.expectedTool.join('/')
|
|
253
|
-
: f.expectedTool;
|
|
254
|
-
const why = f.reason === 'memory_verified_false'
|
|
255
|
-
? `(memory write returned verified=false — fact was not stored)`
|
|
256
|
-
: f.reason === 'tool_errored'
|
|
257
|
-
? `(tool errored)`
|
|
258
|
-
: `(no ${tool} call in trace)`;
|
|
259
|
-
lines.push(`- "${f.claim}" ${why}`);
|
|
260
|
-
}
|
|
261
|
-
return lines.join('\n');
|
|
262
|
-
}
|
|
263
190
|
}
|
|
264
191
|
exports.HonestyEnforcement = HonestyEnforcement;
|
|
265
|
-
// ─────────────────────────────────────────────────────────────────────
|
|
266
|
-
// helpers (exported for tests)
|
|
267
|
-
// ─────────────────────────────────────────────────────────────────────
|
|
268
|
-
function splitSentences(text) {
|
|
269
|
-
// Split on sentence terminators while keeping reasonable bounds.
|
|
270
|
-
// Don't try to be clever about abbreviations — false positives are
|
|
271
|
-
// benign (we just inspect more granular slices).
|
|
272
|
-
return text
|
|
273
|
-
.split(/(?<=[.!?])\s+|\n+/)
|
|
274
|
-
.map((s) => s.trim())
|
|
275
|
-
.filter((s) => s.length > 0);
|
|
276
|
-
}
|
|
277
|
-
function memoryFiredButUnverified(pat, trace) {
|
|
278
|
-
if (pat.kind !== 'memory')
|
|
279
|
-
return false;
|
|
280
|
-
return trace.some((t) => pat.tools.includes(t.name) && !t.error && t.verified === false);
|
|
281
|
-
}
|
|
282
|
-
function toolFiredButErrored(pat, trace) {
|
|
283
|
-
return trace.some((t) => pat.tools.includes(t.name) && !!t.error);
|
|
284
|
-
}
|
|
285
|
-
exports.__test__ = {
|
|
286
|
-
splitSentences,
|
|
287
|
-
PATTERNS,
|
|
288
|
-
NEGATION_RE,
|
|
289
|
-
};
|
package/dist/tools/v4/index.js
CHANGED
|
@@ -246,6 +246,60 @@ function registerWriteTools(registry) {
|
|
|
246
246
|
function registerAllTools(registry) {
|
|
247
247
|
registerReadOnlyTools(registry);
|
|
248
248
|
registerWriteTools(registry);
|
|
249
|
+
// v4.8.0 Phase 2.2 — register the 7 semantic ui_* event tools.
|
|
250
|
+
// All uiOnly: true → the dispatch loop in core/v4/aidenAgent.ts
|
|
251
|
+
// bypasses execute and fires onUiEvent on the caller. execute()
|
|
252
|
+
// throws as a safety guard: if the uiOnly branch ever misfires
|
|
253
|
+
// and an executor is reached, that's a wiring bug, not a render.
|
|
254
|
+
// Renderer is a no-op stub in this phase; Phase 2.3 lands chrome.
|
|
255
|
+
const ui = (name, description, properties, required) => ({
|
|
256
|
+
schema: { name, description, inputSchema: { type: 'object', properties, required } },
|
|
257
|
+
execute: async () => { throw new Error(`${name} is uiOnly — dispatch branch should bypass execute`); },
|
|
258
|
+
category: 'read', mutates: false, uiOnly: true,
|
|
259
|
+
});
|
|
260
|
+
const str = { type: 'string' };
|
|
261
|
+
const num = { type: 'number' };
|
|
262
|
+
registry.register(ui('ui_task_update', 'Signal current task state for the live task panel. Append-only stream.', { task_id: str, label: { type: 'string', description: '≤80 chars' },
|
|
263
|
+
status: { type: 'string', enum: ['running', 'blocked', 'paused'] },
|
|
264
|
+
kind: { type: 'string', enum: ['task', 'subagent'] }, depth: num, parent_id: str }, ['task_id', 'label', 'status']));
|
|
265
|
+
registry.register(ui('ui_task_done', 'Signal a task is complete. Pairs with a prior ui_task_update.', { task_id: str, status: { type: 'string', enum: ['success', 'failure', 'blocked'] },
|
|
266
|
+
summary: { type: 'string', description: 'Optional, ≤120 chars' } }, ['task_id', 'status']));
|
|
267
|
+
registry.register(ui('ui_command_result', 'Surface shell output as a formatted block.', { command: str, stdout: str, stderr: str, exit_code: num }, ['command']));
|
|
268
|
+
registry.register(ui('ui_test_result', 'Pass/fail count after a test run.', { framework: { type: 'string', description: 'e.g. "vitest", "pytest"' },
|
|
269
|
+
passed: num, failed: num, skipped: num, duration_ms: num }, ['framework', 'passed', 'failed']));
|
|
270
|
+
registry.register(ui('ui_approval_request', 'Structured approval prompt before a privileged action.', { prompt: { type: 'string', description: '≤160 chars' },
|
|
271
|
+
risk_tier: { type: 'string', enum: ['low', 'medium', 'high', 'critical'] },
|
|
272
|
+
reason: { type: 'string', description: 'Optional, ≤200 chars' } }, ['prompt', 'risk_tier']));
|
|
273
|
+
registry.register(ui('ui_toast', 'Transient notice to surface without interrupting flow.', { message: { type: 'string', description: '≤120 chars' },
|
|
274
|
+
kind: { type: 'string', enum: ['info', 'success', 'warning', 'error'] } }, ['message', 'kind']));
|
|
275
|
+
registry.register(ui('ui_artifact_created', 'Surface a file or skill created/modified this turn.', { path: str, kind: { type: 'string', enum: ['file', 'skill', 'directory'] },
|
|
276
|
+
preview: { type: 'string', description: 'Optional, ≤200 chars' } }, ['path', 'kind']));
|
|
277
|
+
// v4.8.0 Phase 2.1 — env-gated uiOnly smoke stub. Never registers
|
|
278
|
+
// in production. Set AIDEN_TEST_UI_STUB=1 to enable for the
|
|
279
|
+
// dispatch-branch smoke harness. The execute() throws on purpose:
|
|
280
|
+
// if the uiOnly branch is wired correctly the model can never
|
|
281
|
+
// reach the executor.
|
|
282
|
+
if (process.env.AIDEN_TEST_UI_STUB === '1') {
|
|
283
|
+
registry.register({
|
|
284
|
+
schema: {
|
|
285
|
+
name: '_test_ui_stub',
|
|
286
|
+
description: 'Test-only uiOnly stub for v4.8.0 Phase 2.1 smoke. Set AIDEN_TEST_UI_STUB=1 to enable.',
|
|
287
|
+
inputSchema: {
|
|
288
|
+
type: 'object',
|
|
289
|
+
properties: {
|
|
290
|
+
message: { type: 'string', description: 'Arbitrary message to echo via onUiEvent' },
|
|
291
|
+
},
|
|
292
|
+
required: ['message'],
|
|
293
|
+
},
|
|
294
|
+
},
|
|
295
|
+
execute: async () => {
|
|
296
|
+
throw new Error('_test_ui_stub should never execute — uiOnly branch should bypass it');
|
|
297
|
+
},
|
|
298
|
+
category: 'read',
|
|
299
|
+
mutates: false,
|
|
300
|
+
uiOnly: true,
|
|
301
|
+
});
|
|
302
|
+
}
|
|
249
303
|
}
|
|
250
304
|
var subagentFanout_2 = require("./subagent/subagentFanout");
|
|
251
305
|
Object.defineProperty(exports, "makeSubagentFanoutTool", { enumerable: true, get: function () { return subagentFanout_2.makeSubagentFanoutTool; } });
|
|
@@ -289,6 +289,19 @@ function makeSpawnSubAgentTool(factory) {
|
|
|
289
289
|
// ── 3. Resolve optional parent run / session identifiers ─────────────
|
|
290
290
|
const parentRunId = factory.resolveParentRunId?.();
|
|
291
291
|
const parentSessionId = factory.resolveParentSessionId?.();
|
|
292
|
+
// v4.8.0 Phase 2.5 — emit ui_task_update for the subagent start.
|
|
293
|
+
// Stable task_id correlates with the matching ui_task_done emit
|
|
294
|
+
// after the spawnSubAgent call returns. depth:1 hardcoded today
|
|
295
|
+
// — childBuilder caps recursion at 1 (see SUBAGENT_BLOCKED_TOOL_NAMES
|
|
296
|
+
// 'spawn_sub_agent'). TODO: thread real depth when nested spawns ship.
|
|
297
|
+
const subTaskId = `subagent-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
298
|
+
factory.onUiEvent?.('ui_task_update', {
|
|
299
|
+
task_id: subTaskId,
|
|
300
|
+
label: goalPreview,
|
|
301
|
+
status: 'running',
|
|
302
|
+
kind: 'subagent',
|
|
303
|
+
depth: 1,
|
|
304
|
+
});
|
|
292
305
|
// ── 4. Invoke the primitive. NEVER throws — always envelope. ─────────
|
|
293
306
|
const result = await (0, spawnSubAgent_1.spawnSubAgent)(spec, {
|
|
294
307
|
// ChildBuilderDeps fields:
|
|
@@ -310,6 +323,16 @@ function makeSpawnSubAgentTool(factory) {
|
|
|
310
323
|
parentRunId,
|
|
311
324
|
parentSessionId,
|
|
312
325
|
});
|
|
326
|
+
// v4.8.0 Phase 2.5 — emit ui_task_done with the same subTaskId
|
|
327
|
+
// so the display layer can finalize the in-flight row.
|
|
328
|
+
const doneStatus = result.ok ? 'success' :
|
|
329
|
+
result.status === 'interrupted' ? 'blocked' :
|
|
330
|
+
result.status === 'timeout' ? 'blocked' : 'failure';
|
|
331
|
+
factory.onUiEvent?.('ui_task_done', {
|
|
332
|
+
task_id: subTaskId,
|
|
333
|
+
status: doneStatus,
|
|
334
|
+
summary: `${result.metrics.apiCalls} calls · ${result.exitReason}`,
|
|
335
|
+
});
|
|
313
336
|
// Completion log — pairs with "spawn_sub_agent invoked" so a
|
|
314
337
|
// grep on parentSessionId surfaces invoke → complete in order.
|
|
315
338
|
logger.info('spawn_sub_agent completed', {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aiden-runtime",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.8.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -254,7 +254,7 @@
|
|
|
254
254
|
"epub2": "^3.0.2",
|
|
255
255
|
"execa": "^8.0.1",
|
|
256
256
|
"express": "^4.18.2",
|
|
257
|
-
"form-data": "^4.0.
|
|
257
|
+
"form-data": "^4.0.4",
|
|
258
258
|
"imap-simple": "^5.1.0",
|
|
259
259
|
"js-tiktoken": "^1.0.21",
|
|
260
260
|
"js-yaml": "^4.1.1",
|
|
@@ -284,7 +284,7 @@
|
|
|
284
284
|
"uuid": "^9.0.0",
|
|
285
285
|
"whatsapp-web.js": "^1.26.0",
|
|
286
286
|
"wrap-ansi": "^9.0.2",
|
|
287
|
-
"ws": "^8.20.
|
|
287
|
+
"ws": "^8.20.1"
|
|
288
288
|
},
|
|
289
289
|
"optionalDependencies": {
|
|
290
290
|
"decibri": "*",
|
|
@@ -296,7 +296,13 @@
|
|
|
296
296
|
"semver": "^7.5.2",
|
|
297
297
|
"postcss": "^8.5.10",
|
|
298
298
|
"hono": "^4.12.16",
|
|
299
|
-
"minimatch": "^9.0.9"
|
|
299
|
+
"minimatch": "^9.0.9",
|
|
300
|
+
"qs": ">=6.14.1",
|
|
301
|
+
"tough-cookie": ">=4.1.3",
|
|
302
|
+
"protobufjs": ">=7.5.8",
|
|
303
|
+
"request": {
|
|
304
|
+
"form-data": "^2.5.5"
|
|
305
|
+
}
|
|
300
306
|
},
|
|
301
307
|
"devDependencies": {
|
|
302
308
|
"@types/better-sqlite3": "^7.6.13",
|