@datename/fastcode-termux 0.0.0-stage → 0.1.6-beta

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 (347) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +418 -2
  3. package/bin/fastcode.mjs +59 -0
  4. package/dist/agent/approval.d.ts +50 -0
  5. package/dist/agent/approval.js +7 -0
  6. package/dist/agent/compaction.d.ts +39 -0
  7. package/dist/agent/compaction.js +227 -0
  8. package/dist/agent/context.d.ts +29 -0
  9. package/dist/agent/context.js +62 -0
  10. package/dist/agent/guards.d.ts +43 -0
  11. package/dist/agent/guards.js +269 -0
  12. package/dist/agent/ignore.d.ts +11 -0
  13. package/dist/agent/ignore.js +129 -0
  14. package/dist/agent/limits.d.ts +24 -0
  15. package/dist/agent/limits.js +71 -0
  16. package/dist/agent/process-tree.d.ts +2 -0
  17. package/dist/agent/process-tree.js +31 -0
  18. package/dist/agent/protocol-mode.d.ts +6 -0
  19. package/dist/agent/protocol-mode.js +51 -0
  20. package/dist/agent/protocol.d.ts +4 -0
  21. package/dist/agent/protocol.js +129 -0
  22. package/dist/agent/registry.d.ts +17 -0
  23. package/dist/agent/registry.js +90 -0
  24. package/dist/agent/runner.d.ts +106 -0
  25. package/dist/agent/runner.js +352 -0
  26. package/dist/agent/shell-session.d.ts +37 -0
  27. package/dist/agent/shell-session.js +400 -0
  28. package/dist/agent/stream.d.ts +29 -0
  29. package/dist/agent/stream.js +103 -0
  30. package/dist/agent/tool-schemas.d.ts +2 -0
  31. package/dist/agent/tool-schemas.js +63 -0
  32. package/dist/agent/tools/append_file.d.ts +2 -0
  33. package/dist/agent/tools/append_file.js +26 -0
  34. package/dist/agent/tools/apply_patch.d.ts +10 -0
  35. package/dist/agent/tools/apply_patch.js +52 -0
  36. package/dist/agent/tools/copy_file.d.ts +2 -0
  37. package/dist/agent/tools/copy_file.js +30 -0
  38. package/dist/agent/tools/delete_file.d.ts +2 -0
  39. package/dist/agent/tools/delete_file.js +24 -0
  40. package/dist/agent/tools/grep.d.ts +2 -0
  41. package/dist/agent/tools/grep.js +23 -0
  42. package/dist/agent/tools/index.d.ts +2 -0
  43. package/dist/agent/tools/index.js +32 -0
  44. package/dist/agent/tools/list_files.d.ts +2 -0
  45. package/dist/agent/tools/list_files.js +17 -0
  46. package/dist/agent/tools/make_dir.d.ts +2 -0
  47. package/dist/agent/tools/make_dir.js +24 -0
  48. package/dist/agent/tools/move_path.d.ts +2 -0
  49. package/dist/agent/tools/move_path.js +47 -0
  50. package/dist/agent/tools/paths.d.ts +7 -0
  51. package/dist/agent/tools/paths.js +84 -0
  52. package/dist/agent/tools/pwd.d.ts +2 -0
  53. package/dist/agent/tools/pwd.js +10 -0
  54. package/dist/agent/tools/read_file.d.ts +2 -0
  55. package/dist/agent/tools/read_file.js +21 -0
  56. package/dist/agent/tools/read_many.d.ts +2 -0
  57. package/dist/agent/tools/read_many.js +25 -0
  58. package/dist/agent/tools/readers.d.ts +6 -0
  59. package/dist/agent/tools/readers.js +216 -0
  60. package/dist/agent/tools/replace_in_file.d.ts +2 -0
  61. package/dist/agent/tools/replace_in_file.js +30 -0
  62. package/dist/agent/tools/shell.d.ts +2 -0
  63. package/dist/agent/tools/shell.js +48 -0
  64. package/dist/agent/tools/stat_path.d.ts +2 -0
  65. package/dist/agent/tools/stat_path.js +31 -0
  66. package/dist/agent/tools/types.d.ts +39 -0
  67. package/dist/agent/tools/types.js +1 -0
  68. package/dist/agent/tools/validate.d.ts +6 -0
  69. package/dist/agent/tools/validate.js +29 -0
  70. package/dist/agent/tools/write_file.d.ts +2 -0
  71. package/dist/agent/tools/write_file.js +26 -0
  72. package/dist/agent/tools.d.ts +3 -0
  73. package/dist/agent/tools.js +2 -0
  74. package/dist/app.d.ts +4 -0
  75. package/dist/app.js +708 -0
  76. package/dist/cli.d.ts +1 -0
  77. package/dist/cli.js +569 -0
  78. package/dist/commands/about.d.ts +8 -0
  79. package/dist/commands/about.js +16 -0
  80. package/dist/commands/agent.d.ts +11 -0
  81. package/dist/commands/agent.js +18 -0
  82. package/dist/commands/attach.d.ts +9 -0
  83. package/dist/commands/attach.js +15 -0
  84. package/dist/commands/bg.d.ts +7 -0
  85. package/dist/commands/bg.js +9 -0
  86. package/dist/commands/cancel.d.ts +10 -0
  87. package/dist/commands/cancel.js +10 -0
  88. package/dist/commands/clear.d.ts +11 -0
  89. package/dist/commands/clear.js +9 -0
  90. package/dist/commands/compact.d.ts +10 -0
  91. package/dist/commands/compact.js +22 -0
  92. package/dist/commands/config.d.ts +7 -0
  93. package/dist/commands/config.js +24 -0
  94. package/dist/commands/context.d.ts +7 -0
  95. package/dist/commands/context.js +24 -0
  96. package/dist/commands/cost.d.ts +9 -0
  97. package/dist/commands/cost.js +13 -0
  98. package/dist/commands/cwd.d.ts +7 -0
  99. package/dist/commands/cwd.js +7 -0
  100. package/dist/commands/edit.d.ts +10 -0
  101. package/dist/commands/edit.js +36 -0
  102. package/dist/commands/effort.d.ts +25 -0
  103. package/dist/commands/effort.js +48 -0
  104. package/dist/commands/exit.d.ts +9 -0
  105. package/dist/commands/exit.js +8 -0
  106. package/dist/commands/expand.d.ts +10 -0
  107. package/dist/commands/expand.js +8 -0
  108. package/dist/commands/help.d.ts +6 -0
  109. package/dist/commands/help.js +18 -0
  110. package/dist/commands/index.d.ts +9 -0
  111. package/dist/commands/index.js +80 -0
  112. package/dist/commands/jobs.d.ts +7 -0
  113. package/dist/commands/jobs.js +16 -0
  114. package/dist/commands/kill.d.ts +7 -0
  115. package/dist/commands/kill.js +11 -0
  116. package/dist/commands/login.d.ts +18 -0
  117. package/dist/commands/login.js +63 -0
  118. package/dist/commands/logout.d.ts +9 -0
  119. package/dist/commands/logout.js +45 -0
  120. package/dist/commands/model.d.ts +24 -0
  121. package/dist/commands/model.js +50 -0
  122. package/dist/commands/plugin.d.ts +11 -0
  123. package/dist/commands/plugin.js +74 -0
  124. package/dist/commands/provider.d.ts +24 -0
  125. package/dist/commands/provider.js +39 -0
  126. package/dist/commands/reset.d.ts +9 -0
  127. package/dist/commands/reset.js +13 -0
  128. package/dist/commands/skills.d.ts +11 -0
  129. package/dist/commands/skills.js +106 -0
  130. package/dist/commands/steer.d.ts +11 -0
  131. package/dist/commands/steer.js +13 -0
  132. package/dist/commands/system.d.ts +11 -0
  133. package/dist/commands/system.js +20 -0
  134. package/dist/commands/theme.d.ts +11 -0
  135. package/dist/commands/theme.js +22 -0
  136. package/dist/commands/tools.d.ts +7 -0
  137. package/dist/commands/tools.js +29 -0
  138. package/dist/commands/whoami.d.ts +9 -0
  139. package/dist/commands/whoami.js +52 -0
  140. package/dist/config/paths.d.ts +4 -0
  141. package/dist/config/paths.js +59 -0
  142. package/dist/config/store.d.ts +89 -0
  143. package/dist/config/store.js +260 -0
  144. package/dist/config/themes.d.ts +73 -0
  145. package/dist/config/themes.js +77 -0
  146. package/dist/config/trust.d.ts +22 -0
  147. package/dist/config/trust.js +153 -0
  148. package/dist/index.d.ts +1 -0
  149. package/dist/index.js +31 -0
  150. package/dist/lib/diagnostics.d.ts +66 -0
  151. package/dist/lib/diagnostics.js +195 -0
  152. package/dist/lib/http.d.ts +23 -0
  153. package/dist/lib/http.js +128 -0
  154. package/dist/lib/markdown.d.ts +1 -0
  155. package/dist/lib/markdown.js +32 -0
  156. package/dist/lib/model-meta.d.ts +19 -0
  157. package/dist/lib/model-meta.js +113 -0
  158. package/dist/lib/native-tools.d.ts +12 -0
  159. package/dist/lib/native-tools.js +21 -0
  160. package/dist/lib/net.d.ts +33 -0
  161. package/dist/lib/net.js +168 -0
  162. package/dist/lib/pkg.d.ts +5 -0
  163. package/dist/lib/pkg.js +27 -0
  164. package/dist/lib/platform.d.ts +7 -0
  165. package/dist/lib/platform.js +57 -0
  166. package/dist/lib/startup.d.ts +6 -0
  167. package/dist/lib/startup.js +25 -0
  168. package/dist/lib/uiMode.d.ts +2 -0
  169. package/dist/lib/uiMode.js +7 -0
  170. package/dist/patch/apply.d.ts +15 -0
  171. package/dist/patch/apply.js +172 -0
  172. package/dist/patch/parse.d.ts +29 -0
  173. package/dist/patch/parse.js +112 -0
  174. package/dist/patch/plan.d.ts +49 -0
  175. package/dist/patch/plan.js +273 -0
  176. package/dist/plugins/manager.d.ts +23 -0
  177. package/dist/plugins/manager.js +101 -0
  178. package/dist/providers/anthropic.d.ts +22 -0
  179. package/dist/providers/anthropic.js +119 -0
  180. package/dist/providers/custom.d.ts +33 -0
  181. package/dist/providers/custom.js +25 -0
  182. package/dist/providers/deepseek.d.ts +34 -0
  183. package/dist/providers/deepseek.js +16 -0
  184. package/dist/providers/google.d.ts +33 -0
  185. package/dist/providers/google.js +19 -0
  186. package/dist/providers/groq.d.ts +34 -0
  187. package/dist/providers/groq.js +21 -0
  188. package/dist/providers/index.d.ts +10 -0
  189. package/dist/providers/index.js +67 -0
  190. package/dist/providers/mistral.d.ts +34 -0
  191. package/dist/providers/mistral.js +20 -0
  192. package/dist/providers/moonshot.d.ts +34 -0
  193. package/dist/providers/moonshot.js +23 -0
  194. package/dist/providers/ollama.d.ts +33 -0
  195. package/dist/providers/ollama.js +22 -0
  196. package/dist/providers/openai.d.ts +34 -0
  197. package/dist/providers/openai.js +26 -0
  198. package/dist/providers/openaiCompatible.d.ts +26 -0
  199. package/dist/providers/openaiCompatible.js +158 -0
  200. package/dist/providers/openrouter.d.ts +34 -0
  201. package/dist/providers/openrouter.js +27 -0
  202. package/dist/providers/qwen.d.ts +34 -0
  203. package/dist/providers/qwen.js +24 -0
  204. package/dist/providers/xai.d.ts +34 -0
  205. package/dist/providers/xai.js +19 -0
  206. package/dist/providers/zhipu.d.ts +34 -0
  207. package/dist/providers/zhipu.js +24 -0
  208. package/dist/session/log.d.ts +52 -0
  209. package/dist/session/log.js +331 -0
  210. package/dist/session/projection.d.ts +43 -0
  211. package/dist/session/projection.js +257 -0
  212. package/dist/skills/manager.d.ts +18 -0
  213. package/dist/skills/manager.js +201 -0
  214. package/dist/skills/parser.d.ts +18 -0
  215. package/dist/skills/parser.js +65 -0
  216. package/dist/types.d.ts +205 -0
  217. package/dist/types.js +1 -0
  218. package/dist/ui/Banner.d.ts +5 -0
  219. package/dist/ui/Banner.js +65 -0
  220. package/dist/ui/Message.d.ts +1 -0
  221. package/dist/ui/Message.js +24 -0
  222. package/dist/ui/Picker.d.ts +8 -0
  223. package/dist/ui/Picker.js +24 -0
  224. package/dist/ui/Prompt.d.ts +9 -0
  225. package/dist/ui/Prompt.js +38 -0
  226. package/dist/ui/Spinner.d.ts +5 -0
  227. package/dist/ui/Spinner.js +39 -0
  228. package/dist/ui/StatusBar.d.ts +11 -0
  229. package/dist/ui/StatusBar.js +20 -0
  230. package/dist/ui/TrustGate.d.ts +5 -0
  231. package/dist/ui/TrustGate.js +26 -0
  232. package/package.json +96 -4
  233. package/src/agent/approval.ts +47 -0
  234. package/src/agent/compaction.ts +276 -0
  235. package/src/agent/context.ts +97 -0
  236. package/src/agent/guards.ts +270 -0
  237. package/src/agent/ignore.ts +147 -0
  238. package/src/agent/limits.ts +71 -0
  239. package/src/agent/process-tree.ts +32 -0
  240. package/src/agent/protocol-mode.ts +59 -0
  241. package/src/agent/protocol.ts +129 -0
  242. package/src/agent/registry.ts +98 -0
  243. package/src/agent/runner.ts +443 -0
  244. package/src/agent/shell-session.ts +449 -0
  245. package/src/agent/stream.ts +143 -0
  246. package/src/agent/tool-schemas.ts +67 -0
  247. package/src/agent/tools/append_file.ts +28 -0
  248. package/src/agent/tools/apply_patch.ts +54 -0
  249. package/src/agent/tools/copy_file.ts +32 -0
  250. package/src/agent/tools/delete_file.ts +26 -0
  251. package/src/agent/tools/grep.ts +25 -0
  252. package/src/agent/tools/index.ts +34 -0
  253. package/src/agent/tools/list_files.ts +19 -0
  254. package/src/agent/tools/make_dir.ts +26 -0
  255. package/src/agent/tools/move_path.ts +50 -0
  256. package/src/agent/tools/paths.ts +92 -0
  257. package/src/agent/tools/pwd.ts +12 -0
  258. package/src/agent/tools/read_file.ts +23 -0
  259. package/src/agent/tools/read_many.ts +27 -0
  260. package/src/agent/tools/readers.ts +228 -0
  261. package/src/agent/tools/replace_in_file.ts +32 -0
  262. package/src/agent/tools/shell.ts +53 -0
  263. package/src/agent/tools/stat_path.ts +33 -0
  264. package/src/agent/tools/types.ts +42 -0
  265. package/src/agent/tools/validate.ts +35 -0
  266. package/src/agent/tools/write_file.ts +28 -0
  267. package/src/agent/tools.ts +3 -0
  268. package/src/app.ts +708 -0
  269. package/src/cli.ts +335 -0
  270. package/src/commands/about.ts +16 -0
  271. package/src/commands/agent.ts +18 -0
  272. package/src/commands/attach.ts +16 -0
  273. package/src/commands/bg.ts +9 -0
  274. package/src/commands/cancel.ts +10 -0
  275. package/src/commands/clear.ts +9 -0
  276. package/src/commands/compact.ts +23 -0
  277. package/src/commands/config.ts +24 -0
  278. package/src/commands/context.ts +25 -0
  279. package/src/commands/cost.ts +13 -0
  280. package/src/commands/cwd.ts +7 -0
  281. package/src/commands/edit.ts +37 -0
  282. package/src/commands/effort.ts +49 -0
  283. package/src/commands/exit.ts +8 -0
  284. package/src/commands/expand.ts +8 -0
  285. package/src/commands/help.ts +18 -0
  286. package/src/commands/index.ts +80 -0
  287. package/src/commands/jobs.ts +17 -0
  288. package/src/commands/kill.ts +12 -0
  289. package/src/commands/login.ts +63 -0
  290. package/src/commands/logout.ts +45 -0
  291. package/src/commands/model.ts +50 -0
  292. package/src/commands/plugin.ts +74 -0
  293. package/src/commands/provider.ts +39 -0
  294. package/src/commands/reset.ts +13 -0
  295. package/src/commands/skills.ts +106 -0
  296. package/src/commands/steer.ts +13 -0
  297. package/src/commands/system.ts +20 -0
  298. package/src/commands/theme.ts +22 -0
  299. package/src/commands/tools.ts +29 -0
  300. package/src/commands/whoami.ts +52 -0
  301. package/src/config/paths.ts +59 -0
  302. package/src/config/store.ts +293 -0
  303. package/src/config/themes.ts +77 -0
  304. package/src/config/trust.ts +183 -0
  305. package/src/index.ts +31 -0
  306. package/src/lib/diagnostics.ts +195 -0
  307. package/src/lib/http.ts +132 -0
  308. package/src/lib/markdown.ts +32 -0
  309. package/src/lib/model-meta.ts +138 -0
  310. package/src/lib/native-tools.ts +32 -0
  311. package/src/lib/net.ts +204 -0
  312. package/src/lib/pkg.ts +35 -0
  313. package/src/lib/platform.ts +57 -0
  314. package/src/lib/startup.ts +25 -0
  315. package/src/lib/uiMode.ts +7 -0
  316. package/src/patch/apply.ts +194 -0
  317. package/src/patch/parse.ts +131 -0
  318. package/src/patch/plan.ts +317 -0
  319. package/src/plugins/manager.ts +101 -0
  320. package/src/providers/anthropic.ts +119 -0
  321. package/src/providers/custom.ts +25 -0
  322. package/src/providers/deepseek.ts +16 -0
  323. package/src/providers/google.ts +19 -0
  324. package/src/providers/groq.ts +21 -0
  325. package/src/providers/index.ts +68 -0
  326. package/src/providers/mistral.ts +20 -0
  327. package/src/providers/moonshot.ts +23 -0
  328. package/src/providers/ollama.ts +22 -0
  329. package/src/providers/openai.ts +26 -0
  330. package/src/providers/openaiCompatible.ts +158 -0
  331. package/src/providers/openrouter.ts +27 -0
  332. package/src/providers/qwen.ts +24 -0
  333. package/src/providers/xai.ts +19 -0
  334. package/src/providers/zhipu.ts +24 -0
  335. package/src/session/log.ts +374 -0
  336. package/src/session/projection.ts +308 -0
  337. package/src/skills/manager.ts +201 -0
  338. package/src/skills/parser.ts +65 -0
  339. package/src/types.ts +205 -0
  340. package/src/ui/Banner.ts +68 -0
  341. package/src/ui/Message.ts +24 -0
  342. package/src/ui/Picker.ts +24 -0
  343. package/src/ui/Prompt.ts +38 -0
  344. package/src/ui/Spinner.ts +39 -0
  345. package/src/ui/StatusBar.ts +20 -0
  346. package/src/ui/TrustGate.ts +26 -0
  347. package/tsconfig.json +22 -0
