aiden-runtime 4.1.2 → 4.1.4
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/dist/cli/v4/aidenCLI.js +10 -0
- package/dist/cli/v4/callbacks.js +85 -13
- package/dist/cli/v4/chatSession.js +250 -24
- package/dist/cli/v4/commands/doctor.js +23 -27
- package/dist/cli/v4/commands/model.js +30 -1
- package/dist/cli/v4/defaultSoul.js +69 -2
- package/dist/cli/v4/display/capabilityCard.js +135 -0
- package/dist/cli/v4/display/frame.js +234 -0
- package/dist/cli/v4/display/progressBar.js +137 -0
- package/dist/cli/v4/display/sessionEndCard.js +127 -0
- package/dist/cli/v4/display/toolTrail.js +172 -0
- package/dist/cli/v4/display.js +891 -153
- package/dist/cli/v4/doctor.js +377 -75
- package/dist/cli/v4/promotionPrompt.js +135 -5
- package/dist/cli/v4/replyRenderer.js +487 -26
- package/dist/cli/v4/skinEngine.js +26 -4
- package/dist/cli/v4/toolPreview.js +82 -19
- package/dist/core/tools/nowPlaying.js +7 -15
- package/dist/core/v4/aidenAgent.js +9 -0
- package/dist/core/v4/promptBuilder.js +2 -1
- package/dist/core/v4/sessionDistiller.js +48 -1
- package/dist/core/v4/toolRegistry.js +16 -1
- package/dist/core/version.js +1 -1
- package/dist/moat/plannerGuard.js +19 -0
- package/dist/providers/v4/anthropicAdapter.js +25 -2
- package/dist/providers/v4/errors.js +92 -0
- package/dist/tools/v4/index.js +24 -1
- package/dist/tools/v4/sessions/recallSession.js +14 -0
- package/dist/tools/v4/system/_psHelpers.js +70 -2
- package/dist/tools/v4/system/appInput.js +154 -0
- package/dist/tools/v4/system/appLaunch.js +136 -10
- package/dist/tools/v4/system/mediaKey.js +35 -4
- package/dist/tools/v4/system/mediaSessions.js +163 -0
- package/dist/tools/v4/system/mediaTransport.js +211 -0
- package/package.json +2 -1
- package/skills/system_control.md +56 -6
|
@@ -29,9 +29,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
29
29
|
exports.TOOL_PRIMARY_ARG = void 0;
|
|
30
30
|
exports.buildToolPreview = buildToolPreview;
|
|
31
31
|
/**
|
|
32
|
-
* Map of tool-name →
|
|
33
|
-
*
|
|
34
|
-
* entries.
|
|
32
|
+
* Map of tool-name → preview extractor (string key OR function).
|
|
33
|
+
* Stable contract; tests assert specific entries.
|
|
35
34
|
*/
|
|
36
35
|
exports.TOOL_PRIMARY_ARG = {
|
|
37
36
|
// ── terminal / execution ─────────────────────────────────────────────
|
|
@@ -86,6 +85,51 @@ exports.TOOL_PRIMARY_ARG = {
|
|
|
86
85
|
system_info: '',
|
|
87
86
|
now_playing: '',
|
|
88
87
|
get_natural_events: '',
|
|
88
|
+
// ── v4.1.4-media — three-layer media-control bundle ──────────────────
|
|
89
|
+
// `media_sessions` has no args by schema; the empty-arg preview is
|
|
90
|
+
// suppressed by buildToolPreview returning ''.
|
|
91
|
+
// `media_transport` → preview by target ("spotify"), the actionable
|
|
92
|
+
// identifier the user typed. `action` is intentionally NOT chosen —
|
|
93
|
+
// GSMTC actions (play/pause/toggle) are short, the target is the
|
|
94
|
+
// discriminator.
|
|
95
|
+
// `media_key` is layer-3 fallback; show `action` since there's no
|
|
96
|
+
// target to surface (it's a blind keystroke).
|
|
97
|
+
// `app_input` shows `app` so the user sees which window got the keys.
|
|
98
|
+
media_sessions: '',
|
|
99
|
+
media_transport: 'target',
|
|
100
|
+
media_key: 'action',
|
|
101
|
+
app_input: 'app',
|
|
102
|
+
// ── v4.1.4 Phase 3b' (Issue H) ───────────────────────────────────────
|
|
103
|
+
// app_launch needs custom logic: when `app === 'explorer.exe'` the
|
|
104
|
+
// binary is just the URI dispatcher and the meaningful target is in
|
|
105
|
+
// `args[0]` (e.g. 'spotify:track/...'). Surface the protocol scheme
|
|
106
|
+
// ('spotify') rather than the dispatch binary. Falls through to the
|
|
107
|
+
// app name for normal exe launches.
|
|
108
|
+
app_launch: (args) => {
|
|
109
|
+
if (!args || typeof args !== 'object')
|
|
110
|
+
return '';
|
|
111
|
+
const a = args;
|
|
112
|
+
const appRaw = typeof a.app === 'string' ? a.app.trim() : '';
|
|
113
|
+
// URI-protocol case: explorer.exe + 'scheme:...' in args[0].
|
|
114
|
+
if (appRaw.toLowerCase() === 'explorer.exe' && Array.isArray(a.args)) {
|
|
115
|
+
const first = a.args[0];
|
|
116
|
+
if (typeof first === 'string' && first.length > 0) {
|
|
117
|
+
// Scheme requires ≥2 chars so Windows drive letters
|
|
118
|
+
// (`C:/path`) don't mis-detect as the scheme `C`. Real URI
|
|
119
|
+
// schemes (spotify, vscode, http, file, etc.) are all
|
|
120
|
+
// multi-char by RFC.
|
|
121
|
+
const m = first.match(/^([A-Za-z][A-Za-z0-9+.-]+):/);
|
|
122
|
+
if (m)
|
|
123
|
+
return m[1]; // 'spotify:track/...' → 'spotify'
|
|
124
|
+
return first; // No protocol — surface the raw arg
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return appRaw;
|
|
128
|
+
},
|
|
129
|
+
// Clipboard write — the actual text being copied is the meaningful
|
|
130
|
+
// target. Reads have no args worth showing (empty schema).
|
|
131
|
+
clipboard_write: 'text',
|
|
132
|
+
clipboard_read: '',
|
|
89
133
|
};
|
|
90
134
|
/**
|
|
91
135
|
* Maximum visible characters for the preview value. Long commands /
|
|
@@ -107,28 +151,47 @@ function buildToolPreview(toolName, args) {
|
|
|
107
151
|
if (!Object.prototype.hasOwnProperty.call(exports.TOOL_PRIMARY_ARG, toolName)) {
|
|
108
152
|
return null;
|
|
109
153
|
}
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
return '';
|
|
115
|
-
const raw = args[argKey];
|
|
116
|
-
if (raw === undefined || raw === null)
|
|
117
|
-
return '';
|
|
154
|
+
const extractor = exports.TOOL_PRIMARY_ARG[toolName];
|
|
155
|
+
// v4.1.4 Phase 3b' (Issue H1): function extractor path. Used by
|
|
156
|
+
// tools whose preview can't be expressed as a single key lookup
|
|
157
|
+
// (e.g. app_launch with URI-protocol routing through explorer.exe).
|
|
118
158
|
let str;
|
|
119
|
-
if (typeof
|
|
120
|
-
str = raw;
|
|
121
|
-
}
|
|
122
|
-
else if (typeof raw === 'number' || typeof raw === 'boolean') {
|
|
123
|
-
str = String(raw);
|
|
124
|
-
}
|
|
125
|
-
else {
|
|
159
|
+
if (typeof extractor === 'function') {
|
|
126
160
|
try {
|
|
127
|
-
|
|
161
|
+
const out = extractor(args);
|
|
162
|
+
str = typeof out === 'string' ? out : '';
|
|
128
163
|
}
|
|
129
164
|
catch {
|
|
165
|
+
// Extractor threw — degrade to empty preview rather than crash
|
|
166
|
+
// the tool-row render. The tool name + state cluster still
|
|
167
|
+
// carries enough info.
|
|
168
|
+
str = '';
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
// String-key path (legacy, unchanged behaviour).
|
|
173
|
+
const argKey = extractor;
|
|
174
|
+
if (argKey === '')
|
|
175
|
+
return '';
|
|
176
|
+
if (!args || typeof args !== 'object')
|
|
177
|
+
return '';
|
|
178
|
+
const raw = args[argKey];
|
|
179
|
+
if (raw === undefined || raw === null)
|
|
180
|
+
return '';
|
|
181
|
+
if (typeof raw === 'string') {
|
|
182
|
+
str = raw;
|
|
183
|
+
}
|
|
184
|
+
else if (typeof raw === 'number' || typeof raw === 'boolean') {
|
|
130
185
|
str = String(raw);
|
|
131
186
|
}
|
|
187
|
+
else {
|
|
188
|
+
try {
|
|
189
|
+
str = JSON.stringify(raw);
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
str = String(raw);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
132
195
|
}
|
|
133
196
|
// Collapse whitespace so multi-line commands stay on one preview row.
|
|
134
197
|
str = str.replace(/\s+/g, ' ').trim();
|
|
@@ -2,27 +2,19 @@
|
|
|
2
2
|
// core/tools/nowPlaying.ts — Live media session query via Windows WinRT
|
|
3
3
|
// Uses GlobalSystemMediaTransportControlsSessionManager (works for Spotify,
|
|
4
4
|
// YouTube in browser, Windows Media Player, and any SMTC-registered app).
|
|
5
|
+
//
|
|
6
|
+
// v4.1.4-media: the WinRT `Await` PS5.1 reflection bridge moved into the
|
|
7
|
+
// shared helper `tools/v4/system/_psHelpers.ts::winRtAwaitPreamble()` so
|
|
8
|
+
// the three GSMTC callers (this file + mediaSessions + mediaTransport)
|
|
9
|
+
// share one canonical implementation.
|
|
5
10
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
11
|
exports.getNowPlaying = getNowPlaying;
|
|
7
12
|
const child_process_1 = require("child_process");
|
|
8
13
|
const util_1 = require("util");
|
|
14
|
+
const _psHelpers_1 = require("../../tools/v4/system/_psHelpers");
|
|
9
15
|
const execAsync = (0, util_1.promisify)(child_process_1.exec);
|
|
10
|
-
// PowerShell uses System.WindowsRuntimeSystemExtensions.AsTask to bridge
|
|
11
|
-
// WinRT IAsyncOperation<T> into a .NET Task — required because PS5.1 cannot
|
|
12
|
-
// await WinRT async operations natively via .GetAwaiter().
|
|
13
16
|
const PS_SCRIPT = `
|
|
14
|
-
|
|
15
|
-
function Await($WinRtTask, $ResultType) {
|
|
16
|
-
$m = ([System.WindowsRuntimeSystemExtensions].GetMethods() | Where-Object {
|
|
17
|
-
$_.Name -eq 'AsTask' -and
|
|
18
|
-
$_.GetParameters().Count -eq 1 -and
|
|
19
|
-
$_.GetParameters()[0].ParameterType.Name -eq 'IAsyncOperation\`1'
|
|
20
|
-
})[0]
|
|
21
|
-
$m = $m.MakeGenericMethod($ResultType)
|
|
22
|
-
$t = $m.Invoke($null, @($WinRtTask))
|
|
23
|
-
$t.Wait(-1) | Out-Null
|
|
24
|
-
$t.Result
|
|
25
|
-
}
|
|
17
|
+
${(0, _psHelpers_1.winRtAwaitPreamble)()}
|
|
26
18
|
$mgType = [Windows.Media.Control.GlobalSystemMediaTransportControlsSessionManager,Windows.Media.Control,ContentType=WindowsRuntime]
|
|
27
19
|
$mgr = Await ($mgType::RequestAsync()) $mgType
|
|
28
20
|
$s = $mgr.GetCurrentSession()
|
|
@@ -650,6 +650,15 @@ class AidenAgent {
|
|
|
650
650
|
else if (evt.type === 'tool_call') {
|
|
651
651
|
runOptions.onToolCallStart?.(evt.toolCall);
|
|
652
652
|
}
|
|
653
|
+
else if (evt.type === 'progress') {
|
|
654
|
+
// v4.1.4 Part 1.6 — drive the per-turn token progress bar.
|
|
655
|
+
// Defensive try/catch — a misbehaving display sink must not
|
|
656
|
+
// tear down the stream consumer.
|
|
657
|
+
try {
|
|
658
|
+
runOptions.onProgress?.(evt.outputTokens, evt.maxTokens);
|
|
659
|
+
}
|
|
660
|
+
catch { /* progress sink errors don't block streaming */ }
|
|
661
|
+
}
|
|
653
662
|
else if (evt.type === 'done') {
|
|
654
663
|
finalOutput = evt.output;
|
|
655
664
|
}
|
|
@@ -128,7 +128,8 @@ const EXECUTION_DISCIPLINE_PROSE = [
|
|
|
128
128
|
'file"), you MUST immediately make the corresponding tool call in the same response.',
|
|
129
129
|
'Never end your turn with a promise of future action — execute it now. Every',
|
|
130
130
|
'response should either contain tool calls that make progress, or deliver a final',
|
|
131
|
-
'result.
|
|
131
|
+
'result. When the user requests an action, take it. When the user requests',
|
|
132
|
+
'discussion, discuss.',
|
|
132
133
|
].join('\n');
|
|
133
134
|
/**
|
|
134
135
|
* Llama-3.3-specific tool-call format guard. Adapter-side recovery picks
|
|
@@ -192,7 +192,19 @@ function recoverBullets(raw) {
|
|
|
192
192
|
}
|
|
193
193
|
return [];
|
|
194
194
|
}
|
|
195
|
-
|
|
195
|
+
/**
|
|
196
|
+
* v4.1.3-essentials distillation-fix: default raised from 4000ms to
|
|
197
|
+
* 12000ms after visual smoke showed chatgpt-plus Codex regularly
|
|
198
|
+
* exceeded the original budget for 800-token summaries on
|
|
199
|
+
* cold-start. Symptom: every `/quit` distillation returned
|
|
200
|
+
* `partial:true` with empty bullets/decisions/open_items, killing
|
|
201
|
+
* both the MEMORY.md update path AND the promotion prompt.
|
|
202
|
+
*
|
|
203
|
+
* 12s gives comfortable headroom while still aborting genuinely
|
|
204
|
+
* stuck calls. Power users can override via `AIDEN_SUMMARY_TIMEOUT_MS`
|
|
205
|
+
* env var (consumed by `resolveSummaryTimeoutMs()` in chatSession).
|
|
206
|
+
*/
|
|
207
|
+
const DEFAULT_TIMEOUT_MS = 12000;
|
|
196
208
|
/**
|
|
197
209
|
* Phase v4.1.2-bug-Y: max chars of tool-result content surfaced to the
|
|
198
210
|
* auxiliary LLM. Covers typical error messages + JSON-payload heads
|
|
@@ -372,11 +384,46 @@ async function distillSession(opts) {
|
|
|
372
384
|
setTimeout(() => resolve({ ok: false, error: new Error(`auxiliary call timed out after ${timeoutMs}ms`), timedOut: true }), timeoutMs);
|
|
373
385
|
}),
|
|
374
386
|
]);
|
|
387
|
+
// v4.1.3-essentials distillation-fix: emit a diagnostic line for
|
|
388
|
+
// each of the three failure classes so the caller can surface the
|
|
389
|
+
// root cause. Previously all three paths produced an identical
|
|
390
|
+
// `partial:true + empty` result with no signal about WHICH failure
|
|
391
|
+
// fired. Safe to call onDiagnostic synchronously — caller wraps in
|
|
392
|
+
// try/catch so a throwing sink doesn't break distillation.
|
|
393
|
+
const diag = (msg) => {
|
|
394
|
+
if (!opts.onDiagnostic)
|
|
395
|
+
return;
|
|
396
|
+
try {
|
|
397
|
+
opts.onDiagnostic(msg);
|
|
398
|
+
}
|
|
399
|
+
catch { /* never break distillation */ }
|
|
400
|
+
};
|
|
375
401
|
let semantic;
|
|
376
402
|
if (llmRaw.ok) {
|
|
377
403
|
semantic = parseLLMDistillation(llmRaw.content);
|
|
404
|
+
if (semantic.partial) {
|
|
405
|
+
// Parser fell back to bullets-only or fully-empty — the LLM
|
|
406
|
+
// returned content but it wasn't valid JSON. First-200-chars
|
|
407
|
+
// hint lets the user / debugger see what shape the model
|
|
408
|
+
// actually emitted (often a chatty preamble that confused
|
|
409
|
+
// the JSON extractor).
|
|
410
|
+
const head = llmRaw.content.trim().slice(0, 200).replace(/\n/g, ' ');
|
|
411
|
+
diag(`auxiliary returned unparseable JSON (first 200 chars: ${head})`);
|
|
412
|
+
}
|
|
378
413
|
}
|
|
379
414
|
else {
|
|
415
|
+
// Race resolved with the failure branch — either the timeout
|
|
416
|
+
// fired or auxiliaryClient.call threw. Hoist `error` into a
|
|
417
|
+
// local so the narrowed type stays stable inside the branch
|
|
418
|
+
// (TS can't infer `error` exists on `llmRaw` because the union
|
|
419
|
+
// overlaps with the success branch in its type literal).
|
|
420
|
+
const failure = llmRaw;
|
|
421
|
+
if (failure.timedOut === true) {
|
|
422
|
+
diag(`auxiliary call timed out after ${timeoutMs}ms`);
|
|
423
|
+
}
|
|
424
|
+
else {
|
|
425
|
+
diag(`auxiliary call failed: ${failure.error.message}`);
|
|
426
|
+
}
|
|
380
427
|
semantic = {
|
|
381
428
|
bullets: [],
|
|
382
429
|
decisions: [],
|
|
@@ -154,7 +154,22 @@ class ToolRegistry {
|
|
|
154
154
|
}
|
|
155
155
|
try {
|
|
156
156
|
const result = await handler.execute(args, context);
|
|
157
|
-
|
|
157
|
+
// v4.1.3-repl-polish: lift `degraded` + `degradedReason` from the
|
|
158
|
+
// handler's inner result to the outer ToolCallResult so the CLI
|
|
159
|
+
// trail row can render the partial-yellow state. Tools opt in by
|
|
160
|
+
// setting these on the object they return; without this lift the
|
|
161
|
+
// flags would sit on `out.result.degraded` where callbacks.ts
|
|
162
|
+
// can't see them. Strict typeof checks avoid promoting truthy-
|
|
163
|
+
// but-wrong-shape junk (numbers, strings, nested objects).
|
|
164
|
+
const inner = result;
|
|
165
|
+
const out = { id: call.id, name: call.name, result };
|
|
166
|
+
if (typeof inner?.degraded === 'boolean' && inner.degraded) {
|
|
167
|
+
out.degraded = true;
|
|
168
|
+
if (typeof inner.degradedReason === 'string') {
|
|
169
|
+
out.degradedReason = inner.degradedReason;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return out;
|
|
158
173
|
}
|
|
159
174
|
catch (err) {
|
|
160
175
|
const message = err instanceof Error ? err.message : String(err);
|
package/dist/core/version.js
CHANGED
|
@@ -91,6 +91,25 @@ const RULES = [
|
|
|
91
91
|
keywords: /\b(process|background|long.?running|server|spawn|kill|daemon)\b/i,
|
|
92
92
|
toolsets: ['process'],
|
|
93
93
|
},
|
|
94
|
+
// Media playback control (v4.1.4-media)
|
|
95
|
+
//
|
|
96
|
+
// Without this, intents like "list media sessions" matched the
|
|
97
|
+
// `sessions` rule via the bare word "session" → narrowed surface to
|
|
98
|
+
// toolset `sessions` only → `media_sessions` (toolset `system`) was
|
|
99
|
+
// filtered out and the model honestly reported it as unavailable.
|
|
100
|
+
// UNION semantics mean both rules contribute on phrases that hit
|
|
101
|
+
// both ("media sessions" → sessions + system), giving the model the
|
|
102
|
+
// full picture without needing a dedicated `media` toolset.
|
|
103
|
+
//
|
|
104
|
+
// Toolset is `system` (broad but minimal blast radius): the bundle
|
|
105
|
+
// covers media_sessions, media_transport, media_key, app_input,
|
|
106
|
+
// now_playing, plus the plausibly-relevant app_launch / volume_set /
|
|
107
|
+
// os_process_list. Carving out a dedicated `media` toolset is a
|
|
108
|
+
// separate slice if the surface noise becomes a real problem.
|
|
109
|
+
{
|
|
110
|
+
keywords: /\b(play|pause|skip|spotify|music|song|video|youtube|track|playback|media)\b/i,
|
|
111
|
+
toolsets: ['system'],
|
|
112
|
+
},
|
|
94
113
|
];
|
|
95
114
|
/** Always-on tools regardless of mode. The agent needs schema lookup
|
|
96
115
|
* + skill discovery + session search to be useful even on cold turns. */
|
|
@@ -114,7 +114,7 @@ class AnthropicAdapter {
|
|
|
114
114
|
};
|
|
115
115
|
return;
|
|
116
116
|
}
|
|
117
|
-
yield* decodeStream(reply.body);
|
|
117
|
+
yield* decodeStream(reply.body, input.maxTokens ?? DEFAULT_MAX_TOKENS);
|
|
118
118
|
}
|
|
119
119
|
// ── Request body assembly ────────────────────────────────────────────────
|
|
120
120
|
buildBody(input, streaming) {
|
|
@@ -404,13 +404,20 @@ function decodeUsage(u) {
|
|
|
404
404
|
}
|
|
405
405
|
return out;
|
|
406
406
|
}
|
|
407
|
-
async function* decodeStream(body) {
|
|
407
|
+
async function* decodeStream(body, maxTokens) {
|
|
408
408
|
const blocks = new Map();
|
|
409
409
|
const toolCalls = [];
|
|
410
410
|
let stopReason;
|
|
411
411
|
let usage = undefined;
|
|
412
412
|
// Stable text emission order: walk content blocks by index at end-of-stream.
|
|
413
413
|
const textOrder = [];
|
|
414
|
+
// v4.1.4 Part 1.6: track the last-emitted output-token count so we
|
|
415
|
+
// only yield a `progress` event when the counter actually advances.
|
|
416
|
+
// Anthropic emits `message_delta.usage.output_tokens` as a running
|
|
417
|
+
// total — multiple deltas may carry the same value if no new tokens
|
|
418
|
+
// were produced between them. Deduping keeps the event stream
|
|
419
|
+
// proportional to real progress.
|
|
420
|
+
let lastProgressEmitted = -1;
|
|
414
421
|
for await (const payload of (0, chatCompletionsAdapter_1.parseSseStream)(body)) {
|
|
415
422
|
if (!payload || payload === '[DONE]')
|
|
416
423
|
continue;
|
|
@@ -490,6 +497,22 @@ async function* decodeStream(body) {
|
|
|
490
497
|
stopReason = evt.delta.stop_reason;
|
|
491
498
|
if (evt.usage) {
|
|
492
499
|
usage = { ...(usage ?? {}), ...evt.usage };
|
|
500
|
+
// v4.1.4 Part 1.6 — emit a `progress` event when the running
|
|
501
|
+
// output-token counter advances. The display layer uses these
|
|
502
|
+
// for the ▰▱ progress bar. Deduped via `lastProgressEmitted`
|
|
503
|
+
// so a stream of message_delta events with no real progress
|
|
504
|
+
// doesn't flood the consumer.
|
|
505
|
+
const outputTokens = typeof evt.usage.output_tokens === 'number'
|
|
506
|
+
? evt.usage.output_tokens
|
|
507
|
+
: -1;
|
|
508
|
+
if (outputTokens > lastProgressEmitted) {
|
|
509
|
+
lastProgressEmitted = outputTokens;
|
|
510
|
+
yield {
|
|
511
|
+
type: 'progress',
|
|
512
|
+
outputTokens,
|
|
513
|
+
maxTokens,
|
|
514
|
+
};
|
|
515
|
+
}
|
|
493
516
|
}
|
|
494
517
|
break;
|
|
495
518
|
}
|
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
18
|
exports.ProviderRateLimitError = exports.ProviderTimeoutError = exports.ProviderError = void 0;
|
|
19
19
|
exports.formatRawForMessage = formatRawForMessage;
|
|
20
|
+
exports.classifyProviderError = classifyProviderError;
|
|
21
|
+
exports.suggestForErrorClass = suggestForErrorClass;
|
|
20
22
|
/**
|
|
21
23
|
* Format a raw response body for inclusion in the user-facing error
|
|
22
24
|
* message. Recognises three JSON envelope shapes and falls back to the
|
|
@@ -109,3 +111,93 @@ class ProviderRateLimitError extends ProviderError {
|
|
|
109
111
|
}
|
|
110
112
|
}
|
|
111
113
|
exports.ProviderRateLimitError = ProviderRateLimitError;
|
|
114
|
+
function classifyProviderError(err) {
|
|
115
|
+
if (err == null)
|
|
116
|
+
return 'other';
|
|
117
|
+
// 1. Type-based class detection (fastest, most structured).
|
|
118
|
+
if (err instanceof ProviderRateLimitError)
|
|
119
|
+
return 'rate_limit';
|
|
120
|
+
if (err instanceof ProviderTimeoutError)
|
|
121
|
+
return 'transport';
|
|
122
|
+
if (err instanceof ProviderError) {
|
|
123
|
+
if (err.statusCode === 413)
|
|
124
|
+
return 'context_overflow';
|
|
125
|
+
if (err.statusCode === 429)
|
|
126
|
+
return 'rate_limit';
|
|
127
|
+
if (err.statusCode === 401 || err.statusCode === 403)
|
|
128
|
+
return 'auth';
|
|
129
|
+
}
|
|
130
|
+
// 2. Fall back to message scanning. Adapters that pass through the
|
|
131
|
+
// upstream JSON `error.message` verbatim land here.
|
|
132
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
133
|
+
const lc = msg.toLowerCase();
|
|
134
|
+
// Context overflow / 413 family. Groq's free-tier TPM cap triggers
|
|
135
|
+
// these on the first turn once the prompt + tool schemas inflate.
|
|
136
|
+
if (lc.includes('413') ||
|
|
137
|
+
lc.includes('context_length_exceeded') ||
|
|
138
|
+
lc.includes('context length') ||
|
|
139
|
+
lc.includes('too large') ||
|
|
140
|
+
lc.includes('maximum context length') ||
|
|
141
|
+
lc.includes('payload too large')) {
|
|
142
|
+
return 'context_overflow';
|
|
143
|
+
}
|
|
144
|
+
// Rate-limit family — 429 / TPM / quota / "too many requests".
|
|
145
|
+
if (lc.includes('429') ||
|
|
146
|
+
lc.includes('rate_limit') ||
|
|
147
|
+
lc.includes('rate limit') ||
|
|
148
|
+
lc.includes('too many requests') ||
|
|
149
|
+
lc.includes('quota') ||
|
|
150
|
+
lc.includes('tpm')) {
|
|
151
|
+
return 'rate_limit';
|
|
152
|
+
}
|
|
153
|
+
// Auth family — 401 / 403 / invalid keys / unauthenticated.
|
|
154
|
+
if (lc.includes('401') ||
|
|
155
|
+
lc.includes('403') ||
|
|
156
|
+
lc.includes('invalid_api_key') ||
|
|
157
|
+
lc.includes('invalid api key') ||
|
|
158
|
+
lc.includes('unauthenticated') ||
|
|
159
|
+
lc.includes('unauthorized') ||
|
|
160
|
+
lc.includes('forbidden')) {
|
|
161
|
+
return 'auth';
|
|
162
|
+
}
|
|
163
|
+
// Transport — network, DNS, timeouts that escaped the typed path.
|
|
164
|
+
if (lc.includes('econnrefused') ||
|
|
165
|
+
lc.includes('enotfound') ||
|
|
166
|
+
lc.includes('etimedout') ||
|
|
167
|
+
lc.includes('socket hang up') ||
|
|
168
|
+
lc.includes('network')) {
|
|
169
|
+
return 'transport';
|
|
170
|
+
}
|
|
171
|
+
return 'other';
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* v4.1.3-prebump: produce a single-sentence actionable hint for the
|
|
175
|
+
* given error class. Returns null for `'other'` so the caller can keep
|
|
176
|
+
* its existing default suggestion. Provider name is surfaced where it
|
|
177
|
+
* sharpens the advice ("groq rate-limited" reads clearer than
|
|
178
|
+
* "rate limit").
|
|
179
|
+
*
|
|
180
|
+
* Pure helper. The REPL displays the result; tests assert it. No
|
|
181
|
+
* registry / state access — feed it the class + provider name.
|
|
182
|
+
*/
|
|
183
|
+
function suggestForErrorClass(cls, providerName) {
|
|
184
|
+
const p = providerName ?? 'this provider';
|
|
185
|
+
switch (cls) {
|
|
186
|
+
case 'context_overflow':
|
|
187
|
+
return (`${p} returned 413 (context too large). The combined system prompt ` +
|
|
188
|
+
`+ tool schemas exceed ${p}'s context window. Try \`/model\` to ` +
|
|
189
|
+
`switch to a provider with more headroom (chatgpt-plus, anthropic, ` +
|
|
190
|
+
`deepseek).`);
|
|
191
|
+
case 'rate_limit':
|
|
192
|
+
return (`${p} is rate-limited. Wait a minute, or run \`/model\` to switch ` +
|
|
193
|
+
`to another authed provider while ${p} cools off.`);
|
|
194
|
+
case 'auth':
|
|
195
|
+
return (`${p} rejected the credentials. Run \`/auth status\` (or check the ` +
|
|
196
|
+
`relevant API key env var) and \`/auth login\` if needed.`);
|
|
197
|
+
case 'transport':
|
|
198
|
+
return (`Network or transport error reaching ${p}. Check connectivity, then ` +
|
|
199
|
+
`retry — or \`/model\` to a local provider (ollama) for offline work.`);
|
|
200
|
+
case 'other':
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
}
|
package/dist/tools/v4/index.js
CHANGED
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
* Status: PHASE 8.
|
|
22
22
|
*/
|
|
23
23
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
-
exports.
|
|
24
|
+
exports.memoryReplaceTool = exports.memoryAddTool = exports.processWaitTool = exports.processKillTool = exports.processLogReadTool = exports.processListTool = exports.processSpawnTool = exports.executeCodeTool = exports.shellExecTool = exports.appInputTool = exports.mediaTransportTool = exports.mediaSessionsTool = exports.clipboardWriteTool = exports.clipboardReadTool = exports.appCloseTool = exports.appLaunchTool = exports.volumeSetTool = exports.mediaKeyTool = exports.osProcessListTool = exports.screenshotTool = exports.naturalEventsTool = exports.nowPlayingTool = exports.systemInfoTool = exports.makeLookupToolSchema = exports.skillManageTool = exports.skillViewTool = exports.skillsListTool = exports.sessionListTool = exports.sessionSearchTool = exports.browserCloseTool = exports.browserScrollTool = exports.browserFillTool = exports.browserTypeTool = exports.browserClickTool = exports.browserNavigateTool = exports.browserGetUrlTool = exports.browserExtractTool = exports.browserScreenshotTool = exports.fileCopyTool = exports.fileMoveTool = exports.fileDeleteTool = exports.filePatchTool = exports.fileWriteTool = exports.fileListTool = exports.fileReadTool = exports.deepResearchTool = exports.webPageTool = exports.webFetchTool = exports.webSearchTool = exports.makeSubagentFanoutTool = void 0;
|
|
25
|
+
exports.sessionSummaryTool = exports.memoryRemoveTool = void 0;
|
|
25
26
|
exports.registerReadOnlyTools = registerReadOnlyTools;
|
|
26
27
|
exports.registerWriteTools = registerWriteTools;
|
|
27
28
|
exports.registerAllTools = registerAllTools;
|
|
@@ -66,6 +67,13 @@ const appLaunch_1 = require("./system/appLaunch");
|
|
|
66
67
|
const appClose_1 = require("./system/appClose");
|
|
67
68
|
const clipboardRead_1 = require("./system/clipboardRead");
|
|
68
69
|
const clipboardWrite_1 = require("./system/clipboardWrite");
|
|
70
|
+
// v4.1.4-media — three-layer media-control bundle.
|
|
71
|
+
// Layer 2 (OS media session): mediaSessions (read) + mediaTransport (write).
|
|
72
|
+
// Layer 3 fallback (mediaKey, blind keystroke) remains unchanged.
|
|
73
|
+
// Layer 1 (semantic API) is per-app and out of this slice.
|
|
74
|
+
const mediaSessions_1 = require("./system/mediaSessions");
|
|
75
|
+
const mediaTransport_1 = require("./system/mediaTransport");
|
|
76
|
+
const appInput_1 = require("./system/appInput");
|
|
69
77
|
// Phase v4.1.2-update — natural-language self-update entry point.
|
|
70
78
|
// Routes through the same shared executeInstall executor as `/update install`.
|
|
71
79
|
const aidenSelfUpdate_1 = require("./system/aidenSelfUpdate");
|
|
@@ -123,6 +131,9 @@ function registerReadOnlyTools(registry) {
|
|
|
123
131
|
registry.register(screenshot_1.screenshotTool);
|
|
124
132
|
registry.register(osProcessList_1.osProcessListTool);
|
|
125
133
|
registry.register(clipboardRead_1.clipboardReadTool);
|
|
134
|
+
// v4.1.4-media — GSMTC session enumeration (read). Pair with
|
|
135
|
+
// mediaTransport (write) in the write-tools registration below.
|
|
136
|
+
registry.register(mediaSessions_1.mediaSessionsTool);
|
|
126
137
|
registry.register((0, lookupToolSchema_1.makeLookupToolSchema)(registry));
|
|
127
138
|
// Phase v4.1-subagent — register a stub for subagent_fanout so its
|
|
128
139
|
// schema is visible to the agent loop, the MCP server, and the
|
|
@@ -198,6 +209,11 @@ function registerWriteTools(registry) {
|
|
|
198
209
|
registry.register(appLaunch_1.appLaunchTool);
|
|
199
210
|
registry.register(appClose_1.appCloseTool);
|
|
200
211
|
registry.register(clipboardWrite_1.clipboardWriteTool);
|
|
212
|
+
// v4.1.4-media — verified GSMTC transport (replaces mediaKey for
|
|
213
|
+
// the "name an app, play/pause it" case) + focused-window SendKeys
|
|
214
|
+
// (escape hatch when GSMTC doesn't enumerate the surface).
|
|
215
|
+
registry.register(mediaTransport_1.mediaTransportTool);
|
|
216
|
+
registry.register(appInput_1.appInputTool);
|
|
201
217
|
}
|
|
202
218
|
/** Register every v4 tool. Most callers want this. */
|
|
203
219
|
function registerAllTools(registry) {
|
|
@@ -281,6 +297,13 @@ var clipboardRead_2 = require("./system/clipboardRead");
|
|
|
281
297
|
Object.defineProperty(exports, "clipboardReadTool", { enumerable: true, get: function () { return clipboardRead_2.clipboardReadTool; } });
|
|
282
298
|
var clipboardWrite_2 = require("./system/clipboardWrite");
|
|
283
299
|
Object.defineProperty(exports, "clipboardWriteTool", { enumerable: true, get: function () { return clipboardWrite_2.clipboardWriteTool; } });
|
|
300
|
+
// v4.1.4-media exports — three-layer media-control bundle.
|
|
301
|
+
var mediaSessions_2 = require("./system/mediaSessions");
|
|
302
|
+
Object.defineProperty(exports, "mediaSessionsTool", { enumerable: true, get: function () { return mediaSessions_2.mediaSessionsTool; } });
|
|
303
|
+
var mediaTransport_2 = require("./system/mediaTransport");
|
|
304
|
+
Object.defineProperty(exports, "mediaTransportTool", { enumerable: true, get: function () { return mediaTransport_2.mediaTransportTool; } });
|
|
305
|
+
var appInput_2 = require("./system/appInput");
|
|
306
|
+
Object.defineProperty(exports, "appInputTool", { enumerable: true, get: function () { return appInput_2.appInputTool; } });
|
|
284
307
|
var shellExec_2 = require("./terminal/shellExec");
|
|
285
308
|
Object.defineProperty(exports, "shellExecTool", { enumerable: true, get: function () { return shellExec_2.shellExecTool; } });
|
|
286
309
|
var executeCode_2 = require("./executeCode");
|
|
@@ -132,6 +132,14 @@ exports.recallSessionTool = {
|
|
|
132
132
|
include_full: args.include_full === true,
|
|
133
133
|
};
|
|
134
134
|
const ranked = (0, distillationIndex_1.rankDistillations)(dists, recallQuery);
|
|
135
|
+
// v4.1.3-repl-polish: mark degraded when any matched session was
|
|
136
|
+
// distilled with the Phase A+B `partial: true` flag (LLM-timeout
|
|
137
|
+
// path — deterministic fields present, semantic bullets/decisions
|
|
138
|
+
// may be empty). The model still gets the full match list; the
|
|
139
|
+
// trail row renders yellow so the user knows recall completed
|
|
140
|
+
// against partial data.
|
|
141
|
+
const partialCount = ranked.matches.filter((m) => m.partial === true).length;
|
|
142
|
+
const degraded = partialCount > 0;
|
|
135
143
|
return {
|
|
136
144
|
success: true,
|
|
137
145
|
query: recallQuery.query,
|
|
@@ -142,6 +150,12 @@ exports.recallSessionTool = {
|
|
|
142
150
|
// delta is malformed files; the agent can suggest running aiden
|
|
143
151
|
// doctor to inspect.
|
|
144
152
|
scanned: ids.length,
|
|
153
|
+
...(degraded && {
|
|
154
|
+
degraded: true,
|
|
155
|
+
degradedReason: partialCount === 1
|
|
156
|
+
? '1 matched session has partial distillation data'
|
|
157
|
+
: `${partialCount} matched sessions have partial distillation data`,
|
|
158
|
+
}),
|
|
145
159
|
};
|
|
146
160
|
// Note re: subsystem health — wire-up happens at the runtime
|
|
147
161
|
// construction layer (cli/v4/aidenCLI.ts) where the registry is
|