@burtson-labs/agent-core 1.6.13

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 (195) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +88 -0
  3. package/dist/index.d.ts +16 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +52 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/mcp/activation.d.ts +60 -0
  8. package/dist/mcp/activation.d.ts.map +1 -0
  9. package/dist/mcp/activation.js +139 -0
  10. package/dist/mcp/activation.js.map +1 -0
  11. package/dist/mcp/clientPool.d.ts +202 -0
  12. package/dist/mcp/clientPool.d.ts.map +1 -0
  13. package/dist/mcp/clientPool.js +469 -0
  14. package/dist/mcp/clientPool.js.map +1 -0
  15. package/dist/mcp/index.d.ts +18 -0
  16. package/dist/mcp/index.d.ts.map +1 -0
  17. package/dist/mcp/index.js +28 -0
  18. package/dist/mcp/index.js.map +1 -0
  19. package/dist/mcp/server.d.ts +43 -0
  20. package/dist/mcp/server.d.ts.map +1 -0
  21. package/dist/mcp/server.js +130 -0
  22. package/dist/mcp/server.js.map +1 -0
  23. package/dist/mcp/toolAdapter.d.ts +57 -0
  24. package/dist/mcp/toolAdapter.d.ts.map +1 -0
  25. package/dist/mcp/toolAdapter.js +223 -0
  26. package/dist/mcp/toolAdapter.js.map +1 -0
  27. package/dist/mcp/types.d.ts +122 -0
  28. package/dist/mcp/types.d.ts.map +1 -0
  29. package/dist/mcp/types.js +15 -0
  30. package/dist/mcp/types.js.map +1 -0
  31. package/dist/providers/deterministic-provider.d.ts +21 -0
  32. package/dist/providers/deterministic-provider.d.ts.map +1 -0
  33. package/dist/providers/deterministic-provider.js +80 -0
  34. package/dist/providers/deterministic-provider.js.map +1 -0
  35. package/dist/providers/provider-client.d.ts +12 -0
  36. package/dist/providers/provider-client.d.ts.map +1 -0
  37. package/dist/providers/provider-client.js +11 -0
  38. package/dist/providers/provider-client.js.map +1 -0
  39. package/dist/runtime/AgentRuntime.d.ts +67 -0
  40. package/dist/runtime/AgentRuntime.d.ts.map +1 -0
  41. package/dist/runtime/AgentRuntime.js +382 -0
  42. package/dist/runtime/AgentRuntime.js.map +1 -0
  43. package/dist/security/secretPatterns.d.ts +76 -0
  44. package/dist/security/secretPatterns.d.ts.map +1 -0
  45. package/dist/security/secretPatterns.js +290 -0
  46. package/dist/security/secretPatterns.js.map +1 -0
  47. package/dist/tools/ask-user-tool.d.ts +19 -0
  48. package/dist/tools/ask-user-tool.d.ts.map +1 -0
  49. package/dist/tools/ask-user-tool.js +148 -0
  50. package/dist/tools/ask-user-tool.js.map +1 -0
  51. package/dist/tools/compactMessages.d.ts +52 -0
  52. package/dist/tools/compactMessages.d.ts.map +1 -0
  53. package/dist/tools/compactMessages.js +158 -0
  54. package/dist/tools/compactMessages.js.map +1 -0
  55. package/dist/tools/core-tools.d.ts +29 -0
  56. package/dist/tools/core-tools.d.ts.map +1 -0
  57. package/dist/tools/core-tools.js +2214 -0
  58. package/dist/tools/core-tools.js.map +1 -0
  59. package/dist/tools/git-tools.d.ts +32 -0
  60. package/dist/tools/git-tools.d.ts.map +1 -0
  61. package/dist/tools/git-tools.js +330 -0
  62. package/dist/tools/git-tools.js.map +1 -0
  63. package/dist/tools/index.d.ts +15 -0
  64. package/dist/tools/index.d.ts.map +1 -0
  65. package/dist/tools/index.js +31 -0
  66. package/dist/tools/index.js.map +1 -0
  67. package/dist/tools/language-adapters.d.ts +48 -0
  68. package/dist/tools/language-adapters.d.ts.map +1 -0
  69. package/dist/tools/language-adapters.js +299 -0
  70. package/dist/tools/language-adapters.js.map +1 -0
  71. package/dist/tools/loop/compactionTrigger.d.ts +47 -0
  72. package/dist/tools/loop/compactionTrigger.d.ts.map +1 -0
  73. package/dist/tools/loop/compactionTrigger.js +32 -0
  74. package/dist/tools/loop/compactionTrigger.js.map +1 -0
  75. package/dist/tools/loop/finalAnswerNudges.d.ts +68 -0
  76. package/dist/tools/loop/finalAnswerNudges.d.ts.map +1 -0
  77. package/dist/tools/loop/finalAnswerNudges.js +87 -0
  78. package/dist/tools/loop/finalAnswerNudges.js.map +1 -0
  79. package/dist/tools/loop/goalAnchor.d.ts +72 -0
  80. package/dist/tools/loop/goalAnchor.d.ts.map +1 -0
  81. package/dist/tools/loop/goalAnchor.js +76 -0
  82. package/dist/tools/loop/goalAnchor.js.map +1 -0
  83. package/dist/tools/loop/llmStream.d.ts +70 -0
  84. package/dist/tools/loop/llmStream.d.ts.map +1 -0
  85. package/dist/tools/loop/llmStream.js +181 -0
  86. package/dist/tools/loop/llmStream.js.map +1 -0
  87. package/dist/tools/loop/parallelExecute.d.ts +57 -0
  88. package/dist/tools/loop/parallelExecute.d.ts.map +1 -0
  89. package/dist/tools/loop/parallelExecute.js +54 -0
  90. package/dist/tools/loop/parallelExecute.js.map +1 -0
  91. package/dist/tools/loop/singleToolExecute.d.ts +71 -0
  92. package/dist/tools/loop/singleToolExecute.d.ts.map +1 -0
  93. package/dist/tools/loop/singleToolExecute.js +139 -0
  94. package/dist/tools/loop/singleToolExecute.js.map +1 -0
  95. package/dist/tools/loop/toolCallNormalize.d.ts +57 -0
  96. package/dist/tools/loop/toolCallNormalize.d.ts.map +1 -0
  97. package/dist/tools/loop/toolCallNormalize.js +99 -0
  98. package/dist/tools/loop/toolCallNormalize.js.map +1 -0
  99. package/dist/tools/loop/turnSetup.d.ts +43 -0
  100. package/dist/tools/loop/turnSetup.d.ts.map +1 -0
  101. package/dist/tools/loop/turnSetup.js +48 -0
  102. package/dist/tools/loop/turnSetup.js.map +1 -0
  103. package/dist/tools/ocr.d.ts +52 -0
  104. package/dist/tools/ocr.d.ts.map +1 -0
  105. package/dist/tools/ocr.js +238 -0
  106. package/dist/tools/ocr.js.map +1 -0
  107. package/dist/tools/post-edit-checks.d.ts +46 -0
  108. package/dist/tools/post-edit-checks.d.ts.map +1 -0
  109. package/dist/tools/post-edit-checks.js +236 -0
  110. package/dist/tools/post-edit-checks.js.map +1 -0
  111. package/dist/tools/skill-loader.d.ts +94 -0
  112. package/dist/tools/skill-loader.d.ts.map +1 -0
  113. package/dist/tools/skill-loader.js +422 -0
  114. package/dist/tools/skill-loader.js.map +1 -0
  115. package/dist/tools/skill-registry.d.ts +44 -0
  116. package/dist/tools/skill-registry.d.ts.map +1 -0
  117. package/dist/tools/skill-registry.js +118 -0
  118. package/dist/tools/skill-registry.js.map +1 -0
  119. package/dist/tools/skill-types.d.ts +38 -0
  120. package/dist/tools/skill-types.d.ts.map +1 -0
  121. package/dist/tools/skill-types.js +10 -0
  122. package/dist/tools/skill-types.js.map +1 -0
  123. package/dist/tools/skills/code-review-skill.d.ts +9 -0
  124. package/dist/tools/skills/code-review-skill.d.ts.map +1 -0
  125. package/dist/tools/skills/code-review-skill.js +66 -0
  126. package/dist/tools/skills/code-review-skill.js.map +1 -0
  127. package/dist/tools/skills/core-skill.d.ts +13 -0
  128. package/dist/tools/skills/core-skill.d.ts.map +1 -0
  129. package/dist/tools/skills/core-skill.js +23 -0
  130. package/dist/tools/skills/core-skill.js.map +1 -0
  131. package/dist/tools/skills/git-skill.d.ts +10 -0
  132. package/dist/tools/skills/git-skill.d.ts.map +1 -0
  133. package/dist/tools/skills/git-skill.js +30 -0
  134. package/dist/tools/skills/git-skill.js.map +1 -0
  135. package/dist/tools/skills/index.d.ts +17 -0
  136. package/dist/tools/skills/index.d.ts.map +1 -0
  137. package/dist/tools/skills/index.js +49 -0
  138. package/dist/tools/skills/index.js.map +1 -0
  139. package/dist/tools/skills/interaction-skill.d.ts +14 -0
  140. package/dist/tools/skills/interaction-skill.d.ts.map +1 -0
  141. package/dist/tools/skills/interaction-skill.js +24 -0
  142. package/dist/tools/skills/interaction-skill.js.map +1 -0
  143. package/dist/tools/skills/mail-search-skill.d.ts +25 -0
  144. package/dist/tools/skills/mail-search-skill.d.ts.map +1 -0
  145. package/dist/tools/skills/mail-search-skill.js +343 -0
  146. package/dist/tools/skills/mail-search-skill.js.map +1 -0
  147. package/dist/tools/skills/plan-skill.d.ts +10 -0
  148. package/dist/tools/skills/plan-skill.d.ts.map +1 -0
  149. package/dist/tools/skills/plan-skill.js +126 -0
  150. package/dist/tools/skills/plan-skill.js.map +1 -0
  151. package/dist/tools/skills/semantic-search-skill.d.ts +22 -0
  152. package/dist/tools/skills/semantic-search-skill.d.ts.map +1 -0
  153. package/dist/tools/skills/semantic-search-skill.js +244 -0
  154. package/dist/tools/skills/semantic-search-skill.js.map +1 -0
  155. package/dist/tools/skills/test-gen-skill.d.ts +9 -0
  156. package/dist/tools/skills/test-gen-skill.d.ts.map +1 -0
  157. package/dist/tools/skills/test-gen-skill.js +123 -0
  158. package/dist/tools/skills/test-gen-skill.js.map +1 -0
  159. package/dist/tools/tool-registry.d.ts +60 -0
  160. package/dist/tools/tool-registry.d.ts.map +1 -0
  161. package/dist/tools/tool-registry.js +200 -0
  162. package/dist/tools/tool-registry.js.map +1 -0
  163. package/dist/tools/tool-types.d.ts +281 -0
  164. package/dist/tools/tool-types.d.ts.map +1 -0
  165. package/dist/tools/tool-types.js +10 -0
  166. package/dist/tools/tool-types.js.map +1 -0
  167. package/dist/tools/tool-use-loop.d.ts +231 -0
  168. package/dist/tools/tool-use-loop.d.ts.map +1 -0
  169. package/dist/tools/tool-use-loop.js +2057 -0
  170. package/dist/tools/tool-use-loop.js.map +1 -0
  171. package/dist/tools/tool-use-parser.d.ts +78 -0
  172. package/dist/tools/tool-use-parser.d.ts.map +1 -0
  173. package/dist/tools/tool-use-parser.js +427 -0
  174. package/dist/tools/tool-use-parser.js.map +1 -0
  175. package/dist/tools/toolAvailabilityDetector.d.ts +48 -0
  176. package/dist/tools/toolAvailabilityDetector.d.ts.map +1 -0
  177. package/dist/tools/toolAvailabilityDetector.js +156 -0
  178. package/dist/tools/toolAvailabilityDetector.js.map +1 -0
  179. package/dist/tools/unified-patch.d.ts +87 -0
  180. package/dist/tools/unified-patch.d.ts.map +1 -0
  181. package/dist/tools/unified-patch.js +217 -0
  182. package/dist/tools/unified-patch.js.map +1 -0
  183. package/dist/types/agent.d.ts +69 -0
  184. package/dist/types/agent.d.ts.map +1 -0
  185. package/dist/types/agent.js +54 -0
  186. package/dist/types/agent.js.map +1 -0
  187. package/dist/types/tasks.d.ts +22 -0
  188. package/dist/types/tasks.d.ts.map +1 -0
  189. package/dist/types/tasks.js +3 -0
  190. package/dist/types/tasks.js.map +1 -0
  191. package/dist/utils/event-emitter.d.ts +13 -0
  192. package/dist/utils/event-emitter.d.ts.map +1 -0
  193. package/dist/utils/event-emitter.js +54 -0
  194. package/dist/utils/event-emitter.js.map +1 -0
  195. package/package.json +33 -0
