@visual-z/xeno 0.8.4 → 0.9.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 (350) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +31 -746
  3. package/bin/xeno.exe +26 -0
  4. package/install.cjs +88 -0
  5. package/package.json +18 -48
  6. package/docs/app-server.md +0 -115
  7. package/docs/foundations.md +0 -88
  8. package/docs/install.md +0 -244
  9. package/docs/providers.md +0 -172
  10. package/docs/research/foundation-clients-2026-10-09.md +0 -27
  11. package/docs/research/foundation-editing-2026-10-09.md +0 -56
  12. package/docs/research/foundation-network-2026-10-09.md +0 -37
  13. package/docs/research/foundation-workflow-2026-10-09.md +0 -37
  14. package/src/apps/acp-server.ts +0 -41
  15. package/src/apps/agent-profiles.ts +0 -19
  16. package/src/apps/app-server.ts +0 -30
  17. package/src/apps/approve.ts +0 -49
  18. package/src/apps/cli.ts +0 -99
  19. package/src/apps/commands.ts +0 -319
  20. package/src/apps/completions.ts +0 -87
  21. package/src/apps/control-transport.ts +0 -90
  22. package/src/apps/export-cli.ts +0 -24
  23. package/src/apps/goal-flow.ts +0 -70
  24. package/src/apps/image-draft.ts +0 -46
  25. package/src/apps/input.ts +0 -312
  26. package/src/apps/model-catalog.ts +0 -69
  27. package/src/apps/preferences.ts +0 -20
  28. package/src/apps/project-trust.ts +0 -9
  29. package/src/apps/provider-connect.ts +0 -346
  30. package/src/apps/question-ui.ts +0 -27
  31. package/src/apps/render.ts +0 -181
  32. package/src/apps/resources.ts +0 -66
  33. package/src/apps/review.ts +0 -109
  34. package/src/apps/session-hub.ts +0 -96
  35. package/src/apps/session-lifecycle.ts +0 -64
  36. package/src/apps/shell.ts +0 -2071
  37. package/src/apps/startup-input.ts +0 -135
  38. package/src/apps/startup.ts +0 -1677
  39. package/src/apps/ui/app.ts +0 -1818
  40. package/src/apps/ui/control-model.ts +0 -43
  41. package/src/apps/ui/extension-host.ts +0 -194
  42. package/src/apps/ui/image-cache.ts +0 -121
  43. package/src/apps/ui/palette.ts +0 -88
  44. package/src/apps/ui/project.ts +0 -127
  45. package/src/apps/ui/standalone.ts +0 -148
  46. package/src/apps/ui/status-model.ts +0 -68
  47. package/src/apps/ui/surface.ts +0 -91
  48. package/src/apps/ui/theme.ts +0 -86
  49. package/src/apps/update-check.ts +0 -112
  50. package/src/apps/workflow.ts +0 -58
  51. package/src/core/acceptance.ts +0 -139
  52. package/src/core/action-defaults.ts +0 -58
  53. package/src/core/activity-status.ts +0 -52
  54. package/src/core/agent-profile.ts +0 -130
  55. package/src/core/attachment-input.ts +0 -17
  56. package/src/core/blind-checks.ts +0 -72
  57. package/src/core/boundary.ts +0 -174
  58. package/src/core/branch-receipt.ts +0 -26
  59. package/src/core/catalog-merge.ts +0 -19
  60. package/src/core/checkpoints.ts +0 -85
  61. package/src/core/churn.ts +0 -61
  62. package/src/core/command-artifacts.ts +0 -94
  63. package/src/core/command-contract.ts +0 -151
  64. package/src/core/completion-action.ts +0 -20
  65. package/src/core/completion-review.ts +0 -98
  66. package/src/core/context-detail.ts +0 -37
  67. package/src/core/context-policy.ts +0 -133
  68. package/src/core/context-window.ts +0 -54
  69. package/src/core/continuation.ts +0 -130
  70. package/src/core/control-protocol.ts +0 -69
  71. package/src/core/control-view.ts +0 -39
  72. package/src/core/cycle.ts +0 -17
  73. package/src/core/data-query.ts +0 -201
  74. package/src/core/deadline.ts +0 -40
  75. package/src/core/derive.ts +0 -516
  76. package/src/core/draft-images.ts +0 -56
  77. package/src/core/edit-plan.ts +0 -200
  78. package/src/core/environment.ts +0 -181
  79. package/src/core/event-validation.ts +0 -220
  80. package/src/core/events.ts +0 -670
  81. package/src/core/evidence-ladder.ts +0 -62
  82. package/src/core/evidence-links.ts +0 -68
  83. package/src/core/evidence.ts +0 -240
  84. package/src/core/execution.ts +0 -18
  85. package/src/core/exhaustive.ts +0 -18
  86. package/src/core/export.ts +0 -179
  87. package/src/core/extension-flags.ts +0 -14
  88. package/src/core/external-effects.ts +0 -488
  89. package/src/core/failure-recovery.ts +0 -173
  90. package/src/core/file-precondition.ts +0 -78
  91. package/src/core/file-references.ts +0 -35
  92. package/src/core/frontmatter.ts +0 -213
  93. package/src/core/git-status.ts +0 -29
  94. package/src/core/goal-view.ts +0 -64
  95. package/src/core/goal.ts +0 -200
  96. package/src/core/handoff.ts +0 -479
  97. package/src/core/history-query.ts +0 -139
  98. package/src/core/history.ts +0 -237
  99. package/src/core/image-limits.ts +0 -72
  100. package/src/core/images.ts +0 -46
  101. package/src/core/integration-health.ts +0 -34
  102. package/src/core/json-event.ts +0 -131
  103. package/src/core/json-pointer.ts +0 -46
  104. package/src/core/keymap.ts +0 -153
  105. package/src/core/lsp-framing.ts +0 -34
  106. package/src/core/mcp-config.ts +0 -68
  107. package/src/core/model-picker.ts +0 -64
  108. package/src/core/model-preferences.ts +0 -28
  109. package/src/core/model-replay.ts +0 -22
  110. package/src/core/model-scope.ts +0 -53
  111. package/src/core/note-guidance.ts +0 -34
  112. package/src/core/oauth-fields.ts +0 -44
  113. package/src/core/observation-view.ts +0 -152
  114. package/src/core/outcome.ts +0 -44
  115. package/src/core/overflow.ts +0 -59
  116. package/src/core/packages.ts +0 -47
  117. package/src/core/patch.ts +0 -130
  118. package/src/core/path-suggestions.ts +0 -59
  119. package/src/core/permissions.ts +0 -410
  120. package/src/core/process-receipt.ts +0 -105
  121. package/src/core/process-state.ts +0 -37
  122. package/src/core/program-status.ts +0 -40
  123. package/src/core/progress.ts +0 -150
  124. package/src/core/project.ts +0 -431
  125. package/src/core/prompt-policy.ts +0 -46
  126. package/src/core/prompts.ts +0 -111
  127. package/src/core/question.ts +0 -94
  128. package/src/core/queue.ts +0 -78
  129. package/src/core/read-bounds.ts +0 -24
  130. package/src/core/read-observation.ts +0 -106
  131. package/src/core/recovery-sources.ts +0 -74
  132. package/src/core/regex-dialect.ts +0 -25
  133. package/src/core/request-budget.ts +0 -23
  134. package/src/core/request-progress.ts +0 -87
  135. package/src/core/request-state.ts +0 -273
  136. package/src/core/resource-selection.ts +0 -36
  137. package/src/core/response-recovery.ts +0 -59
  138. package/src/core/resume.ts +0 -59
  139. package/src/core/review.ts +0 -68
  140. package/src/core/ripgrep-release.ts +0 -64
  141. package/src/core/runtime-target.ts +0 -94
  142. package/src/core/scheduling.ts +0 -8
  143. package/src/core/search-observation.ts +0 -97
  144. package/src/core/secrets.ts +0 -188
  145. package/src/core/session-archive.ts +0 -4
  146. package/src/core/session-browser.ts +0 -82
  147. package/src/core/session-entry.ts +0 -61
  148. package/src/core/session-exit.ts +0 -42
  149. package/src/core/session-format.ts +0 -169
  150. package/src/core/session-position.ts +0 -26
  151. package/src/core/session-tree.ts +0 -147
  152. package/src/core/settings.ts +0 -192
  153. package/src/core/skills.ts +0 -165
  154. package/src/core/sql-query.ts +0 -211
  155. package/src/core/stall.ts +0 -128
  156. package/src/core/stop-report.ts +0 -68
  157. package/src/core/text-normalize.ts +0 -81
  158. package/src/core/theme.ts +0 -166
  159. package/src/core/thinking.ts +0 -26
  160. package/src/core/tokens.ts +0 -151
  161. package/src/core/tool-contract.ts +0 -153
  162. package/src/core/tool-discovery.ts +0 -87
  163. package/src/core/tool-failure.ts +0 -81
  164. package/src/core/tool-input.ts +0 -44
  165. package/src/core/transcript.ts +0 -108
  166. package/src/core/tui-mode.ts +0 -29
  167. package/src/core/update-check.ts +0 -157
  168. package/src/core/version.ts +0 -55
  169. package/src/core/web-content.ts +0 -80
  170. package/src/core/work-state.ts +0 -154
  171. package/src/core/workflow-mode.ts +0 -34
  172. package/src/core/working-set.ts +0 -119
  173. package/src/core/yaml.ts +0 -282
  174. package/src/runtime/acp-agent.ts +0 -289
  175. package/src/runtime/agent-profiles.ts +0 -21
  176. package/src/runtime/auth.ts +0 -367
  177. package/src/runtime/blind-check.ts +0 -36
  178. package/src/runtime/branch-jobs.ts +0 -36
  179. package/src/runtime/catalog.ts +0 -285
  180. package/src/runtime/clipboard.ts +0 -125
  181. package/src/runtime/command-artifacts.ts +0 -64
  182. package/src/runtime/connection-reuse.ts +0 -40
  183. package/src/runtime/control-client.ts +0 -149
  184. package/src/runtime/control-connection.ts +0 -139
  185. package/src/runtime/counter-check.ts +0 -74
  186. package/src/runtime/environment-tools.ts +0 -60
  187. package/src/runtime/evidence-notes.ts +0 -35
  188. package/src/runtime/exec-sessions.ts +0 -456
  189. package/src/runtime/execution-control.ts +0 -40
  190. package/src/runtime/extension-loader.ts +0 -37
  191. package/src/runtime/extension-ui.ts +0 -47
  192. package/src/runtime/extensions.ts +0 -370
  193. package/src/runtime/formatter-tool.ts +0 -38
  194. package/src/runtime/formatter.ts +0 -50
  195. package/src/runtime/foundation.ts +0 -116
  196. package/src/runtime/history-cache.ts +0 -57
  197. package/src/runtime/history-tools.ts +0 -168
  198. package/src/runtime/images.ts +0 -80
  199. package/src/runtime/integrations.ts +0 -216
  200. package/src/runtime/loop.ts +0 -3108
  201. package/src/runtime/lsp-client.ts +0 -201
  202. package/src/runtime/mcp-client.ts +0 -264
  203. package/src/runtime/mcp-oauth.ts +0 -168
  204. package/src/runtime/mcp-schema.ts +0 -51
  205. package/src/runtime/model-config.ts +0 -232
  206. package/src/runtime/model-data.LICENSE +0 -27
  207. package/src/runtime/model-data.ts +0 -21
  208. package/src/runtime/model-discovery.ts +0 -208
  209. package/src/runtime/model-replay.ts +0 -10
  210. package/src/runtime/model-scope.ts +0 -20
  211. package/src/runtime/model-sources.ts +0 -151
  212. package/src/runtime/model.ts +0 -686
  213. package/src/runtime/models-fetch.ts +0 -57
  214. package/src/runtime/oauth-device.ts +0 -86
  215. package/src/runtime/oauth.ts +0 -853
  216. package/src/runtime/open-image.ts +0 -42
  217. package/src/runtime/openai-responses.ts +0 -304
  218. package/src/runtime/openai.ts +0 -697
  219. package/src/runtime/output-schema.ts +0 -26
  220. package/src/runtime/package-manager.ts +0 -125
  221. package/src/runtime/patch-tool.ts +0 -38
  222. package/src/runtime/permission-resources.ts +0 -18
  223. package/src/runtime/pi-provider.ts +0 -423
  224. package/src/runtime/program-status.ts +0 -38
  225. package/src/runtime/progress.ts +0 -26
  226. package/src/runtime/provider-access.ts +0 -21
  227. package/src/runtime/provider-connect.ts +0 -109
  228. package/src/runtime/provider-presets.ts +0 -98
  229. package/src/runtime/provider-protocols.ts +0 -29
  230. package/src/runtime/provider-request.ts +0 -87
  231. package/src/runtime/provider-tool-policy.ts +0 -33
  232. package/src/runtime/provider.ts +0 -580
  233. package/src/runtime/question-tool.ts +0 -62
  234. package/src/runtime/retry.ts +0 -447
  235. package/src/runtime/review-tool.ts +0 -48
  236. package/src/runtime/rich-text.ts +0 -36
  237. package/src/runtime/runtime-target.ts +0 -149
  238. package/src/runtime/secrets.ts +0 -79
  239. package/src/runtime/self-update.ts +0 -74
  240. package/src/runtime/session-control.ts +0 -265
  241. package/src/runtime/session-hub.ts +0 -85
  242. package/src/runtime/stream-idle.ts +0 -80
  243. package/src/runtime/tool-broker.ts +0 -137
  244. package/src/runtime/tool-discovery.ts +0 -58
  245. package/src/runtime/tool-queue.ts +0 -77
  246. package/src/runtime/tools.ts +0 -1820
  247. package/src/runtime/turn-effects.ts +0 -31
  248. package/src/runtime/update-check.ts +0 -48
  249. package/src/runtime/usage.ts +0 -253
  250. package/src/runtime/version.ts +0 -12
  251. package/src/runtime/web-http.ts +0 -56
  252. package/src/runtime/web-tools.ts +0 -88
  253. package/src/runtime/working-notes.ts +0 -60
  254. package/src/runtime/workspace-lifecycle.ts +0 -141
  255. package/src/store/acceptance.ts +0 -15
  256. package/src/store/agent-profile-files.ts +0 -13
  257. package/src/store/atomic.ts +0 -31
  258. package/src/store/auth-store.ts +0 -50
  259. package/src/store/blind-checks.ts +0 -32
  260. package/src/store/blobs.ts +0 -143
  261. package/src/store/checkpoint-history.ts +0 -32
  262. package/src/store/command-artifacts.ts +0 -96
  263. package/src/store/command.ts +0 -111
  264. package/src/store/config.ts +0 -69
  265. package/src/store/context-files.ts +0 -436
  266. package/src/store/file-checkpoints.ts +0 -108
  267. package/src/store/file-index.ts +0 -174
  268. package/src/store/foundation-config.ts +0 -24
  269. package/src/store/git-checkpoints.ts +0 -67
  270. package/src/store/git-status.ts +0 -15
  271. package/src/store/history-search.ts +0 -19
  272. package/src/store/log.ts +0 -725
  273. package/src/store/lsp-document.ts +0 -30
  274. package/src/store/mcp-credentials.ts +0 -85
  275. package/src/store/models.ts +0 -60
  276. package/src/store/observations.ts +0 -224
  277. package/src/store/packages.ts +0 -98
  278. package/src/store/patch.ts +0 -92
  279. package/src/store/permission-paths.ts +0 -34
  280. package/src/store/process-receipts.ts +0 -34
  281. package/src/store/query-source.ts +0 -49
  282. package/src/store/recovery.ts +0 -44
  283. package/src/store/replay.ts +0 -76
  284. package/src/store/resource-index.ts +0 -170
  285. package/src/store/resources.ts +0 -285
  286. package/src/store/restore.ts +0 -110
  287. package/src/store/review.ts +0 -109
  288. package/src/store/runtime-target.ts +0 -64
  289. package/src/store/search.ts +0 -728
  290. package/src/store/secrets.ts +0 -44
  291. package/src/store/serial.ts +0 -13
  292. package/src/store/session-archive.ts +0 -165
  293. package/src/store/session-browser.ts +0 -68
  294. package/src/store/session-position.ts +0 -27
  295. package/src/store/sessions.ts +0 -525
  296. package/src/store/source-cancellation.ts +0 -6
  297. package/src/store/tool-binaries.ts +0 -90
  298. package/src/store/trust.ts +0 -82
  299. package/src/store/update-cache.ts +0 -36
  300. package/src/store/user-settings.ts +0 -30
  301. package/src/store/workspace-clone.ts +0 -42
  302. package/src/store/workspace-observation.ts +0 -113
  303. package/src/store/workspace.ts +0 -115
  304. package/src/store/worktree.ts +0 -278
  305. package/src/tui/ansi-spans.ts +0 -126
  306. package/src/tui/diff.ts +0 -160
  307. package/src/tui/exhaustive.ts +0 -14
  308. package/src/tui/fuzzy.ts +0 -75
  309. package/src/tui/style.ts +0 -208
  310. package/src/tui/ui/atomic-draft.ts +0 -121
  311. package/src/tui/ui/branch-panel.ts +0 -47
  312. package/src/tui/ui/canvas.ts +0 -96
  313. package/src/tui/ui/colors.ts +0 -70
  314. package/src/tui/ui/composer.ts +0 -199
  315. package/src/tui/ui/control-panel.ts +0 -25
  316. package/src/tui/ui/control-sidebar.ts +0 -49
  317. package/src/tui/ui/dialog.ts +0 -398
  318. package/src/tui/ui/draft.ts +0 -23
  319. package/src/tui/ui/duration.ts +0 -16
  320. package/src/tui/ui/edge.ts +0 -186
  321. package/src/tui/ui/image-size.ts +0 -44
  322. package/src/tui/ui/ink.ts +0 -210
  323. package/src/tui/ui/items.ts +0 -42
  324. package/src/tui/ui/keys.ts +0 -40
  325. package/src/tui/ui/layout.ts +0 -74
  326. package/src/tui/ui/list-model.ts +0 -86
  327. package/src/tui/ui/list-view.ts +0 -172
  328. package/src/tui/ui/mascot.ts +0 -67
  329. package/src/tui/ui/messages.ts +0 -518
  330. package/src/tui/ui/motion.ts +0 -28
  331. package/src/tui/ui/paths.ts +0 -10
  332. package/src/tui/ui/pictures.ts +0 -108
  333. package/src/tui/ui/prompt-history.ts +0 -60
  334. package/src/tui/ui/screen.ts +0 -579
  335. package/src/tui/ui/status.ts +0 -403
  336. package/src/tui/ui/styled.ts +0 -85
  337. package/src/tui/ui/tool-card.ts +0 -150
  338. package/src/tui/width.ts +0 -567
  339. package/src/vendor/pi-rich/LICENSE.upstream +0 -21
  340. package/src/vendor/pi-rich/UPSTREAM.json +0 -17
  341. package/src/vendor/pi-rich/clipboard-command.ts +0 -44
  342. package/src/vendor/pi-rich/exif-orientation.ts +0 -182
  343. package/src/vendor/pi-rich/html.ts +0 -51
  344. package/src/vendor/pi-rich/image-convert.ts +0 -49
  345. package/src/vendor/pi-rich/image-resize-core.ts +0 -164
  346. package/src/vendor/pi-rich/image-resize-worker.ts +0 -42
  347. package/src/vendor/pi-rich/image-resize.ts +0 -123
  348. package/src/vendor/pi-rich/mime.ts +0 -116
  349. package/src/vendor/pi-rich/photon.ts +0 -139
  350. package/src/vendor/pi-rich/syntax-highlight.ts +0 -214
