@datename/fastcode-termux 0.1.6-beta → 0.1.6

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 (246) hide show
  1. package/README.md +6 -24
  2. package/dist/agent/guards.d.ts +0 -8
  3. package/dist/agent/guards.js +0 -195
  4. package/dist/agent/protocol.js +1 -4
  5. package/dist/agent/tools.d.ts +1 -3
  6. package/dist/agent/tools.js +377 -2
  7. package/dist/app.js +103 -335
  8. package/dist/cli.js +311 -362
  9. package/dist/commands/index.js +0 -22
  10. package/dist/commands/login.d.ts +2 -4
  11. package/dist/commands/login.js +10 -12
  12. package/dist/commands/logout.js +16 -8
  13. package/dist/commands/model.d.ts +0 -13
  14. package/dist/commands/model.js +22 -42
  15. package/dist/commands/provider.d.ts +0 -13
  16. package/dist/commands/provider.js +9 -20
  17. package/dist/commands/whoami.js +5 -6
  18. package/dist/config/store.d.ts +12 -32
  19. package/dist/config/store.js +65 -117
  20. package/dist/config/trust.d.ts +8 -21
  21. package/dist/config/trust.js +13 -99
  22. package/dist/lib/diagnostics.d.ts +4 -29
  23. package/dist/lib/diagnostics.js +4 -60
  24. package/dist/lib/http.d.ts +1 -11
  25. package/dist/lib/http.js +4 -62
  26. package/dist/providers/anthropic.d.ts +0 -1
  27. package/dist/providers/anthropic.js +0 -12
  28. package/dist/providers/custom.d.ts +0 -9
  29. package/dist/providers/deepseek.d.ts +0 -10
  30. package/dist/providers/deepseek.js +1 -2
  31. package/dist/providers/google.d.ts +0 -9
  32. package/dist/providers/groq.d.ts +0 -10
  33. package/dist/providers/groq.js +1 -2
  34. package/dist/providers/index.d.ts +201 -10
  35. package/dist/providers/index.js +0 -30
  36. package/dist/providers/mistral.d.ts +0 -10
  37. package/dist/providers/mistral.js +1 -2
  38. package/dist/providers/moonshot.d.ts +0 -10
  39. package/dist/providers/moonshot.js +1 -2
  40. package/dist/providers/ollama.d.ts +0 -9
  41. package/dist/providers/openai.d.ts +0 -10
  42. package/dist/providers/openai.js +1 -2
  43. package/dist/providers/openaiCompatible.d.ts +0 -9
  44. package/dist/providers/openaiCompatible.js +10 -58
  45. package/dist/providers/openrouter.d.ts +0 -10
  46. package/dist/providers/openrouter.js +1 -2
  47. package/dist/providers/qwen.d.ts +0 -10
  48. package/dist/providers/qwen.js +1 -2
  49. package/dist/providers/xai.d.ts +0 -10
  50. package/dist/providers/xai.js +1 -2
  51. package/dist/providers/zhipu.d.ts +0 -10
  52. package/dist/providers/zhipu.js +1 -2
  53. package/dist/session/log.d.ts +12 -35
  54. package/dist/session/log.js +10 -206
  55. package/dist/types.d.ts +1 -60
  56. package/dist/ui/Message.d.ts +7 -1
  57. package/dist/ui/Message.js +2 -13
  58. package/dist/ui/Prompt.d.ts +2 -1
  59. package/dist/ui/Prompt.js +2 -2
  60. package/dist/ui/Spinner.js +5 -11
  61. package/dist/ui/StatusBar.d.ts +1 -2
  62. package/dist/ui/StatusBar.js +5 -6
  63. package/package.json +1 -1
  64. package/src/agent/guards.ts +0 -196
  65. package/src/agent/protocol.ts +1 -4
  66. package/src/agent/tools.ts +377 -3
  67. package/src/app.ts +103 -335
  68. package/src/cli.ts +3 -30
  69. package/src/commands/index.ts +0 -22
  70. package/src/commands/login.ts +10 -12
  71. package/src/commands/logout.ts +16 -8
  72. package/src/commands/model.ts +22 -42
  73. package/src/commands/provider.ts +9 -20
  74. package/src/commands/whoami.ts +5 -6
  75. package/src/config/store.ts +69 -122
  76. package/src/config/trust.ts +21 -137
  77. package/src/lib/diagnostics.ts +4 -60
  78. package/src/lib/http.ts +4 -66
  79. package/src/providers/anthropic.ts +1 -13
  80. package/src/providers/deepseek.ts +1 -2
  81. package/src/providers/groq.ts +1 -2
  82. package/src/providers/index.ts +1 -32
  83. package/src/providers/mistral.ts +1 -2
  84. package/src/providers/moonshot.ts +1 -2
  85. package/src/providers/openai.ts +1 -2
  86. package/src/providers/openaiCompatible.ts +10 -58
  87. package/src/providers/openrouter.ts +1 -2
  88. package/src/providers/qwen.ts +1 -2
  89. package/src/providers/xai.ts +1 -2
  90. package/src/providers/zhipu.ts +1 -2
  91. package/src/session/log.ts +28 -267
  92. package/src/types.ts +1 -63
  93. package/src/ui/Message.ts +2 -13
  94. package/src/ui/Prompt.ts +2 -2
  95. package/src/ui/Spinner.ts +5 -11
  96. package/src/ui/StatusBar.ts +5 -6
  97. package/dist/agent/approval.d.ts +0 -50
  98. package/dist/agent/approval.js +0 -7
  99. package/dist/agent/compaction.d.ts +0 -39
  100. package/dist/agent/compaction.js +0 -227
  101. package/dist/agent/context.d.ts +0 -29
  102. package/dist/agent/context.js +0 -62
  103. package/dist/agent/ignore.d.ts +0 -11
  104. package/dist/agent/ignore.js +0 -129
  105. package/dist/agent/process-tree.d.ts +0 -2
  106. package/dist/agent/process-tree.js +0 -31
  107. package/dist/agent/protocol-mode.d.ts +0 -6
  108. package/dist/agent/protocol-mode.js +0 -51
  109. package/dist/agent/registry.d.ts +0 -17
  110. package/dist/agent/registry.js +0 -90
  111. package/dist/agent/runner.d.ts +0 -106
  112. package/dist/agent/runner.js +0 -352
  113. package/dist/agent/shell-session.d.ts +0 -37
  114. package/dist/agent/shell-session.js +0 -400
  115. package/dist/agent/stream.d.ts +0 -29
  116. package/dist/agent/stream.js +0 -103
  117. package/dist/agent/tool-schemas.d.ts +0 -2
  118. package/dist/agent/tool-schemas.js +0 -63
  119. package/dist/agent/tools/append_file.d.ts +0 -2
  120. package/dist/agent/tools/append_file.js +0 -26
  121. package/dist/agent/tools/apply_patch.d.ts +0 -10
  122. package/dist/agent/tools/apply_patch.js +0 -52
  123. package/dist/agent/tools/copy_file.d.ts +0 -2
  124. package/dist/agent/tools/copy_file.js +0 -30
  125. package/dist/agent/tools/delete_file.d.ts +0 -2
  126. package/dist/agent/tools/delete_file.js +0 -24
  127. package/dist/agent/tools/grep.d.ts +0 -2
  128. package/dist/agent/tools/grep.js +0 -23
  129. package/dist/agent/tools/index.d.ts +0 -2
  130. package/dist/agent/tools/index.js +0 -32
  131. package/dist/agent/tools/list_files.d.ts +0 -2
  132. package/dist/agent/tools/list_files.js +0 -17
  133. package/dist/agent/tools/make_dir.d.ts +0 -2
  134. package/dist/agent/tools/make_dir.js +0 -24
  135. package/dist/agent/tools/move_path.d.ts +0 -2
  136. package/dist/agent/tools/move_path.js +0 -47
  137. package/dist/agent/tools/paths.d.ts +0 -7
  138. package/dist/agent/tools/paths.js +0 -84
  139. package/dist/agent/tools/pwd.d.ts +0 -2
  140. package/dist/agent/tools/pwd.js +0 -10
  141. package/dist/agent/tools/read_file.d.ts +0 -2
  142. package/dist/agent/tools/read_file.js +0 -21
  143. package/dist/agent/tools/read_many.d.ts +0 -2
  144. package/dist/agent/tools/read_many.js +0 -25
  145. package/dist/agent/tools/readers.d.ts +0 -6
  146. package/dist/agent/tools/readers.js +0 -216
  147. package/dist/agent/tools/replace_in_file.d.ts +0 -2
  148. package/dist/agent/tools/replace_in_file.js +0 -30
  149. package/dist/agent/tools/shell.d.ts +0 -2
  150. package/dist/agent/tools/shell.js +0 -48
  151. package/dist/agent/tools/stat_path.d.ts +0 -2
  152. package/dist/agent/tools/stat_path.js +0 -31
  153. package/dist/agent/tools/types.d.ts +0 -39
  154. package/dist/agent/tools/types.js +0 -1
  155. package/dist/agent/tools/validate.d.ts +0 -6
  156. package/dist/agent/tools/validate.js +0 -29
  157. package/dist/agent/tools/write_file.d.ts +0 -2
  158. package/dist/agent/tools/write_file.js +0 -26
  159. package/dist/commands/attach.d.ts +0 -9
  160. package/dist/commands/attach.js +0 -15
  161. package/dist/commands/bg.d.ts +0 -7
  162. package/dist/commands/bg.js +0 -9
  163. package/dist/commands/cancel.d.ts +0 -10
  164. package/dist/commands/cancel.js +0 -10
  165. package/dist/commands/compact.d.ts +0 -10
  166. package/dist/commands/compact.js +0 -22
  167. package/dist/commands/context.d.ts +0 -7
  168. package/dist/commands/context.js +0 -24
  169. package/dist/commands/edit.d.ts +0 -10
  170. package/dist/commands/edit.js +0 -36
  171. package/dist/commands/effort.d.ts +0 -25
  172. package/dist/commands/effort.js +0 -48
  173. package/dist/commands/expand.d.ts +0 -10
  174. package/dist/commands/expand.js +0 -8
  175. package/dist/commands/jobs.d.ts +0 -7
  176. package/dist/commands/jobs.js +0 -16
  177. package/dist/commands/kill.d.ts +0 -7
  178. package/dist/commands/kill.js +0 -11
  179. package/dist/commands/steer.d.ts +0 -11
  180. package/dist/commands/steer.js +0 -13
  181. package/dist/lib/model-meta.d.ts +0 -19
  182. package/dist/lib/model-meta.js +0 -113
  183. package/dist/lib/native-tools.d.ts +0 -12
  184. package/dist/lib/native-tools.js +0 -21
  185. package/dist/lib/net.d.ts +0 -33
  186. package/dist/lib/net.js +0 -168
  187. package/dist/patch/apply.d.ts +0 -15
  188. package/dist/patch/apply.js +0 -172
  189. package/dist/patch/parse.d.ts +0 -29
  190. package/dist/patch/parse.js +0 -112
  191. package/dist/patch/plan.d.ts +0 -49
  192. package/dist/patch/plan.js +0 -273
  193. package/dist/session/projection.d.ts +0 -43
  194. package/dist/session/projection.js +0 -257
  195. package/dist/ui/Picker.d.ts +0 -8
  196. package/dist/ui/Picker.js +0 -24
  197. package/src/agent/approval.ts +0 -47
  198. package/src/agent/compaction.ts +0 -276
  199. package/src/agent/context.ts +0 -97
  200. package/src/agent/ignore.ts +0 -147
  201. package/src/agent/process-tree.ts +0 -32
  202. package/src/agent/protocol-mode.ts +0 -59
  203. package/src/agent/registry.ts +0 -98
  204. package/src/agent/runner.ts +0 -443
  205. package/src/agent/shell-session.ts +0 -449
  206. package/src/agent/stream.ts +0 -143
  207. package/src/agent/tool-schemas.ts +0 -67
  208. package/src/agent/tools/append_file.ts +0 -28
  209. package/src/agent/tools/apply_patch.ts +0 -54
  210. package/src/agent/tools/copy_file.ts +0 -32
  211. package/src/agent/tools/delete_file.ts +0 -26
  212. package/src/agent/tools/grep.ts +0 -25
  213. package/src/agent/tools/index.ts +0 -34
  214. package/src/agent/tools/list_files.ts +0 -19
  215. package/src/agent/tools/make_dir.ts +0 -26
  216. package/src/agent/tools/move_path.ts +0 -50
  217. package/src/agent/tools/paths.ts +0 -92
  218. package/src/agent/tools/pwd.ts +0 -12
  219. package/src/agent/tools/read_file.ts +0 -23
  220. package/src/agent/tools/read_many.ts +0 -27
  221. package/src/agent/tools/readers.ts +0 -228
  222. package/src/agent/tools/replace_in_file.ts +0 -32
  223. package/src/agent/tools/shell.ts +0 -53
  224. package/src/agent/tools/stat_path.ts +0 -33
  225. package/src/agent/tools/types.ts +0 -42
  226. package/src/agent/tools/validate.ts +0 -35
  227. package/src/agent/tools/write_file.ts +0 -28
  228. package/src/commands/attach.ts +0 -16
  229. package/src/commands/bg.ts +0 -9
  230. package/src/commands/cancel.ts +0 -10
  231. package/src/commands/compact.ts +0 -23
  232. package/src/commands/context.ts +0 -25
  233. package/src/commands/edit.ts +0 -37
  234. package/src/commands/effort.ts +0 -49
  235. package/src/commands/expand.ts +0 -8
  236. package/src/commands/jobs.ts +0 -17
  237. package/src/commands/kill.ts +0 -12
  238. package/src/commands/steer.ts +0 -13
  239. package/src/lib/model-meta.ts +0 -138
  240. package/src/lib/native-tools.ts +0 -32
  241. package/src/lib/net.ts +0 -204
  242. package/src/patch/apply.ts +0 -194
  243. package/src/patch/parse.ts +0 -131
  244. package/src/patch/plan.ts +0 -317
  245. package/src/session/projection.ts +0 -308
  246. package/src/ui/Picker.ts +0 -24