@@ -0,0 +1,139 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createToolDispatcher = createToolDispatcher;
4
+ const tool_use_parser_1 = require("../tool-use-parser");
5
+ function createToolDispatcher(deps) {
6
+ const { registry, ctx, beforeToolExecute, emit, recentCallKeys, repeatLimit, filesReadThisTurn, filesWrittenThisTurn, isFileEditTool, onEditToolSucceeded } = deps;
7
+ return async function dispatchOne(tc) {
8
+ // Build a coarse signature for repeat detection. For file-writing
9
+ // tools (write_file, apply_edit, replace_range) we key on path + a short hash of
10
+ // the find/replace/content so we catch "same edit retried with
11
+ // same payload" (the original target — model corrupted a write
12
+ // and is looping) WITHOUT flagging "8 different edits to the
13
+ // same file" (legitimate batch refactor — // on S3Api with gpt-oss commenting all methods in one .cs file).
14
+ // For other tools we fall back to a truncated JSON of the params
15
+ // so unrelated calls don't collide.
16
+ const pathish = tc.params.path ?? tc.params.file ?? tc.params.filepath;
17
+ const isEditTool = tc.name === 'apply_edit' || tc.name === 'replace_range' || tc.name === 'write_file';
18
+ let callKey;
19
+ if (pathish && isEditTool) {
20
+ // Cheap deterministic hash of the payload — enough to
21
+ // distinguish 8 different edits to the same file. Not
22
+ // crypto, just collision-resistant for a window of 3 calls.
23
+ const payload = `${tc.params.find ?? ''}::${tc.params.replace ?? ''}::${tc.params.content ?? ''}::${tc.params.start_line ?? ''}::${tc.params.end_line ?? ''}`;
24
+ let h = 0;
25
+ for (let i = 0; i < payload.length; i++) {
26
+ h = (h * 31 + payload.charCodeAt(i)) | 0;
27
+ }
28
+ callKey = `${tc.name}::${pathish}::${h.toString(36)}`;
29
+ }
30
+ else if (pathish) {
31
+ callKey = `${tc.name}::${pathish}`;
32
+ }
33
+ else {
34
+ callKey = `${tc.name}::${JSON.stringify(tc.params).slice(0, 160)}`;
35
+ }
36
+ recentCallKeys.push(callKey);
37
+ if (recentCallKeys.length > repeatLimit)
38
+ recentCallKeys.shift();
39
+ if (recentCallKeys.length === repeatLimit &&
40
+ recentCallKeys.every((k) => k === callKey)) {
41
+ emit('tool_loop:repeat_breaker', { name: tc.name, key: callKey });
42
+ return {
43
+ name: tc.name,
44
+ output: `Loop detected: ${tc.name} has been invoked ${repeatLimit} times in a row against the same target (${pathish ?? 'identical params'}) without progress. This usually means the last write landed malformed — most often an unescaped \`"\` inside the JSON content string truncated the file. STOP retrying. Either (a) produce a final answer that explains the issue to the user, or (b) break the content into smaller edits. Do not call ${tc.name} with these params again.`,
45
+ isError: true
46
+ };
47
+ }
48
+ const tool = registry.get(tc.name);
49
+ if (!tool) {
50
+ emit('tool_loop:tool_not_found', { name: tc.name });
51
+ return { name: tc.name, output: `Error: tool "${tc.name}" is not registered.`, isError: true };
52
+ }
53
+ // Also surface the RAW tool_call block (first 400 chars) so
54
+ // observers can diagnose parser-edge cases. When a param
55
+ // extraction goes wrong (unknown wrapper key, nested array,
56
+ // etc.) the isError result is the symptom; the raw block is
57
+ // the evidence. Without it, debugging an empty-params call
58
+ // requires re-running with extra instrumentation.
59
+ //
60
+ // NOTE: the edit-tool counter is incremented inside the try
61
+ // block below, AFTER the result returns and only when
62
+ // `!result.isError`. Counting attempts (which an earlier
63
+ // version did at this point) made the false-completion
64
+ // detector blind to the worst variant of the hallucination:
65
+ // model fires 8 apply_edits, every single one fails with
66
+ // "find not found", model produces a confident "I have
67
+ // fixed the bug" summary. The counter said 8, the detector
68
+ // saw a non-zero count, the user read the lie. Observed
69
+ // 2026-05-01 on the bandit website's plans.tsx grid bug.
70
+ emit('tool_loop:tool_execute', {
71
+ name: tc.name,
72
+ params: tc.params,
73
+ rawSnippet: tc.raw.slice(0, 400)
74
+ });
75
+ const gate = await beforeToolExecute({ name: tc.name, params: tc.params });
76
+ if (!gate.allow) {
77
+ const reason = gate.reason ?? 'blocked by pre-execute guard';
78
+ emit('tool_loop:tool_blocked', { name: tc.name, reason });
79
+ return { name: tc.name, output: `Blocked: ${reason}`, isError: true };
80
+ }
81
+ try {
82
+ const result = await tool.execute(tc.params, ctx);
83
+ // Only count edits that actually landed. A `find`-not-found
84
+ // or schema-rejected edit returns isError:true and changed
85
+ // nothing on disk; counting it would let the false-
86
+ // completion detector wave through "I have fixed the bug"
87
+ // claims that are false.
88
+ const normalizeFilePath = (raw) => {
89
+ if (typeof raw !== 'string' || !raw)
90
+ return null;
91
+ // Use basename so `src/App.jsx` and `~/proj/src/App.jsx`
92
+ // collide — the user's goal text typically uses the bare
93
+ // filename so a basename comparison is the most forgiving
94
+ // way to ask "did we touch the file the user named?".
95
+ const parts = raw.split(/[/\\]/);
96
+ return parts[parts.length - 1].toLowerCase();
97
+ };
98
+ if (isFileEditTool(tc.name) && !result.isError) {
99
+ onEditToolSucceeded();
100
+ const p = tc.params?.path;
101
+ const norm = normalizeFilePath(p);
102
+ if (norm)
103
+ filesWrittenThisTurn.add(norm);
104
+ }
105
+ if (tc.name === 'read_file' && !result.isError) {
106
+ const p = tc.params?.path;
107
+ const norm = normalizeFilePath(p);
108
+ if (norm)
109
+ filesReadThisTurn.add(norm);
110
+ }
111
+ // Include a short output snippet in the event — critical for
112
+ // downstream observers (eval runner, turn log) to know WHY
113
+ // a tool errored rather than just THAT it did. Capped so long
114
+ // successful results don't flood the event bus.
115
+ //
116
+ // outputSnippet is rendered to the host UI (tool
117
+ // cards in the extension, dim recap in the CLI). Redact
118
+ // before emitting so the user's terminal scrollback doesn't
119
+ // capture raw secrets even when the model's context
120
+ // already has the redacted version. The model-facing path
121
+ // goes through buildToolResultsMessage → formatToolResult,
122
+ // which already applies the same redactor at the parser
123
+ // boundary, so both paths converge on the same masked text.
124
+ emit('tool_loop:tool_result', {
125
+ name: tc.name,
126
+ isError: result.isError,
127
+ outputLength: result.output.length,
128
+ outputSnippet: (0, tool_use_parser_1.applySecretRedactionIfEnabled)(result.output.slice(0, 280))
129
+ });
130
+ return { name: tc.name, output: result.output, isError: result.isError };
131
+ }
132
+ catch (err) {
133
+ const msg = err instanceof Error ? err.message : String(err);
134
+ emit('tool_loop:tool_error', { name: tc.name, error: msg });
135
+ return { name: tc.name, output: `Error executing tool "${tc.name}": ${msg}`, isError: true };
136
+ }
137
+ };
138
+ }
139
+ //# sourceMappingURL=singleToolExecute.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"singleToolExecute.js","sourceRoot":"","sources":["../../../src/tools/loop/singleToolExecute.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Tool-call batch normalization for ToolUseLoop.runWithMessages.
3
+ *
4
+ * After parseToolCalls returns the raw list of `<tool_call>` blocks the
5
+ * model emitted in one iteration, the loop has to:
6
+ *
7
+ * 1. Drop byte-identical duplicate calls (panic-fanout mode emits the
8
+ * same search multiple times — observed 2026-04-26 with gpt-oss:120b
9
+ * on H100 fanning out 25 calls in one iteration, four of them
10
+ * identical `search_code writeInsightsReport`s).
11
+ * 2. Keep at most ONE foreground (synchronous) `task` call per
12
+ * iteration — the model can't make progress on three subagents
13
+ * simultaneously and the wait time stacks.
14
+ * 3. Slice the batch to `maxParallelTools` so a 25-call fanout
15
+ * doesn't drown the next iteration's tool-result payload.
16
+ * 4. Slice further to fit the remaining per-turn budget
17
+ * (`maxTotalTools - totalToolsExecuted`).
18
+ *
19
+ * Each step emits a telemetry event on the loop's `emit` sink so hosts
20
+ * can surface "we dropped N duplicates" / "we capped at N parallel" in
21
+ * the UI. The caller updates `totalToolsExecuted` from the returned
22
+ * `accepted` count.
23
+ *
24
+ * Pure with respect to the loop's mutable state: input is a `ToolCall[]`,
25
+ * output is a new `ToolCall[]` plus drop counts plus the events emitted
26
+ * via the supplied callback. The original input list is not mutated.
27
+ */
28
+ import type { ParsedToolCall } from '../tool-use-parser';
29
+ export type ToolCallNormalizeEmit = (type: string, payload?: unknown) => void;
30
+ export interface NormalizeToolCallBatchArgs {
31
+ /** The raw parsed list straight off this iteration's chat response. */
32
+ toolCalls: ReadonlyArray<ParsedToolCall>;
33
+ /** Iteration index — used as the event `iteration` field. */
34
+ iteration: number;
35
+ /** Max calls executed in parallel within a single iteration. */
36
+ maxParallelTools: number;
37
+ /** Hard cap on tool calls across the full turn. */
38
+ maxTotalTools: number;
39
+ /** Running total of tools executed prior to this iteration. */
40
+ totalToolsExecuted: number;
41
+ /** Event sink (typically the loop's `emit`). */
42
+ emit: ToolCallNormalizeEmit;
43
+ }
44
+ export interface NormalizeToolCallBatchResult {
45
+ /** The final list of calls to execute this iteration. */
46
+ accepted: ParsedToolCall[];
47
+ /** Byte-identical duplicates removed. */
48
+ dedupedCount: number;
49
+ /** Excess foreground `task` calls dropped (kept at most one). */
50
+ droppedForegroundTaskCalls: number;
51
+ /** Calls dropped because the batch exceeded `maxParallelTools`. */
52
+ droppedParallelCap: number;
53
+ /** Calls dropped because the batch would exceed the per-turn cap. */
54
+ droppedTotalCap: number;
55
+ }
56
+ export declare function normalizeToolCallBatch(args: NormalizeToolCallBatchArgs): NormalizeToolCallBatchResult;
57
+ //# sourceMappingURL=toolCallNormalize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toolCallNormalize.d.ts","sourceRoot":"","sources":["../../../src/tools/loop/toolCallNormalize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEzD,MAAM,MAAM,qBAAqB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;AAE9E,MAAM,WAAW,0BAA0B;IACzC,uEAAuE;IACvE,SAAS,EAAE,aAAa,CAAC,cAAc,CAAC,CAAC;IACzC,6DAA6D;IAC7D,SAAS,EAAE,MAAM,CAAC;IAClB,gEAAgE;IAChE,gBAAgB,EAAE,MAAM,CAAC;IACzB,mDAAmD;IACnD,aAAa,EAAE,MAAM,CAAC;IACtB,+DAA+D;IAC/D,kBAAkB,EAAE,MAAM,CAAC;IAC3B,gDAAgD;IAChD,IAAI,EAAE,qBAAqB,CAAC;CAC7B;AAED,MAAM,WAAW,4BAA4B;IAC3C,yDAAyD;IACzD,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,yCAAyC;IACzC,YAAY,EAAE,MAAM,CAAC;IACrB,iEAAiE;IACjE,0BAA0B,EAAE,MAAM,CAAC;IACnC,mEAAmE;IACnE,kBAAkB,EAAE,MAAM,CAAC;IAC3B,qEAAqE;IACrE,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,0BAA0B,GAAG,4BAA4B,CAoGrG"}
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normalizeToolCallBatch = normalizeToolCallBatch;
4
+ function normalizeToolCallBatch(args) {
5
+ const { iteration, maxParallelTools, maxTotalTools, totalToolsExecuted, emit } = args;
6
+ let accepted = [...args.toolCalls];
7
+ // 1. Byte-identical dedup. `${name}::${JSON.stringify(params)}` is the
8
+ // signature. Only runs when there are 2+ calls — single-call iterations
9
+ // can't contain a duplicate of themselves.
10
+ let dedupedCount = 0;
11
+ if (accepted.length > 1) {
12
+ const seen = new Set();
13
+ const deduped = [];
14
+ for (const tc of accepted) {
15
+ const sig = `${tc.name}::${JSON.stringify(tc.params)}`;
16
+ if (seen.has(sig)) {
17
+ dedupedCount++;
18
+ continue;
19
+ }
20
+ seen.add(sig);
21
+ deduped.push(tc);
22
+ }
23
+ if (dedupedCount > 0) {
24
+ emit('tool_loop:tool_call_deduped', {
25
+ iteration,
26
+ removed: dedupedCount,
27
+ kept: deduped.length
28
+ });
29
+ }
30
+ accepted = deduped;
31
+ }
32
+ // 2. Foreground-task fanout cap. A `task` call with
33
+ // run_in_background != 'true' blocks the parent iteration on the
34
+ // subagent's completion; running multiple in one iteration stacks
35
+ // serially. Keep the first foreground task, drop the rest. Background
36
+ // tasks are unaffected — they're fire-and-forget.
37
+ let droppedForegroundTaskCalls = 0;
38
+ if (accepted.length > 1) {
39
+ let keptForegroundTask = false;
40
+ const scoped = [];
41
+ for (const tc of accepted) {
42
+ const isForegroundTask = tc.name === 'task' &&
43
+ String(tc.params.run_in_background ?? '').toLowerCase() !== 'true';
44
+ if (!isForegroundTask) {
45
+ scoped.push(tc);
46
+ continue;
47
+ }
48
+ if (!keptForegroundTask) {
49
+ scoped.push(tc);
50
+ keptForegroundTask = true;
51
+ continue;
52
+ }
53
+ droppedForegroundTaskCalls++;
54
+ }
55
+ if (droppedForegroundTaskCalls > 0) {
56
+ emit('tool_loop:foreground_task_fanout_capped', {
57
+ iteration,
58
+ kept: 1,
59
+ dropped: droppedForegroundTaskCalls
60
+ });
61
+ accepted = scoped;
62
+ }
63
+ }
64
+ // 3. Per-iteration parallel cap.
65
+ let droppedParallelCap = 0;
66
+ if (accepted.length > maxParallelTools) {
67
+ droppedParallelCap = accepted.length - maxParallelTools;
68
+ emit('tool_loop:tool_call_capped', {
69
+ iteration,
70
+ requested: accepted.length + 0,
71
+ kept: maxParallelTools,
72
+ dropped: droppedParallelCap
73
+ });
74
+ accepted = accepted.slice(0, maxParallelTools);
75
+ }
76
+ // 4. Per-turn total cap. Slice to fit remaining budget; the next
77
+ // iteration short-circuits on the count check.
78
+ let droppedTotalCap = 0;
79
+ const remainingBudget = Math.max(0, maxTotalTools - totalToolsExecuted);
80
+ if (accepted.length > remainingBudget) {
81
+ droppedTotalCap = accepted.length - remainingBudget;
82
+ emit('tool_loop:tool_call_total_capped', {
83
+ iteration,
84
+ requested: accepted.length,
85
+ kept: remainingBudget,
86
+ totalSoFar: totalToolsExecuted,
87
+ maxTotalTools
88
+ });
89
+ accepted = accepted.slice(0, remainingBudget);
90
+ }
91
+ return {
92
+ accepted,
93
+ dedupedCount,
94
+ droppedForegroundTaskCalls,
95
+ droppedParallelCap,
96
+ droppedTotalCap
97
+ };
98
+ }
99
+ //# sourceMappingURL=toolCallNormalize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toolCallNormalize.js","sourceRoot":"","sources":["../../../src/tools/loop/toolCallNormalize.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * One-shot setup helpers for ToolUseLoop.runWithMessages — work that
3
+ * runs once per turn BEFORE the iteration loop.
4
+ *
5
+ * Current contents: `resolveTurnGoal`. Future Session 1/2/3 extractions
6
+ * (system-prompt assembly, native-tools schema build, counter init)
7
+ * will land here too. Kept under `loop/` so the orchestrator imports
8
+ * stay grouped with the other Arc 3 modules.
9
+ */
10
+ import type { ToolLoopMessage } from '../tool-types';
11
+ export interface ResolveTurnGoalArgs {
12
+ seedMessages: ReadonlyArray<ToolLoopMessage>;
13
+ }
14
+ export interface ResolvedTurnGoal {
15
+ /** The user message that anchors THIS turn — what the model is being
16
+ * asked to do right now. Used by the goal-anchor reminder injected
17
+ * before final-answer iterations to defeat recency bias from long
18
+ * tool-result chains. Empty string when the seed has no user message. */
19
+ originalGoal: string;
20
+ /** Count of earlier user prompts in the seed history (everything
21
+ * before the most-recent substantive one). Used by the goal-anchor
22
+ * injection to add an "ignore prior prompts" note when there are
23
+ * earlier conversation turns the model might confuse for the goal. */
24
+ priorUserPromptCount: number;
25
+ }
26
+ /**
27
+ * Resolve the per-turn goal anchor from the seed message history.
28
+ *
29
+ * Walks the seed messages forward to find the most-recent user prompt.
30
+ * If that prompt is a bare continuation token ("keep going", "yes",
31
+ * "good lets keep going" — see CONTINUATION_PROMPT_PHRASES in
32
+ * tool-use-loop.ts), walks BACKWARD through history for the most
33
+ * recent SUBSTANTIVE prompt and anchors on that instead.
34
+ *
35
+ * Why the walkback: the original bug was a 60-iteration linter-fix
36
+ * turn that anchored every iteration on "good lets keep going"
37
+ * because that was the literal last user message. The recall block
38
+ * became "remind yourself to keep going" and gave the model zero
39
+ * useful steering. Walking back finds the real goal ("fix the
40
+ * remaining TS errors") and uses THAT as the anchor.
41
+ */
42
+ export declare function resolveTurnGoal(args: ResolveTurnGoalArgs): ResolvedTurnGoal;
43
+ //# sourceMappingURL=turnSetup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"turnSetup.d.ts","sourceRoot":"","sources":["../../../src/tools/loop/turnSetup.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAGrD,MAAM,WAAW,mBAAmB;IAClC,YAAY,EAAE,aAAa,CAAC,eAAe,CAAC,CAAC;CAC9C;AAED,MAAM,WAAW,gBAAgB;IAC/B;;;6EAGyE;IACzE,YAAY,EAAE,MAAM,CAAC;IACrB;;;0EAGsE;IACtE,oBAAoB,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,mBAAmB,GAAG,gBAAgB,CAuB3E"}
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveTurnGoal = resolveTurnGoal;
4
+ const tool_use_loop_1 = require("../tool-use-loop");
5
+ /**
6
+ * Resolve the per-turn goal anchor from the seed message history.
7
+ *
8
+ * Walks the seed messages forward to find the most-recent user prompt.
9
+ * If that prompt is a bare continuation token ("keep going", "yes",
10
+ * "good lets keep going" — see CONTINUATION_PROMPT_PHRASES in
11
+ * tool-use-loop.ts), walks BACKWARD through history for the most
12
+ * recent SUBSTANTIVE prompt and anchors on that instead.
13
+ *
14
+ * Why the walkback: the original bug was a 60-iteration linter-fix
15
+ * turn that anchored every iteration on "good lets keep going"
16
+ * because that was the literal last user message. The recall block
17
+ * became "remind yourself to keep going" and gave the model zero
18
+ * useful steering. Walking back finds the real goal ("fix the
19
+ * remaining TS errors") and uses THAT as the anchor.
20
+ */
21
+ function resolveTurnGoal(args) {
22
+ const { seedMessages } = args;
23
+ let originalGoal = '';
24
+ let priorUserPromptCount = 0;
25
+ for (const msg of seedMessages) {
26
+ if (msg.role === 'user' && typeof msg.content === 'string' && msg.content.trim()) {
27
+ if (originalGoal)
28
+ priorUserPromptCount++;
29
+ originalGoal = msg.content;
30
+ }
31
+ }
32
+ if (originalGoal && (0, tool_use_loop_1.isContinuationPrompt)(originalGoal)) {
33
+ for (let i = seedMessages.length - 1; i >= 0; i--) {
34
+ const m = seedMessages[i];
35
+ if (m.role !== 'user' || typeof m.content !== 'string')
36
+ continue;
37
+ const c = m.content.trim();
38
+ if (!c)
39
+ continue;
40
+ if (!(0, tool_use_loop_1.isContinuationPrompt)(c)) {
41
+ originalGoal = m.content;
42
+ break;
43
+ }
44
+ }
45
+ }
46
+ return { originalGoal, priorUserPromptCount };
47
+ }
48
+ //# sourceMappingURL=turnSetup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"turnSetup.js","sourceRoot":"","sources":["../../../src/tools/loop/turnSetup.ts"],"names":[],"mappings":";;AA6CA,0CAuBC;AA1DD,oDAAwD;AAmBxD;;;;;;;;;;;;;;;GAeG;AACH,SAAgB,eAAe,CAAC,IAAyB;IACvD,MAAM,EAAE,YAAY,EAAE,GAAG,IAAI,CAAC;IAC9B,IAAI,YAAY,GAAG,EAAE,CAAC;IACtB,IAAI,oBAAoB,GAAG,CAAC,CAAC;IAC7B,KAAK,MAAM,GAAG,IAAI,YAAY,EAAE,CAAC;QAC/B,IAAI,GAAG,CAAC,IAAI,KAAK,MAAM,IAAI,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,IAAI,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC;YACjF,IAAI,YAAY;gBAAE,oBAAoB,EAAE,CAAC;YACzC,YAAY,GAAG,GAAG,CAAC,OAAO,CAAC;QAC7B,CAAC;IACH,CAAC;IACD,IAAI,YAAY,IAAI,IAAA,oCAAoB,EAAC,YAAY,CAAC,EAAE,CAAC;QACvD,KAAK,IAAI,CAAC,GAAG,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YAClD,MAAM,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC;YAC1B,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ;gBAAE,SAAS;YACjE,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;YAC3B,IAAI,CAAC,CAAC;gBAAE,SAAS;YACjB,IAAI,CAAC,IAAA,oCAAoB,EAAC,CAAC,CAAC,EAAE,CAAC;gBAC7B,YAAY,GAAG,CAAC,CAAC,OAAO,CAAC;gBACzB,MAAM;YACR,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,CAAC;AAChD,CAAC"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Local OCR — extract printed text from an image file without an LLM.
3
+ *
4
+ * Dispatcher picks the best available engine for the platform:
5
+ * macOS → Apple Vision via `swift` running a VNRecognizeTextRequest
6
+ * script. ~100-300ms per image, excellent on rendered text
7
+ * (code, logs, stack traces, dialogs).
8
+ * Linux → tesseract CLI (`apt install tesseract-ocr`). ~500ms-2s.
9
+ * Windows → tesseract CLI (PowerShell.Windows.Media.Ocr could be
10
+ * added later; tesseract covers the common case today).
11
+ *
12
+ * Used by the extension/CLI on bandit-logic turns where a user paste
13
+ * an image. When OCR yields usable text, we inline it into the prompt
14
+ * as an `[Image text (OCR): …]` block and skip the model swap. When
15
+ * OCR returns nothing (diagrams, photos, blurry), we fall back to a
16
+ * vision-capable model for that turn.
17
+ *
18
+ * Binary OCR engines themselves are not bundled — we shell out to what
19
+ * the user's OS already ships. Linux/Windows users see a one-time
20
+ * install hint when tesseract is missing.
21
+ */
22
+ export interface OcrResult {
23
+ /** Extracted text. May be empty if OCR produced no confident output. */
24
+ text: string;
25
+ /** Engine that produced the text. Useful for telemetry. */
26
+ engine: 'apple-vision' | 'tesseract' | 'windows-ocr' | 'none';
27
+ /** Wall-clock time the OCR call took. */
28
+ durationMs: number;
29
+ }
30
+ export interface OcrOptions {
31
+ /** Abort the OCR run after this many ms. Defaults to 8s — catches
32
+ * a Vision hang without starving a long but legitimate recognition. */
33
+ timeoutMs?: number;
34
+ }
35
+ export declare function detectOcrAvailability(): {
36
+ apple: boolean;
37
+ tesseract: boolean;
38
+ };
39
+ /**
40
+ * Extract text from an image at `imagePath`. Returns an OcrResult with
41
+ * empty `.text` on failure rather than throwing — callers treat "empty
42
+ * text" as the signal to fall back to an LLM-vision path.
43
+ */
44
+ export declare function extractImageText(imagePath: string, options?: OcrOptions): Promise<OcrResult>;
45
+ /**
46
+ * Heuristic for "the OCR captured enough to be useful". Used by the
47
+ * image-handling path to decide between "inline OCR text + stay on
48
+ * current model" vs "fall back to a vision-capable model". Very cheap;
49
+ * runs once per image.
50
+ */
51
+ export declare function ocrYieldedUsefulText(text: string): boolean;
52
+ //# sourceMappingURL=ocr.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ocr.d.ts","sourceRoot":"","sources":["../../src/tools/ocr.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAOH,MAAM,WAAW,SAAS;IACxB,wEAAwE;IACxE,IAAI,EAAE,MAAM,CAAC;IACb,2DAA2D;IAC3D,MAAM,EAAE,cAAc,GAAG,WAAW,GAAG,aAAa,GAAG,MAAM,CAAC;IAC9D,yCAAyC;IACzC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,UAAU;IACzB;4EACwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AASD,wBAAgB,qBAAqB,IAAI;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAM9E;AAWD;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,CAyBlG;AAwGD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAS1D"}