@animalabs/agent-framework 0.14.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/dist/src/agent.d.ts +10 -0
  2. package/dist/src/agent.d.ts.map +1 -1
  3. package/dist/src/agent.js +39 -0
  4. package/dist/src/agent.js.map +1 -1
  5. package/dist/src/framework.d.ts +5 -2
  6. package/dist/src/framework.d.ts.map +1 -1
  7. package/dist/src/framework.js +126 -36
  8. package/dist/src/framework.js.map +1 -1
  9. package/dist/src/modules/history/index.d.ts +69 -0
  10. package/dist/src/modules/history/index.d.ts.map +1 -0
  11. package/dist/src/modules/history/index.js +519 -0
  12. package/dist/src/modules/history/index.js.map +1 -0
  13. package/dist/src/modules/history/search-regex-worker.d.ts +23 -0
  14. package/dist/src/modules/history/search-regex-worker.d.ts.map +1 -0
  15. package/dist/src/modules/history/search-regex-worker.js +57 -0
  16. package/dist/src/modules/history/search-regex-worker.js.map +1 -0
  17. package/dist/src/modules/index.d.ts +1 -0
  18. package/dist/src/modules/index.d.ts.map +1 -1
  19. package/dist/src/modules/index.js +1 -0
  20. package/dist/src/modules/index.js.map +1 -1
  21. package/dist/src/modules/workspace/index.d.ts.map +1 -1
  22. package/dist/src/modules/workspace/index.js +42 -1
  23. package/dist/src/modules/workspace/index.js.map +1 -1
  24. package/dist/src/modules/workspace/types.d.ts +5 -0
  25. package/dist/src/modules/workspace/types.d.ts.map +1 -1
  26. package/dist/src/modules/workspace/types.js.map +1 -1
  27. package/dist/src/types/trace.d.ts +4 -0
  28. package/dist/src/types/trace.d.ts.map +1 -1
  29. package/dist/test/history-module.test.d.ts +2 -0
  30. package/dist/test/history-module.test.d.ts.map +1 -0
  31. package/dist/test/history-module.test.js +449 -0
  32. package/dist/test/history-module.test.js.map +1 -0
  33. package/dist/test/kv-unified-open-flight-retry.test.d.ts +2 -0
  34. package/dist/test/kv-unified-open-flight-retry.test.d.ts.map +1 -0
  35. package/dist/test/kv-unified-open-flight-retry.test.js +189 -0
  36. package/dist/test/kv-unified-open-flight-retry.test.js.map +1 -0
  37. package/dist/test/kv-unified-wiring.test.js +39 -0
  38. package/dist/test/kv-unified-wiring.test.js.map +1 -1
  39. package/dist/test/puppet-tool-call.test.js +113 -1
  40. package/dist/test/puppet-tool-call.test.js.map +1 -1
  41. package/dist/test/puppet-turn-reservation.test.d.ts +2 -0
  42. package/dist/test/puppet-turn-reservation.test.d.ts.map +1 -0
  43. package/dist/test/puppet-turn-reservation.test.js +139 -0
  44. package/dist/test/puppet-turn-reservation.test.js.map +1 -0
  45. package/dist/test/save-recent-image.test.js +29 -0
  46. package/dist/test/save-recent-image.test.js.map +1 -1
  47. package/dist/test/tool-result-spill.test.js +114 -0
  48. package/dist/test/tool-result-spill.test.js.map +1 -1
  49. package/dist/test/workspace-read-text.test.d.ts +2 -0
  50. package/dist/test/workspace-read-text.test.d.ts.map +1 -0
  51. package/dist/test/workspace-read-text.test.js +101 -0
  52. package/dist/test/workspace-read-text.test.js.map +1 -0
  53. package/dist/tsconfig.tsbuildinfo +1 -1
  54. package/package.json +3 -3
  55. package/src/agent.ts +41 -0
  56. package/src/framework.ts +132 -43
  57. package/src/modules/history/index.ts +613 -0
  58. package/src/modules/history/search-regex-worker.ts +76 -0
  59. package/src/modules/index.ts +2 -0
  60. package/src/modules/workspace/index.ts +42 -1
  61. package/src/modules/workspace/types.ts +5 -0
  62. package/src/types/trace.ts +4 -0