package/src/ui/Prompt.ts CHANGED
@@ -15,9 +15,9 @@ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
15
15
  import { Box, Text } from "ink";
16
16
  import TextInput from "ink-text-input";
17
17
  import { COMMANDS } from "../commands/index.js";
18
- export const Prompt = ({ theme, value, onChange, onSubmit, disabled, placeholder, mask, }) => {
18
+ export const Prompt = ({ theme, value, onChange, onSubmit, disabled, placeholder, stable = false, mask, }) => {
19
19
  const suggestion = autocomplete(value);
20
- return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: disabled ? theme.muted : theme.accent, paddingX: 1, children: [_jsxs(Box, { children: [_jsx(Text, { color: theme.accent, bold: true, children: "\u276F " }), disabled ? (_jsx(Text, { color: theme.muted, children: placeholder ?? "Working… (Esc to cancel)" })) : (_jsx(TextInput, { value: value, onChange: onChange, onSubmit: onSubmit, placeholder: placeholder ?? "Type a message or /help …", showCursor: true, mask: mask }))] }), !disabled && suggestion ? (_jsx(Box, { marginLeft: 2, children: _jsxs(Text, { color: theme.muted, children: ["\u21AA ", suggestion] }) })) : null] }));
20
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: disabled ? theme.muted : theme.accent, paddingX: 1, children: [_jsxs(Box, { children: [_jsx(Text, { color: theme.accent, bold: true, children: "\u276F " }), disabled ? (_jsx(Text, { color: theme.muted, children: placeholder ?? "Working… (Esc to cancel)" })) : (_jsx(TextInput, { value: value, onChange: onChange, onSubmit: onSubmit, placeholder: placeholder ?? "Type a message or /help …", showCursor: !stable, mask: mask }))] }), !disabled && suggestion ? (_jsx(Box, { marginLeft: 2, children: _jsxs(Text, { color: theme.muted, children: ["\u21AA ", suggestion] }) })) : null] }));
21
21
  };
