@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,331 @@
1
+ /**
2
+ * Append-only JSONL session log.
3
+ *
4
+ * Every entry is flushed as its own line, so a crash (or a kill from the
5
+ * Android task switcher) still leaves a readable transcript that can be
6
+ * resumed. A size cap keeps the sessions directory bounded; the oldest logs are
7
+ * pruned first.
8
+ *
9
+ * FASTCODE_SESSION_LOG=0 disable logging
10
+ * FASTCODE_SESSION_CACHE_MB=N sessions cache cap in MB (default 256)
11
+ * FASTCODE_FSYNC=0|1 force checkpoint fsync off/on
12
+ */
13
+ import { appendFileSync, closeSync, existsSync, fsyncSync, mkdirSync, openSync, readdirSync, readFileSync, readSync, rmSync, statSync, writeFileSync, writeSync } from "node:fs";
14
+ import { basename, join } from "node:path";
15
+ import { getPaths } from "../config/paths.js";
16
+ const NL = String.fromCharCode(10);
17
+ export function sessionCacheLimitBytes() {
18
+ const mb = Number(process.env["FASTCODE_SESSION_CACHE_MB"]);
19
+ return (Number.isFinite(mb) && mb > 0 ? mb : 256) * 1024 * 1024;
20
+ }
21
+ export function loggingEnabled() {
22
+ return process.env["FASTCODE_SESSION_LOG"] !== "0";
23
+ }
24
+ export function projectionPath(file) {
25
+ return file.replace(/[.]jsonl$/, ".projection.json");
26
+ }
27
+ export function fsyncMode() {
28
+ const raw = (process.env["FASTCODE_FSYNC"] ?? "").trim().toLowerCase();
29
+ if (["1", "true", "yes", "on"].includes(raw))
30
+ return "on";
31
+ if (["0", "false", "no", "off"].includes(raw))
32
+ return "off";
33
+ return "auto";
34
+ }
35
+ function onSharedStorage(root) {
36
+ const normalized = root.replace(/\\/g, "/");
37
+ return normalized === "/sdcard"
38
+ || normalized.startsWith("/sdcard/")
39
+ || normalized === "/storage/emulated"
40
+ || normalized.startsWith("/storage/emulated/");
41
+ }
42
+ export function fsyncEnabled(type = "checkpoint") {
43
+ if (type !== "checkpoint")
44
+ return false;
45
+ const mode = fsyncMode();
46
+ if (mode === "on")
47
+ return true;
48
+ if (mode === "off")
49
+ return false;
50
+ return onSharedStorage(getPaths().root);
51
+ }
52
+ function writeLine(file, line, type) {
53
+ if (!fsyncEnabled(type)) {
54
+ appendFileSync(file, line, "utf8");
55
+ return;
56
+ }
57
+ const fd = openSync(file, "a");
58
+ try {
59
+ writeSync(fd, line);
60
+ fsyncSync(fd);
61
+ }
62
+ finally {
63
+ closeSync(fd);
64
+ }
65
+ }
66
+ function countLines(file) {
67
+ if (!existsSync(file))
68
+ return 0;
69
+ try {
70
+ return readFileSync(file, "utf8").split(NL).filter(Boolean).length;
71
+ }
72
+ catch {
73
+ return 0;
74
+ }
75
+ }
76
+ function newId() {
77
+ return new Date().toISOString().replace(/[:.]/g, "-") + "-" + Math.random().toString(36).slice(2, 6);
78
+ }
79
+ function openLog(id, file, meta) {
80
+ let closed = false;
81
+ let seq = countLines(file);
82
+ const append = (entry) => {
83
+ if (closed || !loggingEnabled())
84
+ return 0;
85
+ const next = seq + 1;
86
+ const line = JSON.stringify({ ts: Date.now(), seq: next, ...entry }) + NL;
87
+ try {
88
+ writeLine(file, line, String(entry.type ?? ""));
89
+ seq = next;
90
+ return next;
91
+ }
92
+ catch {
93
+ return 0;
94
+ }
95
+ };
96
+ append({ type: "session_start", id, cwd: process.cwd(), ...meta });
97
+ return {
98
+ id,
99
+ file,
100
+ append,
101
+ count() {
102
+ return seq;
103
+ },
104
+ close() {
105
+ append({ type: "session_end" });
106
+ closed = true;
107
+ try {
108
+ pruneSessions();
109
+ }
110
+ catch {
111
+ // best effort
112
+ }
113
+ },
114
+ };
115
+ }
116
+ export function createSessionLog(meta = {}) {
117
+ const { sessions } = getPaths();
118
+ mkdirSync(sessions, { recursive: true });
119
+ const id = newId();
120
+ const file = join(sessions, id + ".jsonl");
121
+ return openLog(id, file, meta);
122
+ }
123
+ export function openSessionLog(file, meta = {}) {
124
+ const { sessions } = getPaths();
125
+ mkdirSync(sessions, { recursive: true });
126
+ return openLog(undefined, existsSync(file) ? file : join(sessions, file), meta);
127
+ }
128
+ export function listSessions() {
129
+ const { sessions } = getPaths();
130
+ if (!existsSync(sessions))
131
+ return [];
132
+ return readdirSync(sessions)
133
+ .filter((name) => name.endsWith(".jsonl"))
134
+ .map((name) => {
135
+ const file = join(sessions, name);
136
+ let bytes = 0;
137
+ let modified = "";
138
+ let messages = 0;
139
+ try {
140
+ const info = statSync(file);
141
+ bytes = info.size;
142
+ modified = info.mtime.toISOString();
143
+ messages = readFileSync(file, "utf8").split(NL).filter((line) => line.includes('"type":"user"') ||
144
+ (line.includes('"type":"message"') && line.includes('"role":"user"'))).length;
145
+ }
146
+ catch {
147
+ // unreadable: report what we have
148
+ }
149
+ return { id: name.replace(/[.]jsonl$/, ""), file, bytes, modified, messages };
150
+ })
151
+ .sort((a, b) => (a.modified < b.modified ? 1 : -1));
152
+ }
153
+ export function resolveSessionFile(idOrFile) {
154
+ const { sessions } = getPaths();
155
+ const looksLikePath = idOrFile.includes("/") || idOrFile.includes(String.fromCharCode(92)) || idOrFile.endsWith(".jsonl");
156
+ return looksLikePath ? idOrFile : join(sessions, idOrFile + ".jsonl");
157
+ }
158
+ export function readSession(idOrFile) {
159
+ const file = resolveSessionFile(idOrFile);
160
+ if (!existsSync(file))
161
+ return [];
162
+ return readFileSync(file, "utf8")
163
+ .split(NL)
164
+ .filter(Boolean)
165
+ .map((line) => {
166
+ try {
167
+ return JSON.parse(line);
168
+ }
169
+ catch {
170
+ return undefined;
171
+ }
172
+ })
173
+ .filter(Boolean);
174
+ }
175
+ export function loadSessionMessages(idOrFile) {
176
+ return readSession(idOrFile)
177
+ .filter((entry) => (entry.type === "message" || entry.type === "user" || entry.type === "assistant_raw") && typeof entry.content === "string")
178
+ .map((entry) => ({
179
+ role: entry.type === "assistant_raw" ? "assistant" : entry.role ?? "user",
180
+ content: entry.content,
181
+ }));
182
+ }
183
+ export function latestSessionFile() {
184
+ const all = listSessions();
185
+ return all.length > 0 ? all[0].file : undefined;
186
+ }
187
+ function summarize(id, file, entries) {
188
+ let cwd = "";
189
+ let providerId = "";
190
+ let model = "";
191
+ let turns = 0;
192
+ let activeFiles = [];
193
+ let messages = 0;
194
+ let lastTs = 0;
195
+ for (const entry of entries) {
196
+ if (typeof entry.ts === "number")
197
+ lastTs = entry.ts;
198
+ if (entry.type === "session_start") {
199
+ cwd = typeof entry.cwd === "string" ? entry.cwd : cwd;
200
+ providerId = typeof entry.providerId === "string" ? entry.providerId : providerId;
201
+ model = typeof entry.model === "string" ? entry.model : model;
202
+ }
203
+ else if (entry.type === "message") {
204
+ messages += 1;
205
+ }
206
+ else if (entry.type === "checkpoint") {
207
+ cwd = typeof entry.cwd === "string" ? entry.cwd : cwd;
208
+ providerId = typeof entry.providerId === "string" ? entry.providerId : providerId;
209
+ model = typeof entry.model === "string" ? entry.model : model;
210
+ if (typeof entry.turn === "number")
211
+ turns = entry.turn;
212
+ if (Array.isArray(entry.activeFiles))
213
+ activeFiles = entry.activeFiles;
214
+ }
215
+ }
216
+ return { id, file, cwd, providerId, model, turns, activeFiles, messages, lastTs };
217
+ }
218
+ function readLastEntry(file) {
219
+ let size = 0;
220
+ try {
221
+ size = statSync(file).size;
222
+ }
223
+ catch {
224
+ return undefined;
225
+ }
226
+ if (size === 0)
227
+ return undefined;
228
+ const length = Math.min(size, 64 * 1024);
229
+ const fd = openSync(file, "r");
230
+ try {
231
+ const buffer = Buffer.alloc(length);
232
+ readSync(fd, buffer, 0, length, size - length);
233
+ const lines = buffer.toString("utf8").trimEnd().split(NL);
234
+ for (let i = lines.length - 1; i >= 0; i -= 1) {
235
+ if (!lines[i])
236
+ continue;
237
+ try {
238
+ return JSON.parse(lines[i]);
239
+ }
240
+ catch {
241
+ return { type: "unparsable_tail" };
242
+ }
243
+ }
244
+ return undefined;
245
+ }
246
+ finally {
247
+ closeSync(fd);
248
+ }
249
+ }
250
+ export function findUnclosedSessions() {
251
+ const { sessions } = getPaths();
252
+ if (!existsSync(sessions))
253
+ return [];
254
+ const files = readdirSync(sessions)
255
+ .filter((name) => name.endsWith(".jsonl"))
256
+ .map((name) => {
257
+ const file = join(sessions, name);
258
+ let modified = 0;
259
+ try {
260
+ modified = statSync(file).mtimeMs;
261
+ }
262
+ catch {
263
+ // unreadable
264
+ }
265
+ return { name, file, modified };
266
+ })
267
+ .sort((a, b) => b.modified - a.modified);
268
+ const out = [];
269
+ for (const entry of files) {
270
+ const last = readLastEntry(entry.file);
271
+ if (!last || last.type === "session_end")
272
+ continue;
273
+ let entries;
274
+ try {
275
+ entries = readSession(entry.file);
276
+ }
277
+ catch {
278
+ continue;
279
+ }
280
+ if (entries.length === 0 || !entries.some((item) => item.type === "session_start"))
281
+ continue;
282
+ out.push(summarize(entry.name.replace(/[.]jsonl$/, ""), entry.file, entries));
283
+ }
284
+ return out;
285
+ }
286
+ export function latestUnclosedSession() {
287
+ return findUnclosedSessions()[0];
288
+ }
289
+ function ensureProjection(file) {
290
+ const target = projectionPath(file);
291
+ if (existsSync(target))
292
+ return;
293
+ const entries = readSession(file);
294
+ if (entries.length === 0)
295
+ return;
296
+ const summary = summarize(basename(file).replace(/[.]jsonl$/, ""), file, entries);
297
+ const lastUser = [...entries].reverse().find((entry) => entry.type === "message" && entry.role === "user");
298
+ const lastAssistant = [...entries].reverse().find((entry) => entry.type === "message" && entry.role === "assistant");
299
+ const payload = {
300
+ version: 1,
301
+ kind: "pruned_session",
302
+ source: basename(file),
303
+ createdAt: new Date().toISOString(),
304
+ summary: {
305
+ cwd: summary.cwd,
306
+ providerId: summary.providerId,
307
+ model: summary.model,
308
+ turns: summary.turns,
309
+ messages: summary.messages,
310
+ activeFiles: summary.activeFiles.slice(0, 200),
311
+ lastUser: typeof lastUser?.content === "string" ? lastUser.content.slice(0, 2000) : "",
312
+ lastAssistant: typeof lastAssistant?.content === "string" ? lastAssistant.content.slice(0, 2000) : "",
313
+ },
314
+ };
315
+ writeFileSync(target, JSON.stringify(payload, null, 2), "utf8");
316
+ }
317
+ export function pruneSessions(limit = sessionCacheLimitBytes()) {
318
+ const all = listSessions();
319
+ let total = all.reduce((sum, entry) => sum + entry.bytes, 0);
320
+ for (let i = all.length - 1; i >= 0 && total > limit; i -= 1) {
321
+ try {
322
+ ensureProjection(all[i].file);
323
+ rmSync(all[i].file, { force: true });
324
+ total -= all[i].bytes;
325
+ }
326
+ catch {
327
+ // keep pruning the rest
328
+ }
329
+ }
330
+ return total;
331
+ }
@@ -0,0 +1,43 @@
1
+ import type { ChatMessage } from "../types.js";
2
+ import { type ActiveFile } from "./log.js";
3
+ export interface SessionMeta {
4
+ cwd: string;
5
+ providerId: string;
6
+ model: string;
7
+ }
8
+ export interface ProjectionFingerprint {
9
+ toolCatalogHash: string;
10
+ modelId: string;
11
+ cwdDigest: string;
12
+ compactionSource: "local" | "server" | "none";
13
+ }
14
+ export interface Projection {
15
+ version: 1;
16
+ kind: "projection";
17
+ cwd: string;
18
+ providerId: string;
19
+ model: string;
20
+ fingerprint: ProjectionFingerprint;
21
+ messages: ChatMessage[];
22
+ activeFiles: ActiveFile[];
23
+ lastFactSeq: number;
24
+ }
25
+ export interface ResumeState {
26
+ projection: Projection;
27
+ messages: ChatMessage[];
28
+ meta: SessionMeta;
29
+ warnings: string[];
30
+ rebuilt: boolean;
31
+ }
32
+ export declare function unpairedToolNotice(tool: string): string;
33
+ export declare function cwdDigest(cwd: string): string;
34
+ export declare function projectionFingerprint(meta: SessionMeta, compactionSource?: ProjectionFingerprint["compactionSource"]): ProjectionFingerprint;
35
+ export declare function sameFingerprint(a: ProjectionFingerprint | undefined, b: ProjectionFingerprint): boolean;
36
+ export declare function sessionMetaFromFacts(facts: any[]): SessionMeta;
37
+ export declare function sessionMetaFromFile(file: string): SessionMeta;
38
+ export declare function factsToMessages(facts: any[]): ChatMessage[];
39
+ export declare function compactionSourceFromFacts(facts: any[]): ProjectionFingerprint["compactionSource"];
40
+ export declare function buildProjection(facts: any[], metaOverride?: Partial<SessionMeta>): Projection;
41
+ export declare function readProjection(file: string): Projection | undefined;
42
+ export declare function writeProjection(file: string, projection: Projection): void;
43
+ export declare function resumeSession(file: string, currentCwd?: string): ResumeState;
@@ -0,0 +1,257 @@
1
+ import { createHash } from "node:crypto";
2
+ import { readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
3
+ import { formatToolResults } from "../agent/protocol.js";
4
+ import { toolCatalogHash } from "../agent/registry.js";
5
+ import { projectionPath, readSession } from "./log.js";
6
+ const READ_ONLY_TOOLS = new Set(["read_file", "read_many", "grep", "list_files", "stat_path", "pwd"]);
7
+ const IDEMPOTENT_TOOLS = new Set(["write_file"]);
8
+ const MAX_MESSAGE_CHARS = 4000;
9
+ const MAX_PROJECTION_BYTES = 19_000;
10
+ export function unpairedToolNotice(tool) {
11
+ if (READ_ONLY_TOOLS.has(tool))
12
+ return "执行中断(只读工具,可安全重试)。";
13
+ if (IDEMPOTENT_TOOLS.has(tool))
14
+ return "执行中断,可安全重试(write_file 为全量写)。";
15
+ return "执行状态未知:进程中断时命令可能已完成。请先验证再操作(stat / git status / git diff),不要直接重做。";
16
+ }
17
+ export function cwdDigest(cwd) {
18
+ return createHash("sha1").update(cwd).digest("hex").slice(0, 8);
19
+ }
20
+ export function projectionFingerprint(meta, compactionSource = "none") {
21
+ return {
22
+ toolCatalogHash: toolCatalogHash(),
23
+ modelId: `${meta.providerId}::${meta.model}`,
24
+ cwdDigest: cwdDigest(meta.cwd),
25
+ compactionSource,
26
+ };
27
+ }
28
+ export function sameFingerprint(a, b) {
29
+ return Boolean(a) && a.toolCatalogHash === b.toolCatalogHash && a.modelId === b.modelId &&
30
+ a.cwdDigest === b.cwdDigest && a.compactionSource === b.compactionSource;
31
+ }
32
+ export function sessionMetaFromFacts(facts) {
33
+ let cwd = "";
34
+ let providerId = "";
35
+ let model = "";
36
+ for (const fact of facts) {
37
+ if (fact?.type === "session_start" || fact?.type === "checkpoint") {
38
+ if (typeof fact.cwd === "string" && fact.cwd)
39
+ cwd = fact.cwd;
40
+ if (typeof fact.providerId === "string" && fact.providerId)
41
+ providerId = fact.providerId;
42
+ if (typeof fact.model === "string" && fact.model)
43
+ model = fact.model;
44
+ }
45
+ }
46
+ return { cwd, providerId, model };
47
+ }
48
+ export function sessionMetaFromFile(file) {
49
+ return sessionMetaFromFacts(readSession(file));
50
+ }
51
+ function legacyMessages(facts) {
52
+ return facts
53
+ .filter((fact) => fact?.type === "message" && typeof fact.content === "string")
54
+ .map((fact) => ({ role: fact.role, content: fact.content }));
55
+ }
56
+ export function factsToMessages(facts) {
57
+ const calls = new Map();
58
+ const pending = [];
59
+ const messages = [];
60
+ let nativeTurn = false;
61
+ const flush = () => {
62
+ for (const [id, call] of calls) {
63
+ const output = unpairedToolNotice(String(call?.tool ?? "unknown"));
64
+ if (nativeTurn)
65
+ messages.push({ role: "tool", toolCallId: id, content: output });
66
+ else
67
+ pending.push({ call, ok: false, output });
68
+ }
69
+ calls.clear();
70
+ if (pending.length > 0)
71
+ messages.push({ role: "user", content: formatToolResults(pending.splice(0)) });
72
+ };
73
+ for (const fact of facts) {
74
+ if (!fact)
75
+ continue;
76
+ if (fact.type === "user") {
77
+ flush();
78
+ nativeTurn = false;
79
+ if (typeof fact.content === "string" && fact.content)
80
+ messages.push({ role: "user", content: fact.content });
81
+ }
82
+ else if (fact.type === "assistant_raw") {
83
+ flush();
84
+ const nativeCalls = Array.isArray(fact.toolCalls) ? fact.toolCalls : undefined;
85
+ nativeTurn = Boolean(nativeCalls && nativeCalls.length > 0);
86
+ if (typeof fact.content === "string")
87
+ messages.push({ role: "assistant", content: fact.content, ...(nativeTurn ? { toolCalls: nativeCalls } : {}) });
88
+ }
89
+ else if (fact.type === "tool_call") {
90
+ calls.set(String(fact.id ?? ""), fact.args ?? { tool: fact.tool });
91
+ }
92
+ else if (fact.type === "tool_result") {
93
+ const id = String(fact.id ?? "");
94
+ const call = calls.get(id) ?? { tool: fact.tool ?? "unknown" };
95
+ calls.delete(id);
96
+ if (nativeTurn) {
97
+ messages.push({ role: "tool", toolCallId: id, content: String(fact.output ?? "") });
98
+ }
99
+ else {
100
+ pending.push({
101
+ call,
102
+ ok: Boolean(fact.ok),
103
+ output: String(fact.output ?? ""),
104
+ });
105
+ }
106
+ }
107
+ }
108
+ flush();
109
+ if (messages.length === 0)
110
+ return legacyMessages(facts);
111
+ return messages;
112
+ }
113
+ export function compactionSourceFromFacts(facts) {
114
+ let source = "none";
115
+ for (const fact of facts) {
116
+ if (fact?.type === "compacted" && (fact.source === "local" || fact.source === "server"))
117
+ source = fact.source;
118
+ }
119
+ return source;
120
+ }
121
+ function latestCompaction(facts) {
122
+ for (let i = facts.length - 1; i >= 0; i -= 1) {
123
+ if (facts[i]?.type === "compacted")
124
+ return facts[i];
125
+ }
126
+ return undefined;
127
+ }
128
+ function applySummary(messages, facts) {
129
+ const latest = latestCompaction(facts);
130
+ if (!latest || latest.source !== "local" || typeof latest.summaryRef !== "string")
131
+ return messages;
132
+ try {
133
+ const summary = readFileSync(latest.summaryRef, "utf8");
134
+ const keep = Number(latest.keepMessages);
135
+ const tail = Number.isFinite(keep) && keep > 0 ? messages.slice(-keep) : messages;
136
+ const first = messages[0]?.role === "user" ? messages[0] : undefined;
137
+ const marker = { role: "system", content: `[context summary]\n${summary}` };
138
+ if (first && tail[0] !== first)
139
+ return [first, marker, ...tail];
140
+ return [marker, ...tail];
141
+ }
142
+ catch {
143
+ return messages;
144
+ }
145
+ }
146
+ export function buildProjection(facts, metaOverride) {
147
+ const detected = sessionMetaFromFacts(facts);
148
+ const meta = {
149
+ cwd: metaOverride?.cwd || detected.cwd || process.cwd(),
150
+ providerId: metaOverride?.providerId || detected.providerId,
151
+ model: metaOverride?.model || detected.model,
152
+ };
153
+ const messages = clampMessages(applySummary(factsToMessages(facts), facts));
154
+ const source = compactionSourceFromFacts(facts);
155
+ let activeFiles = [];
156
+ let lastFactSeq = 0;
157
+ for (const fact of facts) {
158
+ if (typeof fact?.seq === "number" && fact.seq > lastFactSeq)
159
+ lastFactSeq = fact.seq;
160
+ if (fact?.type === "checkpoint" && Array.isArray(fact.activeFiles))
161
+ activeFiles = fact.activeFiles;
162
+ }
163
+ const projection = {
164
+ version: 1,
165
+ kind: "projection",
166
+ cwd: meta.cwd,
167
+ providerId: meta.providerId,
168
+ model: meta.model,
169
+ fingerprint: projectionFingerprint(meta, source),
170
+ messages,
171
+ activeFiles: activeFiles.slice(0, 200),
172
+ lastFactSeq,
173
+ };
174
+ return projection;
175
+ }
176
+ function clampMessages(messages) {
177
+ const list = messages.map((message) => {
178
+ const content = message.content.length > MAX_MESSAGE_CHARS
179
+ ? message.content.slice(0, MAX_MESSAGE_CHARS) + "\n[...truncated in projection...]"
180
+ : message.content;
181
+ return {
182
+ role: message.role,
183
+ content,
184
+ ...(message.toolCalls ? { toolCalls: message.toolCalls } : {}),
185
+ ...(message.toolCallId ? { toolCallId: message.toolCallId } : {}),
186
+ };
187
+ });
188
+ while (list.length > 1 && Buffer.byteLength(JSON.stringify(list), "utf8") > MAX_PROJECTION_BYTES) {
189
+ if (list.length > 2 && list[0].role === "user")
190
+ list.splice(1, 1);
191
+ else
192
+ list.shift();
193
+ }
194
+ return list;
195
+ }
196
+ export function readProjection(file) {
197
+ try {
198
+ const raw = JSON.parse(readFileSync(projectionPath(file), "utf8"));
199
+ if (!raw || raw.version !== 1 || !Array.isArray(raw.messages) || !raw.fingerprint)
200
+ return undefined;
201
+ return raw;
202
+ }
203
+ catch {
204
+ return undefined;
205
+ }
206
+ }
207
+ export function writeProjection(file, projection) {
208
+ const target = projectionPath(file);
209
+ const tmp = target + "." + process.pid + ".tmp";
210
+ try {
211
+ writeFileSync(tmp, JSON.stringify(projection, null, 2), "utf8");
212
+ renameSync(tmp, target);
213
+ }
214
+ catch (err) {
215
+ try {
216
+ rmSync(tmp, { force: true });
217
+ }
218
+ catch {
219
+ // best effort
220
+ }
221
+ throw err;
222
+ }
223
+ }
224
+ export function resumeSession(file, currentCwd = process.cwd()) {
225
+ const facts = readSession(file);
226
+ const detected = sessionMetaFromFacts(facts);
227
+ const meta = {
228
+ cwd: detected.cwd || currentCwd,
229
+ providerId: detected.providerId,
230
+ model: detected.model,
231
+ };
232
+ const expected = projectionFingerprint(meta, compactionSourceFromFacts(facts));
233
+ const stored = readProjection(file);
234
+ const warnings = [];
235
+ let projection;
236
+ let rebuilt = false;
237
+ if (stored && sameFingerprint(stored.fingerprint, expected)) {
238
+ projection = stored;
239
+ }
240
+ else {
241
+ projection = buildProjection(facts, meta);
242
+ rebuilt = true;
243
+ if (stored)
244
+ warnings.push("Projection fingerprint changed (tools/model/cwd); rebuilt from facts.");
245
+ else
246
+ warnings.push("No projection found; rebuilt from facts.");
247
+ try {
248
+ writeProjection(file, projection);
249
+ }
250
+ catch {
251
+ warnings.push("Could not write the projection (filesystem is read-only?); using the in-memory rebuild.");
252
+ }
253
+ }
254
+ if (detected.cwd && detected.cwd !== currentCwd)
255
+ warnings.push(`Session was recorded in ${detected.cwd}; current directory is ${currentCwd}.`);
256
+ return { projection, messages: projection.messages, meta, warnings, rebuilt };
257
+ }
@@ -0,0 +1,18 @@
1
+ /** List skills currently installed under ~/.fastcode/skills. */
2
+ export declare function listInstalledSkills(): any[];
3
+ /** Look up a skill by name (matches folder name OR frontmatter name). */
4
+ export declare function getSkill(name: any): any;
5
+ /** Install a skill. Returns the parsed manifest on success. */
6
+ export declare function installSkill(spec: any, opts?: any): Promise<{
7
+ name: any;
8
+ description: any;
9
+ source: any;
10
+ path: string;
11
+ body: any;
12
+ }>;
13
+ /** Remove an installed skill by name or folder. */
14
+ export declare function uninstallSkill(name: any): boolean;
15
+ /** Search the registry for skills matching a query. */
16
+ export declare function searchRegistry(query: any, registry?: string): Promise<any>;
17
+ /** Compose the system-prompt addendum for a list of enabled skill names. */
18
+ export declare function buildSkillsPrompt(enabled: any): string;