@datename/fastcode-termux 0.1.6-beta → 0.1.8-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) hide show
  1. package/LICENSE +4 -0
  2. package/README.md +50 -17
  3. package/dist/agent/approval-json.d.ts +43 -0
  4. package/dist/agent/approval-json.js +69 -0
  5. package/dist/agent/approval-native.d.ts +3 -0
  6. package/dist/agent/approval-native.js +165 -0
  7. package/dist/agent/approval-store.d.ts +16 -0
  8. package/dist/agent/approval-store.js +21 -0
  9. package/dist/agent/approval.d.ts +33 -0
  10. package/dist/agent/approval.js +40 -5
  11. package/dist/agent/compaction.d.ts +2 -0
  12. package/dist/agent/compaction.js +60 -17
  13. package/dist/agent/guards.d.ts +16 -16
  14. package/dist/agent/guards.js +978 -24
  15. package/dist/agent/policy/defaults.d.ts +16 -0
  16. package/dist/agent/policy/defaults.js +17 -0
  17. package/dist/agent/policy/egress.d.ts +32 -0
  18. package/dist/agent/policy/egress.js +280 -0
  19. package/dist/agent/process-tree.d.ts +20 -0
  20. package/dist/agent/process-tree.js +81 -10
  21. package/dist/agent/protocol.d.ts +20 -1
  22. package/dist/agent/protocol.js +165 -38
  23. package/dist/agent/registry.d.ts +3 -1
  24. package/dist/agent/registry.js +168 -11
  25. package/dist/agent/runner.d.ts +21 -0
  26. package/dist/agent/runner.js +44 -5
  27. package/dist/agent/shell-ast.d.ts +11 -0
  28. package/dist/agent/shell-ast.js +204 -0
  29. package/dist/agent/shell-session.d.ts +14 -0
  30. package/dist/agent/shell-session.js +251 -40
  31. package/dist/agent/tool-schemas.js +26 -7
  32. package/dist/agent/tools/append_file.js +3 -1
  33. package/dist/agent/tools/apply_patch.js +44 -4
  34. package/dist/agent/tools/copy_file.js +26 -3
  35. package/dist/agent/tools/delete_file.js +22 -3
  36. package/dist/agent/tools/grep.js +35 -4
  37. package/dist/agent/tools/make_dir.js +3 -1
  38. package/dist/agent/tools/move_path.d.ts +7 -0
  39. package/dist/agent/tools/move_path.js +31 -5
  40. package/dist/agent/tools/read_file.js +63 -3
  41. package/dist/agent/tools/read_many.js +21 -3
  42. package/dist/agent/tools/readers.d.ts +53 -1
  43. package/dist/agent/tools/readers.js +236 -26
  44. package/dist/agent/tools/replace_in_file.js +38 -5
  45. package/dist/agent/tools/shell.js +2 -2
  46. package/dist/agent/tools/types.d.ts +26 -0
  47. package/dist/agent/tools/validate.d.ts +15 -2
  48. package/dist/agent/tools/validate.js +66 -3
  49. package/dist/agent/tools/write_file.js +3 -1
  50. package/dist/app.d.ts +2 -1
  51. package/dist/app.js +526 -85
  52. package/dist/cli-main.js +363 -0
  53. package/dist/cli.js +397 -337
  54. package/dist/commands/about.js +1 -1
  55. package/dist/commands/clear.d.ts +1 -0
  56. package/dist/commands/clear.js +1 -1
  57. package/dist/commands/compact.d.ts +12 -0
  58. package/dist/commands/compact.js +14 -2
  59. package/dist/commands/context.js +5 -4
  60. package/dist/commands/index.js +2 -0
  61. package/dist/commands/jobs.js +3 -2
  62. package/dist/commands/kill.js +3 -1
  63. package/dist/commands/login.d.ts +2 -2
  64. package/dist/commands/login.js +15 -15
  65. package/dist/commands/model.d.ts +1 -13
  66. package/dist/commands/model.js +6 -38
  67. package/dist/commands/provider.d.ts +1 -15
  68. package/dist/commands/provider.js +4 -22
  69. package/dist/commands/skills.js +34 -4
  70. package/dist/commands/threads.d.ts +9 -0
  71. package/dist/commands/threads.js +26 -0
  72. package/dist/config/store.d.ts +21 -0
  73. package/dist/config/store.js +47 -9
  74. package/dist/config/trust.d.ts +9 -0
  75. package/dist/config/trust.js +41 -2
  76. package/dist/headless.d.ts +7 -0
  77. package/dist/headless.js +213 -0
  78. package/dist/index.js +19 -3
  79. package/dist/lib/debug.d.ts +2 -0
  80. package/dist/lib/debug.js +22 -0
  81. package/dist/lib/diagnostics.d.ts +19 -0
  82. package/dist/lib/diagnostics.js +32 -6
  83. package/dist/lib/highlight-subset.d.ts +21 -0
  84. package/dist/lib/highlight-subset.js +88 -0
  85. package/dist/lib/lifecycle.d.ts +5 -0
  86. package/dist/lib/lifecycle.js +62 -0
  87. package/dist/lib/markdown.js +22 -4
  88. package/dist/providers/deepseek.js +1 -1
  89. package/dist/providers/openaiCompatible.d.ts +24 -2
  90. package/dist/providers/openaiCompatible.js +20 -6
  91. package/dist/providers/spec.d.ts +12 -0
  92. package/dist/providers/spec.js +29 -0
  93. package/dist/session/log.d.ts +14 -2
  94. package/dist/session/log.js +56 -27
  95. package/dist/session/projection.d.ts +34 -1
  96. package/dist/session/projection.js +315 -46
  97. package/dist/skills/manager.d.ts +15 -0
  98. package/dist/skills/manager.js +135 -18
  99. package/dist/types.d.ts +15 -0
  100. package/dist/ui/StatusBar.js +1 -1
  101. package/package.json +3 -2
  102. package/src/agent/approval-json.ts +103 -0
  103. package/src/agent/approval-native.ts +168 -0
  104. package/src/agent/approval-store.ts +38 -0
  105. package/src/agent/approval.ts +58 -11
  106. package/src/agent/compaction.ts +62 -17
  107. package/src/agent/guards.ts +1049 -25
  108. package/src/agent/policy/defaults.ts +34 -0
  109. package/src/agent/policy/egress.ts +335 -0
  110. package/src/agent/process-tree.ts +105 -23
  111. package/src/agent/protocol.ts +187 -39
  112. package/src/agent/registry.ts +186 -14
  113. package/src/agent/runner.ts +50 -7
  114. package/src/agent/shell-ast.ts +227 -0
  115. package/src/agent/shell-session.ts +265 -41
  116. package/src/agent/tool-schemas.ts +26 -7
  117. package/src/agent/tools/append_file.ts +3 -1
  118. package/src/agent/tools/apply_patch.ts +45 -4
  119. package/src/agent/tools/copy_file.ts +27 -3
  120. package/src/agent/tools/delete_file.ts +22 -3
  121. package/src/agent/tools/grep.ts +36 -4
  122. package/src/agent/tools/make_dir.ts +3 -1
  123. package/src/agent/tools/move_path.ts +38 -5
  124. package/src/agent/tools/read_file.ts +64 -3
  125. package/src/agent/tools/read_many.ts +21 -3
  126. package/src/agent/tools/readers.ts +289 -26
  127. package/src/agent/tools/replace_in_file.ts +39 -5
  128. package/src/agent/tools/shell.ts +2 -2
  129. package/src/agent/tools/types.ts +43 -1
  130. package/src/agent/tools/validate.ts +73 -4
  131. package/src/agent/tools/write_file.ts +3 -1
  132. package/src/app.ts +533 -82
  133. package/src/{cli.ts → cli-main.ts} +34 -6
  134. package/src/commands/about.ts +1 -1
  135. package/src/commands/clear.ts +1 -1
  136. package/src/commands/compact.ts +14 -2
  137. package/src/commands/context.ts +5 -4
  138. package/src/commands/index.ts +2 -0
  139. package/src/commands/jobs.ts +3 -2
  140. package/src/commands/kill.ts +3 -1
  141. package/src/commands/login.ts +15 -15
  142. package/src/commands/model.ts +6 -38
  143. package/src/commands/provider.ts +4 -22
  144. package/src/commands/skills.ts +34 -4
  145. package/src/commands/threads.ts +28 -0
  146. package/src/config/store.ts +47 -9
  147. package/src/config/trust.ts +45 -2
  148. package/src/headless.ts +225 -0
  149. package/src/index.ts +19 -3
  150. package/src/lib/debug.ts +24 -0
  151. package/src/lib/diagnostics.ts +32 -6
  152. package/src/lib/highlight-subset.ts +93 -0
  153. package/src/lib/lifecycle.ts +69 -0
  154. package/src/lib/markdown.ts +22 -4
  155. package/src/providers/deepseek.ts +1 -1
  156. package/src/providers/openaiCompatible.ts +47 -10
  157. package/src/providers/spec.ts +42 -0
  158. package/src/session/log.ts +70 -27
  159. package/src/session/projection.ts +360 -47
  160. package/src/skills/manager.ts +140 -18
  161. package/src/types/highlight-shims.d.ts +14 -0
  162. package/src/types.ts +16 -1
  163. package/src/ui/StatusBar.ts +1 -1
  164. package/tsconfig.json +1 -0
  165. /package/dist/{cli.d.ts → cli-main.d.ts} +0 -0
