@visual-z/xeno 0.8.4 → 0.9.1

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 +19 -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
@@ -1,38 +0,0 @@
1
- import { encodeProgramStatus, PROGRAM_STATUS_QUERY, programStatusReply, type ProgramStatus } from "../core/program-status.ts";
2
-
3
- /** Terminal transport only. Capability detection has no relationship to agent evidence. */
4
- export class ProgramStatusReporter {
5
- private readonly write: (text: string) => void;
6
- private readonly id: string;
7
- private readonly mode: "auto" | "on" | "off";
8
- private supported = false;
9
- private started = false;
10
- private closed = false;
11
- private last: ProgramStatus = { state: "idle" };
12
- private emitted = "";
13
- constructor(options: { write: (text: string) => void; id: string; tty: boolean; mode?: string; multiplexed?: boolean }) {
14
- this.write = options.write; this.id = options.id;
15
- this.mode = !options.tty || options.mode === "0" ? "off" : options.mode === "1" ? "on" : options.multiplexed ? "off" : "auto";
16
- }
17
- start(): void {
18
- if (this.started || this.closed) return; this.started = true;
19
- if (this.mode === "auto") this.write(PROGRAM_STATUS_QUERY);
20
- if (this.mode === "on") { this.supported = true; this.flush(); }
21
- }
22
- receive(sequence: string): void {
23
- if (this.closed || !this.started || this.mode !== "auto" || !programStatusReply(sequence)) return;
24
- this.supported = true; this.flush();
25
- }
26
- update(status: ProgramStatus): void { this.last = { ...status }; if (this.started) this.flush(); }
27
- private flush(): void {
28
- if (this.closed || !this.supported) return;
29
- const sequence = encodeProgramStatus(this.last, this.id);
30
- if (sequence !== this.emitted) { this.emitted = sequence; this.write(sequence); }
31
- }
32
- close(): void {
33
- if (this.closed) return;
34
- // done/error are durable terminal records. Clear only our own transient record.
35
- if (this.supported && this.last.state !== "done" && this.last.state !== "error") this.write(encodeProgramStatus({ state: "clear" }, this.id));
36
- this.closed = true;
37
- }
38
- }
@@ -1,26 +0,0 @@
1
- /** Display updates are transient; only the completed operation may become a durable result. */
2
- export async function* withProgress<T, E>(start: (emit: (event: E) => void) => Promise<T>): AsyncGenerator<E, T> {
3
- const queue: E[] = [];
4
- let wake: (() => void) | undefined;
5
- let accepting = true;
6
- const box: { result?: { ok: true; value: T } | { ok: false; error: unknown } } = {};
7
- const pending = start(event => { if (accepting) queue.push(event); wake?.(); }).then(
8
- value => { box.result = { ok: true, value }; wake?.(); },
9
- error => { box.result = { ok: false, error }; wake?.(); },
10
- );
11
- try {
12
- while (box.result === undefined || queue.length) {
13
- if (queue.length) { yield queue.shift()!; continue; }
14
- await new Promise<void>(resolve => { wake = resolve; });
15
- wake = undefined;
16
- }
17
- } finally {
18
- accepting = false;
19
- queue.length = 0;
20
- await pending;
21
- }
22
- await pending;
23
- if (!box.result) throw new Error("operation settled without a result");
24
- if (!box.result.ok) throw box.result.error;
25
- return box.result.value;
26
- }
@@ -1,21 +0,0 @@
1
- import type { ModelSpec } from "./model.ts";
2
-
3
- /** Secrets are not model metadata. Enumerating/serializing a model cannot expose config values. */
4
- export interface ProviderAccess {
5
- apiKey?: string;
6
- headers?: Record<string, string | null>;
7
- env?: Record<string, string>;
8
- auth?: false | "ambient";
9
- authHeader?: boolean | string;
10
- options?: Record<string, unknown>;
11
- body?: Record<string, unknown>;
12
- invalid?: string;
13
- }
14
- const access = new WeakMap<ModelSpec, ProviderAccess>();
15
- export function setProviderAccess(model: ModelSpec, value: ProviderAccess): void { access.set(model, value); }
16
- export function providerAccess(model: ModelSpec): ProviderAccess { return access.get(model) ?? {}; }
17
- export function hasProviderAccess(model: ModelSpec): boolean { return access.has(model); }
18
- export function copyProviderAccess(from: ModelSpec, to: ModelSpec): void {
19
- const value = access.get(from);
20
- if (value) access.set(to, value);
21
- }
@@ -1,109 +0,0 @@
1
- /**
2
- * The `models.json` edit `/provider` makes for a connected provider, as a pure transform.
3
- *
4
- * The file stays the single source of model configuration and keeps the
5
- * documented `providers.<id>` shape (docs/providers.md), so a connected
6
- * provider is indistinguishable from a hand-written one and `/reload`,
7
- * startup and `--list-models` need no second code path. Secrets never enter
8
- * it: keys go to `auth.json` (0600) or stay `$ENV` references.
9
- *
10
- * - Catalog provider: `allowlist` is the chosen ids, so the picker offers
11
- * exactly what the user enabled; ids the live listing has but the catalog
12
- * lacks are added under `models`.
13
- * - Custom endpoint: `api` + `baseUrl` + `models`. Limits/capabilities are
14
- * copied from a catalog model with the same id when the listing lacks
15
- * them (a gateway serving `claude-sonnet-4-5` has that model's limits);
16
- * price never is, because gateway billing is not the vendor's.
17
- */
18
- import type { ModelSpec } from "./model.ts";
19
- import type { ProviderApi } from "./provider-protocols.ts";
20
- import type { DiscoveredModel } from "./model-discovery.ts";
21
-
22
- export interface ConnectRequest {
23
- id: string;
24
- name: string;
25
- api: ProviderApi;
26
- baseUrl: string;
27
- /** True when the provider ships in the catalog with its own models. */
28
- catalog: boolean;
29
- /** Catalog default base URL; an equal value is not written as an override. */
30
- catalogBaseUrl?: string;
31
- keyless: boolean;
32
- /** `$VAR` / `{env:VAR}` reference kept in config instead of a stored key. */
33
- keyReference?: string;
34
- chosen: readonly DiscoveredModel[];
35
- }
36
-
37
- function record(value: unknown): value is Record<string, unknown> {
38
- return value !== null && typeof value === "object" && !Array.isArray(value);
39
- }
40
-
41
- /** The bare model name a gateway may prefix: `anthropic/claude-x` → `claude-x`. */
42
- function bare(id: string): string {
43
- return id.slice(id.lastIndexOf("/") + 1).toLowerCase();
44
- }
45
-
46
- export function catalogTwin(id: string, catalog: readonly ModelSpec[]): ModelSpec | undefined {
47
- const exact = catalog.find(model => model.id === id);
48
- if (exact) return exact;
49
- const name = bare(id);
50
- return catalog.find(model => bare(model.id) === name);
51
- }
52
-
53
- export function modelEntry(model: DiscoveredModel, catalog: readonly ModelSpec[]): Record<string, unknown> {
54
- const twin = catalogTwin(model.id, catalog);
55
- const entry: Record<string, unknown> = {};
56
- const name = model.name ?? twin?.name;
57
- if (name && name !== model.id) entry["name"] = name;
58
- const contextWindow = model.contextWindow ?? twin?.contextWindow;
59
- const maxTokens = model.maxTokens ?? twin?.maxTokens;
60
- if (contextWindow) entry["contextWindow"] = contextWindow;
61
- if (maxTokens) entry["maxTokens"] = maxTokens;
62
- if (twin?.reasoning) entry["reasoning"] = true;
63
- if (twin?.input?.includes("image")) entry["input"] = [...twin.input];
64
- return entry;
65
- }
66
-
67
- export function applyConnect(json: unknown, request: ConnectRequest, catalog: readonly ModelSpec[]): Record<string, unknown> {
68
- // The legacy flat array form is still a valid file; keep its rows.
69
- const root: Record<string, unknown> = Array.isArray(json) ? { models: json } : record(json) ? { ...json } : {};
70
- const providers = record(root["providers"]) ? { ...root["providers"] } : {};
71
- const previous = record(providers[request.id]) ? providers[request.id] as Record<string, unknown> : {};
72
- const known = new Set(catalog.filter(model => model.provider === request.id).map(model => model.id));
73
- const entry: Record<string, unknown> = { ...previous };
74
- for (const key of ["allowlist", "whitelist", "models", "disabled", "enabled"]) delete entry[key];
75
- if (!request.catalog) {
76
- entry["name"] = request.name;
77
- entry["api"] = request.api;
78
- // Unknown Chat Completions gateways: the lowest common denominator. The
79
- // `developer` role, `store` and `max_completion_tokens` are OpenAI-only
80
- // and are the usual 400s from DeepSeek-, vLLM- and one-api-style servers.
81
- if (request.api === "openai-completions" && !record(entry["compat"])) entry["compat"] = { supportsDeveloperRole: false, supportsStore: false, maxTokensField: "max_tokens" };
82
- }
83
- if (!request.catalog || request.baseUrl !== request.catalogBaseUrl) entry["baseUrl"] = request.baseUrl;
84
- else delete entry["baseUrl"];
85
- if (request.keyless) entry["auth"] = false;
86
- else if (entry["auth"] === false) delete entry["auth"];
87
- if (request.keyReference) entry["apiKey"] = request.keyReference;
88
- const models: Record<string, unknown> = {};
89
- for (const model of request.chosen) {
90
- if (request.catalog && known.has(model.id)) continue;
91
- models[model.id] = modelEntry(model, catalog);
92
- }
93
- if (Object.keys(models).length) entry["models"] = models;
94
- if (request.catalog) entry["allowlist"] = request.chosen.map(model => model.id);
95
- providers[request.id] = entry;
96
- root["providers"] = providers;
97
- return root;
98
- }
99
-
100
- /** Remove what `/provider` wrote for a provider; other providers and flat rows are untouched. */
101
- export function applyDisconnect(json: unknown, id: string): Record<string, unknown> {
102
- const root: Record<string, unknown> = Array.isArray(json) ? { models: json } : record(json) ? { ...json } : {};
103
- if (record(root["providers"])) {
104
- const providers = { ...root["providers"] };
105
- delete providers[id];
106
- root["providers"] = providers;
107
- }
108
- return root;
109
- }
@@ -1,98 +0,0 @@
1
- /**
2
- * What `/provider` offers for custom endpoints before the user types anything.
3
- *
4
- * A preset names a catalog provider (whose API, base URL and model metadata
5
- * already ship in model-data) or a custom endpoint family. Order is the
6
- * "Popular" section, modelled on OpenCode's PROVIDER_PRIORITY and magpie's
7
- * vendor list; everything else in the catalog stays reachable by search.
8
- * Key URLs are where a user actually gets a key, shown as a hint.
9
- */
10
- import { SOURCE_PROVIDERS } from "./model-data.ts";
11
- import type { ProviderApi } from "./provider-protocols.ts";
12
-
13
- export interface ProviderPreset {
14
- /** Catalog provider id, or the suggested id for a custom endpoint. */
15
- id: string;
16
- label: string;
17
- api: ProviderApi;
18
- baseUrl: string;
19
- /** Present for catalog providers: their models carry metadata already. */
20
- catalog: boolean;
21
- /** Custom endpoints ask for id and URL; keyless ones skip the key. */
22
- custom: boolean;
23
- keyless?: boolean;
24
- keyUrl?: string;
25
- hint?: string;
26
- }
27
-
28
- const POPULAR: readonly { id: string; keyUrl?: string; hint?: string }[] = [
29
- { id: "anthropic", keyUrl: "https://console.anthropic.com/settings/keys" },
30
- { id: "openai", keyUrl: "https://platform.openai.com/api-keys" },
31
- { id: "google", keyUrl: "https://aistudio.google.com/apikey", hint: "Gemini API" },
32
- { id: "deepseek", keyUrl: "https://platform.deepseek.com/api_keys" },
33
- { id: "openrouter", keyUrl: "https://openrouter.ai/keys", hint: "one key, many vendors" },
34
- { id: "moonshotai-cn", keyUrl: "https://platform.moonshot.cn/console/api-keys", hint: "Kimi" },
35
- { id: "kimi-coding", keyUrl: "https://www.kimi.com/coding", hint: "Kimi Coding plan" },
36
- { id: "zhipuai", keyUrl: "https://open.bigmodel.cn/usercenter/apikeys", hint: "GLM" },
37
- { id: "zhipuai-coding-plan", keyUrl: "https://open.bigmodel.cn/usercenter/apikeys", hint: "GLM Coding plan" },
38
- { id: "siliconflow-cn", keyUrl: "https://cloud.siliconflow.cn/account/ak" },
39
- { id: "alibaba-cn", keyUrl: "https://bailian.console.aliyun.com/?apiKey=1", hint: "Qwen / DashScope" },
40
- { id: "volcengine", keyUrl: "https://console.volcengine.com/ark", hint: "Doubao / Ark" },
41
- { id: "minimax-cn", keyUrl: "https://platform.minimaxi.com/user-center/basic-information/interface-key" },
42
- { id: "xai", keyUrl: "https://console.x.ai" },
43
- { id: "groq", keyUrl: "https://console.groq.com/keys" },
44
- { id: "mistral", keyUrl: "https://console.mistral.ai/api-keys" },
45
- ];
46
-
47
- /** Custom families: the four wire formats a generic endpoint can speak. */
48
- export const CUSTOM_PRESETS: readonly ProviderPreset[] = [
49
- { id: "custom-openai", label: "OpenAI-compatible endpoint", api: "openai-completions", baseUrl: "https://", catalog: false, custom: true, hint: "Chat Completions · /v1/chat/completions" },
50
- { id: "custom-responses", label: "OpenAI Responses endpoint", api: "openai-responses", baseUrl: "https://", catalog: false, custom: true, hint: "Responses API · /v1/responses" },
51
- { id: "custom-anthropic", label: "Anthropic-compatible endpoint", api: "anthropic-messages", baseUrl: "https://", catalog: false, custom: true, hint: "Messages API · /v1/messages" },
52
- { id: "custom-gemini", label: "Gemini-compatible endpoint", api: "google-generative-ai", baseUrl: "https://generativelanguage.googleapis.com/v1beta", catalog: false, custom: true, hint: "generateContent" },
53
- { id: "ollama", label: "Ollama (local)", api: "openai-completions", baseUrl: "http://127.0.0.1:11434/v1", catalog: false, custom: true, keyless: true, hint: "no key needed" },
54
- { id: "lmstudio", label: "LM Studio (local)", api: "openai-completions", baseUrl: "http://127.0.0.1:1234/v1", catalog: false, custom: true, keyless: true, hint: "no key needed" },
55
- ];
56
-
57
- function fromCatalog(id: string, extra: { keyUrl?: string; hint?: string } = {}): ProviderPreset | undefined {
58
- const source = SOURCE_PROVIDERS[id];
59
- if (!source?.api || !source.baseUrl) return undefined;
60
- return { id, label: source.name, api: source.api, baseUrl: source.baseUrl, catalog: true, custom: false,
61
- ...(extra.keyUrl ? { keyUrl: extra.keyUrl } : {}), ...(extra.hint ? { hint: extra.hint } : {}) };
62
- }
63
-
64
- export function popularPresets(): ProviderPreset[] {
65
- return POPULAR.flatMap(entry => fromCatalog(entry.id, entry) ?? []);
66
- }
67
-
68
- export function presetById(id: string): ProviderPreset | undefined {
69
- return CUSTOM_PRESETS.find(preset => preset.id === id) ?? fromCatalog(id, POPULAR.find(entry => entry.id === id));
70
- }
71
-
72
- /** Provider ids are config keys and auth.json keys: OpenCode's rule. */
73
- export function validProviderId(id: string): boolean {
74
- return /^[a-z0-9][a-z0-9_-]{0,63}$/.test(id);
75
- }
76
-
77
- /**
78
- * The valid id a rejected one most likely meant ("My Gateway" → "my-gateway"),
79
- * or "" when nothing Latin survives ("网关"). Only offered in the error, never
80
- * applied silently: the id becomes a config and auth.json key the user will
81
- * type in /model, so they should see it before it is saved.
82
- */
83
- export function slugProviderId(raw: string): string {
84
- if (validProviderId(raw)) return raw;
85
- return raw.toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^[^a-z0-9]+/, "").slice(0, 64).replace(/-+$/, "");
86
- }
87
-
88
- /** A readable default id from a URL host: api.example.com → example. */
89
- export function suggestProviderId(baseUrl: string, taken: ReadonlySet<string>): string {
90
- let host = "custom";
91
- try { host = new URL(baseUrl).hostname; } catch { /* keep default */ }
92
- const parts = host.split(".").filter(part => !["api", "www", "openai", "gateway", "com", "cn", "net", "io", "ai", "org", "dev", "co"].includes(part));
93
- let base = (parts.at(-1) ?? "custom").toLowerCase().replace(/[^a-z0-9_-]/g, "-").replace(/^-+/, "") || "custom";
94
- if (/^\d/.test(base) || base === "127" || host === "localhost") base = "local";
95
- let id = base, n = 2;
96
- while (taken.has(id)) id = `${base}-${n++}`;
97
- return id;
98
- }
@@ -1,29 +0,0 @@
1
- /** Protocol vocabulary is local; SDK types and IO never enter core (D13). */
2
- export const PROVIDER_APIS = [
3
- "anthropic-messages", "openai-completions", "openai-responses", "google-generative-ai",
4
- "google-vertex", "bedrock-converse-stream", "azure-openai-responses", "openai-codex-responses",
5
- "mistral-conversations", "pi-messages",
6
- ] as const;
7
- export type ProviderApi = typeof PROVIDER_APIS[number];
8
- export function isProviderApi(value: unknown): value is ProviderApi {
9
- return typeof value === "string" && (PROVIDER_APIS as readonly string[]).includes(value);
10
- }
11
-
12
- /** SDK dispatch from OpenCode's model-level `provider.npm` before provider defaults. */
13
- export const SDK_APIS: Readonly<Record<string, ProviderApi>> = {
14
- "@ai-sdk/anthropic": "anthropic-messages",
15
- "@ai-sdk/openai": "openai-responses",
16
- "@ai-sdk/openai-compatible": "openai-completions",
17
- "@openrouter/ai-sdk-provider": "openai-completions",
18
- "@ai-sdk/groq": "openai-completions",
19
- "@ai-sdk/cerebras": "openai-completions",
20
- "@ai-sdk/deepinfra": "openai-completions",
21
- "@ai-sdk/togetherai": "openai-completions",
22
- "@ai-sdk/xai": "openai-completions",
23
- "@ai-sdk/mistral": "mistral-conversations",
24
- "@ai-sdk/google": "google-generative-ai",
25
- "@ai-sdk/google-vertex": "google-vertex",
26
- "@ai-sdk/amazon-bedrock": "bedrock-converse-stream",
27
- "@ai-sdk/azure": "azure-openai-responses",
28
- "@ai-sdk/gateway": "anthropic-messages",
29
- };
@@ -1,87 +0,0 @@
1
- import { providerEnvironment, resolveKeyValue, type ResolveEffects } from "./auth.ts";
2
- import { providerAccess } from "./provider-access.ts";
3
- import type { ModelSpec } from "./model.ts";
4
- import { GROK_PROVIDER, GROK_BASE_URL, GROK_PROTOCOL_VERSION } from "./oauth.ts";
5
- import { XENO_VERSION } from "./version.ts";
6
-
7
- const SDK_IDENTITY_HEADERS = ["x-app", "editor-version", "editor-plugin-version", "copilot-integration-id", "http-referer", "x-title"];
8
-
9
- /** D18/D16: a protocol SDK may not silently identify xeno as a different product. */
10
- function clientIdentity(headers: Headers): void {
11
- headers.set("User-Agent", `xeno/${XENO_VERSION}`);
12
- for (const name of SDK_IDENTITY_HEADERS) headers.delete(name);
13
- if (headers.has("originator")) headers.set("originator", "xeno");
14
- const beta = headers.get("anthropic-beta");
15
- if (beta) headers.set("anthropic-beta", beta.split(",").filter(value => !value.trim().startsWith("claude-code-")).join(","));
16
- }
17
-
18
- export function requestEffects(): ResolveEffects {
19
- return { env: name => process.env[name], run: command => {
20
- const result = Bun.spawnSync(["bash", "-lc", command]);
21
- if (result.exitCode !== 0) throw new Error("credential command failed");
22
- return new TextDecoder().decode(result.stdout);
23
- } };
24
- }
25
-
26
- export function providerRequest(spec: ModelSpec, key: string, effects: ResolveEffects = requestEffects(), fetchImpl: typeof fetch = globalThis.fetch, oauth = false): {
27
- baseUrl: string; env: Record<string, string>; headers: Record<string, string | null>; fetch: typeof fetch;
28
- options: Record<string, unknown>; body: Record<string, unknown>; key?: string;
29
- } {
30
- const access = providerAccess(spec);
31
- if (access.invalid) throw new Error(access.invalid);
32
- const scoped = providerEnvironment(spec, effects);
33
- const env = scoped.env;
34
- const resolve = (value: string, field: string) => {
35
- const resolved = resolveKeyValue(value, scoped.effects);
36
- if (!resolved.ok) throw new Error(`${spec.provider} ${field}: could not resolve configured value`);
37
- return resolved.key;
38
- };
39
- let baseUrl = spec.baseUrl ?? (spec.api === "anthropic-messages" ? "https://api.anthropic.com" : "");
40
- if (baseUrl) baseUrl = resolve(baseUrl, "baseUrl");
41
- if (baseUrl && !/^https?:\/\//.test(baseUrl)) throw new Error(`${spec.provider} baseUrl must use http or https`);
42
- const headers: Record<string, string | null> = { ...spec.defaultHeaders };
43
- for (const [name, value] of Object.entries(access.headers ?? {})) headers[name] = value === null ? null : resolve(value, `headers.${name}`);
44
- if (spec.provider === GROK_PROVIDER) {
45
- if (baseUrl.replace(/\/$/, "") !== GROK_BASE_URL) throw new Error("Grok account tokens require the official inference proxy");
46
- // These select the auth protocol and backend; the User-Agent still identifies xeno (D18).
47
- headers["X-XAI-Token-Auth"] = "xai-grok-cli";
48
- headers["x-grok-model-override"] = spec.wireId ?? spec.id;
49
- headers["x-grok-client-version"] = GROK_PROTOCOL_VERSION;
50
- headers["x-grok-client-identifier"] = "xeno";
51
- }
52
- const anthropicOAuth = spec.api === "anthropic-messages" && spec.provider !== "github-copilot" && (oauth || key.includes("sk-ant-oat"));
53
- const noAutomaticAuth = access.auth === false || access.authHeader === false || typeof access.authHeader === "string" || anthropicOAuth;
54
- if (noAutomaticAuth && (spec.api === "google-generative-ai" || spec.api === "google-vertex")) {
55
- throw new Error(`${spec.api} owns API-key authentication; use apiKey or an OpenAI-compatible local endpoint`);
56
- }
57
- if (noAutomaticAuth && spec.api === "bedrock-converse-stream") env["AWS_BEDROCK_SKIP_AUTH"] = "1";
58
- if (typeof access.authHeader === "string") headers[access.authHeader] = key;
59
- if (anthropicOAuth && access.auth !== false && access.authHeader !== false) {
60
- headers["authorization"] = `Bearer ${key}`;
61
- headers["anthropic-beta"] = "oauth-2025-04-20";
62
- }
63
- // Apply the same identity to native SDKs which own their HTTP stack.
64
- const identity = new Headers();
65
- for (const [name, value] of Object.entries(headers)) if (value !== null) identity.set(name, value);
66
- clientIdentity(identity);
67
- for (const name of Object.keys(headers)) if (!identity.has(name)) headers[name] = null;
68
- identity.forEach((value, name) => { headers[name] = value; });
69
- delete headers["user-agent"];
70
- headers["User-Agent"] = `xeno/${XENO_VERSION}`;
71
- // SDKs still require a key to construct local clients. The placeholder never leaves this boundary.
72
- const privateKey = noAutomaticAuth ? "xeno-keyless-local" : key || undefined;
73
- const transport = (async (input: RequestInfo | URL, init?: RequestInit) => {
74
- const request = new Request(input, init);
75
- const grokAccount = spec.provider === GROK_PROVIDER;
76
- // Check the SDK's final target too, and never forward an account token through a redirect.
77
- if (grokAccount && new URL(request.url).origin !== new URL(GROK_BASE_URL).origin) {
78
- throw new Error("Grok account tokens require the official inference proxy");
79
- }
80
- const nextHeaders = new Headers(request.headers);
81
- if (noAutomaticAuth) for (const name of ["authorization", "x-api-key", "api-key", "x-goog-api-key"]) nextHeaders.delete(name);
82
- for (const [name, value] of Object.entries(headers)) { if (value === null) nextHeaders.delete(name); else nextHeaders.set(name, value); }
83
- clientIdentity(nextHeaders);
84
- return fetchImpl(new Request(request, { headers: nextHeaders, ...(grokAccount ? { redirect: "error" } : {}) }));
85
- }) as typeof fetch;
86
- return { baseUrl, env, headers, fetch: transport, options: access.options ?? {}, body: access.body ?? {}, ...(privateKey ? { key: privateKey } : {}) };
87
- }
@@ -1,33 +0,0 @@
1
- /** D6: native request controls may change; frozen system and tool declarations may not. */
2
- import { assertNever } from "../core/exhaustive.ts";
3
- import type { Api } from "./model.ts";
4
-
5
- export interface ProviderSendOptions { toolUse: "disabled" }
6
-
7
- export function supportsToolDisabling(api: Api): boolean {
8
- // Bedrock's SDK implements none by deleting toolConfig. It cannot preserve D6.
9
- switch (api) {
10
- case "anthropic-messages": case "openai-completions": case "openai-responses": case "azure-openai-responses": case "openai-codex-responses":
11
- case "google-generative-ai": case "google-vertex": case "mistral-conversations": case "pi-messages": return true;
12
- case "bedrock-converse-stream": return false;
13
- default: return assertNever(api, "tool disabling capability");
14
- }
15
- }
16
- const object = (value: unknown): Record<string, unknown> => value !== null && typeof value === "object" && !Array.isArray(value) ? value as Record<string, unknown> : {};
17
-
18
- /** Called after provider body overrides, at the last native payload boundary. */
19
- export function disablePayloadTools(api: Api, payload: Record<string, unknown>): Record<string, unknown> {
20
- switch (api) {
21
- case "anthropic-messages": return { ...payload, tool_choice: { type: "none" } };
22
- case "openai-completions": case "openai-responses": case "azure-openai-responses": case "openai-codex-responses":
23
- return { ...payload, tool_choice: "none" };
24
- case "google-generative-ai": case "google-vertex": {
25
- const config = object(payload["config"]), toolConfig = object(config["toolConfig"]);
26
- return { ...payload, config: { ...config, toolConfig: { ...toolConfig, functionCallingConfig: { mode: "NONE" } } } };
27
- }
28
- case "mistral-conversations": return { ...payload, toolChoice: "none" };
29
- case "pi-messages": return { ...payload, options: { ...object(payload["options"]), toolChoice: "none" } };
30
- case "bedrock-converse-stream": throw new Error("Bedrock cannot disable tools while preserving frozen definitions");
31
- default: return assertNever(api, "tool disabling protocol");
32
- }
33
- }