@moxxy/sdk 0.14.2 → 0.14.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/compactor-helpers.d.ts.map +1 -1
  2. package/dist/compactor-helpers.js +9 -5
  3. package/dist/compactor-helpers.js.map +1 -1
  4. package/dist/mode/collect-stream.d.ts +68 -0
  5. package/dist/mode/collect-stream.d.ts.map +1 -0
  6. package/dist/mode/collect-stream.js +181 -0
  7. package/dist/mode/collect-stream.js.map +1 -0
  8. package/dist/mode/project-messages.d.ts +84 -0
  9. package/dist/mode/project-messages.d.ts.map +1 -0
  10. package/dist/mode/project-messages.js +392 -0
  11. package/dist/mode/project-messages.js.map +1 -0
  12. package/dist/mode/single-shot.d.ts +17 -0
  13. package/dist/mode/single-shot.d.ts.map +1 -0
  14. package/dist/mode/single-shot.js +53 -0
  15. package/dist/mode/single-shot.js.map +1 -0
  16. package/dist/mode/stable-hash.d.ts +7 -0
  17. package/dist/mode/stable-hash.d.ts.map +1 -0
  18. package/dist/mode/stable-hash.js +20 -0
  19. package/dist/mode/stable-hash.js.map +1 -0
  20. package/dist/mode/stuck-loop.d.ts +39 -0
  21. package/dist/mode/stuck-loop.d.ts.map +1 -0
  22. package/dist/mode/stuck-loop.js +51 -0
  23. package/dist/mode/stuck-loop.js.map +1 -0
  24. package/dist/mode-helpers.d.ts +15 -175
  25. package/dist/mode-helpers.d.ts.map +1 -1
  26. package/dist/mode-helpers.js +14 -666
  27. package/dist/mode-helpers.js.map +1 -1
  28. package/dist/tunnel.d.ts.map +1 -1
  29. package/dist/tunnel.js +11 -1
  30. package/dist/tunnel.js.map +1 -1
  31. package/package.json +1 -1
  32. package/src/compactor-helpers.test.ts +21 -0
  33. package/src/compactor-helpers.ts +10 -5
  34. package/src/mode/collect-stream.ts +247 -0
  35. package/src/mode/project-messages.test.ts +121 -0
  36. package/src/mode/project-messages.ts +461 -0
  37. package/src/mode/single-shot.ts +63 -0
  38. package/src/mode/stable-hash.ts +20 -0
  39. package/src/mode/stuck-loop.ts +89 -0
  40. package/src/mode-helpers.ts +32 -850
  41. package/src/mode.test.ts +20 -0
  42. package/src/token-accounting.test.ts +90 -0
  43. package/src/tunnel.test.ts +21 -0
  44. package/src/tunnel.ts +10 -1