22
22
  function autocomplete(value) {
23
23
  if (!value.startsWith("/") || value.includes(" "))
package/src/ui/Spinner.ts CHANGED
@@ -21,19 +21,13 @@ const VERBS = [
21
21
  ];
22
22
  export const Spinner = ({ theme, label, stable = false }) => {
23
23
  const [tick, setTick] = useState(0);
24
- // stable UI only lowers the redraw rate; it must still animate. A frozen
25
- // frame made long waits look like a hang.
26
- const configured = Number(process.env["FASTCODE_SPINNER_MS"]);
27
- const interval = Number.isFinite(configured) && configured >= 60
28
- ? configured
29
- : stable
30
- ? 120
31
- : 80;
32
24
  useEffect(() => {
33
- const id = setInterval(() => setTick((t) => t + 1), interval);
25
+ if (stable)
26
+ return;
27
+ const id = setInterval(() => setTick((t) => t + 1), 80);
34
28
  return () => clearInterval(id);
35
- }, [interval]);
36
- const frame = FRAMES[tick % FRAMES.length];
29
+ }, [stable]);
30
+ const frame = stable ? "⠋" : FRAMES[tick % FRAMES.length];
37
31
  const verb = label ?? VERBS[Math.floor(tick / 24) % VERBS.length];