@@ -0,0 +1,69 @@
1
+ /**
2
+ * HistoryModule — read-only access to an agent's full uncompressed message
3
+ * history, backed by context-manager's native chronicle secondary index
4
+ * (`queryMessagesByTime` / `queryMessagesByChannel` /
5
+ * `queryMessagesByTimeAndChannel` / `getChannelMessageCounts` /
6
+ * `getChannelTokenStats`, @animalabs/context-manager >= 0.6.0). Those calls
7
+ * are O(log n + k) against chronicle's `/timestamp` and
8
+ * `/metadata/external/channelId` field indexes, not a full-store scan, so
9
+ * this module can answer "what happened in #foo last Tuesday" against a
10
+ * multi-million-message store without walking the whole log.
11
+ *
12
+ * Three tools:
13
+ * - `stats` — per-channel message counts (all-time) + token totals
14
+ * (range-scoped), for orienting before pulling raw content.
15
+ * - `extract` — paginated raw messages for a time range and/or channel.
16
+ * - `search` — substring/regex match over a narrowed candidate window,
17
+ * with an explicit truncation signal when the caller's
18
+ * filter was too broad for `maxScan`.
19
+ *
20
+ * Kept deliberately read-only, same posture as HealthModule: no side
21
+ * effects, no message mutation. `onProcess` is a no-op.
22
+ *
23
+ * Every query dispatch collapses onto `queryMessagesByTimeAndChannel` — its
24
+ * own implementation already handles "only a time range", "only a channel",
25
+ * or "neither" by delegating to the single-filter native queries (see
26
+ * context-manager's `test/message-store-history-index.test.ts`), so this
27
+ * module doesn't need to re-decide which of the three query methods to call.
28
+ */
29
+ import type { ContextManager } from '@animalabs/context-manager';
30
+ import type { Module, ModuleContext } from '../../types/module.js';
31
+ import type { ToolDefinition, ToolCall, ToolResult, ProcessEvent } from '../../types/events.js';
32
+ import type { EventResponse, ProcessState } from '../../types/module.js';
33
+ export declare class HistoryModule implements Module {
34
+ readonly name = "history";
35
+ private ctx;
36
+ private cm;
37
+ /**
38
+ * Wire the context-manager instance. Host calls this after
39
+ * ContextManager.open() so the module can issue the native index-backed
40
+ * queries — ModuleContext itself exposes no store/context-manager
41
+ * reference, only the narrower message CRUD surface (addMessage/getMessage/
42
+ * queryMessages), which can't do range or channel queries.
43
+ */
44
+ bind(contextManager: ContextManager): void;
45
+ start(ctx: ModuleContext): Promise<void>;
46
+ stop(): Promise<void>;
47
+ getTools(): ToolDefinition[];
48
+ handleToolCall(call: ToolCall): Promise<ToolResult>;
49
+ onProcess(_event: ProcessEvent, _state: ProcessState): Promise<EventResponse>;
50
+ private handleStats;
51
+ private handleExtract;
52
+ private handleSearch;
53
+ /**
54
+ * Run regex matching for `search` on a worker thread with a hard
55
+ * wall-clock deadline, so a catastrophically-backtracking pattern can be
56
+ * forcibly killed instead of hanging the framework. One worker per call
57
+ * (not per candidate — spawn overhead would dominate at scale; not a
58
+ * persistent pool — a fresh worker per call means one bad pattern can
59
+ * never contaminate a later search). Always terminated on the way out,
60
+ * success or failure, so nothing lingers.
61
+ *
62
+ * On timeout or a worker-side error this THROWS (caught by
63
+ * handleToolCall's try/catch, same as every other error path in this
64
+ * module) rather than returning an empty match list — a timed-out search
65
+ * must never be indistinguishable from a clean "no matches" result.
66
+ */
67
+ private searchWithRegexWorker;
68
+ }
69
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/modules/history/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAKH,OAAO,KAAK,EAAE,cAAc,EAAkD,MAAM,4BAA4B,CAAC;AAEjH,OAAO,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACnE,OAAO,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAChG,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAgFzE,qBAAa,aAAc,YAAW,MAAM;IAC1C,QAAQ,CAAC,IAAI,aAAa;IAE1B,OAAO,CAAC,GAAG,CAA8B;IACzC,OAAO,CAAC,EAAE,CAA+B;IAEzC;;;;;;OAMG;IACH,IAAI,CAAC,cAAc,EAAE,cAAc,GAAG,IAAI;IAIpC,KAAK,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAIxC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAI3B,QAAQ,IAAI,cAAc,EAAE;IAiEtB,cAAc,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC;IAmCnD,SAAS,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC;IAQnF,OAAO,CAAC,WAAW;IAoCnB,OAAO,CAAC,aAAa;YA8BP,YAAY;IAsF1B;;;;;;;;;;;;;OAaG;YACW,qBAAqB;CAsDpC"}
@@ -0,0 +1,519 @@
1
+ /**
2
+ * HistoryModule — read-only access to an agent's full uncompressed message
3
+ * history, backed by context-manager's native chronicle secondary index
4
+ * (`queryMessagesByTime` / `queryMessagesByChannel` /
5
+ * `queryMessagesByTimeAndChannel` / `getChannelMessageCounts` /
6
+ * `getChannelTokenStats`, @animalabs/context-manager >= 0.6.0). Those calls
7
+ * are O(log n + k) against chronicle's `/timestamp` and
8
+ * `/metadata/external/channelId` field indexes, not a full-store scan, so
9
+ * this module can answer "what happened in #foo last Tuesday" against a
10
+ * multi-million-message store without walking the whole log.
11
+ *
12
+ * Three tools:
13
+ * - `stats` — per-channel message counts (all-time) + token totals
14
+ * (range-scoped), for orienting before pulling raw content.
15
+ * - `extract` — paginated raw messages for a time range and/or channel.
16
+ * - `search` — substring/regex match over a narrowed candidate window,
17
+ * with an explicit truncation signal when the caller's
18
+ * filter was too broad for `maxScan`.
19
+ *
20
+ * Kept deliberately read-only, same posture as HealthModule: no side
21
+ * effects, no message mutation. `onProcess` is a no-op.
22
+ *
23
+ * Every query dispatch collapses onto `queryMessagesByTimeAndChannel` — its
24
+ * own implementation already handles "only a time range", "only a channel",
25
+ * or "neither" by delegating to the single-filter native queries (see
26
+ * context-manager's `test/message-store-history-index.test.ts`), so this
27
+ * module doesn't need to re-decide which of the three query methods to call.
28
+ */
29
+ import { Worker } from 'node:worker_threads';
30
+ import { fileURLToPath } from 'node:url';
31
+ import { dirname, join } from 'node:path';
32
+ // ============================================================================
33
+ // Limits
34
+ // ============================================================================
35
+ const EXTRACT_DEFAULT_LIMIT = 50;
36
+ const EXTRACT_MAX_LIMIT = 200;
37
+ /**
38
+ * Real ceiling of the native chronicle pagination `offset` argument (a u32
39
+ * at the N-API boundary). `Number.MAX_SAFE_INTEGER` is NOT a safe ceiling
40
+ * for this field: a value between 0xFFFFFFFF and MAX_SAFE_INTEGER passes a
41
+ * naive upper-bound check unchanged, then wraps/truncates when it crosses
42
+ * into an unsigned 32-bit int on the native side — silently wrong
43
+ * pagination (repro: offset:4294967296 against a real store "succeeded"
44
+ * and returned page 0) instead of a clean failure. Clamping to this ceiling
45
+ * (same clamp-not-reject behavior `limit` already has) means anything
46
+ * beyond it lands on an offset no real store's message count will ever
47
+ * reach, i.e. a correctly-empty page, rather than wrapping onto a wrong one.
48
+ */
49
+ const NATIVE_OFFSET_MAX = 0xffffffff; // 4294967295
50
+ const SEARCH_DEFAULT_LIMIT = 20;
51
+ const SEARCH_MAX_LIMIT = 100;
52
+ const SEARCH_DEFAULT_MAX_SCAN = 5000;
53
+ const SEARCH_MAX_MAX_SCAN = 50000;
54
+ /** Characters of surrounding context kept on each side of a search snippet. */
55
+ const SNIPPET_CONTEXT_CHARS = 80;
56
+ /** Fallback snippet length when a match position isn't meaningful to center on. */
57
+ const SNIPPET_FALLBACK_CHARS = 160;
58
+ /**
59
+ * Wall-clock deadline for a single regex-mode search call, enforced by
60
+ * forcibly terminating the worker thread doing the matching (see
61
+ * search-regex-worker.ts's header for why a worker — not a
62
+ * Promise.race/setTimeout — is required to actually interrupt a stuck
63
+ * synchronous RegExp.exec()). Generous enough for any legitimate pattern
64
+ * against a few thousand short strings; short enough that a catastrophic
65
+ * pattern doesn't tie up a worker (or an agent's turn) for long.
66
+ */
67
+ const SEARCH_REGEX_TIMEOUT_MS = 2000;
68
+ /** Compiled sibling of search-regex-worker.ts — resolved at runtime the same
69
+ * way gate-script.ts locates gate-script-worker.js, so it tracks whatever
70
+ * directory this module's own compiled output lives in. */
71
+ const SEARCH_WORKER_PATH = join(dirname(fileURLToPath(import.meta.url)), 'search-regex-worker.js');
72
+ export class HistoryModule {
73
+ name = 'history';
74
+ ctx = null;
75
+ cm = null;
76
+ /**
77
+ * Wire the context-manager instance. Host calls this after
78
+ * ContextManager.open() so the module can issue the native index-backed
79
+ * queries — ModuleContext itself exposes no store/context-manager
80
+ * reference, only the narrower message CRUD surface (addMessage/getMessage/
81
+ * queryMessages), which can't do range or channel queries.
82
+ */
83
+ bind(contextManager) {
84
+ this.cm = contextManager;
85
+ }
86
+ async start(ctx) {
87
+ this.ctx = ctx;
88
+ }
89
+ async stop() {
90
+ this.ctx = null;
91
+ }
92
+ getTools() {
93
+ return [
94
+ {
95
+ name: 'stats',
96
+ description: 'Orient before pulling raw history: per-channel message counts and token totals. ' +
97
+ 'messageCountsAllTime is always all-time and all-channel-scope (the underlying index has no ' +
98
+ 'range dimension) — it is NOT limited by from/to, only optionally filtered to one channelId. ' +
99
+ 'tokenStatsForRange IS scoped to from/to when given. The two are reported separately, never ' +
100
+ 'merged, because their range semantics differ.',
101
+ inputSchema: {
102
+ type: 'object',
103
+ properties: {
104
+ from: { type: 'string', description: 'ISO 8601 inclusive lower bound for tokenStatsForRange. Omit for open-ended.' },
105
+ to: { type: 'string', description: 'ISO 8601 inclusive upper bound for tokenStatsForRange. Omit for open-ended.' },
106
+ channelId: { type: 'string', description: 'Restrict both parts of the response to one channel.' },
107
+ },
108
+ },
109
+ },
110
+ {
111
+ name: 'extract',
112
+ description: 'Fetch raw, uncompressed messages for a time range and/or channel, paginated oldest-first. ' +
113
+ 'Use after `stats` to pull the actual content. format:"text" flattens each message to a short ' +
114
+ 'readable string (tool_use/tool_result/thinking/images rendered as bracketed labels); ' +
115
+ 'format:"raw" returns the content blocks unmodified.',
116
+ inputSchema: {
117
+ type: 'object',
118
+ properties: {
119
+ from: { type: 'string', description: 'ISO 8601 inclusive lower bound. Omit for open-ended.' },
120
+ to: { type: 'string', description: 'ISO 8601 inclusive upper bound. Omit for open-ended.' },
121
+ channelId: { type: 'string', description: 'Restrict to one channel.' },
122
+ limit: { type: 'number', description: `Max messages to return (default ${EXTRACT_DEFAULT_LIMIT}, hard cap ${EXTRACT_MAX_LIMIT}). Must be a non-negative integer.` },
123
+ offset: { type: 'number', description: `Number of matching messages to skip (default 0, capped to ${NATIVE_OFFSET_MAX}). Must be a non-negative integer.` },
124
+ format: { type: 'string', enum: ['text', 'raw'], description: 'Content rendering (default "text").' },
125
+ },
126
+ },
127
+ },
128
+ {
129
+ name: 'search',
130
+ description: 'Search message history for a substring (default) or regex match, within an optional time ' +
131
+ 'range/channel window. Narrows candidates via the same query as `extract` before matching, up ' +
132
+ 'to maxScan candidates — if the narrowed window is larger than maxScan, the response reports ' +
133
+ 'truncated:true up front rather than silently missing later matches; narrow the filter or raise ' +
134
+ 'maxScan and retry. regex:true matching runs under a wall-clock deadline and is cleanly failed ' +
135
+ `(not silently empty) if a pattern is too slow — avoid nested-quantifier patterns like (a+)+.`,
136
+ inputSchema: {
137
+ type: 'object',
138
+ properties: {
139
+ query: { type: 'string', description: 'Substring (or regex source, when regex:true) to search for.' },
140
+ regex: { type: 'boolean', description: 'Treat query as a regular expression (default false).' },
141
+ caseSensitive: { type: 'boolean', description: 'Case-sensitive match (default false).' },
142
+ from: { type: 'string', description: 'ISO 8601 inclusive lower bound. Omit for open-ended.' },
143
+ to: { type: 'string', description: 'ISO 8601 inclusive upper bound. Omit for open-ended.' },
144
+ channelId: { type: 'string', description: 'Restrict to one channel.' },
145
+ limit: { type: 'number', description: `Max matches to return (default ${SEARCH_DEFAULT_LIMIT}, hard cap ${SEARCH_MAX_LIMIT}). Must be a non-negative integer.` },
146
+ maxScan: { type: 'number', description: `Max candidate messages to scan (default ${SEARCH_DEFAULT_MAX_SCAN}, hard cap ${SEARCH_MAX_MAX_SCAN}). Must be a non-negative integer.` },
147
+ },
148
+ required: ['query'],
149
+ },
150
+ },
151
+ ];
152
+ }
153
+ async handleToolCall(call) {
154
+ try {
155
+ if (!this.cm) {
156
+ throw new Error('HistoryModule not bound — host must call bind(contextManager) before tool dispatch.');
157
+ }
158
+ switch (call.name) {
159
+ case 'stats':
160
+ return this.handleStats((call.input ?? {}));
161
+ case 'extract':
162
+ return this.handleExtract((call.input ?? {}));
163
+ case 'search':
164
+ return await this.handleSearch((call.input ?? {}));
165
+ default:
166
+ return { success: false, isError: true, error: `Unknown tool: ${call.name}` };
167
+ }
168
+ }
169
+ catch (error) {
170
+ // Catches our own validation errors (bad ISO date, invalid regex,
171
+ // out-of-range limit/offset/maxScan, unbound module), a regex-search
172
+ // worker timeout or worker-side error (see handleSearch/
173
+ // searchWithRegexWorker — a ReDoS-shaped pattern surfaces here as a
174
+ // clean timeout error, never a hang), and context-manager's
175
+ // capability-absent error ("Chronicle history index unsupported...",
176
+ // thrown by queryMessagesByTime/queryMessagesByChannel/
177
+ // queryMessagesByTimeAndChannel/getChannelMessageCounts/
178
+ // getChannelTokenStats on a chronicle build that predates the native
179
+ // index-query capability) — all surfaced as a normal tool error
180
+ // rather than crashing the module.
181
+ return {
182
+ success: false,
183
+ isError: true,
184
+ error: error instanceof Error ? error.message : String(error),
185
+ };
186
+ }
187
+ }
188
+ async onProcess(_event, _state) {
189
+ return {};
190
+ }
191
+ // ==========================================================================
192
+ // stats
193
+ // ==========================================================================
194
+ handleStats(input) {
195
+ const fromMs = parseIsoDate(input.from, 'from');
196
+ const toMs = parseIsoDate(input.to, 'to');
197
+ const cm = this.cm;
198
+ let messageCountsAllTime = cm.getChannelMessageCounts();
199
+ let tokenStatsForRange = cm.getChannelTokenStats({ fromMs, toMs });
200
+ if (input.channelId) {
201
+ messageCountsAllTime = messageCountsAllTime.filter((c) => c.channelId === input.channelId);
202
+ tokenStatsForRange = {
203
+ // Totals stay whole-range (they're documented as store-wide-for-the-range,
204
+ // not per-channel); only the byChannel breakdown is narrowed. Labeling the
205
+ // field `byChannel` (rather than folding a channel-filtered total into
206
+ // totalMessages/totalTokensEstimate) keeps this from reading as a
207
+ // misleadingly-merged single-channel total.
208
+ totalMessages: tokenStatsForRange.totalMessages,
209
+ totalTokensEstimate: tokenStatsForRange.totalTokensEstimate,
210
+ byChannel: tokenStatsForRange.byChannel.filter((c) => c.channelId === input.channelId),
211
+ };
212
+ }
213
+ return {
214
+ success: true,
215
+ data: {
216
+ query: { from: input.from ?? null, to: input.to ?? null, channelId: input.channelId ?? null },
217
+ messageCountsAllTime,
218
+ tokenStatsForRange,
219
+ },
220
+ };
221
+ }
222
+ // ==========================================================================
223
+ // extract
224
+ // ==========================================================================
225
+ handleExtract(input) {
226
+ const fromMs = parseIsoDate(input.from, 'from');
227
+ const toMs = parseIsoDate(input.to, 'to');
228
+ const limit = clampCount(input.limit, EXTRACT_DEFAULT_LIMIT, EXTRACT_MAX_LIMIT, 'limit');
229
+ const offset = clampCount(input.offset, 0, NATIVE_OFFSET_MAX, 'offset');
230
+ const format = input.format ?? 'text';
231
+ const cm = this.cm;
232
+ const result = cm.queryMessagesByTimeAndChannel({
233
+ fromMs,
234
+ toMs,
235
+ channelId: input.channelId,
236
+ limit,
237
+ offset,
238
+ });
239
+ return {
240
+ success: true,
241
+ data: {
242
+ matchedCount: result.matchedCount,
243
+ returned: result.messages.length,
244
+ messages: result.messages.map((msg) => projectMessage(msg, format)),
245
+ },
246
+ };
247
+ }
248
+ // ==========================================================================
249
+ // search
250
+ // ==========================================================================
251
+ async handleSearch(input) {
252
+ if (!input.query) {
253
+ throw new Error('search requires a non-empty "query".');
254
+ }
255
+ const fromMs = parseIsoDate(input.from, 'from');
256
+ const toMs = parseIsoDate(input.to, 'to');
257
+ const limit = clampCount(input.limit, SEARCH_DEFAULT_LIMIT, SEARCH_MAX_LIMIT, 'limit');
258
+ const maxScan = clampCount(input.maxScan, SEARCH_DEFAULT_MAX_SCAN, SEARCH_MAX_MAX_SCAN, 'maxScan');
259
+ const caseSensitive = input.caseSensitive ?? false;
260
+ const flags = caseSensitive ? '' : 'i';
261
+ // Validate regex SYNTAX up front — an invalid pattern is a clean tool
262
+ // error, not a crash mid-scan. This does NOT bound match TIME (a
263
+ // syntactically valid pattern can still backtrack catastrophically),
264
+ // which is why regex-mode matching itself runs on a worker below rather
265
+ // than here.
266
+ if (input.regex) {
267
+ try {
268
+ new RegExp(input.query, flags);
269
+ }
270
+ catch (error) {
271
+ throw new Error(`Invalid regex "${input.query}": ${error instanceof Error ? error.message : String(error)}`);
272
+ }
273
+ }
274
+ const needle = caseSensitive ? input.query : input.query.toLowerCase();
275
+ const cm = this.cm;
276
+ // Fetch one more than maxScan so an oversized candidate window is
277
+ // detected up front (before any matching is attempted), rather than
278
+ // silently scanning maxScan candidates and returning as if that were the
279
+ // whole window. See the tool description's `truncated` contract.
280
+ const probe = cm.queryMessagesByTimeAndChannel({
281
+ fromMs,
282
+ toMs,
283
+ channelId: input.channelId,
284
+ limit: maxScan + 1,
285
+ offset: 0,
286
+ });
287
+ const truncated = probe.messages.length > maxScan;
288
+ const candidates = truncated ? probe.messages.slice(0, maxScan) : probe.messages;
289
+ if (input.regex) {
290
+ // Regex matching against caller-supplied patterns is ReDoS-shaped: a
291
+ // pathological pattern (e.g. `(a+)+$`) can take catastrophically long
292
+ // against one candidate string, and a synchronous RegExp.exec() on
293
+ // this thread would block the WHOLE framework's event loop — every
294
+ // agent's turns, health checks, timers — for as long as it runs, with
295
+ // no way to interrupt it from this same thread. Route matching
296
+ // through a worker thread instead, which can be forcibly terminated
297
+ // on a deadline. See search-regex-worker.ts's header for the full
298
+ // rationale.
299
+ const { matches, scanned } = await this.searchWithRegexWorker(candidates, input.query, flags, limit);
300
+ return { success: true, data: { scanned, candidatePoolSize: candidates.length, truncated, matches } };
301
+ }
302
+ // Plain substring search (String.prototype.indexOf) is inherently
303
+ // linear in input length — no ReDoS-equivalent risk — so it stays
304
+ // in-process. This is also the common case, so it pays no worker-spawn
305
+ // overhead.
306
+ const matches = [];
307
+ let scanned = 0;
308
+ for (const msg of candidates) {
309
+ // Check the limit BEFORE doing any work for this candidate — not
310
+ // after pushing a match — so limit:0 (a valid clampCount value: it's
311
+ // >= 0) correctly yields zero matches instead of one. Checking
312
+ // post-push would always let through the match that first reaches
313
+ // the limit. Same fix mirrored in search-regex-worker.ts's loop.
314
+ if (matches.length >= limit)
315
+ break;
316
+ scanned++;
317
+ const text = flattenContent(msg.content);
318
+ const hit = matchSubstring(text, needle, caseSensitive);
319
+ if (!hit)
320
+ continue;
321
+ matches.push({
322
+ id: String(msg.id),
323
+ timestamp: msg.timestamp.toISOString(),
324
+ participant: msg.participant,
325
+ channelId: getChannelId(msg) ?? null,
326
+ snippet: snippetAround(text, hit.index, hit.length),
327
+ });
328
+ }
329
+ return {
330
+ success: true,
331
+ data: { scanned, candidatePoolSize: candidates.length, truncated, matches },
332
+ };
333
+ }
334
+ /**
335
+ * Run regex matching for `search` on a worker thread with a hard
336
+ * wall-clock deadline, so a catastrophically-backtracking pattern can be
337
+ * forcibly killed instead of hanging the framework. One worker per call
338
+ * (not per candidate — spawn overhead would dominate at scale; not a
339
+ * persistent pool — a fresh worker per call means one bad pattern can
340
+ * never contaminate a later search). Always terminated on the way out,
341
+ * success or failure, so nothing lingers.
342
+ *
343
+ * On timeout or a worker-side error this THROWS (caught by
344
+ * handleToolCall's try/catch, same as every other error path in this
345
+ * module) rather than returning an empty match list — a timed-out search
346
+ * must never be indistinguishable from a clean "no matches" result.
347
+ */
348
+ async searchWithRegexWorker(candidates, pattern, flags, limit) {
349
+ const texts = candidates.map((msg) => flattenContent(msg.content));
350
+ let worker;
351
+ try {
352
+ const { matches: rawMatches, scanned } = await new Promise((resolve, reject) => {
353
+ worker = new Worker(SEARCH_WORKER_PATH, { workerData: { texts, pattern, flags, limit } });
354
+ const timer = setTimeout(() => {
355
+ reject(new Error(`search timed out after ${SEARCH_REGEX_TIMEOUT_MS}ms while matching regex "${pattern}" against ` +
356
+ `up to ${texts.length} candidate(s) — the pattern may be catastrophically slow (exponential ` +
357
+ 'backtracking) against this data; try a simpler pattern, a literal substring search ' +
358
+ '(regex:false), or a smaller maxScan.'));
359
+ }, SEARCH_REGEX_TIMEOUT_MS);
360
+ timer.unref?.();
361
+ worker.once('message', (msg) => {
362
+ clearTimeout(timer);
363
+ if (msg.type === 'error')
364
+ reject(new Error(msg.error));
365
+ else
366
+ resolve({ matches: msg.matches, scanned: msg.scanned });
367
+ });
368
+ worker.once('error', (err) => {
369
+ clearTimeout(timer);
370
+ reject(err);
371
+ });
372
+ });
373
+ const matches = rawMatches.map(({ candidateIndex, matchIndex, matchLength }) => {
374
+ const msg = candidates[candidateIndex];
375
+ const text = texts[candidateIndex];
376
+ return {
377
+ id: String(msg.id),
378
+ timestamp: msg.timestamp.toISOString(),
379
+ participant: msg.participant,
380
+ channelId: getChannelId(msg) ?? null,
381
+ snippet: snippetAround(text, matchIndex, matchLength),
382
+ };
383
+ });
384
+ return { matches, scanned };
385
+ }
386
+ finally {
387
+ // Always kill the worker — whether it finished, errored, or is still
388
+ // stuck mid-backtrack when the deadline hit. terminate() on an
389
+ // already-exited worker is a harmless no-op.
390
+ if (worker)
391
+ void worker.terminate().catch(() => { });
392
+ }
393
+ }
394
+ }
395
+ // ============================================================================
396
+ // Helpers
397
+ // ============================================================================
398
+ /** Parse an ISO 8601 date string to Unix ms. Throws a clear error (not a
399
+ * crash) on an unparseable string; returns undefined for an omitted field
400
+ * (open-ended bound). */
401
+ function parseIsoDate(value, field) {
402
+ if (value === undefined)
403
+ return undefined;
404
+ const ms = Date.parse(value);
405
+ if (Number.isNaN(ms)) {
406
+ throw new Error(`Invalid ISO 8601 date for "${field}": ${JSON.stringify(value)}`);
407
+ }
408
+ return ms;
409
+ }
410
+ /**
411
+ * Validate a pagination-ish numeric input (limit/offset/maxScan) and clamp
412
+ * it to an upper bound. `Math.min(value, max)` alone is NOT sufficient here:
413
+ * it only enforces an upper bound, so `Math.min(-1, 200)` is `-1`, not a
414
+ * sane value — a negative limit/offset/maxScan would sail straight past the
415
+ * "hard cap" and reach a native chronicle call expecting an unsigned
416
+ * pagination argument (observed: `extract({limit:-1})` returning every
417
+ * message in the store instead of being capped). Rejects (rather than
418
+ * silently clamping) anything that isn't a finite non-negative integer, so
419
+ * a caller mistake is surfaced as a clean tool error instead of silently
420
+ * doing something other than what was asked.
421
+ */
422
+ function clampCount(value, def, max, field) {
423
+ if (value === undefined)
424
+ return def;
425
+ if (typeof value !== 'number' || !Number.isFinite(value)) {
426
+ throw new Error(`"${field}" must be a finite number, got ${JSON.stringify(value)}.`);
427
+ }
428
+ if (!Number.isInteger(value)) {
429
+ throw new Error(`"${field}" must be an integer, got ${value}.`);
430
+ }
431
+ if (value < 0) {
432
+ throw new Error(`"${field}" must be >= 0, got ${value}.`);
433
+ }
434
+ return Math.min(value, max);
435
+ }
436
+ /** channelId lives at metadata.external.channelId — same path context-manager's
437
+ * own native index reads from (message-store.ts's CHANNEL_FIELD). Metadata's
438
+ * index-signature type means this needs an explicit narrow, same as
439
+ * MessageStore.getChannelTokenStats does internally. */
440
+ /**
441
+ * Two channel-id metadata shapes exist in the wild: `metadata.channelId`
442
+ * (what agent-framework's real MCPL ingestion — `handleMcplChannelIncoming`
443
+ * — actually writes) and `metadata.external.channelId` (an older
444
+ * convention). context-manager 0.9.1+ indexes and queries BOTH (see
445
+ * MessageStore.extractChannelId there), so a channel-filtered result can
446
+ * come from either shape — check the same order here so a displayed
447
+ * `channelId` never reads `null` on a message that was, in fact, matched by
448
+ * channel.
449
+ */
450
+ function getChannelId(msg) {
451
+ const metadata = msg.metadata;
452
+ if (typeof metadata?.channelId === 'string')
453
+ return metadata.channelId;
454
+ return typeof metadata?.external?.channelId === 'string' ? metadata.external.channelId : undefined;
455
+ }
456
+ function projectMessage(msg, format) {
457
+ return {
458
+ id: String(msg.id),
459
+ timestamp: msg.timestamp.toISOString(),
460
+ participant: msg.participant,
461
+ channelId: getChannelId(msg) ?? null,
462
+ content: format === 'raw' ? msg.content : flattenContent(msg.content),
463
+ };
464
+ }
465
+ /** Flatten a message's content blocks to one short, readable string. Text
466
+ * blocks verbatim; everything else (tool_use/tool_result/thinking/media) as
467
+ * a short bracketed label — this is for agent readability and search
468
+ * matching, not byte-faithful reconstruction. */
469
+ function flattenContent(content) {
470
+ return content.map(blockLabel).join(' ').trim();
471
+ }
472
+ function blockLabel(block) {
473
+ switch (block.type) {
474
+ case 'text':
475
+ return block.text;
476
+ case 'tool_use':
477
+ return `[tool_use: ${block.name}]`;
478
+ case 'tool_result':
479
+ return block.toolName ? `[tool_result: ${block.toolName}]` : '[tool_result]';
480
+ case 'thinking':
481
+ case 'redacted_thinking':
482
+ return '[thinking]';
483
+ case 'image':
484
+ return '[image]';
485
+ case 'generated_image':
486
+ return '[image]';
487
+ case 'document':
488
+ return '[document]';
489
+ case 'audio':
490
+ return '[audio]';
491
+ case 'video':
492
+ return '[video]';
493
+ default:
494
+ // Exhaustiveness guard: a future ContentBlock variant falls back to a
495
+ // generic label instead of a compile error at a call site far from here.
496
+ return `[${block.type}]`;
497
+ }
498
+ }
499
+ function matchSubstring(text, needle, caseSensitive) {
500
+ const haystack = caseSensitive ? text : text.toLowerCase();
501
+ const index = haystack.indexOf(needle);
502
+ if (index === -1)
503
+ return null;
504
+ return { index, length: needle.length };
505
+ }
506
+ /** ~SNIPPET_CONTEXT_CHARS of surrounding context on each side of a match, or
507
+ * the first ~SNIPPET_FALLBACK_CHARS of content when no meaningful match
508
+ * position is given (kept simple on purpose — this is a readability aid,
509
+ * not a highlighting engine). */
510
+ function snippetAround(text, index, length) {
511
+ if (index < 0)
512
+ return text.slice(0, SNIPPET_FALLBACK_CHARS);
513
+ const start = Math.max(0, index - SNIPPET_CONTEXT_CHARS);
514
+ const end = Math.min(text.length, index + length + SNIPPET_CONTEXT_CHARS);
515
+ const prefix = start > 0 ? '…' : '';
516
+ const suffix = end < text.length ? '…' : '';
517
+ return prefix + text.slice(start, end) + suffix;
518
+ }
519
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/modules/history/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ export interface SearchWorkerJob {
2
+ /** Flattened candidate text, aligned by index with the parent's candidate array. */
3
+ texts: string[];
4
+ pattern: string;
5
+ flags: string;
6
+ /** Stop scanning once this many matches are found. */
7
+ limit: number;
8
+ }
9
+ export interface SearchWorkerMatch {
10
+ /** Index into `texts` / the parent's candidate array. */
11
+ candidateIndex: number;
12
+ matchIndex: number;
13
+ matchLength: number;
14
+ }
15
+ export type SearchWorkerMessage = {
16
+ type: 'done';
17
+ matches: SearchWorkerMatch[];
18
+ scanned: number;
19
+ } | {
20
+ type: 'error';
21
+ error: string;
22
+ };
23
+ //# sourceMappingURL=search-regex-worker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-regex-worker.d.ts","sourceRoot":"","sources":["../../../../src/modules/history/search-regex-worker.ts"],"names":[],"mappings":"AA4BA,MAAM,WAAW,eAAe;IAC9B,oFAAoF;IACpF,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,sDAAsD;IACtD,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,iBAAiB;IAChC,yDAAyD;IACzD,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,MAAM,mBAAmB,GAC3B;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAC/D;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC"}