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.
Files changed (36) hide show
  1. package/dist/cli/v4/aidenCLI.js +10 -0
  2. package/dist/cli/v4/callbacks.js +85 -13
  3. package/dist/cli/v4/chatSession.js +250 -24
  4. package/dist/cli/v4/commands/doctor.js +23 -27
  5. package/dist/cli/v4/commands/model.js +30 -1
  6. package/dist/cli/v4/defaultSoul.js +69 -2
  7. package/dist/cli/v4/display/capabilityCard.js +135 -0
  8. package/dist/cli/v4/display/frame.js +234 -0
  9. package/dist/cli/v4/display/progressBar.js +137 -0
  10. package/dist/cli/v4/display/sessionEndCard.js +127 -0
  11. package/dist/cli/v4/display/toolTrail.js +172 -0
  12. package/dist/cli/v4/display.js +891 -153
  13. package/dist/cli/v4/doctor.js +377 -75
  14. package/dist/cli/v4/promotionPrompt.js +135 -5
  15. package/dist/cli/v4/replyRenderer.js +487 -26
  16. package/dist/cli/v4/skinEngine.js +26 -4
  17. package/dist/cli/v4/toolPreview.js +82 -19
  18. package/dist/core/tools/nowPlaying.js +7 -15
  19. package/dist/core/v4/aidenAgent.js +9 -0
  20. package/dist/core/v4/promptBuilder.js +2 -1
  21. package/dist/core/v4/sessionDistiller.js +48 -1
  22. package/dist/core/v4/toolRegistry.js +16 -1
  23. package/dist/core/version.js +1 -1
  24. package/dist/moat/plannerGuard.js +19 -0
  25. package/dist/providers/v4/anthropicAdapter.js +25 -2
  26. package/dist/providers/v4/errors.js +92 -0
  27. package/dist/tools/v4/index.js +24 -1
  28. package/dist/tools/v4/sessions/recallSession.js +14 -0
  29. package/dist/tools/v4/system/_psHelpers.js +70 -2
  30. package/dist/tools/v4/system/appInput.js +154 -0
  31. package/dist/tools/v4/system/appLaunch.js +136 -10
  32. package/dist/tools/v4/system/mediaKey.js +35 -4
  33. package/dist/tools/v4/system/mediaSessions.js +163 -0
  34. package/dist/tools/v4/system/mediaTransport.js +211 -0
  35. package/package.json +2 -1
  36. 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 → name of the property in `args` that should render
33
- * as the at-a-glance preview. Stable contract; tests assert specific
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 argKey = exports.TOOL_PRIMARY_ARG[toolName];
111
- if (argKey === '')
112
- return '';
113
- if (!args || typeof args !== 'object')
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 raw === 'string') {
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
- str = JSON.stringify(raw);
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
- Add-Type -AssemblyName System.Runtime.WindowsRuntime
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. Responses that only describe intentions without acting are not acceptable.',
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
- const DEFAULT_TIMEOUT_MS = 4000;
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
- return { id: call.id, name: call.name, result };
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);
@@ -2,4 +2,4 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.VERSION = void 0;
4
4
  // AUTO-GENERATED by scripts/inject-version.js — do not edit by hand
5
- exports.VERSION = '4.1.2';
5
+ exports.VERSION = '4.1.4';
@@ -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
+ }
@@ -21,7 +21,8 @@
21
21
  * Status: PHASE 8.
22
22
  */
23
23
  Object.defineProperty(exports, "__esModule", { value: true });
24
- exports.sessionSummaryTool = exports.memoryRemoveTool = exports.memoryReplaceTool = exports.memoryAddTool = exports.processWaitTool = exports.processKillTool = exports.processLogReadTool = exports.processListTool = exports.processSpawnTool = exports.executeCodeTool = exports.shellExecTool = 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;
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