38
32
  return (_jsxs(Text, { color: theme.accent, children: [frame, " ", _jsxs(Text, { color: theme.muted, children: [verb, "\u2026"] })] }));
39
33
  };
@@ -1,20 +1,19 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Box, Text, useStdout } from "ink";
3
3
  import { platformLabel } from "../lib/platform.js";
4
- export const StatusBar = ({ theme, providerId, model, inputTokens, outputTokens, costUsd, enabledSkillCount, reasoningEffort, hint, }) => {
4
+ export const StatusBar = ({ theme, providerId, model, inputTokens, outputTokens, costUsd, enabledSkillCount, hint, }) => {
5
5
  const { stdout } = useStdout();
6
6
  const columns = stdout?.columns ?? process.stdout.columns ?? 80;
7
7
  const compact = columns < 88;
8
8
  const cost = costUsd > 0 ? `$${costUsd.toFixed(4)}` : "$0.0000";
9
9
  const providerModel = `${providerId}/${model}`;
10
10
  const label = compact ? truncate(providerModel, Math.max(18, columns - 28)) : providerModel;
11
- const effort = reasoningEffort && reasoningEffort !== "auto" ? `effort:${reasoningEffort}` : "";
12
- return (_jsxs(Box, { flexDirection: compact ? "column" : "row", justifyContent: "space-between", borderStyle: "round", borderColor: theme.border, paddingX: 1, children: [_jsxs(Text, { color: theme.muted, children: [_jsx(Text, { color: theme.accent, children: "\u25CF" }), " ", label, _jsx(Text, { color: theme.muted, children: " \u00B7 " }), inputTokens, "\u2191 ", outputTokens, "\u2193 ", cost, effort ? (_jsxs(_Fragment, { children: [_jsx(Text, { color: theme.muted, children: " \u00B7 " }), effort] })) : null, !compact ? (_jsxs(_Fragment, { children: [_jsx(Text, { color: theme.muted, children: " \u00B7 " }), "skills: ", enabledSkillCount, _jsx(Text, { color: theme.muted, children: " \u00B7 " }), platformLabel()] })) : null] }), _jsx(Text, { color: theme.muted, children: compact
13
- ? `${effort ? effort + " \u00B7 " : ""}${platformLabel()} \u00B7 skills:${enabledSkillCount} \u00B7 ${hint ?? "/help \u00B7 esc \u00B7 ctrl+c"}`
14
- : hint ?? "/help \u00B7 esc cancel \u00B7 ctrl+c quit" })] }));
11
+ return (_jsxs(Box, { flexDirection: compact ? "column" : "row", justifyContent: "space-between", borderStyle: "round", borderColor: theme.border, paddingX: 1, children: [_jsxs(Text, { color: theme.muted, children: [_jsx(Text, { color: theme.accent, children: "\u25CF" }), " ", label, _jsx(Text, { color: theme.muted, children: " \u00B7 " }), inputTokens, "\u2191 ", outputTokens, "\u2193 ", cost, !compact ? (_jsxs(_Fragment, { children: [_jsx(Text, { color: theme.muted, children: " \u00B7 " }), "skills: ", enabledSkillCount, _jsx(Text, { color: theme.muted, children: " \u00B7 " }), platformLabel()] })) : null] }), _jsx(Text, { color: theme.muted, children: compact
12
+ ? `${platformLabel()} · skills:${enabledSkillCount} · ${hint ?? "/help · esc · ctrl+c"}`
13
+ : hint ?? "/help · esc cancel · ctrl+c quit" })] }));
15
14
  };
16
15
  function truncate(value, max) {
17
16
  if (value.length <= max)
18
17
  return value;
19
- return `${value.slice(0, Math.max(1, max - 1))}\u2026`;
18
+ return `${value.slice(0, Math.max(1, max - 1))}…`;
20
19
  }
@@ -1,50 +0,0 @@
1
- export type ApprovalScope = "once" | "session" | "always";
2
- export type ApprovalReason = "trust" | "sensitive_path" | "ambiguous_match" | "destructive" | "large_diff";
3
- export interface ChoiceItem {
4
- label: string;
5
- value: string;
6
- }
7
- export type ApprovalPreview = {
8
- kind: "diff";
9
- text: string;
10
- } | {
11
- kind: "choices";
12
- items: ChoiceItem[];
13
- };
14
- export type ApprovalOption = {
15
- kind: "allow";
16
- scope: ApprovalScope;
17
- label: string;
18
- } | {
19
- kind: "deny";
20
- label: string;
21
- } | {
22
- kind: "choose";
23
- label: string;
24
- value: string;
25
- } | {
26
- kind: "feedback";
27
- label: string;
28
- prompt: string;
29
- };
30
- export interface ApprovalRequest {
31
- id: string;
32
- tool: string;
33
- args: unknown;
34
- reason: ApprovalReason;
35
- options: ApprovalOption[];
36
- preview?: ApprovalPreview;
37
- }
38
- export type ApprovalDecision = {
39
- kind: "allow";
40
- scope: ApprovalScope;
41
- } | {
42
- kind: "deny";
43
- reason?: string;
44
- feedback?: string;
45
- } | {
46
- kind: "pending";
47
- request: ApprovalRequest;
48
- };
49
- export type ApprovalResolver = (request: ApprovalRequest) => ApprovalDecision | Promise<ApprovalDecision>;
50
- export declare const DEFAULT_ALLOW_OPTIONS: ApprovalOption[];
@@ -1,7 +0,0 @@
1
- export const DEFAULT_ALLOW_OPTIONS = [
2
- { kind: "allow", scope: "once", label: "Allow once" },
3
- { kind: "allow", scope: "session", label: "Allow for this session" },
4
- { kind: "allow", scope: "always", label: "Always allow" },
5
- { kind: "deny", label: "Deny" },
6
- { kind: "feedback", label: "Deny with feedback", prompt: "Why should this be denied?" },
7
- ];
@@ -1,39 +0,0 @@
1
- import type { ChatMessage } from "../types.js";
2
- export interface CompactionConfig {
3
- enabled: boolean;
4
- localThresholdRatio: number;
5
- keepTurns: number;
6
- serverCompactionAware: boolean;
7
- serverCompactThreshold?: number;
8
- clampAboveServerByTokens: number;
9
- maxSummaryTokens: number;
10
- heapLimitMb: number;
11
- }
12
- export declare const DEFAULT_COMPACTION_CONFIG: CompactionConfig;
13
- export interface CompactionOptions {
14
- contextWindow: number;
15
- providerId?: string;
16
- model?: string;
17
- force?: boolean;
18
- serverOpaqueState?: string;
19
- }
20
- export interface CompactionResult {
21
- messages: ChatMessage[];
22
- compacted: boolean;
23
- reason?: "threshold" | "heap";
24
- source: "local" | "server" | "none";
25
- beforeTokens: number;
26
- afterTokens: number;
27
- beforeChars: number;
28
- afterChars: number;
29
- droppedMessages: number;
30
- keptMessages: number;
31
- summary?: string;
32
- summaryFile?: string;
33
- }
34
- export declare function compactionConfigFromEnv(): CompactionConfig;
35
- export declare function localThresholdTokens(config: CompactionConfig, contextWindow: number): number;
36
- export declare function heapExceeded(config: CompactionConfig): boolean;
37
- export declare function shouldCompact(messages: ChatMessage[], config: CompactionConfig, options: CompactionOptions): boolean;
38
- export declare function compactMessages(messages: ChatMessage[], config: CompactionConfig, options: CompactionOptions): CompactionResult;
39
- export declare function buildSummary(messages: ChatMessage[], config: CompactionConfig, options: Pick<CompactionOptions, "providerId" | "model">): string;
@@ -1,227 +0,0 @@
1
- import { mkdirSync, writeFileSync } from "node:fs";
2
- import { join } from "node:path";
3
- import { estMessagesTokens, estTokens } from "../lib/model-meta.js";
4
- import { getPaths } from "../config/paths.js";
5
- import { parseToolCalls, stripToolCalls } from "./protocol.js";
6
- export const DEFAULT_COMPACTION_CONFIG = {
7
- enabled: true,
8
- localThresholdRatio: 0.7,
9
- keepTurns: 6,
10
- serverCompactionAware: false,
11
- clampAboveServerByTokens: 8192,
12
- maxSummaryTokens: 1500,
13
- heapLimitMb: 220,
14
- };
15
- export function compactionConfigFromEnv() {
16
- const config = { ...DEFAULT_COMPACTION_CONFIG };
17
- const disabled = (process.env["FASTCODE_COMPACTION"] ?? "").trim().toLowerCase();
18
- if (["0", "false", "no", "off"].includes(disabled))
19
- config.enabled = false;
20
- const heap = Number(process.env["FASTCODE_HEAP_LIMIT_MB"]);
21
- if (Number.isFinite(heap) && heap > 0)
22
- config.heapLimitMb = heap;
23
- const server = (process.env["FASTCODE_SERVER_COMPACTION"] ?? "").trim().toLowerCase();
24
- if (["1", "true", "yes", "on"].includes(server))
25
- config.serverCompactionAware = true;
26
- return config;
27
- }
28
- export function localThresholdTokens(config, contextWindow) {
29
- const local = Math.floor(Math.max(1, contextWindow) * config.localThresholdRatio);
30
- if (config.serverCompactionAware && typeof config.serverCompactThreshold === "number" && config.serverCompactThreshold > 0) {
31
- return Math.max(local, config.serverCompactThreshold + config.clampAboveServerByTokens);
32
- }
33
- return local;
34
- }
35
- export function heapExceeded(config) {
36
- const used = process.memoryUsage().heapUsed;
37
- return used > Math.max(0, config.heapLimitMb) * 1024 * 1024;
38
- }
39
- export function shouldCompact(messages, config, options) {
40
- if (!config.enabled)
41
- return false;
42
- if (options.force || heapExceeded(config))
43
- return true;
44
- return messageTokens(messages, options) > localThresholdTokens(config, options.contextWindow);
45
- }
46
- export function compactMessages(messages, config = DEFAULT_COMPACTION_CONFIG, options) {
47
- const beforeTokens = messageTokens(messages, options);
48
- const beforeChars = messageChars(messages);
49
- const heap = heapExceeded(config);
50
- const noop = {
51
- messages,
52
- compacted: false,
53
- source: "none",
54
- beforeTokens,
55
- afterTokens: beforeTokens,
56
- beforeChars,
57
- afterChars: beforeChars,
58
- droppedMessages: 0,
59
- keptMessages: messages.length,
60
- };
61
- if (!config.enabled)
62
- return noop;
63
- const threshold = localThresholdTokens(config, options.contextWindow);
64
- if (!options.force && !heap && beforeTokens <= threshold)
65
- return noop;
66
- const reason = heap && beforeTokens <= threshold ? "heap" : "threshold";
67
- if (config.serverCompactionAware && options.serverOpaqueState) {
68
- return {
69
- ...noop,
70
- compacted: true,
71
- reason,
72
- source: "server",
73
- };
74
- }
75
- const headCount = messages[0]?.role === "user" ? 1 : 0;
76
- const keep = Math.max(2, Math.floor(config.keepTurns * 2));
77
- const tailStart = Math.max(headCount, messages.length - keep);
78
- if (tailStart <= headCount)
79
- return noop;
80
- const middle = messages.slice(headCount, tailStart);
81
- const summary = buildSummary(middle, config, options);
82
- const summaryFile = writeSummaryFile(summary);
83
- const marker = {
84
- role: "system",
85
- content: `[context compacted: ${middle.length} earlier messages were summarized to stay within the context window.` +
86
- `${summaryFile ? ` Summary file: ${summaryFile}.` : ""} Recent turns are kept verbatim.` +
87
- " If an earlier detail is missing, re-read that file with a tool.]\n\n" +
88
- summary,
89
- };
90
- const next = [...messages.slice(0, headCount), marker, ...messages.slice(tailStart)];
91
- return {
92
- messages: next,
93
- compacted: true,
94
- reason,
95
- source: "local",
96
- beforeTokens,
97
- afterTokens: messageTokens(next, options),
98
- beforeChars,
99
- afterChars: messageChars(next),
100
- droppedMessages: middle.length,
101
- keptMessages: next.length - headCount - 1,
102
- summary,
103
- summaryFile,
104
- };
105
- }
106
- function messageTokens(messages, options) {
107
- return estMessagesTokens(messages, { providerId: options.providerId, model: options.model });
108
- }
109
- function messageChars(messages) {
110
- return messages.reduce((sum, message) => sum + (message.content?.length ?? 0), 0);
111
- }
112
- export function buildSummary(messages, config, options) {
113
- const requests = [];
114
- const actions = [];
115
- const outcomes = [];
116
- const notes = [];
117
- const files = new Set();
118
- for (const message of messages) {
119
- if (!message.content)
120
- continue;
121
- if (message.role === "user") {
122
- if (message.content.startsWith("FastCode executed the requested local tools.")) {
123
- for (const line of message.content.split(/\r?\n/)) {
124
- if (/error|failed|not found|denied|timed out/i.test(line))
125
- outcomes.push(clip(line, 240));
126
- }
127
- }
128
- else {
129
- requests.push(clip(message.content, 300));
130
- }
131
- collectFiles(message.content, files);
132
- continue;
133
- }
134
- if (message.role === "assistant") {
135
- const calls = parseToolCalls(message.content);
136
- for (const call of calls)
137
- actions.push(describeCall(call));
138
- const visible = stripToolCalls(message.content);
139
- if (visible)
140
- notes.push(clip(visible, 300));
141
- collectFiles(message.content, files);
142
- continue;
143
- }
144
- const lines = message.content.split(/\r?\n/);
145
- for (const line of lines) {
146
- if (/error|failed|not found|denied|timed out/i.test(line))
147
- outcomes.push(clip(line, 240));
148
- }
149
- collectFiles(message.content, files);
150
- }
151
- const sections = [
152
- "# Session summary",
153
- "",
154
- "## Requests",
155
- ...limitLines(requests, 8).map((line) => `- ${line}`),
156
- "",
157
- "## Actions",
158
- ...limitLines(actions, 20).map((line) => `- ${line}`),
159
- "",
160
- "## Outcomes",
161
- ...limitLines(outcomes, 10).map((line) => `- ${line}`),
162
- "",
163
- "## Files touched",
164
- ...(files.size > 0 ? Array.from(files).slice(0, 40).map((file) => `- ${file}`) : ["- (none recorded)"]),
165
- "",
166
- "## Notes",
167
- ...limitLines(notes, 10).map((line) => `- ${line}`),
168
- ];
169
- const budget = Math.max(200, Math.floor(config.maxSummaryTokens));
170
- const kept = [];
171
- let used = 0;
172
- for (const line of sections) {
173
- const cost = estTokens(line + "\n", options);
174
- if (used + cost > budget) {
175
- kept.push("[... summary truncated ...]");
176
- break;
177
- }
178
- kept.push(line);
179
- used += cost;
180
- }
181
- return kept.join("\n").trimEnd() + "\n";
182
- }
183
- function describeCall(call) {
184
- const tool = String(call?.tool ?? "unknown");
185
- if (tool === "shell" && typeof call.command === "string")
186
- return `shell: ${clip(call.command, 200)}`;
187
- if (typeof call.path === "string")
188
- return `${tool}: ${call.path}`;
189
- if (typeof call.from === "string" || typeof call.to === "string")
190
- return `${tool}: ${call.from ?? "?"} -> ${call.to ?? "?"}`;
191
- if (typeof call.pattern === "string")
192
- return `${tool}: /${clip(call.pattern, 120)}/`;
193
- return tool;
194
- }
195
- function collectFiles(text, files) {
196
- const matches = text.match(/[A-Za-z0-9_@./\\-]+\.[A-Za-z0-9]{1,8}\b/g);
197
- if (!matches)
198
- return;
199
- for (const match of matches) {
200
- if (match.length <= 3 || match.length > 240)
201
- continue;
202
- if (/^\d+(\.\d+)+$/.test(match))
203
- continue;
204
- files.add(match);
205
- if (files.size >= 200)
206
- return;
207
- }
208
- }
209
- function limitLines(lines, max) {
210
- return lines.slice(0, max);
211
- }
212
- function clip(text, max) {
213
- const oneLine = String(text).replace(/\s+/g, " ").trim();
214
- return oneLine.length > max ? oneLine.slice(0, max) + "…" : oneLine;
215
- }
216
- function writeSummaryFile(summary) {
217
- try {
218
- const dir = getPaths().sessions;
219
- mkdirSync(dir, { recursive: true });
220
- const file = join(dir, `summary-${Date.now()}-${Math.random().toString(36).slice(2, 6)}.md`);
221
- writeFileSync(file, summary, "utf8");
222
- return file;
223
- }
224
- catch {
225
- return undefined;
226
- }
227
- }
@@ -1,29 +0,0 @@
1
- /**
2
- * Context compaction for long agent sessions.
3
- *
4
- * The chat loop keeps appending tool results to the conversation, so a long
5
- * investigation can grow past the model's window and fail with a provider 400.
6
- * Instead of silently truncating the tail (which loses the current task), we
7
- * keep the first user request and the most recent turns, and shrink the middle
8
- * into short summaries with an explicit marker.
9
- *
10
- * Token counts are approximated at 4 characters per token. That is deliberately
11
- * crude: it must work for every provider without a tokenizer dependency.
12
- */
13
- import type { ChatMessage } from "../types.js";
14
- export interface CompactOptions {
15
- /** Character budget for the whole history (default 400k ≈ 100k tokens). */
16
- maxChars?: number;
17
- /** Number of trailing messages to keep verbatim. */
18
- keepRecent?: number;
19
- /** Maximum characters kept from one old tool result. */
20
- toolSummaryChars?: number;
21
- }
22
- export interface CompactResult {
23
- messages: ChatMessage[];
24
- compacted: boolean;
25
- beforeChars: number;
26
- afterChars: number;
27
- }
28
- export declare function historyChars(messages: ChatMessage[]): number;
29
- export declare function compactHistory(messages: ChatMessage[], options?: CompactOptions): CompactResult;
@@ -1,62 +0,0 @@
1
- const TOOL_MARKER = "FastCode executed the requested local tools.";
2
- export function historyChars(messages) {
3
- return messages.reduce((sum, message) => sum + (message.content?.length ?? 0), 0);
4
- }
5
- export function compactHistory(messages, options = {}) {
6
- const beforeChars = historyChars(messages);
7
- const maxChars = positive(options.maxChars, 400_000);
8
- const keepRecent = Math.max(2, Math.floor(positive(options.keepRecent, 6)));
9
- const toolSummaryChars = Math.max(200, Math.floor(positive(options.toolSummaryChars, 2_000)));
10
- if (beforeChars <= maxChars || messages.length <= keepRecent + 1) {
11
- return { messages, compacted: false, beforeChars, afterChars: beforeChars };
12
- }
13
- // Keep the original request (usually the first message) plus the newest
14
- // turns. Everything between is summarized, never dropped without a marker.
15
- const head = messages[0]?.role === "user" ? [messages[0]] : [];
16
- const tailStart = Math.max(head.length, messages.length - keepRecent);
17
- const middle = messages.slice(head.length, tailStart);
18
- const summarized = middle.map((message) => ({
19
- role: message.role,
20
- content: summarizeMessage(message.content, toolSummaryChars),
21
- }));
22
- const marker = {
23
- role: "system",
24
- content: `[context compacted: ${middle.length} earlier message(s) were summarized to stay within the context window. ` +
25
- `Recent tool results are kept in full. If an earlier detail is missing, re-read that file with a tool.]`,
26
- };
27
- const next = [...head, marker, ...summarized, ...messages.slice(tailStart)];
28
- return {
29
- messages: next,
30
- compacted: true,
31
- beforeChars,
32
- afterChars: historyChars(next),
33
- };
34
- }
35
- function summarizeMessage(content, limit) {
36
- if (!content)
37
- return content;
38
- if (content.includes(TOOL_MARKER))
39
- return summarizeToolMessage(content, limit);
40
- if (content.length <= limit)
41
- return content;
42
- const head = content.slice(0, Math.floor(limit * 0.6));
43
- const tail = content.slice(-Math.floor(limit * 0.3));
44
- return `${head}\n[… ${content.length - head.length - tail.length} chars summarized …]\n${tail}`;
45
- }
46
- /**
47
- * Tool results have the shape:
48
- * FastCode executed ...\n\nTool result 1 (ok)\ncall: {...}\noutput:\n```...```
49
- * Keep the call and the start/end of each output so file paths and errors
50
- * survive compaction.
51
- */
52
- function summarizeToolMessage(content, limit) {
53
- if (content.length <= limit)
54
- return content;
55
- const keep = Math.max(120, Math.floor((limit - 240) / 2));
56
- const head = content.slice(0, keep);
57
- const tail = content.slice(-keep);
58
- return `${head}\n[… ${content.length - keep * 2} chars of tool output summarized; re-run the tool if you need the middle …]\n${tail}`;
59
- }
60
- function positive(value, fallback) {
61
- return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : fallback;
62
- }
@@ -1,11 +0,0 @@
1
- export interface IgnoreRule {
2
- regex: RegExp;
3
- negate: boolean;
4
- dirOnly: boolean;
5
- }
6
- export interface IgnoreMatcher {
7
- ignored(name: string, fullPath: string, isDir: boolean): boolean;
8
- isBinary(name: string): boolean;
9
- }
10
- export declare function isBinaryName(name: string): boolean;
11
- export declare function createIgnore(root: string): IgnoreMatcher;
@@ -1,129 +0,0 @@
1
- /**
2
- * Ignore rules for directory walks and reads.
3
- *
4
- * Defaults cover the usual dependency/build folders. Users can add more with
5
- * `.fastcodeignore` in the project root, using a gitignore-like syntax:
6
- *
7
- * node_modules/ # trailing slash = directories only
8
- * *.log # basename at any depth
9
- * /secrets.txt # anchored to the project root
10
- * !important.log # re-include (last matching rule wins)
11
- */
12
- import { readFileSync, statSync } from "node:fs";
13
- import { join, relative, sep } from "node:path";
14
- const DEFAULT_NAMES = new Set([
15
- "node_modules",
16
- ".git",
17
- ".hg",
18
- ".svn",
19
- "dist",
20
- "build",
21
- "out",
22
- ".next",
23
- ".nuxt",
24
- ".output",
25
- ".turbo",
26
- ".parcel-cache",
27
- ".cache",
28
- "coverage",
29
- ".nyc_output",
30
- "__pycache__",
31
- ".venv",
32
- "venv",
33
- "target",
34
- ".gradle",
35
- ".idea",
36
- ]);
37
- const BINARY_EXT = /\.(png|jpe?g|gif|webp|ico|bmp|tiff?|pdf|zip|gz|tgz|bz2|xz|7z|rar|exe|dll|so|dylib|bin|wasm|mp3|mp4|mov|avi|mkv|wav|flac|ttf|otf|woff2?|eot|class|jar|apk|ipa|sqlite|db|lock)$/i;
38
- export function isBinaryName(name) {
39
- return BINARY_EXT.test(name);
40
- }
41
- export function createIgnore(root) {
42
- const rules = loadRules(root);
43
- return {
44
- ignored(name, fullPath, isDir) {
45
- if (DEFAULT_NAMES.has(name))
46
- return true;
47
- if (rules.length === 0)
48
- return false;
49
- const rel = relative(root, fullPath).split(sep).join("/");
50
- let ignored = false;
51
- for (const rule of rules) {
52
- if (rule.dirOnly && !isDir)
53
- continue;
54
- if (rule.regex.test(rel) || rule.regex.test(name))
55
- ignored = !rule.negate;
56
- }
57
- return ignored;
58
- },
59
- isBinary(name) {
60
- return BINARY_EXT.test(name);
61
- },
62
- };
63
- }
64
- function loadRules(root) {
65
- const file = join(root, ".fastcodeignore");
66
- let text = "";
67
- try {
68
- const info = statSync(file);
69
- if (!info.isFile())
70
- return [];
71
- text = readFileSync(file, "utf8");
72
- }
73
- catch {
74
- return [];
75
- }
76
- const rules = [];
77
- for (const raw of text.split(/\r?\n/)) {
78
- const line = raw.trim();
79
- if (!line || line.startsWith("#"))
80
- continue;
81
- const negate = line.startsWith("!");
82
- const body = negate ? line.slice(1).trim() : line;
83
- if (!body)
84
- continue;
85
- const dirOnly = body.endsWith("/");
86
- const pattern = dirOnly ? body.slice(0, -1) : body;
87
- const anchored = pattern.startsWith("/");
88
- const glob = anchored ? pattern.slice(1) : pattern;
89
- try {
90
- rules.push({
91
- regex: globToRegExp(glob, anchored),
92
- negate,
93
- dirOnly,
94
- });
95
- }
96
- catch {
97
- // A bad pattern must not disable the whole ignore file.
98
- }
99
- }
100
- return rules;
101
- }
102
- function globToRegExp(glob, anchored) {
103
- let source = "";
104
- for (let i = 0; i < glob.length; i += 1) {
105
- const char = glob[i];
106
- if (char === "*") {
107
- if (glob[i + 1] === "*") {
108
- source += ".*";
109
- i += 1;
110
- if (glob[i + 1] === "/")
111
- i += 1;
112
- }
113
- else {
114
- source += "[^/]*";
115
- }
116
- }
117
- else if (char === "?") {
118
- source += "[^/]";
119
- }
120
- else {
121
- source += escapeRegExp(char);
122
- }
123
- }
124
- // Unanchored patterns match a basename at any depth.
125
- return new RegExp("^" + (anchored ? "" : "(?:.*/)?") + source + "$");
126
- }
127
- function escapeRegExp(value) {
128
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
129
- }
@@ -1,2 +0,0 @@
1
- import { type ChildProcess } from "node:child_process";
2
- export declare function killProcessTree(child: ChildProcess | undefined, signal?: NodeJS.Signals): void;
@@ -1,31 +0,0 @@
1
- import { spawn } from "node:child_process";
2
- export function killProcessTree(child, signal = "SIGTERM") {
3
- if (!child || typeof child.pid !== "number")
4
- return;
5
- if (process.platform === "win32") {
6
- try {
7
- const killer = spawn("taskkill", ["/pid", String(child.pid), "/T", "/F"], { windowsHide: true, stdio: "ignore" });
8
- killer.unref();
9
- }
10
- catch {
11
- try {
12
- child.kill();
13
- }
14
- catch {
15
- // The process may already be gone.
16
- }
17
- }
18
- return;
19
- }
20
- try {
21
- process.kill(-child.pid, signal);
22
- }
23
- catch {
24
- try {
25
- child.kill(signal);
26
- }
27
- catch {
28
- // Same as above.
29
- }
30
- }
31
- }
@@ -1,6 +0,0 @@
1
- export type ToolProtocol = "legacy" | "native";
2
- export type ToolCapability = "native" | "pseudo";
3
- export declare function toolCapabilityKey(providerId: string, model: string): string;
4
- export declare function resolveToolProtocol(config: any, providerId: string, model: string, env?: NodeJS.ProcessEnv): ToolProtocol;
5
- export declare function withToolCapability(config: any, providerId: string, model: string, capability: ToolCapability): any;
6
- export declare function isNativeUnsupportedError(message: string): boolean;