@@ -17,10 +17,10 @@ export type ApprovalPreview =
17
17
  | { kind: "choices"; items: ChoiceItem[] };
18
18
 
19
19
  export type ApprovalOption =
20
- | { kind: "allow"; scope: ApprovalScope; label: string }
21
- | { kind: "deny"; label: string }
22
- | { kind: "choose"; label: string; value: string }
23
- | { kind: "feedback"; label: string; prompt: string };
20
+ | { kind: "allow"; id?: string; scope: ApprovalScope; label: string }
21
+ | { kind: "deny"; id?: string; label: string }
22
+ | { kind: "choose"; id?: string; label: string; value: string; meta?: { recommended?: boolean; risk?: string; files?: number; reason?: string } }
23
+ | { kind: "feedback"; id?: string; label: string; prompt: string };
24
24
 
25
25
  export interface ApprovalRequest {
26
26
  id: string;
@@ -29,19 +29,66 @@ export interface ApprovalRequest {
29
29
  reason: ApprovalReason;
30
30
  options: ApprovalOption[];
31
31
  preview?: ApprovalPreview;
32
+ tier?: 0 | 1 | 2 | 3;
33
+ prompt?: string;
34
+ source?: "system" | "ai" | "free";
35
+ defaultIndex?: number;
36
+ timeoutMs?: number;
37
+ freeInput?: { enabled: true; prompt: string; hint?: string };
32
38
  }
33
39
 
34
40
  export type ApprovalDecision =
35
41
  | { kind: "allow"; scope: ApprovalScope }
36
- | { kind: "deny"; reason?: string; feedback?: string }
37
- | { kind: "pending"; request: ApprovalRequest };
42
+ | { kind: "deny"; reason?: string; feedback?: string; code?: string; target?: string }
43
+ | { kind: "pending"; request: ApprovalRequest }
44
+ | { kind: "choose"; value: string; input?: string }
45
+ | { kind: "feedback"; text: string };
38
46
 
39
47
  export type ApprovalResolver = (request: ApprovalRequest) => ApprovalDecision | Promise<ApprovalDecision>;
40
48
 
41
49
  export const DEFAULT_ALLOW_OPTIONS: ApprovalOption[] = [
42
- { kind: "allow", scope: "once", label: "Allow once" },
43
- { kind: "allow", scope: "session", label: "Allow for this session" },
44
- { kind: "allow", scope: "always", label: "Always allow" },
45
- { kind: "deny", label: "Deny" },
46
- { kind: "feedback", label: "Deny with feedback", prompt: "Why should this be denied?" },
50
+ { kind: "allow", id: "allow-once", scope: "once", label: "Allow once" },
51
+ { kind: "allow", id: "allow-session", scope: "session", label: "Allow for this session" },
52
+ { kind: "allow", id: "allow-always", scope: "always", label: "Always allow" },
53
+ { kind: "deny", id: "deny", label: "Deny" },
54
+ { kind: "feedback", id: "feedback", label: "Deny with feedback", prompt: "Why should this be denied?" },
47
55
  ];
56
+
57
+ export function approvalRequestId(): string {
58
+ return "ap_" + Date.now().toString(36) + "_" + Math.random().toString(36).slice(2, 7);
59
+ }
60
+
61
+ export function approvalOptionForDecision(request: ApprovalRequest, decision: ApprovalDecision): ApprovalOption | undefined {
62
+ if (decision.kind === "allow")
63
+ return request.options.find((option) => option.kind === "allow" && option.scope === decision.scope);
64
+ if (decision.kind === "deny")
65
+ return request.options.find((option) => option.kind === "deny");
66
+ if (decision.kind === "choose")
67
+ return request.options.find((option) => option.kind === "choose" && option.value === decision.value);
68
+ if (decision.kind === "feedback")
69
+ return request.options.find((option) => option.kind === "feedback");
70
+ return undefined;
71
+ }
72
+
73
+ export function parseApprovalChoice(request: ApprovalRequest, text: string): ApprovalOption | undefined {
74
+ const index = Number(String(text ?? "").trim()) - 1;
75
+ if (!Number.isInteger(index) || index < 0 || index >= request.options.length)
76
+ return undefined;
77
+ return request.options[index];
78
+ }
79
+
80
+ export function decisionForOption(option: ApprovalOption, input?: string): ApprovalDecision {
81
+ if (option.kind === "allow")
82
+ return { kind: "allow", scope: option.scope };
83
+ if (option.kind === "choose")
84
+ return { kind: "choose", value: option.value };
85
+ if (option.kind === "feedback") {
86
+ const text = typeof input === "string" ? input.trim() : "";
87
+ // The option's prompt is the question, not the user's answer. Without an
88
+ // actual answer, fail closed instead of sending the prompt back to the model.
89
+ return text
90
+ ? { kind: "feedback", text }
91
+ : { kind: "deny", reason: "feedback was required but not provided (fail closed)" };
92
+ }
93
+ return { kind: "deny", reason: "denied by user" };
94
+ }
@@ -1,5 +1,5 @@
1
- import { mkdirSync, writeFileSync } from "node:fs";
2
- import { join } from "node:path";
1
+ import { mkdirSync, rmSync, writeFileSync } from "node:fs";
2
+ import { basename, join, resolve, sep } from "node:path";
3
3
  import { estMessagesTokens, estTokens } from "../lib/model-meta.js";
4
4
  import { getPaths } from "../config/paths.js";
5
5
  import type { ChatMessage } from "../types.js";
@@ -57,6 +57,15 @@ export function compactionConfigFromEnv(): CompactionConfig {
57
57
  const heap = Number(process.env["FASTCODE_HEAP_LIMIT_MB"]);
58
58
  if (Number.isFinite(heap) && heap > 0)
59
59
  config.heapLimitMb = heap;
60
+ const ratio = Number(process.env["FASTCODE_COMPACTION_RATIO"]);
61
+ if (Number.isFinite(ratio) && ratio > 0 && ratio < 1)
62
+ config.localThresholdRatio = ratio;
63
+ const keepTurns = Number(process.env["FASTCODE_COMPACTION_KEEP_TURNS"]);
64
+ if (Number.isFinite(keepTurns) && keepTurns > 0)
65
+ config.keepTurns = Math.floor(keepTurns);
66
+ const summaryTokens = Number(process.env["FASTCODE_COMPACTION_SUMMARY_TOKENS"]);
67
+ if (Number.isFinite(summaryTokens) && summaryTokens > 0)
68
+ config.maxSummaryTokens = Math.floor(summaryTokens);
60
69
  const server = (process.env["FASTCODE_SERVER_COMPACTION"] ?? "").trim().toLowerCase();
61
70
  if (["1", "true", "yes", "on"].includes(server))
62
71
  config.serverCompactionAware = true;
@@ -176,7 +185,7 @@ export function buildSummary(messages: ChatMessage[], config: CompactionConfig,
176
185
  continue;
177
186
  }
178
187
  if (message.role === "assistant") {
179
- const calls = parseToolCalls(message.content);
188
+ const calls = parseToolCalls(message.content).calls;
180
189
  for (const call of calls)
181
190
  actions.push(describeCall(call));
182
191
  const visible = stripToolCalls(message.content);
@@ -192,23 +201,31 @@ export function buildSummary(messages: ChatMessage[], config: CompactionConfig,
192
201
  }
193
202
  collectFiles(message.content, files);
194
203
  }
204
+ const fileList = Array.from(files);
195
205
  const sections = [
196
206
  "# Session summary",
197
207
  "",
198
- "## Requests",
199
- ...limitLines(requests, 8).map((line) => `- ${line}`),
200
- "",
201
- "## Actions",
202
- ...limitLines(actions, 20).map((line) => `- ${line}`),
208
+ // Conclusions first: when the token budget runs out, keep what the
209
+ // session decided rather than the oldest requests.
210
+ "## Notes",
211
+ ...limitLines(notes, 10, "notes").map((line) => `- ${line}`),
203
212
  "",
204
213
  "## Outcomes",
205
- ...limitLines(outcomes, 10).map((line) => `- ${line}`),
214
+ ...limitLines(outcomes, 10, "outcomes").map((line) => `- ${line}`),
206
215
  "",
207
216
  "## Files touched",
208
- ...(files.size > 0 ? Array.from(files).slice(0, 40).map((file) => `- ${file}`) : ["- (none recorded)"]),
217
+ ...(fileList.length > 0
218
+ ? [
219
+ ...fileList.slice(0, 40).map((file) => `- ${file}`),
220
+ ...(fileList.length > 40 ? [`- (${fileList.length - 40} more files not listed)`] : []),
221
+ ]
222
+ : ["- (none recorded)"]),
209
223
  "",
210
- "## Notes",
211
- ...limitLines(notes, 10).map((line) => `- ${line}`),
224
+ "## Requests",
225
+ ...limitLines(requests, 8, "requests").map((line) => `- ${line}`),
226
+ "",
227
+ "## Actions",
228
+ ...limitLines(actions, 20, "actions").map((line) => `- ${line}`),
212
229
  ];
213
230
  const budget = Math.max(200, Math.floor(config.maxSummaryTokens));
214
231
  const kept: string[] = [];
@@ -243,9 +260,7 @@ function collectFiles(text: string, files: Set<string>): void {
243
260
  if (!matches)
244
261
  return;
245
262
  for (const match of matches) {
246
- if (match.length <= 3 || match.length > 240)
247
- continue;
248
- if (/^\d+(\.\d+)+$/.test(match))
263
+ if (!looksLikeProjectFile(match))
249
264
  continue;
250
265
  files.add(match);
251
266
  if (files.size >= 200)
@@ -253,8 +268,20 @@ function collectFiles(text: string, files: Set<string>): void {
253
268
  }
254
269
  }
255
270
 
256
- function limitLines(lines: string[], max: number): string[] {
257
- return lines.slice(0, max);
271
+ function looksLikeProjectFile(value: string): boolean {
272
+ if (value.length <= 3 || value.length > 240)
273
+ return false;
274
+ if (/^summary-\d/.test(value) || value.includes("//") || value.includes("\\.") || value.startsWith("s/"))
275
+ return false;
276
+ if (/^\d+(\.\d+)+$/.test(value))
277
+ return false;
278
+ return /\.(ts|tsx|js|jsx|mjs|cjs|json|md|txt|py|rs|go|java|kt|kts|c|cc|cpp|h|hpp|sh|bash|zsh|fish|yml|yaml|toml|ini|conf|cfg|env|lock|sql|html|css|scss|sass|less|vue|svelte|rb|php|swift|dart|xml|gradle|properties)$/i.test(value);
279
+ }
280
+
281
+ function limitLines(lines: string[], max: number, label: string): string[] {
282
+ if (lines.length <= max)
283
+ return lines;
284
+ return [...lines.slice(0, max), `(${lines.length - max} more ${label} not listed)`];
258
285
  }
259
286
 
260
287
  function clip(text: string, max: number): string {
@@ -262,6 +289,24 @@ function clip(text: string, max: number): string {
262
289
  return oneLine.length > max ? oneLine.slice(0, max) + "…" : oneLine;
263
290
  }
264
291
 
292
+ /** Delete a summary file only when it is one of our summary-*.md artifacts. */
293
+ export function removeSummaryFile(file?: string): void {
294
+ if (!file)
295
+ return;
296
+ const name = basename(file);
297
+ if (!/^summary-\d+.*\.md$/i.test(name))
298
+ return;
299
+ const sessions = resolve(getPaths().sessions) + sep;
300
+ if (!resolve(file).startsWith(sessions))
301
+ return;
302
+ try {
303
+ rmSync(file, { force: true });
304
+ }
305
+ catch {
306
+ // Best effort; an orphan summary is not worth failing compaction.
307
+ }
308
+ }
309
+
265
310
  function writeSummaryFile(summary: string): string | undefined {
266
311
  try {
267
312
  const dir = getPaths().sessions;