aiden-runtime 4.1.3 → 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/callbacks.js +70 -13
- package/dist/cli/v4/chatSession.js +130 -22
- package/dist/cli/v4/defaultSoul.js +69 -2
- package/dist/cli/v4/display/frame.js +234 -0
- package/dist/cli/v4/display/progressBar.js +137 -0
- package/dist/cli/v4/display.js +427 -21
- package/dist/cli/v4/replyRenderer.js +196 -26
- package/dist/cli/v4/skinEngine.js +15 -4
- package/dist/cli/v4/toolPreview.js +68 -19
- package/dist/core/v4/aidenAgent.js +9 -0
- package/dist/core/v4/promptBuilder.js +2 -1
- package/dist/core/version.js +1 -1
- package/dist/providers/v4/anthropicAdapter.js +25 -2
- package/package.json +2 -1
package/dist/cli/v4/callbacks.js
CHANGED
|
@@ -82,6 +82,15 @@ class CliCallbacks {
|
|
|
82
82
|
}
|
|
83
83
|
this.beforeFirstToolHook = undefined;
|
|
84
84
|
}
|
|
85
|
+
// v4.1.4 reply-quality polish — Part 1.6. Pause activity
|
|
86
|
+
// indicator BEFORE the tool row writes so the indicator's line
|
|
87
|
+
// is clean when the row lands. Fires for every tool, not just
|
|
88
|
+
// the first. Defensive try/catch — a misbehaving hook must not
|
|
89
|
+
// block tool dispatch.
|
|
90
|
+
try {
|
|
91
|
+
this.beforeToolHook?.();
|
|
92
|
+
}
|
|
93
|
+
catch { /* defensive */ }
|
|
85
94
|
const handle = this.display.toolRow(call.name, call.arguments);
|
|
86
95
|
this.toolRows.set(call.id, handle);
|
|
87
96
|
this.toolStartTimes.set(call.id, Date.now());
|
|
@@ -92,14 +101,38 @@ class CliCallbacks {
|
|
|
92
101
|
const startedAt = this.toolStartTimes.get(call.id);
|
|
93
102
|
this.toolRows.delete(call.id);
|
|
94
103
|
this.toolStartTimes.delete(call.id);
|
|
95
|
-
if (!handle || startedAt === undefined)
|
|
104
|
+
if (!handle || startedAt === undefined) {
|
|
105
|
+
// Even if we lost the handle, the indicator may still need to
|
|
106
|
+
// be re-armed so the next gap shows activity. Tool-name-aware
|
|
107
|
+
// verb selection happens in the hook itself.
|
|
108
|
+
try {
|
|
109
|
+
this.afterEachToolHook?.(call.name);
|
|
110
|
+
}
|
|
111
|
+
catch { /* defensive */ }
|
|
96
112
|
return;
|
|
113
|
+
}
|
|
97
114
|
const ms = Date.now() - startedAt;
|
|
98
115
|
const err = result?.error;
|
|
99
116
|
if (typeof err === 'string' && err.includes('URL provenance gate')) {
|
|
100
117
|
handle.blocked();
|
|
101
118
|
return;
|
|
102
119
|
}
|
|
120
|
+
// v4.1.4 reply-quality polish — Part 1.6. Helper used by ALL
|
|
121
|
+
// outcome branches below so the activity indicator gets re-armed
|
|
122
|
+
// for the gap that follows this tool (next tool, or final reply).
|
|
123
|
+
// Tool-name-aware verb selection happens in the hook (chatSession
|
|
124
|
+
// wires it through `verbForActivity`).
|
|
125
|
+
const fireAfter = () => {
|
|
126
|
+
try {
|
|
127
|
+
this.afterEachToolHook?.(call.name);
|
|
128
|
+
}
|
|
129
|
+
catch { /* defensive */ }
|
|
130
|
+
};
|
|
131
|
+
if (typeof err === 'string' && err.includes('URL provenance gate')) {
|
|
132
|
+
handle.blocked();
|
|
133
|
+
fireAfter();
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
103
136
|
if (err) {
|
|
104
137
|
handle.fail(ms);
|
|
105
138
|
// v4.1.3-essentials: when the tool's failure payload includes a
|
|
@@ -111,15 +144,18 @@ class CliCallbacks {
|
|
|
111
144
|
if (result?.capabilityCard) {
|
|
112
145
|
this.display.capabilityCard(result.capabilityCard);
|
|
113
146
|
}
|
|
147
|
+
fireAfter();
|
|
114
148
|
return;
|
|
115
149
|
}
|
|
116
150
|
// v4.1.3-repl-polish: degraded outcome — tool completed but with a
|
|
117
151
|
// partial / best-effort result. Show in trail yellow instead of silent.
|
|
118
152
|
if (result?.degraded) {
|
|
119
153
|
handle.degraded(ms, result.degradedReason);
|
|
154
|
+
fireAfter();
|
|
120
155
|
return;
|
|
121
156
|
}
|
|
122
157
|
handle.ok(ms);
|
|
158
|
+
fireAfter();
|
|
123
159
|
};
|
|
124
160
|
/** ApprovalEngine.callbacks.promptUser */
|
|
125
161
|
this.promptApproval = async (req) => {
|
|
@@ -186,23 +222,29 @@ Reply with ONE word: safe, caution, or dangerous.`;
|
|
|
186
222
|
return false;
|
|
187
223
|
}
|
|
188
224
|
};
|
|
189
|
-
/**
|
|
225
|
+
/**
|
|
226
|
+
* PlannerGuard sink. v4.1.4 Phase 3b' (Q-Planner): moved to
|
|
227
|
+
* verbose-only. The default `normal` mode previously emitted
|
|
228
|
+
* `[planner] kept N tools (reason)` mid-execution, which collided
|
|
229
|
+
* visually with the activity indicator's single-line paint and
|
|
230
|
+
* with streamed deltas. Users running with the default verbose
|
|
231
|
+
* level should see a clean execution surface — planner-guard
|
|
232
|
+
* decisions are useful for debugging but noise during normal use.
|
|
233
|
+
*
|
|
234
|
+
* `verbose` mode keeps the full breakdown for debugging. `compact`
|
|
235
|
+
* stays silent (unchanged).
|
|
236
|
+
*/
|
|
190
237
|
this.onPlannerGuardDecision = (decision) => {
|
|
191
238
|
if (this.verboseMode === 'compact')
|
|
192
239
|
return;
|
|
193
|
-
if (
|
|
240
|
+
if (this.verboseMode !== 'verbose')
|
|
194
241
|
return;
|
|
195
|
-
if (
|
|
196
|
-
const conf = decision.confidence !== undefined
|
|
197
|
-
? ` (conf ${decision.confidence.toFixed(2)})`
|
|
198
|
-
: '';
|
|
199
|
-
this.display.dim(`[planner] ${decision.reason}${conf}: kept ${decision.selectedTools.length} / dropped ${decision.excludedTools.length}`);
|
|
242
|
+
if (decision.reason === 'no_filter')
|
|
200
243
|
return;
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
}
|
|
244
|
+
const conf = decision.confidence !== undefined
|
|
245
|
+
? ` (conf ${decision.confidence.toFixed(2)})`
|
|
246
|
+
: '';
|
|
247
|
+
this.display.dim(`[planner] ${decision.reason}${conf}: kept ${decision.selectedTools.length} / dropped ${decision.excludedTools.length}`);
|
|
206
248
|
};
|
|
207
249
|
/**
|
|
208
250
|
* Phase v4.1-skill-mining — post-turn cue when the miner has
|
|
@@ -280,6 +322,21 @@ Reply with ONE word: safe, caution, or dangerous.`;
|
|
|
280
322
|
this.beforeFirstToolHook = fn;
|
|
281
323
|
this.firstToolFiredThisTurn = false;
|
|
282
324
|
}
|
|
325
|
+
/**
|
|
326
|
+
* v4.1.4 reply-quality polish — Part 1.6.
|
|
327
|
+
*
|
|
328
|
+
* Register paired hooks so chatSession can pause the activity
|
|
329
|
+
* indicator while a tool row writes, and resume it (with a fresh
|
|
330
|
+
* verb derived from the just-completed tool) in the gap before the
|
|
331
|
+
* next tool fires or the final reply arrives.
|
|
332
|
+
*
|
|
333
|
+
* Both fire for EVERY tool, not just the first. Either can be
|
|
334
|
+
* omitted independently. Cleared between turns by passing `undefined`.
|
|
335
|
+
*/
|
|
336
|
+
setActivityIndicatorHooks(opts) {
|
|
337
|
+
this.beforeToolHook = opts.beforeTool;
|
|
338
|
+
this.afterEachToolHook = opts.afterEachTool;
|
|
339
|
+
}
|
|
283
340
|
}
|
|
284
341
|
exports.CliCallbacks = CliCallbacks;
|
|
285
342
|
// Tier-3.1 (v4.1-tier3.1): replaced 🟢/🟡/🔴 emoji badges with
|
|
@@ -70,6 +70,10 @@ exports.formatTokens = formatTokens;
|
|
|
70
70
|
exports.formatDuration = formatDuration;
|
|
71
71
|
exports.renderMemoryConfirmations = renderMemoryConfirmations;
|
|
72
72
|
const display_1 = require("./display");
|
|
73
|
+
// v4.1.4 Part 1.6 — per-turn token progress bar. Fed by `onProgress`
|
|
74
|
+
// events from the streaming adapter; hidden when the adapter doesn't
|
|
75
|
+
// emit progress (honest degradation).
|
|
76
|
+
const progressBar_1 = require("./display/progressBar");
|
|
73
77
|
const uiBuild_1 = require("./uiBuild");
|
|
74
78
|
const sessionSummaryGate_1 = require("./sessionSummaryGate");
|
|
75
79
|
const aidenPrompt_1 = __importDefault(require("./aidenPrompt"));
|
|
@@ -414,9 +418,22 @@ class ChatSession {
|
|
|
414
418
|
// Tier-3-essentials: hard-clear the screen on terminal resize so
|
|
415
419
|
// dropdown re-renders + previous prompt frames don't ghost into
|
|
416
420
|
// the new viewport. No-op on non-TTY / MCP serve mode.
|
|
421
|
+
//
|
|
422
|
+
// v4.1.4 reply-quality polish: also drop the per-chunk stream row
|
|
423
|
+
// counter so a mid-stream resize doesn't try to erase rows that
|
|
424
|
+
// the hard-clear already removed. See `resetStreamFrameForResize`
|
|
425
|
+
// in display.ts for the rationale.
|
|
417
426
|
const restoreResizeGuard = this.opts.promptApi
|
|
418
427
|
? () => { }
|
|
419
|
-
: (0, resizeGuard_1.installResizeGuard)(
|
|
428
|
+
: (0, resizeGuard_1.installResizeGuard)({
|
|
429
|
+
onCleared: () => {
|
|
430
|
+
try {
|
|
431
|
+
this.opts.display
|
|
432
|
+
.resetStreamFrameForResize?.();
|
|
433
|
+
}
|
|
434
|
+
catch { /* defensive — never break the resize listener */ }
|
|
435
|
+
},
|
|
436
|
+
});
|
|
420
437
|
try {
|
|
421
438
|
while (iter < max) {
|
|
422
439
|
iter += 1;
|
|
@@ -885,40 +902,105 @@ class ChatSession {
|
|
|
885
902
|
const baseHistory = newHistory.length > 0
|
|
886
903
|
? [...this.history, ...newHistory, userMsg]
|
|
887
904
|
: [...this.history, userMsg];
|
|
888
|
-
// Phase 16c: streaming gated on display.streaming config
|
|
889
|
-
//
|
|
890
|
-
//
|
|
905
|
+
// Phase 16c: streaming gated on display.streaming config.
|
|
906
|
+
// v4.1.4 Part 1.6: PRODUCTION DEFAULT FLIPPED FROM FALSE TO TRUE.
|
|
907
|
+
// Streaming delivers the activity indicator, tool-row live tick,
|
|
908
|
+
// and token progress bar that the user feedback ("after prompt i
|
|
909
|
+
// just see output") was specifically asking for. Users who
|
|
910
|
+
// explicitly set `display.streaming: false` in config still opt
|
|
911
|
+
// out; the change affects only the default for users who never
|
|
912
|
+
// touched the flag.
|
|
913
|
+
//
|
|
914
|
+
// Test-stub fallback (no ConfigManager) stays at `false` so
|
|
915
|
+
// existing tests that depended on the non-streaming code path
|
|
916
|
+
// don't have to be rewritten in this slice — they exercise the
|
|
917
|
+
// batch-call path that production users on Ollama / non-streaming
|
|
918
|
+
// adapters still hit naturally.
|
|
891
919
|
const streamingEnabled = typeof this.opts.config?.getValue === 'function'
|
|
892
|
-
? this.opts.config.getValue('display.streaming',
|
|
920
|
+
? this.opts.config.getValue('display.streaming', true) === true
|
|
893
921
|
: false;
|
|
894
|
-
//
|
|
895
|
-
//
|
|
896
|
-
|
|
897
|
-
|
|
922
|
+
// v4.1.4 reply-quality polish — Part 1.6. Activity indicator
|
|
923
|
+
// replaces the prior single-shot spinner. Pause/resume hooks make
|
|
924
|
+
// the indicator cooperate with tool rows: it pauses before each
|
|
925
|
+
// tool row writes and resumes (with a tool-aware verb) in the
|
|
926
|
+
// gap that follows, so the user always sees activity feedback
|
|
927
|
+
// during model-thinking time — not just the pre-first-token gap.
|
|
928
|
+
//
|
|
929
|
+
// Initial verb is "thinking" (pre-tools phase). After each tool
|
|
930
|
+
// completes, `verbForActivity(toolName, 'post-tool')` picks a
|
|
931
|
+
// category-aware verb (reading / searching / analyzing / drafting).
|
|
932
|
+
// When the first stream delta arrives OR the final agentTurn is
|
|
933
|
+
// about to write, the indicator stops permanently.
|
|
934
|
+
const indicator = this.opts.display.activityIndicator('thinking');
|
|
935
|
+
let indicatorStopped = false;
|
|
898
936
|
let streamingActive = false;
|
|
899
|
-
const
|
|
900
|
-
if (
|
|
937
|
+
const stopIndicatorOnce = () => {
|
|
938
|
+
if (indicatorStopped)
|
|
901
939
|
return;
|
|
902
|
-
|
|
903
|
-
|
|
940
|
+
indicatorStopped = true;
|
|
941
|
+
indicator.stop();
|
|
942
|
+
// Clear the per-turn pause/resume hooks so they don't fire
|
|
943
|
+
// against a stopped indicator on a subsequent turn. The next
|
|
944
|
+
// turn re-registers fresh hooks.
|
|
945
|
+
try {
|
|
946
|
+
this.opts.callbacks.setActivityIndicatorHooks?.({});
|
|
947
|
+
}
|
|
948
|
+
catch { /* defensive */ }
|
|
904
949
|
};
|
|
905
|
-
// Phase 23.5: stop the
|
|
906
|
-
// tool row prints
|
|
907
|
-
//
|
|
908
|
-
//
|
|
909
|
-
|
|
910
|
-
|
|
950
|
+
// Phase 23.5 carried forward: stop the indicator the moment the
|
|
951
|
+
// first tool row prints — the row itself is the activity surface
|
|
952
|
+
// during a tool. Part 1.6 then resumes via `afterEachTool` so the
|
|
953
|
+
// post-tool gap has its own indicator paint.
|
|
954
|
+
this.opts.callbacks.setBeforeFirstToolHook?.(stopIndicatorOnce);
|
|
955
|
+
// Part 1.6: pause/resume hooks around every tool row. The
|
|
956
|
+
// `beforeTool` hook fires before EACH tool row writes (not just
|
|
957
|
+
// the first), so multi-tool sequences also keep the indicator
|
|
958
|
+
// off the tool-row line. `afterEachTool` resumes with a verb
|
|
959
|
+
// chosen from the just-completed tool's category — best guess
|
|
960
|
+
// for "what the model is doing next". `lastToolName` is captured
|
|
961
|
+
// for tests / observability; the verb decision happens inline.
|
|
962
|
+
this.opts.callbacks.setActivityIndicatorHooks?.({
|
|
963
|
+
beforeTool: () => {
|
|
964
|
+
if (indicatorStopped)
|
|
965
|
+
return;
|
|
966
|
+
indicator.pause();
|
|
967
|
+
// v4.1.4 Part 1.6: hide the progress bar while the tool row
|
|
968
|
+
// owns the screen. The bar paints below the indicator, so
|
|
969
|
+
// it'd otherwise sit between the tool row and any subsequent
|
|
970
|
+
// stream output — visual clutter for tool-heavy turns. The
|
|
971
|
+
// bar is per-turn, not per-stream-segment; once hidden it
|
|
972
|
+
// stays hidden until the next turn's bar is created.
|
|
973
|
+
progressBar?.hide();
|
|
974
|
+
},
|
|
975
|
+
afterEachTool: (toolName) => {
|
|
976
|
+
if (indicatorStopped)
|
|
977
|
+
return;
|
|
978
|
+
indicator.resume((0, display_1.verbForActivity)(toolName, 'post-tool'));
|
|
979
|
+
},
|
|
980
|
+
});
|
|
981
|
+
// v4.1.4 Part 1.6: per-turn progress bar. Created lazily on the
|
|
982
|
+
// first `onProgress` event from the streaming adapter so the bar
|
|
983
|
+
// line doesn't paint until there's something to show. Adapters
|
|
984
|
+
// that don't emit progress (Ollama, most OpenAI-compat) never
|
|
985
|
+
// trigger creation — honest degradation, no fake estimates.
|
|
986
|
+
let progressBar = null;
|
|
911
987
|
try {
|
|
912
988
|
const result = await this.opts.agent.runConversation(baseHistory, {
|
|
913
989
|
stream: streamingEnabled,
|
|
914
990
|
onFirstDelta: streamingEnabled
|
|
915
991
|
? () => {
|
|
916
|
-
|
|
992
|
+
stopIndicatorOnce();
|
|
917
993
|
streamingActive = true;
|
|
918
994
|
}
|
|
919
995
|
: undefined,
|
|
920
996
|
onDelta: streamingEnabled
|
|
921
997
|
? (text) => {
|
|
998
|
+
// v4.1.4 Part 1.6: bar lives ABOVE streamed text. Hide
|
|
999
|
+
// it before each delta writes so the stream output
|
|
1000
|
+
// doesn't land on the bar's line. The bar repaints on
|
|
1001
|
+
// the next `onProgress` event (which Anthropic emits
|
|
1002
|
+
// frequently enough that the bar stays usefully visible).
|
|
1003
|
+
progressBar?.hide();
|
|
922
1004
|
this.opts.display.streamPartial(text);
|
|
923
1005
|
}
|
|
924
1006
|
: undefined,
|
|
@@ -927,8 +1009,30 @@ class ChatSession {
|
|
|
927
1009
|
this.opts.display.streamToolIndicator(call.name);
|
|
928
1010
|
}
|
|
929
1011
|
: undefined,
|
|
1012
|
+
onProgress: streamingEnabled
|
|
1013
|
+
? (outputTokens, maxTokens) => {
|
|
1014
|
+
if (indicatorStopped === false)
|
|
1015
|
+
return;
|
|
1016
|
+
// Lazy-create on first event. The indicator must already
|
|
1017
|
+
// be stopped (first delta arrived) so the bar paints on
|
|
1018
|
+
// its own line below where the indicator was. If the
|
|
1019
|
+
// indicator is still up, skip — the bar would land on
|
|
1020
|
+
// the indicator line and get clobbered by the next tick.
|
|
1021
|
+
if (!progressBar) {
|
|
1022
|
+
progressBar = (0, progressBar_1.createProgressBar)(process.stdout,
|
|
1023
|
+
// Display exposes its skin via getter on the
|
|
1024
|
+
// implementation; cast to any to avoid widening
|
|
1025
|
+
// the public Display surface for one-shot use.
|
|
1026
|
+
this.opts.display.skin);
|
|
1027
|
+
}
|
|
1028
|
+
progressBar.update(outputTokens, maxTokens);
|
|
1029
|
+
}
|
|
1030
|
+
: undefined,
|
|
930
1031
|
});
|
|
931
|
-
|
|
1032
|
+
stopIndicatorOnce();
|
|
1033
|
+
// Hide the progress bar before any post-stream content
|
|
1034
|
+
// (statusFooter, the next prompt) lands on its line.
|
|
1035
|
+
progressBar?.hide();
|
|
932
1036
|
if (streamingActive)
|
|
933
1037
|
this.opts.display.streamComplete();
|
|
934
1038
|
this.history = result.messages;
|
|
@@ -966,7 +1070,11 @@ class ChatSession {
|
|
|
966
1070
|
this.renderStatusLine();
|
|
967
1071
|
}
|
|
968
1072
|
catch (err) {
|
|
969
|
-
|
|
1073
|
+
stopIndicatorOnce();
|
|
1074
|
+
// v4.1.4 Part 1.6: error path must also hide the progress bar
|
|
1075
|
+
// so it doesn't leak across the boundary into the error chrome
|
|
1076
|
+
// or the next prompt.
|
|
1077
|
+
progressBar?.hide();
|
|
970
1078
|
if (streamingActive)
|
|
971
1079
|
this.opts.display.streamComplete();
|
|
972
1080
|
const msg = err?.message ?? String(err);
|
|
@@ -30,7 +30,7 @@ exports.PREVIOUS_BUNDLED_SOULS = exports.DEFAULT_SOUL_MD = exports.BUNDLED_SOUL_
|
|
|
30
30
|
// <act_dont_ask>. ensureSoulMdSeeded compares this against the user's
|
|
31
31
|
// on-disk SOUL.md to decide whether to silent-replace (matches a prior
|
|
32
32
|
// bundled default) or preserve+notify (user-edited).
|
|
33
|
-
exports.BUNDLED_SOUL_VERSION = 'v4.1.
|
|
33
|
+
exports.BUNDLED_SOUL_VERSION = 'v4.1.4';
|
|
34
34
|
exports.DEFAULT_SOUL_MD = `You are Aiden — a local-first AI agent built by Taracod.
|
|
35
35
|
|
|
36
36
|
Identity:
|
|
@@ -40,7 +40,8 @@ Identity:
|
|
|
40
40
|
- You have 40 tools spanning files, browser, terminal, web, memory.
|
|
41
41
|
|
|
42
42
|
Voice:
|
|
43
|
-
-
|
|
43
|
+
- Match the user's energy. When the user asks a thoughtful question (opinion, exploration, comparison), engage thoughtfully. When the user asks transactionally, stay tight.
|
|
44
|
+
- On thoughtful questions, share the reasoning before the answer — what you considered, what you discarded, why.
|
|
44
45
|
- Honest above all — if you didn't do something, say so. If you're not sure, say so.
|
|
45
46
|
- You never claim to "have run" a tool unless the trace shows it.
|
|
46
47
|
|
|
@@ -254,5 +255,71 @@ Limits:
|
|
|
254
255
|
- You're a CLI agent in v4.0.0. No voice, no scheduled jobs, no messaging gateway yet — those are v4.1.
|
|
255
256
|
- You can't bypass approval prompts for dangerous commands.
|
|
256
257
|
- You don't lie to look smart. If you don't know, you say so.
|
|
258
|
+
`,
|
|
259
|
+
// v4.1.2 default — shipped through v4.1.3 (no SOUL change in v4.1.3).
|
|
260
|
+
// v4.1.4 rewrites the Voice block to make conciseness conditional:
|
|
261
|
+
// thoughtful questions get thoughtful engagement, transactional
|
|
262
|
+
// questions stay tight. Adds an explicit reasoning-visibility line.
|
|
263
|
+
// Users on the v4.1.2 / v4.1.3 install have this verbatim text on
|
|
264
|
+
// disk; silent-upgrade picks them up here.
|
|
265
|
+
`You are Aiden — a local-first AI agent built by Taracod.
|
|
266
|
+
|
|
267
|
+
Identity:
|
|
268
|
+
- You run on the user's machine, native Windows/Linux/macOS (not WSL2).
|
|
269
|
+
- You have 72 bundled skills + access to install more via skills.sh.
|
|
270
|
+
- You remember past sessions via persistent storage.
|
|
271
|
+
- You have 40 tools spanning files, browser, terminal, web, memory.
|
|
272
|
+
|
|
273
|
+
Voice:
|
|
274
|
+
- Direct. No fluff. Match the user's energy.
|
|
275
|
+
- Honest above all — if you didn't do something, say so. If you're not sure, say so.
|
|
276
|
+
- You never claim to "have run" a tool unless the trace shows it.
|
|
277
|
+
|
|
278
|
+
Behavior:
|
|
279
|
+
- Default to action over discussion. The user wants results.
|
|
280
|
+
- When asked who you are, identify as Aiden. Not "a large language model."
|
|
281
|
+
- When asked what you can do, mention specific skills/tools, not generic capabilities.
|
|
282
|
+
- If user mentions trading/NSE/markets, you have specialized skills for that.
|
|
283
|
+
|
|
284
|
+
<act_dont_ask>
|
|
285
|
+
When a request has an obvious default interpretation, act on it
|
|
286
|
+
immediately instead of asking for clarification. Examples:
|
|
287
|
+
- "play me a popular song" / "play X on youtube" → load skill_view(media-search)
|
|
288
|
+
and follow it. Substitute fuzzy phrases ("popular song") with a specific
|
|
289
|
+
chart-topper BEFORE searching, then open_url a /watch?v= URL once.
|
|
290
|
+
NEVER search verbatim "popular song" — that returns articles, not music.
|
|
291
|
+
- "what files are in my Downloads?" → file_list on Downloads. Don't ask
|
|
292
|
+
"which user?" — it's the current user.
|
|
293
|
+
- "is port 443 open?" → check this machine. Don't ask "open where?"
|
|
294
|
+
Only ask for clarification when the ambiguity genuinely changes which
|
|
295
|
+
tool you would call.
|
|
296
|
+
</act_dont_ask>
|
|
297
|
+
|
|
298
|
+
<prerequisite_checks>
|
|
299
|
+
Before acting, check whether prerequisite discovery, lookup, or
|
|
300
|
+
context-gathering steps are needed. If a step depends on output from a
|
|
301
|
+
prior step, resolve that dependency first. Don't skip prerequisite
|
|
302
|
+
steps just because the final action seems obvious.
|
|
303
|
+
</prerequisite_checks>
|
|
304
|
+
|
|
305
|
+
<missing_context>
|
|
306
|
+
If required context is missing, do NOT guess or hallucinate. Use the
|
|
307
|
+
appropriate lookup tool when missing information is retrievable
|
|
308
|
+
(file_read, file_list, web_search, fetch_url, session_search,
|
|
309
|
+
system_info). Ask a clarifying question ONLY when no tool can resolve
|
|
310
|
+
the ambiguity.
|
|
311
|
+
</missing_context>
|
|
312
|
+
|
|
313
|
+
<keep_going>
|
|
314
|
+
Work autonomously until the task is fully resolved. Don't stop with a
|
|
315
|
+
plan — execute it. Multi-step tasks (open browser → search → click
|
|
316
|
+
result; or list files → read each → summarise) are expected; chain
|
|
317
|
+
the tool calls within a single turn instead of returning halfway and
|
|
318
|
+
asking the user what to do next.
|
|
319
|
+
</keep_going>
|
|
320
|
+
|
|
321
|
+
Limits:
|
|
322
|
+
- You can't bypass approval prompts for dangerous commands.
|
|
323
|
+
- You don't lie to look smart. If you don't know, you say so.
|
|
257
324
|
`,
|
|
258
325
|
];
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (c) 2026 Shiva Deore (Taracod).
|
|
4
|
+
* Licensed under AGPL-3.0. See LICENSE for details.
|
|
5
|
+
*
|
|
6
|
+
* Aiden — local-first agent.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* cli/v4/display/frame.ts — Phase v4.1.4 reply-quality polish (Part 1.5).
|
|
10
|
+
*
|
|
11
|
+
* Single source of truth for the reply-frame math: terminal width,
|
|
12
|
+
* left gutter, body width, indent-by-depth, and ANSI-aware soft wrap.
|
|
13
|
+
*
|
|
14
|
+
* Before this module the same width formula
|
|
15
|
+
* (`Math.min(out.columns ?? 80, 100)`) lived at 4 different sites with
|
|
16
|
+
* inconsistent offsets, no shared floor, and no actual wrap engine —
|
|
17
|
+
* `marked-terminal` has `reflowText: false` so long prose just spilled
|
|
18
|
+
* to terminal-natural wrap (continuation lines at column 0, not at the
|
|
19
|
+
* gutter). frame.ts replaces all four with one formula and one wrapper.
|
|
20
|
+
*
|
|
21
|
+
* Public surface:
|
|
22
|
+
* - GUTTER 3-col left gutter (assistant body)
|
|
23
|
+
* - BODY_WIDTH_MAX 100 (tunable cap; export so future skin/theme
|
|
24
|
+
* work can override without touching consumers)
|
|
25
|
+
* - getTerminalCols() live `process.stdout.columns` with 80 fallback
|
|
26
|
+
* - getBodyWidth() `max(20, cols - gutter - 2)` capped at
|
|
27
|
+
* `BODY_WIDTH_MAX - gutter - 2`
|
|
28
|
+
* - getIndent(depth) `' '.repeat(GUTTER + depth*2)` — gutter +
|
|
29
|
+
* depth-aware. depth=0 = bare gutter, depth=1 =
|
|
30
|
+
* gutter+2, etc.
|
|
31
|
+
* - wrap(text, opts) ANSI-aware soft wrap via wrap-ansi defaults
|
|
32
|
+
* `{ trim: false, hard: true }`. Returns string
|
|
33
|
+
* with embedded `\n` per wrap point.
|
|
34
|
+
* - applyFrame(body) convenience: indents every line of `body` by
|
|
35
|
+
* GUTTER. No wrap (caller pre-wraps to bodyWidth).
|
|
36
|
+
*
|
|
37
|
+
* Wrap engine: wrap-ansi@9 (ESM-only). Loaded via cached dynamic
|
|
38
|
+
* `import()` so a CJS TypeScript build can still consume it. Until the
|
|
39
|
+
* first wrap finishes resolving, `wrap()` falls back to a passthrough
|
|
40
|
+
* that preserves the input verbatim — wrong visual but never incorrect
|
|
41
|
+
* data. Boot-time prime via `primeFrameAsync()` (best-effort) so the
|
|
42
|
+
* first user-visible wrap call already has the module loaded.
|
|
43
|
+
*/
|
|
44
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
45
|
+
if (k2 === undefined) k2 = k;
|
|
46
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
47
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
48
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
49
|
+
}
|
|
50
|
+
Object.defineProperty(o, k2, desc);
|
|
51
|
+
}) : (function(o, m, k, k2) {
|
|
52
|
+
if (k2 === undefined) k2 = k;
|
|
53
|
+
o[k2] = m[k];
|
|
54
|
+
}));
|
|
55
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
56
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
57
|
+
}) : function(o, v) {
|
|
58
|
+
o["default"] = v;
|
|
59
|
+
});
|
|
60
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
61
|
+
var ownKeys = function(o) {
|
|
62
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
63
|
+
var ar = [];
|
|
64
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
65
|
+
return ar;
|
|
66
|
+
};
|
|
67
|
+
return ownKeys(o);
|
|
68
|
+
};
|
|
69
|
+
return function (mod) {
|
|
70
|
+
if (mod && mod.__esModule) return mod;
|
|
71
|
+
var result = {};
|
|
72
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
73
|
+
__setModuleDefault(result, mod);
|
|
74
|
+
return result;
|
|
75
|
+
};
|
|
76
|
+
})();
|
|
77
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
78
|
+
exports.BODY_WIDTH_MIN = exports.BODY_WIDTH_MAX = exports.GUTTER = void 0;
|
|
79
|
+
exports.getTerminalCols = getTerminalCols;
|
|
80
|
+
exports.getBodyWidth = getBodyWidth;
|
|
81
|
+
exports.getIndent = getIndent;
|
|
82
|
+
exports.primeFrameAsync = primeFrameAsync;
|
|
83
|
+
exports.wrap = wrap;
|
|
84
|
+
exports.applyFrame = applyFrame;
|
|
85
|
+
exports._resetForTests = _resetForTests;
|
|
86
|
+
exports._injectWrapForTests = _injectWrapForTests;
|
|
87
|
+
// ── Tunable constants ─────────────────────────────────────────────────
|
|
88
|
+
/**
|
|
89
|
+
* Left gutter for assistant body. 3 columns matches the visual rhythm
|
|
90
|
+
* established by the boot card, tool trail, and status footer once
|
|
91
|
+
* Part 1.5 lands. Was 2 before this slice — bumped one column so the
|
|
92
|
+
* body breathes against the left edge.
|
|
93
|
+
*/
|
|
94
|
+
exports.GUTTER = 3;
|
|
95
|
+
/**
|
|
96
|
+
* Maximum body width before the visual frame stops growing. Wide
|
|
97
|
+
* terminals (150+ cols) get a body capped at this minus gutter+2
|
|
98
|
+
* because long lines (~120 chars) are harder to read than mid-length
|
|
99
|
+
* (~70-90 chars). Tunable: skin or theme code can override.
|
|
100
|
+
*/
|
|
101
|
+
exports.BODY_WIDTH_MAX = 100;
|
|
102
|
+
/**
|
|
103
|
+
* Hard floor on body width. Below this we render at 20 cols and let
|
|
104
|
+
* the terminal-natural wrap pick up the rest — better than crashing
|
|
105
|
+
* with a negative-width wrap-ansi call on a 5-col terminal.
|
|
106
|
+
*/
|
|
107
|
+
exports.BODY_WIDTH_MIN = 20;
|
|
108
|
+
// ── Width helpers ─────────────────────────────────────────────────────
|
|
109
|
+
/**
|
|
110
|
+
* Live terminal column count. Reads `process.stdout.columns` on every
|
|
111
|
+
* call so resize events propagate without us needing a cache. Falls
|
|
112
|
+
* back to 80 when the stream is non-TTY or hasn't reported a size yet
|
|
113
|
+
* (pipes, CI logs, MCP serve).
|
|
114
|
+
*
|
|
115
|
+
* `out` override exists for testability — display.test.ts injects a
|
|
116
|
+
* fake stream with explicit `columns` to assert various widths.
|
|
117
|
+
*/
|
|
118
|
+
function getTerminalCols(out = process.stdout) {
|
|
119
|
+
const c = out.columns;
|
|
120
|
+
if (typeof c !== 'number' || !Number.isFinite(c) || c < 1)
|
|
121
|
+
return 80;
|
|
122
|
+
return c;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Computed body width — the safe horizontal space inside the frame.
|
|
126
|
+
* Math: `min(BODY_WIDTH_MAX, cols) - GUTTER - 2`. The trailing `-2`
|
|
127
|
+
* leaves visual breathing room on the right margin (mirrors the boot
|
|
128
|
+
* card / tool-row right-pad convention). Floored at BODY_WIDTH_MIN so
|
|
129
|
+
* pathological narrow terminals still get a usable wrap.
|
|
130
|
+
*/
|
|
131
|
+
function getBodyWidth(out = process.stdout) {
|
|
132
|
+
const cols = Math.min(getTerminalCols(out), exports.BODY_WIDTH_MAX);
|
|
133
|
+
const raw = cols - exports.GUTTER - 2;
|
|
134
|
+
return Math.max(exports.BODY_WIDTH_MIN, raw);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Indent string for the given nesting depth. Depth 0 = bare gutter
|
|
138
|
+
* (3 spaces). Each additional level adds 2 spaces. Used by list,
|
|
139
|
+
* blockquote, and code-block renderers so every element shares one
|
|
140
|
+
* indent algebra.
|
|
141
|
+
*/
|
|
142
|
+
function getIndent(depth = 0) {
|
|
143
|
+
const d = Math.max(0, Math.floor(depth));
|
|
144
|
+
return ' '.repeat(exports.GUTTER + d * 2);
|
|
145
|
+
}
|
|
146
|
+
let cachedWrap = null;
|
|
147
|
+
let primePromise = null;
|
|
148
|
+
/**
|
|
149
|
+
* Best-effort load of wrap-ansi. Idempotent. Safe to call from boot.
|
|
150
|
+
* Returns a promise that resolves once the module is loaded (or
|
|
151
|
+
* rejects silently — wrap() will just keep using the passthrough).
|
|
152
|
+
*/
|
|
153
|
+
function primeFrameAsync() {
|
|
154
|
+
if (cachedWrap)
|
|
155
|
+
return Promise.resolve();
|
|
156
|
+
if (primePromise)
|
|
157
|
+
return primePromise;
|
|
158
|
+
primePromise = (async () => {
|
|
159
|
+
try {
|
|
160
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
161
|
+
const mod = await Promise.resolve().then(() => __importStar(require('wrap-ansi')));
|
|
162
|
+
const fn = (mod.default ?? mod);
|
|
163
|
+
if (typeof fn === 'function')
|
|
164
|
+
cachedWrap = fn;
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
// Swallow — fallback is a passthrough, never crashes.
|
|
168
|
+
}
|
|
169
|
+
})();
|
|
170
|
+
return primePromise;
|
|
171
|
+
}
|
|
172
|
+
// Kick off the import at module load. Best effort — if it fails (e.g.
|
|
173
|
+
// missing dep, broken install) we degrade to passthrough.
|
|
174
|
+
primeFrameAsync();
|
|
175
|
+
/**
|
|
176
|
+
* ANSI-aware soft wrap. Defaults `{ trim: false, hard: true }`:
|
|
177
|
+
* - trim: false → preserves leading/trailing whitespace on each
|
|
178
|
+
* visual line (important for code-block indent + alignment).
|
|
179
|
+
* - hard: true → break extremely long words mid-character at width
|
|
180
|
+
* instead of overflowing. Code blocks especially need this.
|
|
181
|
+
*
|
|
182
|
+
* Pure with respect to ANSI: escape sequences pass through wrap-ansi
|
|
183
|
+
* untouched and don't count toward the column budget.
|
|
184
|
+
*
|
|
185
|
+
* Synchronous. When wrap-ansi hasn't finished loading yet (the rare
|
|
186
|
+
* boot-race window), returns `text` unchanged. The user sees the
|
|
187
|
+
* un-wrapped paint exactly once; by the next render the cache is hot.
|
|
188
|
+
*/
|
|
189
|
+
function wrap(text, cols, options = {}) {
|
|
190
|
+
const w = cachedWrap;
|
|
191
|
+
if (!w)
|
|
192
|
+
return text;
|
|
193
|
+
const opts = { trim: options.trim ?? false, hard: options.hard ?? true };
|
|
194
|
+
try {
|
|
195
|
+
return w(text, cols, opts);
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
return text;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Indent every line of `body` by the bare gutter. Empty lines are
|
|
203
|
+
* passed through unindented so blank visual rows don't carry
|
|
204
|
+
* trailing whitespace into the transcript.
|
|
205
|
+
*
|
|
206
|
+
* Caller is responsible for pre-wrapping to `getBodyWidth()` — this
|
|
207
|
+
* function is purely the indent step, not the wrap step. Keeping them
|
|
208
|
+
* separate so callers that already have their own indent (lists,
|
|
209
|
+
* code blocks) can opt out of this and still consume `wrap()`.
|
|
210
|
+
*/
|
|
211
|
+
function applyFrame(body) {
|
|
212
|
+
const ind = getIndent(0);
|
|
213
|
+
return body
|
|
214
|
+
.split('\n')
|
|
215
|
+
.map((ln) => (ln.length === 0 ? '' : `${ind}${ln}`))
|
|
216
|
+
.join('\n');
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Test reset — drops the cached wrap function so a fresh prime can be
|
|
220
|
+
* forced. Used by unit tests to exercise the fallback path AND to
|
|
221
|
+
* confirm post-prime behaviour.
|
|
222
|
+
*/
|
|
223
|
+
function _resetForTests() {
|
|
224
|
+
cachedWrap = null;
|
|
225
|
+
primePromise = null;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Test injection — set the wrap function explicitly. Used by tests
|
|
229
|
+
* that want deterministic behaviour without depending on dynamic
|
|
230
|
+
* `import()` resolution timing.
|
|
231
|
+
*/
|
|
232
|
+
function _injectWrapForTests(fn) {
|
|
233
|
+
cachedWrap = fn;
|
|
234
|
+
}
|