@@ -0,0 +1,352 @@
1
+ import { createHash } from "node:crypto";
2
+ import { readFileSync, statSync } from "node:fs";
3
+ import { estMessagesTokens, estTokens, getModelMeta } from "../lib/model-meta.js";
4
+ import { compactMessages, DEFAULT_COMPACTION_CONFIG } from "./compaction.js";
5
+ import { isLoopExempt, isReadOnlyToolCall, toolCallFingerprint } from "./guards.js";
6
+ import { isNativeUnsupportedError } from "./protocol-mode.js";
7
+ import { formatToolResults, parseToolCalls, stripToolCalls } from "./protocol.js";
8
+ import { runStream } from "./stream.js";
9
+ import { runAgentTool } from "./tools.js";
10
+ export const LOOP_WARNING = "[loop detection] You appear to be repeating the same tool call. " +
11
+ "Do not repeat it again: inspect the result you already have, or change approach.";
12
+ /**
13
+ * Sliding-window loop detector.
14
+ *
15
+ * Semantics (fixed by FC-004, do not re-interpret):
16
+ * - hits count the last `window` tool calls, not turns;
17
+ * - read-only tools use a higher threshold than side-effecting tools (D-02);
18
+ * - the first threshold hit yields a warning and resets that fingerprint's count;
19
+ * - only a second full accumulation yields a terminal error;
20
+ * - the table lives for one run (turn), so a new user task starts clean;
21
+ * - fingerprints are normalized per tool call (absolute paths, timeoutMs removed).
22
+ */
23
+ class LoopState {
24
+ entries = new Map();
25
+ calls = 0;
26
+ readOnlyThreshold;
27
+ sideEffectThreshold;
28
+ window;
29
+ exemptReadOnly;
30
+ constructor(options = {}) {
31
+ this.readOnlyThreshold = positive(options.readOnlyThreshold, 6);
32
+ this.sideEffectThreshold = positive(options.sideEffectThreshold, 3);
33
+ this.window = positive(options.window, 5);
34
+ this.exemptReadOnly = options.exemptReadOnly === true;
35
+ }
36
+ check(fingerprint, readOnly) {
37
+ if (readOnly && this.exemptReadOnly)
38
+ return { action: "ok", occurrences: 0 };
39
+ this.calls += 1;
40
+ const index = this.calls;
41
+ let entry = this.entries.get(fingerprint);
42
+ if (!entry) {
43
+ entry = { warned: false, hits: [] };
44
+ this.entries.set(fingerprint, entry);
45
+ }
46
+ entry.hits = entry.hits.filter((hit) => index - hit < this.window);
47
+ entry.hits.push(index);
48
+ const threshold = readOnly ? this.readOnlyThreshold : this.sideEffectThreshold;
49
+ if (entry.hits.length < threshold)
50
+ return { action: "ok", occurrences: entry.hits.length };
51
+ const occurrences = entry.hits.length;
52
+ if (!entry.warned) {
53
+ entry.warned = true;
54
+ entry.hits = [];
55
+ return { action: "warn", occurrences };
56
+ }
57
+ return { action: "stop", occurrences };
58
+ }
59
+ }
60
+ export async function* runAgent(options) {
61
+ const provider = options.provider;
62
+ const model = options.model;
63
+ const cwd = options.cwd ?? process.cwd();
64
+ const signal = options.signal;
65
+ const maxTurns = Math.max(1, Math.floor(positive(options.maxTurns, 8)));
66
+ const tokenBudget = optionalPositive(options.tokenBudget);
67
+ const costBudget = optionalPositive(options.costBudgetUsd);
68
+ const loop = new LoopState(options.loopDetection);
69
+ let history = [...(options.messages ?? []), { role: "user", content: options.prompt }];
70
+ let currentText = "";
71
+ let lastVisible = "";
72
+ let consumedInput = 0;
73
+ let spentCost = 0;
74
+ let stopReason = "complete";
75
+ let errorMessage = "";
76
+ let naturalFinish = false;
77
+ let toolCallSeq = 0;
78
+ let protocol = options.protocol ?? "legacy";
79
+ let fellBack = false;
80
+ const activeFiles = new Map();
81
+ for (let turn = 0; turn < maxTurns; turn += 1) {
82
+ if (signal?.aborted) {
83
+ stopReason = "aborted";
84
+ break;
85
+ }
86
+ for (const steering of options.takeSteering?.() ?? []) {
87
+ history = [...history, { role: "system", content: "[user steering] " + steering }];
88
+ yield { type: "notice", text: "Steering applied to the next request." };
89
+ }
90
+ const contextWindow = options.contextWindow ?? getModelMeta(provider.id, model).contextWindow;
91
+ const compacted = compactMessages(history, options.compaction ?? DEFAULT_COMPACTION_CONFIG, {
92
+ contextWindow,
93
+ providerId: provider.id,
94
+ model,
95
+ serverOpaqueState: options.serverOpaqueState,
96
+ });
97
+ if (compacted.compacted) {
98
+ history = compacted.messages;
99
+ yield {
100
+ type: "compaction",
101
+ beforeChars: compacted.beforeChars,
102
+ afterChars: compacted.afterChars,
103
+ beforeTokens: compacted.beforeTokens,
104
+ afterTokens: compacted.afterTokens,
105
+ droppedMessages: compacted.droppedMessages,
106
+ keptMessages: compacted.keptMessages,
107
+ reason: compacted.reason ?? "threshold",
108
+ source: compacted.source === "server" ? "server" : "local",
109
+ summaryFile: compacted.summaryFile,
110
+ };
111
+ }
112
+ const promptTokens = estMessagesTokens(history, { providerId: provider.id, model }) +
113
+ estTokens(options.system ?? "", { providerId: provider.id, model });
114
+ if (tokenBudget !== undefined && consumedInput + promptTokens > tokenBudget) {
115
+ stopReason = "token_budget";
116
+ errorMessage = `Token budget exhausted (${consumedInput + promptTokens} > ${tokenBudget} estimated input tokens). Partial output is kept.`;
117
+ yield { type: "error", kind: "token_budget", message: errorMessage, text: lastVisible, used: consumedInput + promptTokens };
118
+ break;
119
+ }
120
+ if (costBudget !== undefined && spentCost >= costBudget) {
121
+ stopReason = "cost_budget";
122
+ errorMessage = `Cost budget exhausted ($${spentCost.toFixed(4)} of $${costBudget}). Partial output is kept.`;
123
+ yield { type: "error", kind: "cost_budget", message: errorMessage, text: lastVisible };
124
+ break;
125
+ }
126
+ currentText = "";
127
+ let resetNotice = "";
128
+ let streamed;
129
+ try {
130
+ streamed = await runStream(provider, {
131
+ model,
132
+ messages: history,
133
+ system: options.system,
134
+ reasoningEffort: options.reasoningEffort,
135
+ ...(protocol === "native" && options.tools && options.tools.length > 0 ? { tools: options.tools } : {}),
136
+ }, options.apiKey, {
137
+ signal,
138
+ idleTimeoutMs: options.idleTimeoutMs,
139
+ onDelta: (delta, text) => {
140
+ currentText = text;
141
+ options.onDelta?.(delta, text);
142
+ },
143
+ onReset: () => {
144
+ resetNotice = "Retried stream returned different content; the earlier partial output was discarded and replaced by the new stream.";
145
+ },
146
+ });
147
+ }
148
+ catch (err) {
149
+ if (err?.name === "AbortError" || signal?.aborted) {
150
+ stopReason = "aborted";
151
+ break;
152
+ }
153
+ errorMessage = String(err?.message ?? err);
154
+ if (protocol === "native" && !fellBack && isNativeUnsupportedError(errorMessage)) {
155
+ fellBack = true;
156
+ protocol = "legacy";
157
+ options.onProtocolFallback?.(`Native tools were rejected (${errorMessage.slice(0, 160)}); retrying with the pseudo protocol.`);
158
+ turn -= 1;
159
+ continue;
160
+ }
161
+ stopReason = "stream_error";
162
+ const partial = stripToolCalls(String(err?.partialText ?? "")).trim();
163
+ if (partial)
164
+ lastVisible = partial;
165
+ yield { type: "error", kind: "stream_error", message: errorMessage, text: partial || undefined };
166
+ break;
167
+ }
168
+ if (signal?.aborted) {
169
+ stopReason = "aborted";
170
+ break;
171
+ }
172
+ if (resetNotice)
173
+ yield { type: "notice", text: resetNotice };
174
+ if (streamed.usage) {
175
+ consumedInput += streamed.usage.inputTokens ?? promptTokens;
176
+ spentCost += streamed.usage.costUsd ?? 0;
177
+ yield { type: "usage", usage: streamed.usage };
178
+ }
179
+ else {
180
+ consumedInput += promptTokens;
181
+ }
182
+ if (streamed.incomplete) {
183
+ const partial = stripToolCalls(streamed.text).trim();
184
+ if (partial)
185
+ lastVisible = partial;
186
+ stopReason = "incomplete";
187
+ errorMessage = `Connection ended before the response finished${streamed.retried > 0 ? ` (retried ${streamed.retried}×)` : ""}. ` +
188
+ "Partial output is kept — type 继续 to continue.";
189
+ yield { type: "notice", text: errorMessage };
190
+ yield { type: "checkpoint", turn: turn + 1, cwd, providerId: provider.id, model, activeFiles: Array.from(activeFiles.values()) };
191
+ break;
192
+ }
193
+ const visibleText = stripToolCalls(streamed.text);
194
+ if (visibleText)
195
+ lastVisible = visibleText;
196
+ const nativeCalls = protocol === "native" && options.allowTools !== false ? (streamed.toolCalls ?? []) : [];
197
+ if (nativeCalls.length > 0) {
198
+ yield { type: "assistant_raw", text: streamed.text, toolCalls: nativeCalls };
199
+ if (visibleText)
200
+ yield { type: "assistant_text", text: visibleText };
201
+ history = [...history, { role: "assistant", content: streamed.text, toolCalls: nativeCalls }];
202
+ for (const native of nativeCalls) {
203
+ if (signal?.aborted) {
204
+ stopReason = "aborted";
205
+ break;
206
+ }
207
+ const id = native.id || `tc_${++toolCallSeq}`;
208
+ let call;
209
+ let parseError = "";
210
+ try {
211
+ const args = native.arguments ? JSON.parse(native.arguments) : {};
212
+ if (!args || typeof args !== "object" || Array.isArray(args))
213
+ throw new Error("arguments must be a JSON object");
214
+ call = { ...args, tool: native.name };
215
+ }
216
+ catch (err) {
217
+ parseError = `invalid arguments for ${native.name}: ${err?.message ?? String(err)}`;
218
+ call = { tool: native.name };
219
+ }
220
+ const fingerprint = toolCallFingerprint(call, cwd);
221
+ if (!isLoopExempt(call.tool)) {
222
+ const verdict = loop.check(fingerprint, isReadOnlyToolCall(call));
223
+ if (verdict.action === "warn") {
224
+ yield { type: "loop_detected", fingerprint, occurrences: verdict.occurrences, warned: false };
225
+ history = [...history, { role: "system", content: LOOP_WARNING }];
226
+ }
227
+ else if (verdict.action === "stop") {
228
+ stopReason = "loop_detected";
229
+ errorMessage = `Loop detected: the same tool call repeated ${verdict.occurrences} times. Stopped before executing it again.`;
230
+ yield { type: "error", kind: "loop_detected", message: errorMessage, text: lastVisible };
231
+ break;
232
+ }
233
+ }
234
+ yield { type: "tool_call", id, call, native: true };
235
+ const result = parseError
236
+ ? { ok: false, output: parseError }
237
+ : await runAgentTool(call, cwd, { signal, turn: turn + 1 });
238
+ trackSideEffects(activeFiles, result.sideEffects);
239
+ yield { type: "tool_result", id, call, ok: Boolean(result.ok), output: String(result.output ?? ""), sideEffects: result.sideEffects };
240
+ history = [...history, { role: "tool", toolCallId: id, content: String(result.output ?? "") }];
241
+ }
242
+ yield { type: "checkpoint", turn: turn + 1, cwd, providerId: provider.id, model, activeFiles: Array.from(activeFiles.values()) };
243
+ if (stopReason === "complete")
244
+ options.onNativeOk?.();
245
+ if (stopReason !== "complete")
246
+ break;
247
+ continue;
248
+ }
249
+ const calls = options.allowTools === false ? [] : parseToolCalls(streamed.text);
250
+ yield { type: "assistant_raw", text: streamed.text };
251
+ if (calls.length === 0) {
252
+ lastVisible = visibleText || streamed.text;
253
+ naturalFinish = true;
254
+ stopReason = "complete";
255
+ yield { type: "checkpoint", turn: turn + 1, cwd, providerId: provider.id, model, activeFiles: Array.from(activeFiles.values()) };
256
+ break;
257
+ }
258
+ history = [...history, { role: "assistant", content: streamed.text }];
259
+ if (visibleText)
260
+ yield { type: "assistant_text", text: visibleText };
261
+ const results = [];
262
+ for (const call of calls) {
263
+ if (signal?.aborted) {
264
+ stopReason = "aborted";
265
+ break;
266
+ }
267
+ const fingerprint = toolCallFingerprint(call, cwd);
268
+ if (!isLoopExempt(call.tool)) {
269
+ const verdict = loop.check(fingerprint, isReadOnlyToolCall(call));
270
+ if (verdict.action === "warn") {
271
+ yield { type: "loop_detected", fingerprint, occurrences: verdict.occurrences, warned: false };
272
+ history = [...history, { role: "system", content: LOOP_WARNING }];
273
+ }
274
+ else if (verdict.action === "stop") {
275
+ stopReason = "loop_detected";
276
+ errorMessage = `Loop detected: the same tool call repeated ${verdict.occurrences} times. Stopped before executing it again.`;
277
+ yield { type: "error", kind: "loop_detected", message: errorMessage, text: lastVisible };
278
+ break;
279
+ }
280
+ }
281
+ const id = `tc_${++toolCallSeq}`;
282
+ yield { type: "tool_call", id, call };
283
+ const result = await runAgentTool(call, cwd, { signal, turn: turn + 1 });
284
+ results.push({ call, ok: Boolean(result.ok), output: String(result.output ?? ""), sideEffects: result.sideEffects });
285
+ trackSideEffects(activeFiles, result.sideEffects);
286
+ yield { type: "tool_result", id, call, ok: Boolean(result.ok), output: String(result.output ?? ""), sideEffects: result.sideEffects };
287
+ }
288
+ if (results.length > 0)
289
+ history = [...history, { role: "user", content: formatToolResults(results) }];
290
+ yield { type: "checkpoint", turn: turn + 1, cwd, providerId: provider.id, model, activeFiles: Array.from(activeFiles.values()) };
291
+ if (stopReason !== "complete")
292
+ break;
293
+ }
294
+ if (stopReason === "stream_error") {
295
+ const partial = lastVisible;
296
+ if (partial)
297
+ history = [...history, { role: "assistant", content: partial }];
298
+ yield { type: "done", history, finalText: partial, stopReason };
299
+ return;
300
+ }
301
+ if (stopReason === "complete" && !naturalFinish)
302
+ stopReason = "max_turns";
303
+ let finalText = "";
304
+ if (stopReason === "aborted") {
305
+ const partial = stripToolCalls(currentText).trim() || lastVisible;
306
+ finalText = (partial ? partial + "\n\n" : "") + "[用户中断]";
307
+ yield { type: "error", kind: "aborted", message: "Cancelled.", text: partial };
308
+ }
309
+ else if (stopReason === "token_budget" || stopReason === "cost_budget" || stopReason === "loop_detected" || stopReason === "incomplete") {
310
+ finalText = lastVisible || errorMessage;
311
+ }
312
+ else if (stopReason === "max_turns") {
313
+ finalText = `Reached the maximum of ${maxTurns} agent tool turns. The work so far is kept in this session — type 继续 to keep going.`;
314
+ }
315
+ else {
316
+ finalText = lastVisible;
317
+ }
318
+ if (finalText)
319
+ history = [...history, { role: "assistant", content: finalText }];
320
+ yield { type: "done", history, finalText, stopReason };
321
+ }
322
+ function positive(value, fallback) {
323
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : fallback;
324
+ }
325
+ function optionalPositive(value) {
326
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : undefined;
327
+ }
328
+ function trackSideEffects(activeFiles, sideEffects) {
329
+ if (!sideEffects)
330
+ return;
331
+ for (const file of [...(sideEffects.touched ?? []), ...(sideEffects.created ?? [])]) {
332
+ const meta = describeActiveFile(file);
333
+ if (meta)
334
+ activeFiles.set(file, meta);
335
+ }
336
+ for (const file of sideEffects.removed ?? [])
337
+ activeFiles.delete(file);
338
+ }
339
+ function describeActiveFile(file) {
340
+ try {
341
+ const info = statSync(file);
342
+ if (!info.isFile())
343
+ return undefined;
344
+ const sha1_8 = info.size > 8 * 1024 * 1024
345
+ ? "large"
346
+ : createHash("sha1").update(readFileSync(file)).digest("hex").slice(0, 8);
347
+ return { path: file, sha1_8, mtime: info.mtime.toISOString() };
348
+ }
349
+ catch {
350
+ return undefined;
351
+ }
352
+ }
@@ -0,0 +1,37 @@
1
+ export type ShellJobStatus = "running" | "done" | "failed" | "killed" | "timeout";
2
+ export interface ShellExecResult {
3
+ ok: boolean;
4
+ status: ShellJobStatus;
5
+ output: string;
6
+ exitCode?: number | null;
7
+ sessionId?: string;
8
+ nextPollMs?: number;
9
+ }
10
+ export interface ShellJobView {
11
+ id: string;
12
+ command: string;
13
+ cwd: string;
14
+ status: ShellJobStatus;
15
+ exitCode?: number | null;
16
+ bytes: number;
17
+ startedAt: number;
18
+ pid?: number;
19
+ }
20
+ interface ExecOptions {
21
+ signal?: AbortSignal;
22
+ turn?: number;
23
+ timeoutMs?: number;
24
+ backgroundAfterMs?: number;
25
+ }
26
+ export declare function executeShellCommand(command: string, cwd: string, options?: ExecOptions): Promise<ShellExecResult>;
27
+ export declare function pollShellSession(id: string, waitMs?: number): Promise<ShellExecResult | undefined>;
28
+ export declare function killShellSession(id: string): boolean;
29
+ export declare function listShellSessions(): ShellJobView[];
30
+ export declare function shutdownShells(): void;
31
+ export declare function shellPoolStats(): {
32
+ spawns: number;
33
+ active: number;
34
+ pool: number;
35
+ maxSpawnPerTurn: number;
36
+ };
37
+ export {};