@@ -0,0 +1,392 @@
1
+ import { computeElisionState, conversationalStub, conversationalStubbed, toolResultBytes, toolResultStub, toolResultStubbed, } from '../elision-state.js';
2
+ import { isToolDisplayResult } from '../tool-display.js';
3
+ /** Appended to the system prompt while elision is active (see projection). */
4
+ export const ELISION_SYSTEM_NOTE = 'Context note: to stay within budget, older turns may appear as stubs like ' +
5
+ '`[output elided — recall("id") to view]` or `[elided user turn · recall({ seq: N })]`. ' +
6
+ 'These are NOT the real content — call the `recall` tool with the given id/seq to fetch ' +
7
+ 'the full text before relying on any detail from an elided turn. Recent turns are always ' +
8
+ 'shown verbatim.';
9
+ /**
10
+ * Compose a model-facing system prompt that includes any base prompt
11
+ * plus a COMPACT skill index (name + description + triggers only).
12
+ *
13
+ * Lazy-loading design: the body is intentionally NOT inlined. The model
14
+ * matches user intent against the description/triggers, then calls the
15
+ * `load_skill` tool to fetch the body of the skill it picked. This keeps
16
+ * the system prompt small even with many skills installed and avoids
17
+ * paying for skill bodies the model never actually follows.
18
+ */
19
+ export function buildSystemPromptWithSkills(baseSystemPrompt, skills) {
20
+ if (skills.length === 0)
21
+ return baseSystemPrompt;
22
+ const header = `## Available skills\n\n` +
23
+ `Each line below is a pre-authored playbook for a specific intent. ` +
24
+ `When the user's request matches one of these (by name, description, ` +
25
+ `or triggers), call \`load_skill({ name: "<skill-name>" })\` FIRST to ` +
26
+ `fetch the full instructions, then follow them verbatim. Prefer using ` +
27
+ `a skill over re-deriving the workflow with ad-hoc tools.\n`;
28
+ const entries = skills
29
+ .map((s) => {
30
+ const fm = s.frontmatter;
31
+ const triggerHint = fm.triggers?.length
32
+ ? ` (triggers: ${fm.triggers.map((t) => `"${t}"`).join(', ')})`
33
+ : '';
34
+ return `- **${fm.name}** — ${fm.description}${triggerHint}`;
35
+ })
36
+ .join('\n');
37
+ const skillBlock = `${header}\n${entries}`;
38
+ return baseSystemPrompt ? `${baseSystemPrompt}\n\n${skillBlock}` : skillBlock;
39
+ }
40
+ function activeCompactionRanges(events) {
41
+ return events
42
+ .filter((event) => event.type === 'compaction' &&
43
+ event.tokensSaved > 0 &&
44
+ event.summary.trim().length > 0 &&
45
+ event.replacedRange[0] <= event.replacedRange[1])
46
+ .map((event) => ({
47
+ from: event.replacedRange[0],
48
+ to: event.replacedRange[1],
49
+ summary: event.summary,
50
+ }));
51
+ }
52
+ function eventInCompactionRange(seq, ranges) {
53
+ for (const range of ranges) {
54
+ if (seq >= range.from && seq <= range.to)
55
+ return range;
56
+ }
57
+ return null;
58
+ }
59
+ /**
60
+ * A compaction lookup that answers "which range (if any) contains `seq`" in
61
+ * O(log ranges) instead of {@link eventInCompactionRange}'s O(ranges) linear
62
+ * scan per event. Compaction ranges are non-overlapping ascending seq prefixes,
63
+ * so a seq belongs to at most one range and binary search over the
64
+ * sorted-by-`from` array returns the SAME range the linear first-match did —
65
+ * byte-identical projection.
66
+ *
67
+ * Defensive fallback: if the ranges are NOT strictly non-overlapping (which the
68
+ * compaction invariant forbids, but a hand-crafted/corrupt log could violate),
69
+ * we keep the exact linear first-match semantics so the projection can never
70
+ * diverge from the old code.
71
+ */
72
+ function makeCompactionLookup(ranges) {
73
+ if (ranges.length === 0)
74
+ return () => null;
75
+ if (ranges.length === 1) {
76
+ const only = ranges[0];
77
+ return (seq) => (seq >= only.from && seq <= only.to ? only : null);
78
+ }
79
+ // Sort a copy by `from` (stable enough — ranges are non-overlapping). Verify
80
+ // the non-overlap invariant on the sorted copy; only then is binary search
81
+ // provably equivalent to the linear first-match.
82
+ const sorted = [...ranges].sort((a, b) => a.from - b.from);
83
+ let nonOverlapping = true;
84
+ for (let i = 1; i < sorted.length; i++) {
85
+ if (sorted[i].from <= sorted[i - 1].to) {
86
+ nonOverlapping = false;
87
+ break;
88
+ }
89
+ }
90
+ if (!nonOverlapping)
91
+ return (seq) => eventInCompactionRange(seq, ranges);
92
+ return (seq) => {
93
+ // Largest `from <= seq`, then a single containment check.
94
+ let lo = 0;
95
+ let hi = sorted.length - 1;
96
+ let cand = -1;
97
+ while (lo <= hi) {
98
+ const mid = (lo + hi) >> 1;
99
+ if (sorted[mid].from <= seq) {
100
+ cand = mid;
101
+ lo = mid + 1;
102
+ }
103
+ else {
104
+ hi = mid - 1;
105
+ }
106
+ }
107
+ if (cand < 0)
108
+ return null;
109
+ const range = sorted[cand];
110
+ return seq <= range.to ? range : null;
111
+ };
112
+ }
113
+ /**
114
+ * Pure projection of a single `user_prompt` event to its content blocks.
115
+ *
116
+ * Extracted from {@link projectMessages} as an independently testable sub-step
117
+ * (u123-5). Returns either the collapsed stub (when the event is an elided
118
+ * conversational turn) or the full text + attachment-expanded blocks — exactly
119
+ * what the inline switch arm produced. Caller decides how to wrap it as a
120
+ * message / record the stable prefix.
121
+ */
122
+ export function projectUserPrompt(event, el) {
123
+ // Elided + conversational: collapse to a stub (anchor/tiny kept full).
124
+ if (conversationalStubbed(event, el)) {
125
+ return [{ type: 'text', text: conversationalStub('user', event.seq) }];
126
+ }
127
+ const blocks = [{ type: 'text', text: event.text }];
128
+ if (event.attachments) {
129
+ for (const att of event.attachments) {
130
+ if (att.kind === 'image') {
131
+ blocks.push({
132
+ type: 'image',
133
+ mediaType: att.mediaType ?? 'image/png',
134
+ data: att.content,
135
+ });
136
+ }
137
+ else if (att.kind === 'document') {
138
+ blocks.push({
139
+ type: 'document',
140
+ mediaType: att.mediaType ?? 'application/pdf',
141
+ data: att.content,
142
+ ...(att.name ? { name: att.name } : {}),
143
+ });
144
+ }
145
+ else {
146
+ blocks.push({
147
+ type: 'text',
148
+ text: `[${att.kind}${att.name ? ` ${att.name}` : ''}]\n${att.content}`,
149
+ });
150
+ }
151
+ }
152
+ }
153
+ return blocks;
154
+ }
155
+ /**
156
+ * Precompute the set of callIds that have a matching tool_result (or
157
+ * tool_call_denied) somewhere in the log. Used to synthesize a fallback
158
+ * `[interrupted]` tool_result for orphan tool_use blocks when the assistant
159
+ * message gets flushed.
160
+ *
161
+ * Without this fallback the provider rejects the whole conversation with
162
+ * "assistant message with 'tool_calls' must be followed by tool messages
163
+ * responding to each 'tool_call_id'". Orphans typically appear after a
164
+ * cancelled turn, an aborted process, or a tool exception that bypassed the
165
+ * loop's tool_result emit path.
166
+ *
167
+ * Extracted as a pure precompute (u123-5); returns a fresh mutable Set the
168
+ * projection augments as it synthesizes orphan results (so a repeated orphan
169
+ * across groups is only emitted once).
170
+ */
171
+ export function resolvedCallIdSet(events) {
172
+ const resolvedCallIds = new Set();
173
+ for (const e of events) {
174
+ if (e.type === 'tool_result' || e.type === 'tool_call_denied') {
175
+ resolvedCallIds.add(e.callId);
176
+ }
177
+ }
178
+ return resolvedCallIds;
179
+ }
180
+ export function projectMessagesFromLog(ctx, opts = {}) {
181
+ return projectMessages(ctx, opts).messages;
182
+ }
183
+ /**
184
+ * Same projection as {@link projectMessagesFromLog} but also reports the
185
+ * stable-prefix boundary so the active cache strategy can place a cross-turn
186
+ * breakpoint. Modes that build messages this way should thread the returned
187
+ * `stablePrefixIndex` into {@link collectProviderStream}.
188
+ */
189
+ export function projectMessages(ctx, opts = {}) {
190
+ const allEvents = ctx.log.slice();
191
+ const compactions = activeCompactionRanges(allEvents);
192
+ const compactionFor = makeCompactionLookup(compactions);
193
+ const emittedCompactions = new Set();
194
+ const el = computeElisionState(allEvents);
195
+ const messages = [];
196
+ // The stable prefix is every message produced from events at/below the
197
+ // elision HWM. Record the latest such message index as we push.
198
+ let stablePrefixIndex = -1;
199
+ const recordStable = (maxSeq) => {
200
+ if (el.hwm >= 0 && maxSeq >= 0 && maxSeq <= el.hwm) {
201
+ stablePrefixIndex = messages.length - 1;
202
+ }
203
+ };
204
+ if (opts.systemPrompt) {
205
+ // When elision is active, tell the model that older turns may be shown as
206
+ // stubs and how to expand them — so it recalls instead of hallucinating.
207
+ // Constant text → busts the system cache once (when elision starts), stable
208
+ // thereafter.
209
+ const sysText = el.hwm >= 0 ? `${opts.systemPrompt}\n\n${ELISION_SYSTEM_NOTE}` : opts.systemPrompt;
210
+ messages.push({ role: 'system', content: [{ type: 'text', text: sysText }] });
211
+ }
212
+ // Pre-scan: build the set of callIds that have a matching tool_result
213
+ // (or tool_call_denied) somewhere in the log. Used to synthesize a
214
+ // fallback `[interrupted]` tool_result for orphan tool_use blocks
215
+ // when the assistant message gets flushed.
216
+ const resolvedCallIds = resolvedCallIdSet(allEvents);
217
+ let pendingAssistant = null;
218
+ let pendingAssistantMaxSeq = -1;
219
+ // Reasoning block awaiting attachment to the current assistant turn. Only set
220
+ // for REPLAYABLE reasoning (Anthropic signature / redacted-or-Codex encrypted
221
+ // blob) — render-only reasoning is never sent back. Attached as content[0] of
222
+ // the turn's assistant message (Anthropic requires the signed thinking block
223
+ // first on an interleaved-thinking tool-use continuation; a missing/unsigned
224
+ // one is a hard 400). Dropped at any turn/compaction boundary it doesn't reach.
225
+ let pendingReasoning = null;
226
+ let pendingReasoningSeq = -1;
227
+ const flush = () => {
228
+ if (!pendingAssistant)
229
+ return;
230
+ const flushed = pendingAssistant;
231
+ const groupMaxSeq = pendingAssistantMaxSeq;
232
+ pendingAssistant = null;
233
+ pendingAssistantMaxSeq = -1;
234
+ messages.push(flushed);
235
+ recordStable(groupMaxSeq);
236
+ // Synthesize fallback tool_result messages for any tool_use blocks
237
+ // whose callId never resolved in the event log. Has to land
238
+ // immediately after the assistant message (and before any
239
+ // subsequent user_prompt / assistant_message) so the provider sees
240
+ // a clean assistant→tool-result chain.
241
+ for (const block of flushed.content) {
242
+ if (block.type === 'tool_use' && !resolvedCallIds.has(block.id)) {
243
+ messages.push({
244
+ role: 'tool_result',
245
+ content: [
246
+ {
247
+ type: 'tool_result',
248
+ toolUseId: block.id,
249
+ content: '[tool call did not return a result — possibly interrupted or cancelled]',
250
+ isError: true,
251
+ },
252
+ ],
253
+ });
254
+ recordStable(groupMaxSeq);
255
+ // Mark synthesized so we don't double-emit if the same orphan
256
+ // appears in multiple groups (defensive — shouldn't normally
257
+ // happen since each tool_call_requested has a unique callId).
258
+ resolvedCallIds.add(block.id);
259
+ }
260
+ }
261
+ };
262
+ for (const e of allEvents) {
263
+ const compaction = compactionFor(e.seq);
264
+ if (compaction) {
265
+ if (!emittedCompactions.has(compaction)) {
266
+ emittedCompactions.add(compaction);
267
+ flush();
268
+ pendingReasoning = null;
269
+ messages.push({
270
+ role: 'user',
271
+ content: [{ type: 'text', text: `[summary of earlier turns]\n${compaction.summary}` }],
272
+ });
273
+ recordStable(compaction.to);
274
+ }
275
+ continue;
276
+ }
277
+ switch (e.type) {
278
+ case 'user_prompt': {
279
+ flush();
280
+ pendingReasoning = null;
281
+ messages.push({ role: 'user', content: projectUserPrompt(e, el) });
282
+ recordStable(e.seq);
283
+ break;
284
+ }
285
+ case 'reasoning_message': {
286
+ // Render-only reasoning (no signature/encrypted) is never replayed —
287
+ // it exists only for the live/scrollback "Thinking" view. Replayable
288
+ // reasoning is stashed for content[0] of this turn's assistant message.
289
+ if (e.signature || e.encrypted) {
290
+ pendingReasoning = {
291
+ type: 'reasoning',
292
+ text: e.content,
293
+ ...(e.signature ? { signature: e.signature } : {}),
294
+ ...(e.redacted ? { redacted: true } : {}),
295
+ ...(e.encrypted ? { encrypted: e.encrypted } : {}),
296
+ };
297
+ pendingReasoningSeq = e.seq;
298
+ }
299
+ break;
300
+ }
301
+ case 'assistant_message':
302
+ flush();
303
+ if (conversationalStubbed(e, el)) {
304
+ pendingReasoning = null;
305
+ messages.push({
306
+ role: 'assistant',
307
+ content: [{ type: 'text', text: conversationalStub('assistant', e.seq) }],
308
+ });
309
+ recordStable(e.seq);
310
+ break;
311
+ }
312
+ // A tool-only turn can log an assistant_message with empty content
313
+ // (end_turn + tool calls, no prose). Projecting it as an empty text
314
+ // block makes some providers (Anthropic) reject the NEXT request and
315
+ // permanently wedges the session. Skip the block — the turn's
316
+ // tool_use blocks are projected from tool_call_requested events —
317
+ // which also un-wedges historical logs that already contain one.
318
+ if (e.content.trim().length === 0) {
319
+ pendingReasoning = null;
320
+ recordStable(e.seq);
321
+ break;
322
+ }
323
+ {
324
+ const content = [];
325
+ if (pendingReasoning) {
326
+ content.push(pendingReasoning);
327
+ pendingReasoning = null;
328
+ }
329
+ content.push({ type: 'text', text: e.content });
330
+ messages.push({ role: 'assistant', content });
331
+ }
332
+ recordStable(e.seq);
333
+ break;
334
+ case 'tool_call_requested': {
335
+ if (!pendingAssistant) {
336
+ // Seed the assistant turn so the signed reasoning block is content[0],
337
+ // ahead of every tool_use (Anthropic's interleaved-thinking ordering).
338
+ pendingAssistant = { role: 'assistant', content: pendingReasoning ? [pendingReasoning] : [] };
339
+ if (pendingReasoning) {
340
+ pendingAssistantMaxSeq = Math.max(pendingAssistantMaxSeq, pendingReasoningSeq);
341
+ pendingReasoning = null;
342
+ }
343
+ }
344
+ pendingAssistantMaxSeq = Math.max(pendingAssistantMaxSeq, e.seq);
345
+ pendingAssistant.content.push({
346
+ type: 'tool_use',
347
+ id: e.callId,
348
+ name: e.name,
349
+ input: e.input,
350
+ });
351
+ break;
352
+ }
353
+ case 'tool_result': {
354
+ flush();
355
+ // Stub bulky old tool output to a recall-able marker (decision shared
356
+ // with estimateContextTokens via toolResultStubbed).
357
+ let text;
358
+ if (toolResultStubbed(e, el)) {
359
+ const recalled = el.recalledCallIds.has(e.callId) || el.recalledSeqs.has(e.seq);
360
+ text = toolResultStub(e.callId, toolResultBytes(e.output), recalled);
361
+ }
362
+ else if (e.error) {
363
+ text = `[error:${e.error.kind}] ${e.error.message}`;
364
+ }
365
+ else if (isToolDisplayResult(e.output)) {
366
+ // Rich result (e.g. a file diff): the model only needs the short
367
+ // `forModel` summary — the structured `display` is for channels.
368
+ text = e.output.forModel;
369
+ }
370
+ else {
371
+ text = typeof e.output === 'string' ? e.output : JSON.stringify(e.output ?? '');
372
+ }
373
+ messages.push({
374
+ role: 'tool_result',
375
+ content: [{ type: 'tool_result', toolUseId: e.callId, content: text, isError: !e.ok }],
376
+ });
377
+ recordStable(e.seq);
378
+ break;
379
+ }
380
+ default:
381
+ break;
382
+ }
383
+ }
384
+ flush();
385
+ if (opts.trailingUserText) {
386
+ // The trailing step nudge is volatile (changes per step), never part of
387
+ // the stable prefix — don't record it.
388
+ messages.push({ role: 'user', content: [{ type: 'text', text: opts.trailingUserText }] });
389
+ }
390
+ return { messages, stablePrefixIndex };
391
+ }
392
+ //# sourceMappingURL=project-messages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-messages.js","sourceRoot":"","sources":["../../src/mode/project-messages.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { ProviderMessage } from '../provider.js';
2
+ import type { ModeContext } from '../mode.js';
3
+ /**
4
+ * Run a single-shot (no-tools) provider turn — the shape every planner /
5
+ * synthesis phase shares. Runs context management (compaction + elision),
6
+ * emits the `provider_request` bookend, streams the response with tools
7
+ * disabled, then emits either an `error` event (returning `null`) or the
8
+ * `provider_response` bookend (returning the collected text).
9
+ *
10
+ * Replaces the ~40-line block each mode phase used to inline; centralizing it
11
+ * keeps event emission uniform and means a fix here (e.g. always running
12
+ * elision) lands for every loop strategy at once.
13
+ */
14
+ export declare function runSingleShotTurn(ctx: ModeContext, messages: ReadonlyArray<ProviderMessage>, opts?: {
15
+ maxTokens?: number;
16
+ }): Promise<string | null>;
17
+ //# sourceMappingURL=single-shot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"single-shot.d.ts","sourceRoot":"","sources":["../../src/mode/single-shot.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAM9C;;;;;;;;;;GAUG;AACH,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,WAAW,EAChB,QAAQ,EAAE,aAAa,CAAC,eAAe,CAAC,EACxC,IAAI,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GAChC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAwCxB"}
@@ -0,0 +1,53 @@
1
+ import { runCompactionIfNeeded } from '../compactor-helpers.js';
2
+ import { runElisionIfNeeded } from '../elision-helpers.js';
3
+ import { usageEventFields } from '../token-accounting.js';
4
+ import { collectProviderStream } from './collect-stream.js';
5
+ /**
6
+ * Run a single-shot (no-tools) provider turn — the shape every planner /
7
+ * synthesis phase shares. Runs context management (compaction + elision),
8
+ * emits the `provider_request` bookend, streams the response with tools
9
+ * disabled, then emits either an `error` event (returning `null`) or the
10
+ * `provider_response` bookend (returning the collected text).
11
+ *
12
+ * Replaces the ~40-line block each mode phase used to inline; centralizing it
13
+ * keeps event emission uniform and means a fix here (e.g. always running
14
+ * elision) lands for every loop strategy at once.
15
+ */
16
+ export async function runSingleShotTurn(ctx, messages, opts = {}) {
17
+ await runCompactionIfNeeded(ctx);
18
+ await runElisionIfNeeded(ctx);
19
+ await ctx.emit({
20
+ type: 'provider_request',
21
+ sessionId: ctx.sessionId,
22
+ turnId: ctx.turnId,
23
+ source: 'system',
24
+ provider: ctx.provider.name,
25
+ model: ctx.model,
26
+ });
27
+ const { text, usage, error } = await collectProviderStream(ctx, messages, {
28
+ includeTools: false,
29
+ ...(opts.maxTokens !== undefined ? { maxTokens: opts.maxTokens } : {}),
30
+ });
31
+ if (error) {
32
+ await ctx.emit({
33
+ type: 'error',
34
+ sessionId: ctx.sessionId,
35
+ turnId: ctx.turnId,
36
+ source: 'system',
37
+ kind: error.retryable ? 'retryable' : 'fatal',
38
+ message: error.message,
39
+ });
40
+ return null;
41
+ }
42
+ await ctx.emit({
43
+ type: 'provider_response',
44
+ sessionId: ctx.sessionId,
45
+ turnId: ctx.turnId,
46
+ source: 'system',
47
+ provider: ctx.provider.name,
48
+ model: ctx.model,
49
+ ...usageEventFields(usage),
50
+ });
51
+ return text;
52
+ }
53
+ //# sourceMappingURL=single-shot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"single-shot.js","sourceRoot":"","sources":["../../src/mode/single-shot.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAE5D;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,GAAgB,EAChB,QAAwC,EACxC,OAA+B,EAAE;IAEjC,MAAM,qBAAqB,CAAC,GAAG,CAAC,CAAC;IACjC,MAAM,kBAAkB,CAAC,GAAG,CAAC,CAAC;IAE9B,MAAM,GAAG,CAAC,IAAI,CAAC;QACb,IAAI,EAAE,kBAAkB;QACxB,SAAS,EAAE,GAAG,CAAC,SAAS;QACxB,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,MAAM,EAAE,QAAQ;QAChB,QAAQ,EAAE,GAAG,CAAC,QAAQ,CAAC,IAAI;QAC3B,KAAK,EAAE,GAAG,CAAC,KAAK;KACjB,CAAC,CAAC;IAEH,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,MAAM,qBAAqB,CAAC,GAAG,EAAE,QAAQ,EAAE;QACxE,YAAY,EAAE,KAAK;QACnB,GAAG,CAAC,IAAI,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACvE,CAAC,CAAC;IACH,IAAI,KAAK,EAAE,CAAC;QACV,MAAM,GAAG,CAAC,IAAI,CAAC;YACb,IAAI,EAAE,OAAO;YACb,SAAS,EAAE,GAAG,CAAC,SAAS;YACxB,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,MAAM,EAAE,QAAQ;YAChB,IAAI,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO;YAC7C,OAAO,EAAE,KAAK,CAAC,OAAO;SACvB,CAAC,CAAC;QACH,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,GAAG,CAAC,IAAI,CAAC;QACb,IAAI,EAAE,mBAAmB;QACzB,SAAS,EAAE,GAAG,CAAC,SAAS;QACxB,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,MAAM,EAAE,QAAQ;QAChB,QAAQ,EAAE,GAAG,CAAC,QAAQ,CAAC,IAAI;QAC3B,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,GAAG,gBAAgB,CAAC,KAAK,CAAC;KAC3B,CAAC,CAAC;IAEH,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Stable, key-order-canonical hash of a tool call's input, so `{a:1,b:2}` and
3
+ * `{b:2,a:1}` produce the same key. Use for any "have I seen this call before"
4
+ * comparison — a raw `JSON.stringify` is NOT order-stable.
5
+ */
6
+ export declare function stableHash(input: unknown): string;
7
+ //# sourceMappingURL=stable-hash.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stable-hash.d.ts","sourceRoot":"","sources":["../../src/mode/stable-hash.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEjD"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Stable, key-order-canonical hash of a tool call's input, so `{a:1,b:2}` and
3
+ * `{b:2,a:1}` produce the same key. Use for any "have I seen this call before"
4
+ * comparison — a raw `JSON.stringify` is NOT order-stable.
5
+ */
6
+ export function stableHash(input) {
7
+ return canonicalize(input);
8
+ }
9
+ function canonicalize(value) {
10
+ if (value === null || value === undefined)
11
+ return 'null';
12
+ if (typeof value !== 'object')
13
+ return JSON.stringify(value);
14
+ if (Array.isArray(value)) {
15
+ return '[' + value.map(canonicalize).join(',') + ']';
16
+ }
17
+ const entries = Object.entries(value).sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
18
+ return '{' + entries.map(([k, v]) => JSON.stringify(k) + ':' + canonicalize(v)).join(',') + '}';
19
+ }
20
+ //# sourceMappingURL=stable-hash.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stable-hash.js","sourceRoot":"","sources":["../../src/mode/stable-hash.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,KAAc;IACvC,OAAO,YAAY,CAAC,KAAK,CAAC,CAAC;AAC7B,CAAC;AAED,SAAS,YAAY,CAAC,KAAc;IAClC,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC;IACzD,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAC5D,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;IACvD,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,KAAgC,CAAC,CAAC,IAAI,CACnE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAC3C,CAAC;IACF,OAAO,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;AAClG,CAAC"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Sliding-window detector for "model keeps making the same tool call".
3
+ *
4
+ * When the same `(toolName, input)` pair appears `repeatThreshold` times in
5
+ * the last `windowSize` calls, the model is almost certainly stuck — polling a
6
+ * tool that returns the same thing, mis-handling an error, etc. Bail early
7
+ * instead of burning through the iteration cap.
8
+ *
9
+ * Shared across every loop strategy so detection is uniform — previously each
10
+ * mode re-rolled this, and one copy used a non-canonical `JSON.stringify`
11
+ * signature that silently missed key-reordered repeats.
12
+ */
13
+ export interface StuckSignal {
14
+ /** True when the loop guard should trip. */
15
+ readonly stuck: boolean;
16
+ /** Repeat count behind the trip — for the error message. */
17
+ readonly count: number;
18
+ /**
19
+ * `exact` = the same (tool, full-input) repeated `repeatThreshold` times.
20
+ * `near` = the same (tool, identity arg — url / file_path / command / …)
21
+ * repeated `nearThreshold` times while only volatile args (maxBytes,
22
+ * timeoutMs) varied. Catches the "refetch the same URL with a bigger
23
+ * maxBytes over and over" loop the exact check sails past.
24
+ */
25
+ readonly kind: 'exact' | 'near';
26
+ }
27
+ export interface StuckLoopDetector {
28
+ readonly windowSize: number;
29
+ readonly repeatThreshold: number;
30
+ /** Record the call and report whether the loop guard should trip. */
31
+ record(toolName: string, input: unknown): StuckSignal;
32
+ }
33
+ export declare function createStuckLoopDetector(opts?: {
34
+ windowSize?: number;
35
+ repeatThreshold?: number;
36
+ nearWindowSize?: number;
37
+ nearThreshold?: number;
38
+ }): StuckLoopDetector;
39
+ //# sourceMappingURL=stuck-loop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stuck-loop.d.ts","sourceRoot":"","sources":["../../src/mode/stuck-loop.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,WAAW;IAC1B,4CAA4C;IAC5C,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,4DAA4D;IAC5D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB;;;;;;OAMG;IACH,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC;CACjC;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,qEAAqE;IACrE,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,WAAW,CAAC;CACvD;AAgBD,wBAAgB,uBAAuB,CACrC,IAAI,GAAE;IACJ,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;CACnB,GACL,iBAAiB,CA+BnB"}
@@ -0,0 +1,51 @@
1
+ import { stableHash } from './stable-hash.js';
2
+ /** Identity arguments that pin "the same target" across volatile-arg variation,
3
+ * best-first. The first present string field wins. */
4
+ const IDENTITY_ARG_KEYS = ['url', 'file_path', 'path', 'command', 'cmd', 'query', 'pattern'];
5
+ function identityArg(input) {
6
+ if (!input || typeof input !== 'object' || Array.isArray(input))
7
+ return null;
8
+ const o = input;
9
+ for (const k of IDENTITY_ARG_KEYS) {
10
+ const v = o[k];
11
+ if (typeof v === 'string' && v.trim().length > 0)
12
+ return `${k}=${v}`;
13
+ }
14
+ return null;
15
+ }
16
+ export function createStuckLoopDetector(opts = {}) {
17
+ const windowSize = opts.windowSize ?? 8;
18
+ const repeatThreshold = opts.repeatThreshold ?? 3;
19
+ // Near-dups need a higher count + a wider window (they're spread out across a
20
+ // burst of other calls), and tolerate a couple of legit "bigger refetch" tries.
21
+ const nearWindowSize = opts.nearWindowSize ?? Math.max(windowSize * 2, 16);
22
+ const nearThreshold = opts.nearThreshold ?? Math.max(repeatThreshold + 2, 5);
23
+ const recent = [];
24
+ const recentNear = [];
25
+ return {
26
+ windowSize,
27
+ repeatThreshold,
28
+ record(toolName, input) {
29
+ const key = `${toolName}|${stableHash(input)}`;
30
+ recent.push(key);
31
+ if (recent.length > windowSize)
32
+ recent.shift();
33
+ const exactCount = recent.filter((k) => k === key).length;
34
+ if (exactCount >= repeatThreshold)
35
+ return { stuck: true, count: exactCount, kind: 'exact' };
36
+ let nearCount = 0;
37
+ const id = identityArg(input);
38
+ if (id !== null) {
39
+ const nearKey = `${toolName}|${id}`;
40
+ recentNear.push(nearKey);
41
+ if (recentNear.length > nearWindowSize)
42
+ recentNear.shift();
43
+ nearCount = recentNear.filter((k) => k === nearKey).length;
44
+ if (nearCount >= nearThreshold)
45
+ return { stuck: true, count: nearCount, kind: 'near' };
46
+ }
47
+ return { stuck: false, count: Math.max(exactCount, nearCount), kind: 'exact' };
48
+ },
49
+ };
50
+ }
51
+ //# sourceMappingURL=stuck-loop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stuck-loop.js","sourceRoot":"","sources":["../../src/mode/stuck-loop.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAoC9C;uDACuD;AACvD,MAAM,iBAAiB,GAAG,CAAC,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC;AAE7F,SAAS,WAAW,CAAC,KAAc;IACjC,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAC7E,MAAM,CAAC,GAAG,KAAgC,CAAC;IAC3C,KAAK,MAAM,CAAC,IAAI,iBAAiB,EAAE,CAAC;QAClC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACf,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;IACvE,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,uBAAuB,CACrC,OAKI,EAAE;IAEN,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,CAAC,CAAC;IACxC,MAAM,eAAe,GAAG,IAAI,CAAC,eAAe,IAAI,CAAC,CAAC;IAClD,8EAA8E;IAC9E,gFAAgF;IAChF,MAAM,cAAc,GAAG,IAAI,CAAC,cAAc,IAAI,IAAI,CAAC,GAAG,CAAC,UAAU,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC;IAC3E,MAAM,aAAa,GAAG,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC,GAAG,CAAC,eAAe,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IAC7E,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,UAAU,GAAa,EAAE,CAAC;IAChC,OAAO;QACL,UAAU;QACV,eAAe;QACf,MAAM,CAAC,QAAQ,EAAE,KAAK;YACpB,MAAM,GAAG,GAAG,GAAG,QAAQ,IAAI,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;YAC/C,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACjB,IAAI,MAAM,CAAC,MAAM,GAAG,UAAU;gBAAE,MAAM,CAAC,KAAK,EAAE,CAAC;YAC/C,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,MAAM,CAAC;YAC1D,IAAI,UAAU,IAAI,eAAe;gBAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;YAE5F,IAAI,SAAS,GAAG,CAAC,CAAC;YAClB,MAAM,EAAE,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;YAC9B,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;gBAChB,MAAM,OAAO,GAAG,GAAG,QAAQ,IAAI,EAAE,EAAE,CAAC;gBACpC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;gBACzB,IAAI,UAAU,CAAC,MAAM,GAAG,cAAc;oBAAE,UAAU,CAAC,KAAK,EAAE,CAAC;gBAC3D,SAAS,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,MAAM,CAAC;gBAC3D,IAAI,SAAS,IAAI,aAAa;oBAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;YACzF,CAAC;YACD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,SAAS,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;QACjF,CAAC;KACF,CAAC;AACJ,CAAC"}