aiden-runtime 4.1.4 → 4.1.5
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 +28 -0
- package/dist/cli/v4/callbacks.js +78 -0
- package/dist/cli/v4/chatSession.js +154 -1
- package/dist/cli/v4/defaultSoul.js +75 -3
- package/dist/cli/v4/display/progressBar.js +41 -8
- package/dist/cli/v4/display.js +248 -15
- package/dist/cli/v4/toolPreview.js +10 -0
- package/dist/core/toolRegistry.js +7 -1
- package/dist/core/v4/aidenAgent.js +63 -0
- package/dist/core/v4/loopTrace.js +257 -0
- package/dist/core/version.js +1 -1
- package/dist/core/webSearch.js +64 -24
- package/package.json +1 -1
- package/plugins/aiden-plugin-cdp-browser/.granted-permissions.json +8 -0
package/dist/cli/v4/display.js
CHANGED
|
@@ -18,12 +18,13 @@
|
|
|
18
18
|
*
|
|
19
19
|
*/
|
|
20
20
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
-
exports.TOOL_ROW_ARG_CAP = exports.TOOL_ROW_NAME_PAD = exports.Display = exports.SPINNER_PHRASES = exports.TOOL_ICONS = void 0;
|
|
21
|
+
exports.TRAIL_HIDE_TOOLS = exports.TOOL_ROW_ARG_CAP = exports.TOOL_ROW_NAME_PAD = exports.Display = exports.SPINNER_PHRASES = exports.TOOL_ICONS = void 0;
|
|
22
22
|
exports.iconForTool = iconForTool;
|
|
23
23
|
exports.detectConfiguredChannels = detectConfiguredChannels;
|
|
24
24
|
exports.summarizeConfiguredChannels = summarizeConfiguredChannels;
|
|
25
25
|
exports.summarizeChannelState = summarizeChannelState;
|
|
26
26
|
exports.voiceIndicator = voiceIndicator;
|
|
27
|
+
exports.makeNoOpToolRowHandle = makeNoOpToolRowHandle;
|
|
27
28
|
exports.previewToolArgs = previewToolArgs;
|
|
28
29
|
exports.verbForActivity = verbForActivity;
|
|
29
30
|
exports.isPreFramedLine = isPreFramedLine;
|
|
@@ -772,7 +773,21 @@ class Display {
|
|
|
772
773
|
* content below MUST call `pause()` first; otherwise their content
|
|
773
774
|
* lands on the indicator line and the next tick clobbers it.
|
|
774
775
|
*/
|
|
775
|
-
|
|
776
|
+
/**
|
|
777
|
+
* v4.1.5 Issue K — wave-bar option.
|
|
778
|
+
*
|
|
779
|
+
* When `opts.waveBar === true` (DEFAULT), the indicator paints a
|
|
780
|
+
* second row BELOW the verb line — a 10-cell `▰▱` snake-scroll
|
|
781
|
+
* heartbeat that gives visible motion during long pre-first-token
|
|
782
|
+
* gaps even when the verb doesn't change. The bar is NOT progress:
|
|
783
|
+
* it's a constant-cadence heartbeat (250ms shared with the dot
|
|
784
|
+
* pulse), explicitly not a percentage indicator.
|
|
785
|
+
*
|
|
786
|
+
* Pass `{ waveBar: false }` for back-compat with v4.1.4 tests that
|
|
787
|
+
* assert single-row geometry. Production callers (chatSession) get
|
|
788
|
+
* the wave bar by default.
|
|
789
|
+
*/
|
|
790
|
+
activityIndicator(initialVerb = 'thinking', opts = {}) {
|
|
776
791
|
const sk = this.skin;
|
|
777
792
|
const out = this.out;
|
|
778
793
|
const isTty = !!out.isTTY;
|
|
@@ -799,6 +814,13 @@ class Display {
|
|
|
799
814
|
// feedback; a separate bottom-of-screen footer can be added in
|
|
800
815
|
// v4.1.5 if wanted, but it must NOT be glued to the indicator.
|
|
801
816
|
const glyph = sk.applyColors('▲', 'brand');
|
|
817
|
+
// v4.1.5 Issue K — wave-bar state. Snake-scroll: a 3-cell `▰`
|
|
818
|
+
// block slides across 10 cells, wrapping at the right edge. Same
|
|
819
|
+
// 250ms tick as the verb dot pulse — one timer drives both rows.
|
|
820
|
+
const waveBarEnabled = opts.waveBar !== false; // default true
|
|
821
|
+
const WAVE_CELLS = 10;
|
|
822
|
+
const WAVE_BLOCK = 3;
|
|
823
|
+
let waveFrame = 0;
|
|
802
824
|
const buildLine = () => {
|
|
803
825
|
const dots = '.'.repeat(dotFrame); // 0..3 dots
|
|
804
826
|
const elapsedSec = Math.floor((Date.now() - startTime) / 1000);
|
|
@@ -808,14 +830,92 @@ class Display {
|
|
|
808
830
|
// `▲ {verb}{dots-padded-to-3}{elapsed?}`
|
|
809
831
|
return `${glyph} ${verb}${dots.padEnd(3, ' ')}${elapsedStr}`;
|
|
810
832
|
};
|
|
833
|
+
/**
|
|
834
|
+
* v4.1.5 Issue K — render the wave-bar row. A 3-cell `▰` block at
|
|
835
|
+
* positions `[waveFrame, waveFrame+1, waveFrame+2]` mod 10. The
|
|
836
|
+
* filled cells paint brand orange, empty cells paint warm-muted.
|
|
837
|
+
* Same width + glyph set as the token progress bar so the two
|
|
838
|
+
* rows feel like a coherent palette (one is heartbeat, the other
|
|
839
|
+
* is real progress).
|
|
840
|
+
*
|
|
841
|
+
* Heartbeat semantics: this is NOT progress. The wave moves at a
|
|
842
|
+
* constant 250ms cadence regardless of any backend metric. It
|
|
843
|
+
* exists purely so the user sees motion during the unobservable
|
|
844
|
+
* TTFT (time-to-first-token) wait. The verb row above carries
|
|
845
|
+
* any real lifecycle signal via `setVerb()`.
|
|
846
|
+
*/
|
|
847
|
+
const buildWave = () => {
|
|
848
|
+
// v4.1.5 Phase 1d (Q-P1) — glyph palette switch. Was `▰`/`▱`
|
|
849
|
+
// (U+25B0/B1, Geometric Shapes) which legacy Windows console
|
|
850
|
+
// fonts render as tofu. Now `▓`/`░` (U+2593/91, Block Elements
|
|
851
|
+
// — in CP437, universally supported). Matches the existing
|
|
852
|
+
// statusFooter chrome that's shipped since v3 without ever
|
|
853
|
+
// being garbled.
|
|
854
|
+
const filled = new Set();
|
|
855
|
+
for (let i = 0; i < WAVE_BLOCK; i += 1) {
|
|
856
|
+
filled.add((waveFrame + i) % WAVE_CELLS);
|
|
857
|
+
}
|
|
858
|
+
// Render cells in order so the snake-scroll visually slides:
|
|
859
|
+
// we paint cell-by-cell with the right color, joined into one
|
|
860
|
+
// string. ANSI runs reset per cell — slight overhead but keeps
|
|
861
|
+
// glyph order true to position. Brand orange filled, warm-muted
|
|
862
|
+
// empty.
|
|
863
|
+
const cells = [];
|
|
864
|
+
for (let c = 0; c < WAVE_CELLS; c += 1) {
|
|
865
|
+
cells.push(filled.has(c)
|
|
866
|
+
? sk.applyColors('▓', 'brand')
|
|
867
|
+
: sk.applyColors('░', 'muted'));
|
|
868
|
+
}
|
|
869
|
+
return cells.join('');
|
|
870
|
+
};
|
|
871
|
+
// v4.1.5 Part 1a — Issue M (Windows ConPTY buffering fix).
|
|
872
|
+
//
|
|
873
|
+
// Prior pattern wrote `\r\x1b[K{indicator}` with NO trailing
|
|
874
|
+
// newline. On Windows ConPTY, `process.stdout` buffers no-newline
|
|
875
|
+
// writes — none of the 60 indicator ticks during a 15s gap
|
|
876
|
+
// actually rendered. The final reply's `\n` chars eventually
|
|
877
|
+
// flushed the buffer, but by then the indicator's stop()-erase
|
|
878
|
+
// had also been buffered + flushed, so the user saw 15s of blank
|
|
879
|
+
// followed by the reply dumping all at once.
|
|
880
|
+
//
|
|
881
|
+
// Fix: indicator OWNS one terminal row. Every write that paints
|
|
882
|
+
// the indicator ends with `\n`, which forces a flush on every
|
|
883
|
+
// platform. The cursor sits on the LINE BELOW the indicator
|
|
884
|
+
// while it's running (one visible empty row gap). When the
|
|
885
|
+
// indicator stops/pauses, we walk back UP to the indicator's
|
|
886
|
+
// row and erase it — the cursor then sits at col 0 of that
|
|
887
|
+
// (now empty) row, ready for the caller to write whatever
|
|
888
|
+
// content follows (header, tool row, stream output).
|
|
889
|
+
//
|
|
890
|
+
// ANSI primitives:
|
|
891
|
+
// `\x1b[1A` — cursor up 1 line
|
|
892
|
+
// `\x1b[2K` — erase the whole current line
|
|
893
|
+
// Sequence on tick: walk up → erase → paint → `\n` → cursor below.
|
|
894
|
+
// Sequence on erase: walk up → erase (no newline). Cursor on the
|
|
895
|
+
// now-empty indicator row, ready for caller.
|
|
896
|
+
const ANSI_UP_ERASE = '\x1b[1A\x1b[2K';
|
|
811
897
|
const renderTick = () => {
|
|
812
898
|
if (stopped || paused || !isTty)
|
|
813
899
|
return;
|
|
814
900
|
dotFrame = (dotFrame + 1) % 4;
|
|
815
|
-
//
|
|
816
|
-
//
|
|
817
|
-
//
|
|
818
|
-
|
|
901
|
+
// v4.1.5 Issue K — wave snake-scroll advances 1 cell per tick.
|
|
902
|
+
// Same 250ms cadence as the dot pulse, so both rows move in
|
|
903
|
+
// visible lockstep. Modulo WAVE_CELLS wraps the leading block
|
|
904
|
+
// back to the left edge.
|
|
905
|
+
waveFrame = (waveFrame + 1) % WAVE_CELLS;
|
|
906
|
+
if (waveBarEnabled) {
|
|
907
|
+
// 2-row layout: walk up TWO rows (two separate up-1+erase
|
|
908
|
+
// sequences, which keeps the `\x1b[1A\x1b[2K` substring
|
|
909
|
+
// assertion-compatible), repaint both, drop newlines so the
|
|
910
|
+
// cursor lands on the row below the wave bar.
|
|
911
|
+
out.write(`${ANSI_UP_ERASE}${ANSI_UP_ERASE}` +
|
|
912
|
+
`${buildLine()}\n` +
|
|
913
|
+
`${buildWave()}\n`);
|
|
914
|
+
}
|
|
915
|
+
else {
|
|
916
|
+
// Single-row layout (back-compat with v4.1.4 tests).
|
|
917
|
+
out.write(`${ANSI_UP_ERASE}${buildLine()}\n`);
|
|
918
|
+
}
|
|
819
919
|
};
|
|
820
920
|
const startTick = () => {
|
|
821
921
|
if (stopped || !isTty || tickTimer !== null)
|
|
@@ -829,12 +929,38 @@ class Display {
|
|
|
829
929
|
}
|
|
830
930
|
};
|
|
831
931
|
const eraseLine = () => {
|
|
832
|
-
|
|
833
|
-
|
|
932
|
+
// Walk up to the indicator's row(s) + erase. Cursor lands at
|
|
933
|
+
// col 0 of the (now empty) verb row. NO trailing newline here:
|
|
934
|
+
// the caller is about to write content on this row, and
|
|
935
|
+
// whatever they write will include their own `\n` to flush
|
|
936
|
+
// the buffer. If we emitted `\n` here, we'd leave a phantom
|
|
937
|
+
// blank row before the caller's content.
|
|
938
|
+
//
|
|
939
|
+
// v4.1.5 Issue K — with wave bar enabled, walk up 2 rows (two
|
|
940
|
+
// up-1+erase sequences). Without the bar, walk up 1 row.
|
|
941
|
+
if (!isTty || !printed)
|
|
942
|
+
return;
|
|
943
|
+
if (waveBarEnabled) {
|
|
944
|
+
out.write(`${ANSI_UP_ERASE}${ANSI_UP_ERASE}`);
|
|
945
|
+
}
|
|
946
|
+
else {
|
|
947
|
+
out.write(ANSI_UP_ERASE);
|
|
948
|
+
}
|
|
834
949
|
};
|
|
835
|
-
// Initial paint — only on TTY.
|
|
950
|
+
// Initial paint — only on TTY. Indicator + `\n` so the buffer
|
|
951
|
+
// flushes and the cursor sits on the row below, ready for the
|
|
952
|
+
// first tick to walk back up.
|
|
953
|
+
//
|
|
954
|
+
// v4.1.5 Issue K — when wave bar is enabled, paint TWO rows:
|
|
955
|
+
// verb row + wave row, each with trailing `\n`. Cursor lands on
|
|
956
|
+
// the row below the wave bar. The first tick will walk up 2.
|
|
836
957
|
if (isTty) {
|
|
837
|
-
|
|
958
|
+
if (waveBarEnabled) {
|
|
959
|
+
out.write(`${buildLine()}\n${buildWave()}\n`);
|
|
960
|
+
}
|
|
961
|
+
else {
|
|
962
|
+
out.write(`${buildLine()}\n`);
|
|
963
|
+
}
|
|
838
964
|
printed = true;
|
|
839
965
|
startTick();
|
|
840
966
|
}
|
|
@@ -861,9 +987,18 @@ class Display {
|
|
|
861
987
|
return;
|
|
862
988
|
// Caller has just finished writing its own content (typically
|
|
863
989
|
// ending with `\n`), so the cursor is on a fresh line below
|
|
864
|
-
// whatever was there.
|
|
865
|
-
//
|
|
866
|
-
|
|
990
|
+
// whatever was there. Paint the indicator + `\n` to claim the
|
|
991
|
+
// current row(s) and leave the cursor on the row below — same
|
|
992
|
+
// invariant the initial paint and tick maintain. Trailing `\n`
|
|
993
|
+
// also flushes Windows ConPTY buffering (Issue M).
|
|
994
|
+
//
|
|
995
|
+
// v4.1.5 Issue K — repaint BOTH rows when wave bar enabled.
|
|
996
|
+
if (waveBarEnabled) {
|
|
997
|
+
out.write(`${buildLine()}\n${buildWave()}\n`);
|
|
998
|
+
}
|
|
999
|
+
else {
|
|
1000
|
+
out.write(`${buildLine()}\n`);
|
|
1001
|
+
}
|
|
867
1002
|
printed = true;
|
|
868
1003
|
startTick();
|
|
869
1004
|
},
|
|
@@ -894,6 +1029,31 @@ class Display {
|
|
|
894
1029
|
// completion so each line in the log carries the final state — no
|
|
895
1030
|
// ANSI cursor games on a dumb sink.
|
|
896
1031
|
toolRow(name, args) {
|
|
1032
|
+
// v4.1.5 Phase 1d (Q-Q2-a) — TRAIL_HIDE_TOOLS suppression.
|
|
1033
|
+
//
|
|
1034
|
+
// Some tools are pure agent plumbing — the model calls them to
|
|
1035
|
+
// introspect its own registry, not to do user-visible work.
|
|
1036
|
+
// `lookup_tool_schema` is the canonical case: during planning
|
|
1037
|
+
// the agent may invoke it 30+ times to discover unfamiliar tool
|
|
1038
|
+
// shapes. Each call is a sub-millisecond in-memory lookup, but
|
|
1039
|
+
// they flood the visible trail with noise that obscures the
|
|
1040
|
+
// actual user-relevant tool calls.
|
|
1041
|
+
//
|
|
1042
|
+
// Short-circuit: hidden tools get a NO-OP handle that satisfies
|
|
1043
|
+
// the `ToolRowHandle` contract (ok/fail/degraded/retry/blocked/
|
|
1044
|
+
// emptyRetry/emptyFail all defined but write nothing). The
|
|
1045
|
+
// execution path itself is unaffected — the agent still calls
|
|
1046
|
+
// the tool, the planner / skill-enforcement trackers still
|
|
1047
|
+
// record it. Only the visual row is suppressed.
|
|
1048
|
+
//
|
|
1049
|
+
// CRITICAL invariant: `setBeforeFirstToolHook` is fired by
|
|
1050
|
+
// callbacks.ts BEFORE `toolRow()` is called (see callbacks.ts
|
|
1051
|
+
// onToolCall 'before' branch), so `turnHadTools` flips even for
|
|
1052
|
+
// hidden tools. The separator logic stays correct regardless of
|
|
1053
|
+
// whether ONLY hidden tools fired this turn.
|
|
1054
|
+
if (exports.TRAIL_HIDE_TOOLS.has(name)) {
|
|
1055
|
+
return makeNoOpToolRowHandle();
|
|
1056
|
+
}
|
|
897
1057
|
const sk = this.skin;
|
|
898
1058
|
// ── Build the fixed left portion (icon + verb + detail) ────────────
|
|
899
1059
|
// v4.1.3-repl-polish: icons default ON; set AIDEN_UI_ICONS=0 to
|
|
@@ -995,8 +1155,31 @@ class Display {
|
|
|
995
1155
|
writeFinal(`ok ${formatToolDuration(durationMs)} after ${retries} ${retries === 1 ? 'retry' : 'retries'}`, 'warn');
|
|
996
1156
|
}
|
|
997
1157
|
else {
|
|
998
|
-
//
|
|
999
|
-
|
|
1158
|
+
// v4.1.5 Issue N — persistent tool trail in scrollback.
|
|
1159
|
+
//
|
|
1160
|
+
// Prior behaviour: silent erase on clean success (`eraseLast()`
|
|
1161
|
+
// with no replacement write). Tool rows for successful tools
|
|
1162
|
+
// vanished, leaving only the markdown reply visible afterward.
|
|
1163
|
+
// The user couldn't see WHAT actions Aiden took unless a tool
|
|
1164
|
+
// failed or degraded.
|
|
1165
|
+
//
|
|
1166
|
+
// Fix: replace the silent erase with a completed-state row
|
|
1167
|
+
// painted entirely in warm-muted (`#b8a89a` from v4.1.4). The
|
|
1168
|
+
// duration suffix replaces the live `running Ns…` chrome; the
|
|
1169
|
+
// whole row reads "done" via reduced visual weight. Failed /
|
|
1170
|
+
// degraded / retry outcomes keep their existing coloured paint
|
|
1171
|
+
// (error red, degraded yellow, warn amber) — only clean success
|
|
1172
|
+
// shifts from "silent" to "muted-persistent."
|
|
1173
|
+
//
|
|
1174
|
+
// The persistence mechanism is the existing `writeFinal` path:
|
|
1175
|
+
// it walks up + erases the running row, then writes the final
|
|
1176
|
+
// row with trailing `\n`. The row sits in scrollback because
|
|
1177
|
+
// `streamComplete` rerenders only the post-tool stream chunk
|
|
1178
|
+
// (via `streamLineCount` which was reset to 0 inside
|
|
1179
|
+
// `commitStreamChunk` before this row wrote). No additional
|
|
1180
|
+
// isolation machinery needed — already verified by 13/13
|
|
1181
|
+
// `smoke-stream-rerender.ts` regressions.
|
|
1182
|
+
writeFinal(formatToolDuration(durationMs), 'muted');
|
|
1000
1183
|
}
|
|
1001
1184
|
},
|
|
1002
1185
|
fail(durationMs, retries = 0) {
|
|
@@ -1698,6 +1881,56 @@ function renderRmsBar(rms) {
|
|
|
1698
1881
|
exports.TOOL_ROW_NAME_PAD = toolTrail_1.TRAIL_VERB_PAD;
|
|
1699
1882
|
/** @deprecated Use TRAIL_DETAIL_CAP. */
|
|
1700
1883
|
exports.TOOL_ROW_ARG_CAP = toolTrail_1.TRAIL_DETAIL_CAP;
|
|
1884
|
+
/**
|
|
1885
|
+
* v4.1.5 Phase 1d (Q-Q2-a) — names of tools that should be SUPPRESSED
|
|
1886
|
+
* from the visible tool-trail row, even though they still execute
|
|
1887
|
+
* normally through the agent loop.
|
|
1888
|
+
*
|
|
1889
|
+
* The canonical case is `lookup_tool_schema`: the agent calls it
|
|
1890
|
+
* during planning to introspect tool registry entries (in-memory
|
|
1891
|
+
* registry get, sub-millisecond per call). On complex prompts the
|
|
1892
|
+
* model may fire it 30+ times in a row, flooding the visible trail
|
|
1893
|
+
* with rows that don't represent user-meaningful work. Suppressing
|
|
1894
|
+
* them keeps the trail focused on the tools that did real work
|
|
1895
|
+
* (web_search, file_read, etc.).
|
|
1896
|
+
*
|
|
1897
|
+
* Suppression happens at `Display.toolRow()` entry — it returns a
|
|
1898
|
+
* no-op handle that satisfies the `ToolRowHandle` contract but
|
|
1899
|
+
* never writes to stdout. The agent's `callbacks.onToolCall`
|
|
1900
|
+
* dispatch is unchanged: `setBeforeFirstToolHook` still fires (so
|
|
1901
|
+
* `turnHadTools` flips for the separator-emission logic), and
|
|
1902
|
+
* skill-enforcement / honesty-trace tracking still records the
|
|
1903
|
+
* call. Only the visual row is hidden.
|
|
1904
|
+
*
|
|
1905
|
+
* Exported as a `Set` so callers can mutate at runtime if they
|
|
1906
|
+
* need to hide additional tools (e.g. user customization, MCP
|
|
1907
|
+
* plumbing tools). Mutation-of-shared-state is intentional — there's
|
|
1908
|
+
* no per-session config plumbing for "trail hidden tools" yet, so
|
|
1909
|
+
* the env-var pattern (`AIDEN_TRAIL_HIDE=tool1,tool2`) would be the
|
|
1910
|
+
* v4.1.6 evolution.
|
|
1911
|
+
*/
|
|
1912
|
+
exports.TRAIL_HIDE_TOOLS = new Set([
|
|
1913
|
+
'lookup_tool_schema',
|
|
1914
|
+
]);
|
|
1915
|
+
/**
|
|
1916
|
+
* v4.1.5 Phase 1d helper — produces a `ToolRowHandle` that satisfies
|
|
1917
|
+
* the contract but writes nothing. Used by hidden tools (see
|
|
1918
|
+
* `TRAIL_HIDE_TOOLS`) and as a safe fallback. All methods are inert.
|
|
1919
|
+
*
|
|
1920
|
+
* Pure — no side effects, no closures over Display state. Safe to
|
|
1921
|
+
* call from any thread / phase.
|
|
1922
|
+
*/
|
|
1923
|
+
function makeNoOpToolRowHandle() {
|
|
1924
|
+
return {
|
|
1925
|
+
ok: () => { },
|
|
1926
|
+
fail: () => { },
|
|
1927
|
+
degraded: () => { },
|
|
1928
|
+
retry: () => { },
|
|
1929
|
+
blocked: () => { },
|
|
1930
|
+
emptyRetry: () => { },
|
|
1931
|
+
emptyFail: () => { },
|
|
1932
|
+
};
|
|
1933
|
+
}
|
|
1701
1934
|
/**
|
|
1702
1935
|
* Build a compact, single-line preview of the tool's arguments. Picks
|
|
1703
1936
|
* the most informative scalar fields when the args are an object, then
|
|
@@ -69,6 +69,16 @@ exports.TOOL_PRIMARY_ARG = {
|
|
|
69
69
|
skill_view: 'name',
|
|
70
70
|
skill_manage: 'action',
|
|
71
71
|
skills_list: '',
|
|
72
|
+
// v4.1.5 Phase 1d (Q-Q1-a) — registry introspection tool. Args
|
|
73
|
+
// shape: `{ toolName: 'web_search' }`. The agent uses this to
|
|
74
|
+
// discover unfamiliar tool schemas during planning. Surface the
|
|
75
|
+
// target tool name so the trail row (when not suppressed via
|
|
76
|
+
// TRAIL_HIDE_TOOLS) reads as the introspected tool, not raw JSON.
|
|
77
|
+
// Note: most callers see this tool suppressed entirely from the
|
|
78
|
+
// visible trail via the TRAIL_HIDE_TOOLS set in display.ts; the
|
|
79
|
+
// extractor exists for code paths that DON'T suppress (verbose
|
|
80
|
+
// mode, log-file capture).
|
|
81
|
+
lookup_tool_schema: 'toolName',
|
|
72
82
|
// ── sessions ─────────────────────────────────────────────────────────
|
|
73
83
|
session_search: 'query',
|
|
74
84
|
session_list: '',
|
|
@@ -1388,7 +1388,13 @@ exports.TOOLS = {
|
|
|
1388
1388
|
return { success: false, output: '', error: `No research results for: ${topic}` };
|
|
1389
1389
|
}
|
|
1390
1390
|
const combined = results.join('\n\n');
|
|
1391
|
-
|
|
1391
|
+
// v4.1.5 Issue O — gated behind AIDEN_DEBUG_WEB to match the
|
|
1392
|
+
// webSearch.ts debug-helper convention. Default off; power users
|
|
1393
|
+
// export the env var to see the research chain.
|
|
1394
|
+
if (process.env.AIDEN_DEBUG_WEB === '1') {
|
|
1395
|
+
// eslint-disable-next-line no-console
|
|
1396
|
+
console.log(`[deep_research] Complete: ${combined.length} chars across ${results.length} passes`);
|
|
1397
|
+
}
|
|
1392
1398
|
return { success: true, output: combined.slice(0, 15000) };
|
|
1393
1399
|
},
|
|
1394
1400
|
// Activate a specialist agent persona — actual synthesis happens in respond phase
|
|
@@ -103,6 +103,10 @@ class AidenAgent {
|
|
|
103
103
|
this.onCompression = opts.onCompression;
|
|
104
104
|
this.refreshMemorySnapshot = opts.refreshMemorySnapshot;
|
|
105
105
|
this.onMemoryRefresh = opts.onMemoryRefresh;
|
|
106
|
+
// v4.1.5 Issue K — phase hooks (all optional, fire defensively).
|
|
107
|
+
this.onMemoryRefreshStart = opts.onMemoryRefreshStart;
|
|
108
|
+
this.onPromptBuilt = opts.onPromptBuilt;
|
|
109
|
+
this.onProviderRequestStart = opts.onProviderRequestStart;
|
|
106
110
|
this.lookupSkillRequiredTools = opts.lookupSkillRequiredTools;
|
|
107
111
|
// Phase v4.1.2-slice3: optional health registry (constructor-
|
|
108
112
|
// injected per the slice3 decision tree — no singleton). When
|
|
@@ -386,6 +390,14 @@ class AidenAgent {
|
|
|
386
390
|
// / 'user' need a snapshot refresh first.
|
|
387
391
|
const needsSnapshot = this.memoryDirty.has('memory') || this.memoryDirty.has('user');
|
|
388
392
|
if (needsSnapshot && this.refreshMemorySnapshot) {
|
|
393
|
+
// v4.1.5 Issue K — fire BEFORE the file I/O so the display layer
|
|
394
|
+
// can switch the activity verb to "refreshing memory" while the
|
|
395
|
+
// read is in flight. Defensive try/catch so a misbehaving hook
|
|
396
|
+
// never blocks the refresh.
|
|
397
|
+
try {
|
|
398
|
+
this.onMemoryRefreshStart?.();
|
|
399
|
+
}
|
|
400
|
+
catch { /* defensive */ }
|
|
389
401
|
let snapshot;
|
|
390
402
|
try {
|
|
391
403
|
snapshot = await this.refreshMemorySnapshot();
|
|
@@ -410,6 +422,21 @@ class AidenAgent {
|
|
|
410
422
|
if (this.cachedSystemPrompt !== null)
|
|
411
423
|
return this.cachedSystemPrompt;
|
|
412
424
|
this.cachedSystemPrompt = await this.promptBuilder.build(this.promptBuilderOptions);
|
|
425
|
+
// v4.1.5 Issue K — fire AFTER the prompt has been assembled, with
|
|
426
|
+
// cardinality so the display layer can surface "preparing prompt:
|
|
427
|
+
// N tools, M skills" or similar. Only fires when the cache MISSED
|
|
428
|
+
// (which is what made us actually build); cached returns skip the
|
|
429
|
+
// hook because nothing was prepared this turn. Defensive try/catch.
|
|
430
|
+
if (this.onPromptBuilt) {
|
|
431
|
+
try {
|
|
432
|
+
this.onPromptBuilt({
|
|
433
|
+
tools: this.tools.length,
|
|
434
|
+
skills: this.promptBuilderOptions.skillsList?.length ?? 0,
|
|
435
|
+
memoryFacts: countMemoryFacts(this.promptBuilderOptions.memorySnapshot),
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
catch { /* defensive */ }
|
|
439
|
+
}
|
|
413
440
|
return this.cachedSystemPrompt;
|
|
414
441
|
}
|
|
415
442
|
async narrowTools(userMsg, history) {
|
|
@@ -629,6 +656,18 @@ class AidenAgent {
|
|
|
629
656
|
*/
|
|
630
657
|
async callProvider(messages, tools, runOptions) {
|
|
631
658
|
const wantStream = runOptions.stream === true && typeof this.provider.callStream === 'function';
|
|
659
|
+
// v4.1.5 Issue K — fire just before the HTTP request opens, so the
|
|
660
|
+
// display layer can transition the activity verb from local-prep
|
|
661
|
+
// ("preparing prompt", "selecting tools") to a network verb
|
|
662
|
+
// ("calling provider"). The wait for TTFT (time-to-first-token) is
|
|
663
|
+
// the longest gap in most turns and is what the wave bar covers.
|
|
664
|
+
// Fires for both streaming and non-streaming paths — caller may use
|
|
665
|
+
// it to add a one-shot indicator on non-streaming providers too.
|
|
666
|
+
// Defensive try/catch (a misbehaving hook must not block dispatch).
|
|
667
|
+
try {
|
|
668
|
+
this.onProviderRequestStart?.(this.providerId);
|
|
669
|
+
}
|
|
670
|
+
catch { /* defensive */ }
|
|
632
671
|
if (!wantStream) {
|
|
633
672
|
return this.provider.call({ messages, tools });
|
|
634
673
|
}
|
|
@@ -671,6 +710,30 @@ class AidenAgent {
|
|
|
671
710
|
}
|
|
672
711
|
exports.AidenAgent = AidenAgent;
|
|
673
712
|
// ── Free helpers ────────────────────────────────────────────────────────
|
|
713
|
+
/**
|
|
714
|
+
* v4.1.5 Issue K — best-effort count of "memory facts" from a
|
|
715
|
+
* MemorySnapshot. Counts markdown bullet-list lines (`- `) in both
|
|
716
|
+
* MEMORY.md and USER.md. This is a fuzzy proxy — the agent stores
|
|
717
|
+
* facts as bullets by convention but free-form prose can also carry
|
|
718
|
+
* fact-like content. Surfaced verbatim to the display layer; treat as
|
|
719
|
+
* "approximately N items in the persistent memory file" rather than
|
|
720
|
+
* a precise inventory.
|
|
721
|
+
*/
|
|
722
|
+
function countMemoryFacts(snapshot) {
|
|
723
|
+
if (!snapshot || typeof snapshot !== 'object')
|
|
724
|
+
return 0;
|
|
725
|
+
const s = snapshot;
|
|
726
|
+
let count = 0;
|
|
727
|
+
for (const md of [s.memoryMd, s.userMd]) {
|
|
728
|
+
if (typeof md !== 'string' || md.length === 0)
|
|
729
|
+
continue;
|
|
730
|
+
for (const line of md.split('\n')) {
|
|
731
|
+
if (line.trim().startsWith('- '))
|
|
732
|
+
count += 1;
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
return count;
|
|
736
|
+
}
|
|
674
737
|
function lastUserMessageContent(history) {
|
|
675
738
|
for (let i = history.length - 1; i >= 0; i--) {
|
|
676
739
|
const m = history[i];
|