package/src/apps/input.ts DELETED
@@ -1,312 +0,0 @@
1
- /**
2
- * Expand @file and !cmd in a user prompt before the line is sent.
3
- *
4
- * These are the user's own keystrokes, not the model's: a shared path with
5
- * the bash tool would either grant the model the user's authority or make
6
- * the user ask permission to run their own shell.
7
- *
8
- * Shell commands trigger only at line start. Explicit @ references may
9
- * occur inline; emails and code literals remain ordinary prompt text.
10
- */
11
-
12
- import { readFile } from "node:fs/promises";
13
- import { isAbsolute, relative, resolve, sep } from "node:path";
14
- import { fuzzyFilter } from "../tui/fuzzy.ts";
15
- import type { FileIndex } from "../store/file-index.ts";
16
- import { fileReferences } from "../core/file-references.ts";
17
- import type { ImageRef } from "../core/images.ts";
18
-
19
- /**
20
- * Per-file cap for @ expansion. Assumed, not measured: 256KB of one file
21
- * already crowds a turn; a silent half-file would let the model invent
22
- * the rest. Hitting it truncates and names the file.
23
- */
24
- export const MAX_EXPAND_FILE_BYTES = 256 * 1024;
25
-
26
- /**
27
- * Bound on captured command output. Assumed, not measured: a command that
28
- * dumps a build log would otherwise be an unbounded allocation. Truncated
29
- * output still carries the exit code -- the exit line is appended after
30
- * the body is capped, so a cut cannot hide whether the command passed.
31
- */
32
- export const MAX_COMMAND_OUTPUT_BYTES = 64 * 1024;
33
-
34
- /**
35
- * Timeout for !cmd. Assumed, not measured: long enough for a test suite,
36
- * short enough that a hung command cannot stall the editor. On expiry the
37
- * process is killed and the timeout is reported as a nonzero exit.
38
- */
39
- export const COMMAND_TIMEOUT_MS = 120_000;
40
-
41
- export interface ExpandContext {
42
- readonly attachImage?: (path: string) => Promise<ImageRef | null>;
43
- readonly workspace: string;
44
- readonly index: FileIndex;
45
- /** Terminal writer for !!cmd (and user-visible notices). */
46
- readonly say: (text: string) => void;
47
- readonly timeoutMs?: number;
48
- readonly maxFileBytes?: number;
49
- readonly maxCommandBytes?: number;
50
- }
51
-
52
- export interface ExpandResult {
53
- readonly images?: readonly ImageRef[];
54
- /** Text to send to the model. Empty when nothing should be sent. */
55
- readonly message: string;
56
- /** False when the input produced no model-facing text (a lone !!cmd). */
57
- readonly send: boolean;
58
- }
59
-
60
- export function matchFiles(index: FileIndex, query: string): string[] {
61
- return fuzzyFilter(index.paths, query);
62
- }
63
-
64
- export async function expandInput(text: string, ctx: ExpandContext): Promise<ExpandResult> {
65
- const out: string[] = [];
66
- const images: ImageRef[] = [];
67
- const attachment = ctx.attachImage;
68
- if (attachment) ctx = { ...ctx, attachImage: async path => { const image = await attachment(path); if (image) images.push(image); return image; } };
69
- const refs = fileReferences(text);
70
- let offset = 0;
71
- for (const line of text.split(/\r?\n/)) {
72
- const start = offset;
73
- offset += line.length + (text.slice(offset + line.length).startsWith("\r\n") ? 2 : 1);
74
- if (line.startsWith("!!")) {
75
- ctx.say(await runUserCommand(line.slice(2), ctx));
76
- continue;
77
- }
78
- if (line.startsWith("!")) {
79
- const rendered = await runUserCommand(line.slice(1), ctx);
80
- ctx.say(rendered);
81
- out.push(rendered);
82
- continue;
83
- }
84
- if (line.trim() === "@") {
85
- out.push(skip(ctx, "@", "empty file reference"));
86
- continue;
87
- }
88
- const here = refs.filter((ref) => ref.start >= start && ref.end <= start + line.length);
89
- if (here.length === 0) { out.push(line); continue; }
90
- let end = 0;
91
- for (const ref of here) {
92
- const prefix = line.slice(end, ref.start - start);
93
- if (prefix.trim()) out.push(prefix.trimEnd());
94
- out.push(await expandReference(ref.path, ctx));
95
- end = ref.end - start;
96
- }
97
- if (line.slice(end).trim()) out.push(line.slice(end).trimStart());
98
- }
99
- const message = out.join("\n");
100
- return { message, send: message.trim().length > 0 || images.length > 0, ...(images.length ? { images } : {}) };
101
- }
102
-
103
- async function expandReference(ref: string, ctx: ExpandContext): Promise<string> {
104
- const abs = confine(ctx.workspace, ref);
105
- if (abs === null) return skip(ctx, ref, "outside the workspace");
106
-
107
- if (ctx.attachImage && /\.(png|jpe?g|gif|webp|bmp)$/i.test(abs)) {
108
- const image = await ctx.attachImage(abs).catch(error => { if ((error as { code?: string }).code === "ENOENT") return null; throw error; });
109
- if (image) return `[Image: ${image.name}, ${image.width}x${image.height}]`;
110
- }
111
-
112
- const got = await tryRead(abs);
113
- if (got.status === "ok") return renderFile(posix(ref), got, ctx);
114
- if (got.reason !== "not found") return skip(ctx, posix(ref), got.reason);
115
-
116
- // Typed path missed; a unique fuzzy hit from the index is the query case.
117
- const hits = matchFiles(ctx.index, ref);
118
- if (hits.length === 1 && hits[0] !== posix(ref)) {
119
- const path = hits[0]!;
120
- const hitAbs = confine(ctx.workspace, path);
121
- if (hitAbs === null) return skip(ctx, path, "outside the workspace");
122
- if (ctx.attachImage && /\.(png|jpe?g|gif|webp|bmp)$/i.test(hitAbs)) {
123
- const image = await ctx.attachImage(hitAbs);
124
- if (image) return `[Image: ${image.name}, ${image.width}x${image.height}]`;
125
- }
126
- const hit = await tryRead(hitAbs);
127
- if (hit.status === "ok") return renderFile(path, hit, ctx);
128
- return skip(ctx, path, hit.reason);
129
- }
130
- if (hits.length > 1) return skip(ctx, ref, `ambiguous (${hits.length} matches)`);
131
- return skip(ctx, posix(ref), got.reason);
132
- }
133
-
134
- function posix(rel: string): string {
135
- return rel.split(sep).join("/");
136
- }
137
-
138
- function confine(workspace: string, rel: string): string | null {
139
- if (rel.length === 0) return null;
140
- const abs = resolve(workspace, rel);
141
- const inside = relative(workspace, abs);
142
- if (inside === "" || inside.startsWith(`..${sep}`) || inside === ".." || isAbsolute(inside)) {
143
- return null;
144
- }
145
- return abs;
146
- }
147
-
148
- type ReadResult =
149
- | { status: "ok"; text: string; bytes: number }
150
- | { status: "skip"; reason: string };
151
-
152
- async function tryRead(abs: string): Promise<ReadResult> {
153
- let buf: Buffer;
154
- try {
155
- buf = await readFile(abs);
156
- } catch (err) {
157
- const code = (err as { code?: string }).code;
158
- if (code === "ENOENT") return { status: "skip", reason: "not found" };
159
- if (code === "EACCES") return { status: "skip", reason: "permission denied" };
160
- if (code === "EISDIR") return { status: "skip", reason: "is a directory" };
161
- const message = err instanceof Error ? err.message : String(err);
162
- return { status: "skip", reason: message };
163
- }
164
- try {
165
- const text = new TextDecoder("utf-8", { fatal: true }).decode(buf);
166
- return { status: "ok", text, bytes: buf.byteLength };
167
- } catch {
168
- return { status: "skip", reason: "not valid UTF-8" };
169
- }
170
- }
171
-
172
- function renderFile(
173
- path: string,
174
- got: Extract<ReadResult, { status: "ok" }>,
175
- ctx: ExpandContext,
176
- ): string {
177
- const cap = ctx.maxFileBytes ?? MAX_EXPAND_FILE_BYTES;
178
- let content = got.text;
179
- let truncated = false;
180
- let omitted = 0;
181
- if (got.bytes > cap) {
182
- content = truncateUtf8(got.text, cap);
183
- truncated = true;
184
- omitted = Math.max(0, got.bytes - Buffer.byteLength(content, "utf8"));
185
- ctx.say(`[truncated ${path}: ${omitted} bytes omitted]`);
186
- }
187
- const inner = truncated
188
- ? `${content}\n[truncated: ${path} exceeded a size limit; ${omitted} bytes omitted]`
189
- : content;
190
- return `<file path="${escapeAttr(path)}">\n${inner}\n</file>`;
191
- }
192
-
193
- function skip(ctx: ExpandContext, path: string, reason: string): string {
194
- const notice = `[skipped ${path}: ${reason}]`;
195
- ctx.say(notice);
196
- return notice;
197
- }
198
-
199
- function escapeAttr(value: string): string {
200
- return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;");
201
- }
202
-
203
- function truncateUtf8(text: string, maxBytes: number): string {
204
- if (maxBytes <= 0) return "";
205
- const buf = Buffer.from(text, "utf8");
206
- if (buf.byteLength <= maxBytes) return text;
207
- let end = maxBytes;
208
- // Do not split a multibyte sequence: a dangling continuation byte is
209
- // not valid UTF-8, and the model would see a replacement character.
210
- while (end > 0 && (buf[end]! & 0xc0) === 0x80) end--;
211
- return buf.subarray(0, end).toString("utf8");
212
- }
213
-
214
- async function runUserCommand(raw: string, ctx: ExpandContext): Promise<string> {
215
- const cmd = raw.trim();
216
- if (cmd.length === 0) return skip(ctx, "!", "empty command");
217
-
218
- const timeoutMs = ctx.timeoutMs ?? COMMAND_TIMEOUT_MS;
219
- const maxBytes = ctx.maxCommandBytes ?? MAX_COMMAND_OUTPUT_BYTES;
220
-
221
- const proc = Bun.spawn(["bash", "-lc", cmd], {
222
- cwd: ctx.workspace,
223
- stdout: "pipe",
224
- stderr: "pipe",
225
- stdin: "ignore",
226
- });
227
-
228
- let timedOut = false;
229
- let escalate: ReturnType<typeof setTimeout> | null = null;
230
- const timer = setTimeout(() => {
231
- timedOut = true;
232
- proc.kill();
233
- escalate = setTimeout(() => proc.kill("SIGKILL"), 1000);
234
- }, timeoutMs);
235
-
236
- let stdout = "";
237
- let stderr = "";
238
- let exit = 1;
239
- try {
240
- const [out, err] = await Promise.all([
241
- readCapped(proc.stdout, maxBytes),
242
- readCapped(proc.stderr, maxBytes),
243
- ]);
244
- stdout = out.text;
245
- stderr = err.text;
246
- if (out.truncated) stdout = `${stdout}\n[stdout truncated]`;
247
- if (err.truncated) stderr = `${stderr}\n[stderr truncated]`;
248
- const rawExit = await proc.exited;
249
- exit = rawExit ?? 1;
250
- // A kill that somehow reports success would put the model back to
251
- // guessing. GNU timeout uses 124; never report 0 after a timeout.
252
- if (timedOut && exit === 0) exit = 124;
253
- } finally {
254
- clearTimeout(timer);
255
- if (escalate !== null) clearTimeout(escalate);
256
- }
257
-
258
- const parts: string[] = [`$ ${cmd}`];
259
- if (stdout.length > 0) parts.push(stripTrailingNewline(stdout));
260
- if (stderr.length > 0) parts.push(stripTrailingNewline(stderr));
261
- if (timedOut) parts.push(`[timed out after ${timeoutMs}ms]`);
262
- // Exit last so a capped body cannot cut the one number D11 needs.
263
- parts.push(`exit ${exit}`);
264
- return parts.join("\n");
265
- }
266
-
267
- function stripTrailingNewline(s: string): string {
268
- return s.endsWith("\n") ? s.slice(0, -1) : s;
269
- }
270
-
271
- async function readCapped(
272
- stream: ReadableStream<Uint8Array> | number | undefined | null,
273
- max: number,
274
- ): Promise<{ text: string; truncated: boolean }> {
275
- if (stream === null || stream === undefined || typeof stream === "number") {
276
- return { text: "", truncated: false };
277
- }
278
- const reader = stream.getReader();
279
- const chunks: Uint8Array[] = [];
280
- let n = 0;
281
- let truncated = false;
282
- while (true) {
283
- const { done, value } = await reader.read();
284
- if (done) break;
285
- if (value === undefined) continue;
286
- if (n >= max) {
287
- truncated = true;
288
- continue;
289
- }
290
- const take = Math.min(value.length, max - n);
291
- chunks.push(take === value.length ? value : value.subarray(0, take));
292
- n += take;
293
- if (take < value.length) truncated = true;
294
- }
295
- const buf = new Uint8Array(n);
296
- let off = 0;
297
- for (const c of chunks) {
298
- buf.set(c, off);
299
- off += c.length;
300
- }
301
- return { text: new TextDecoder().decode(buf), truncated };
302
- }
303
-
304
- /** True when the whole submission is `@` or `@query`, so the TUI can pick. */
305
- export function bareAtQuery(text: string): string | null {
306
- const m = /^@(\S*)$/.exec(text);
307
- return m === null ? null : (m[1] ?? "");
308
- }
309
-
310
- export function isExactIndexedPath(index: FileIndex, query: string): boolean {
311
- return query.length > 0 && index.paths.includes(query);
312
- }
@@ -1,69 +0,0 @@
1
- import { modelKey } from "../core/session-position.ts";
2
- import { asThinkingLevel, clampThinkingLevel, supportedThinkingLevels } from "../core/thinking.ts";
3
- import { createProvider, providerSessionId } from "../runtime/catalog.ts";
4
- import { scopedModels } from "../runtime/model-scope.ts";
5
- import { storedIsOAuth, type Resolved } from "../runtime/auth.ts";
6
- import { outputTokenBudget, type ModelSpec } from "../runtime/model.ts";
7
- import { providerAccess, setProviderAccess } from "../runtime/provider-access.ts";
8
- import { providerReplayKey } from "../runtime/model-replay.ts";
9
- import type { Provider, ProviderConfig } from "../runtime/provider.ts";
10
- import { retryHandler, type App } from "./startup.ts";
11
-
12
- export interface ModelCatalogStage {
13
- readonly models: readonly ModelSpec[];
14
- readonly config: ProviderConfig;
15
- readonly credential: Resolved;
16
- readonly next?: Provider;
17
- readonly needsCut: boolean;
18
- readonly warning?: string;
19
- }
20
- export interface ModelCatalogSelection {
21
- readonly model?: string;
22
- readonly provider?: string;
23
- readonly thinking?: string;
24
- readonly requireCredential?: boolean;
25
- readonly providerFactory?: typeof createProvider;
26
- }
27
-
28
- /** Resolve and construct locally; publication and model requests belong to the checkpoint owner. */
29
- export function stageModelCatalog(app: App, models: readonly ModelSpec[], selection: ModelCatalogSelection = {}): ModelCatalogStage {
30
- const previous = app.providerConfig.spec;
31
- const requested = selection.model === undefined ? undefined : scopedModels([selection.model],
32
- selection.provider ? models.filter(model => model.provider === selection.provider) : models, true).entries[0]?.model;
33
- if (selection.model !== undefined && !requested) throw Error(`Unknown profile model: ${selection.provider ?? ""}/${selection.model}`);
34
- const spec = requested ?? models.find(candidate => modelKey(candidate) === modelKey(previous));
35
- if (!spec) {
36
- const invalid = { ...previous }, warning = `${modelKey(previous)} is disabled or absent from the catalog; choose /model before sending`;
37
- setProviderAccess(invalid, { invalid: warning });
38
- return { models, config: { ...app.providerConfig, spec: invalid }, credential: { ok: false, reason: warning }, needsCut: false, warning };
39
- }
40
- const requestedThinking = selection.thinking ? asThinkingLevel(selection.thinking) : undefined;
41
- if (selection.thinking && (!requestedThinking || !supportedThinkingLevels(spec).includes(requestedThinking))) throw Error(`thinking ${selection.thinking} is not supported by ${modelKey(spec)}`);
42
- const effort = clampThinkingLevel(spec, requestedThinking ?? app.providerConfig.effort);
43
- const unchanged = JSON.stringify([previous, providerAccess(previous)]) === JSON.stringify([spec, providerAccess(spec)]) && effort === app.providerConfig.effort;
44
- const credential = selection.requireCredential || !unchanged ? app.credentialOf(spec) : app.credential;
45
- if (selection.requireCredential && !credential.ok) throw Error(credential.reason);
46
- if (unchanged) return { models, config: app.providerConfig, credential, needsCut: false };
47
- const config = { ...app.providerConfig, spec, maxTokens: outputTokenBudget(spec), effort };
48
- const next = (selection.providerFactory ?? createProvider)(config, credential.ok ? credential.key : "", retryHandler(app.io), {
49
- sessionId: providerSessionId(app.session.config.logDir), effects: app.effects, oauth: !app.args.apiKey && storedIsOAuth(app.auth, spec.provider),
50
- });
51
- return { models, config, credential, next, needsCut: providerReplayKey(previous) !== providerReplayKey(spec) };
52
- }
53
-
54
- /** The caller has committed any required checkpoint before publishing these process-local views. */
55
- export function applyModelCatalog(app: App, stage: ModelCatalogStage): void {
56
- if (stage.next) app.session.provider = stage.next;
57
- app.providerConfig = stage.config; app.credential = stage.credential; app.catalog = stage.models;
58
- if (stage.warning) app.io.say(app.io.palette.yellow(` ${stage.warning}`));
59
- if (app.ui) app.ui.settings = { ...app.ui.settings, thinking: stage.config.effort };
60
- }
61
-
62
- /** Publish the active profile with its directory after the shared staging path validates it. */
63
- export async function publishModelCatalog(app: App, models: readonly ModelSpec[], checkpointed = false): Promise<boolean> {
64
- if (app.session.running) throw Error("cannot reload model configuration during a turn");
65
- const stage = stageModelCatalog(app, models);
66
- if (stage.next && stage.needsCut && !checkpointed) await app.session.switchModel(stage.next);
67
- applyModelCatalog(app, stage);
68
- return stage.needsCut && !checkpointed;
69
- }
@@ -1,20 +0,0 @@
1
- /** User intent owns personal defaults; CLI overrides and restored/extension state only own a session. */
2
- import { mergeSettings, type Settings } from "../core/settings.ts";
3
- import { writeUserSettings } from "../store/resources.ts";
4
- import { describeError } from "./render.ts";
5
- import type { App } from "./startup.ts";
6
-
7
- export type PreferenceOrigin = "user" | "session";
8
-
9
- export async function commitUserSettings(app: App, patch: Settings): Promise<boolean> {
10
- const ui = app.ui;
11
- if (!ui) return false;
12
- ui.settings = mergeSettings({ builtin: ui.settings, user: patch });
13
- try { await writeUserSettings(patch); }
14
- catch (error) {
15
- app.io.say(app.io.palette.yellow(` The change is active in this session, but could not save settings for the next startup: ${describeError(error)}`));
16
- return false;
17
- }
18
- ui.userOverrides = mergeSettings({ builtin: ui.userOverrides, user: patch });
19
- return true;
20
- }
@@ -1,9 +0,0 @@
1
- /** Project trust decisions; the picker itself is apps/ui/standalone.ts (startup) and apps/ui/app.ts (/trust). */
2
- import { dirname } from "node:path";
3
- import { saveTrust } from "../store/trust.ts";
4
-
5
- export type TrustChoice = "once" | "here" | "parent" | "never";
6
- export async function applyTrustChoice(workspace: string, choice: TrustChoice): Promise<boolean> {
7
- if (choice !== "once") await saveTrust(choice === "parent" ? dirname(workspace) : workspace, choice !== "never");
8
- return choice !== "never";
9
- }