Compare commits

..

192 Commits

Author SHA1 Message Date
Kit Langton edee757028 docs: fix package manager code blocks 2026-08-13 14:57:45 +00:00
opencode-agent[bot] a6a712a3ac fix(stats): keep tablet chart tooltips above bars (#42197) 2026-08-13 04:55:13 -05:00
opencode-agent[bot] 1288161528 chore: generate 2026-08-13 07:18:58 +00:00
Luke Parker 154f298fe9 fix(app): unify v2 server and session lifecycle (#41930) 2026-08-13 17:17:11 +10:00
Luke Parker 75c82e046b feat(app): redesign non-modal settings (#40845) 2026-08-13 16:52:07 +10:00
Dax Raad aec4b711f6 ci: make v2 publishing manual 2026-08-13 01:13:11 -04:00
Dax Raad 721f9bfcff docs: remove beta data deletion warnings 2026-08-13 00:57:12 -04:00
Kit Langton 56973e0ca4 fix(core): reject inherited workspace providers (#42227) 2026-08-12 22:55:32 -04:00
Kit Langton c253d4d311 fix(tui): highlight queued prompts on hover (#42219) 2026-08-12 22:32:30 -04:00
opencode-agent[bot] 33a1bd2e90 docs(desktop): add Solid best practices guidance (#42211)
Co-authored-by: Luke Parker <10430890+Hona@users.noreply.github.com>
2026-08-13 11:56:55 +10:00
Kit Langton 20197c6b8a test(core): reuse project service stub (#42208) 2026-08-12 21:47:09 -04:00
James Long 230b2f8488 fix(tui): omit implicit cd autocomplete prefix (#42206) 2026-08-12 21:39:42 -04:00
Kit Langton a78c8d8972 test(core): reuse permission service stub (#42205) 2026-08-12 21:26:58 -04:00
Luke Parker bac3631ded refactor(app): align UI packages with solid best practices (#41977)
Co-authored-by: Brendan Allan <14191578+Brendonovich@users.noreply.github.com>
2026-08-13 01:23:40 +00:00
opencode-agent[bot] 664b0ba2d6 chore: generate 2026-08-13 01:22:11 +00:00
Luke Parker 9c8b50b989 feat(deskltop): use opencode2 in WSL (#42199) 2026-08-13 11:20:48 +10:00
opencode-agent[bot] 4358a02cf4 chore: generate 2026-08-13 01:11:25 +00:00
Kit Langton a31058d76c fix(core): skip shell parsing when permissions allow all (#42203) 2026-08-12 21:09:52 -04:00
Kit Langton 91ba9f2412 fix(tui): repair baseline unit failures (#42198) 2026-08-12 21:04:50 -04:00
opencode-agent[bot] 3125f67708 chore: generate 2026-08-13 00:52:58 +00:00
Kit Langton 550e9cc636 feat(catalog): auto-generated Open Graph cards per capture (#42201) 2026-08-12 20:51:32 -04:00
Luke Parker af127a643b refactor(desktop): run local server from source (#42194) 2026-08-13 10:44:45 +10:00
Kit Langton 77aa1cfede fix(tui): truncate queued prompt preview (#42196) 2026-08-12 20:42:41 -04:00
opencode-agent[bot] a2ed936283 chore: generate 2026-08-13 00:30:58 +00:00
Kit Langton b17fbf41e3 feat(catalog): click-to-annotate captures with GitHub issue handoff (#42183) 2026-08-12 20:29:32 -04:00
Kit Langton 9d6e05b6e4 fix(cli): inset update footer (#42189) 2026-08-12 20:15:56 -04:00
Kit Langton 9b805c140f feat(tui): render Mermaid GitGraph diagrams (#42179) 2026-08-12 20:10:52 -04:00
Kit Langton d31a994c27 feat(tui): render Mermaid timelines (#42130) 2026-08-12 19:55:39 -04:00
Kit Langton 76640a5c9c feat(sdk): re-export Event schema from sdk-next (#42175) 2026-08-12 19:44:33 -04:00
Kit Langton 76dbaf20ad fix(catalog): serve app shell at lab route (#42159) 2026-08-12 18:18:24 -04:00
opencode-agent[bot] 9dd0e39867 chore: generate 2026-08-12 22:03:37 +00:00
Kit Langton 69a465d33b feat: adopt drive and TUI catalog (#42133) 2026-08-12 18:01:16 -04:00
Simon Klee 66c2967520 deps: update OpenTUI to 0.5.2 (#42148) 2026-08-12 23:09:41 +02:00
opencode-agent[bot] 883c9e3cb4 chore: generate 2026-08-12 20:49:54 +00:00
Kit Langton 0d703d39e7 fix(core): adopt pre-project sessions on directory resolution (#42100) 2026-08-12 20:47:55 +00:00
Kit Langton bc2251d6d8 feat(sdk): configure external workspace providers (#42142) 2026-08-12 16:47:12 -04:00
opencode-agent[bot] b034154e09 chore: generate 2026-08-12 20:45:10 +00:00
Kit Langton c894f87771 feat(tui): refine session tab activity indicators (#42144) 2026-08-12 16:43:58 -04:00
opencode-agent[bot] fc5c781085 chore: generate 2026-08-12 20:29:39 +00:00
Kit Langton f6aa1a67f0 fix(tui): improve selected spinner contrast (#42141) 2026-08-12 16:27:52 -04:00
Dax 0859f77153 fix(core): continue sessions after location move (#42138) 2026-08-12 16:25:50 -04:00
Kit Langton 7a22ac865d fix(tui): navigate filtered plugins (#42127) 2026-08-12 16:20:52 -04:00
Dax b75dd58f7c fix(tui): continue pending work after interrupt (#42131) 2026-08-12 15:55:53 -04:00
Kit Langton 56197e621a refactor(codemode): route transpilation through a #transpile conditional import (#42121) 2026-08-12 15:43:30 -04:00
Kit Langton d8f62cfdcb fix(core): resolve watcher binding require lazily (#42122) 2026-08-12 15:40:41 -04:00
Dax Raad c9539979bd fix(tui): highlight moved session location 2026-08-12 15:37:41 -04:00
opencode-agent[bot] f5e6b8c769 chore: generate 2026-08-12 19:29:55 +00:00
Kit Langton 2025ed939a feat(server): typed no-execution-plane environment for workerd profile (#42113) 2026-08-12 19:28:07 +00:00
Dax a7642737c7 feat(core): add generic session inbox (#42109) 2026-08-12 15:28:04 -04:00
opencode-agent[bot] c081e12c60 chore: generate 2026-08-12 19:10:52 +00:00
opencode-agent[bot] 8d5dd206a6 fix(core): validate tool definitions on registration (#42117)
Co-authored-by: Aiden Cline <rekram1-node@users.noreply.github.com>
2026-08-12 14:08:13 -05:00
Kit Langton c6a86acb0b fix(tui): harden Mermaid rendering limits (#42123) 2026-08-12 15:06:28 -04:00
opencode-agent[bot] c7bee09632 fix(core): preserve Copilot fallback provider (#42099)
Co-authored-by: Aiden Cline <rekram1-node@users.noreply.github.com>
2026-08-12 14:03:27 -05:00
opencode-agent[bot] a3625cd2cb chore: generate 2026-08-12 18:50:05 +00:00
Kit Langton 0dec446ee6 fix(tui): restore tab marquee hover (#42093) 2026-08-12 14:47:34 -04:00
James Long dff6eb631b fix(tui): show worktree names in session list (#42108) 2026-08-12 14:44:48 -04:00
opencode-agent[bot] 3251bff6ff chore: generate 2026-08-12 18:35:52 +00:00
Kit Langton f713d39c61 fix(tui): keep interrupt confirmation visible (#42096) 2026-08-12 14:33:53 -04:00
opencode-agent[bot] c80472b6d5 chore: add neriousy to team members (#42106)
Co-authored-by: Aiden Cline <rekram1-node@users.noreply.github.com>
2026-08-12 13:30:49 -05:00
Kit Langton c86b3079ac fix(tui): render Mermaid rich labels compactly (#42103) 2026-08-12 14:28:54 -04:00
opencode-agent[bot] f8f50ee375 chore: generate 2026-08-12 18:10:29 +00:00
Kit Langton 912a1c4e8a fix(tui): move debug overlay into developer tools (#42098) 2026-08-12 14:08:25 -04:00
Kit Langton a841fc22bb feat(server): workerd runtime profile and SDK workerd entrypoint (#41918) 2026-08-12 14:07:56 -04:00
Kit Langton 723f5e0028 fix(tui): consolidate developer tools controls (#42088) 2026-08-12 13:23:14 -04:00
Kit Langton 17cae23dce fix(tui): stabilize repeated open menu (#42086) 2026-08-12 13:17:13 -04:00
Kit Langton fd30b9765d refactor(core): spawn stdio MCP servers through the location environment (#42076) 2026-08-12 13:16:12 -04:00
Dax Raad a20d945245 docs: base worktrees on v2 2026-08-12 12:56:00 -04:00
Kit Langton 10ebf70a07 feat(tui): add session tab context menu (#42068) 2026-08-12 16:45:59 +00:00
opencode-agent[bot] b50924b993 test: stabilize Windows integration coverage (#42079)
Co-authored-by: Kit Langton <kit.langton@gmail.com>
2026-08-12 12:45:38 -04:00
Kit Langton b24b1b3f16 fix(tui): keep exact-fit tab titles stationary (#42073) 2026-08-12 12:17:45 -04:00
Kit Langton 930b0751b1 fix(core): generate session titles before model execution (#42067) 2026-08-12 12:08:24 -04:00
Matt Robinson f06a86eeac feat(client): support service version ranges (#42023)
Co-authored-by: Dax Raad <d@ironbay.co>
2026-08-12 09:01:37 -07:00
Kit Langton 653b7d79cd fix(tui): restore navigation keybind defaults (#42066) 2026-08-12 11:57:39 -04:00
Kit Langton 70ce0d0970 feat(tui): jump between open menu sections (#42061) 2026-08-12 15:52:51 +00:00
opencode-agent[bot] 0777e84598 fix(tui): fill image message background (#42062)
Co-authored-by: Simon Klee <hello@simonklee.dk>
2026-08-12 15:51:19 +00:00
Kit Langton bef795b2fe fix(tui): smooth session tab marquees (#42055) 2026-08-12 15:49:17 +00:00
Kit Langton 70853b1e5b feat(tui): surface plugin failures (#41940) 2026-08-12 10:51:00 -04:00
opencode-agent[bot] 1fea1c2ebc fix(core): route Muse models to Meta prompt (#42036)
Co-authored-by: Aiden Cline <rekram1-node@users.noreply.github.com>
2026-08-12 09:31:59 -05:00
opencode-agent[bot] 1da591b84d fix(core): return content-only Code Mode results (#41954)
Co-authored-by: Aiden Cline <rekram1-node@users.noreply.github.com>
2026-08-12 09:14:41 -05:00
opencode-agent[bot] b990f9a5c1 chore: generate 2026-08-12 13:32:36 +00:00
Dax Raad 9769e7012c feat(core): configure package publishing 2026-08-12 09:30:34 -04:00
opencode-agent[bot] 5fd28cffc5 refactor(tui): use command IDs for v2 keybinds (#41882)
Requested by Sebastian Herrlinger (@kommander). Rebased onto current v2 with conflicts resolved and all required checks passing.
2026-08-12 12:53:24 +00:00
Luke Parker c8dffb6893 test(app): deflake cached tab paint probe (#41965) 2026-08-12 20:21:27 +10:00
opencode-agent[bot] 01cbdcb8e4 fix(ai): batch Anthropic parallel tool results (#41948)
Co-authored-by: Aiden Cline <rekram1-node@users.noreply.github.com>
2026-08-12 00:18:50 -05:00
opencode-agent[bot] bf751a907d fix(core): jitter session retry delays (#41942)
Co-authored-by: Aiden Cline <rekram1-node@users.noreply.github.com>
2026-08-11 23:58:20 -05:00
opencode-agent[bot] 9d63ca8f90 chore: generate 2026-08-12 04:19:09 +00:00
opencode-agent[bot] e3bd82013c fix(ai): preserve stream transport failures (#41779)
Co-authored-by: Aiden Cline <rekram1-node@users.noreply.github.com>
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2026-08-11 23:17:48 -05:00
Kit Langton 90b6fa0eab fix(core): expose local attachment paths (#41789) 2026-08-11 23:32:49 -04:00
Kit Langton 5b7b1830d2 refactor(server): resume suspended sessions unconditionally in the fetch entry (#41938) 2026-08-12 03:31:30 +00:00
Kit Langton a8fc664b6d fix(tui): pad markdown table cells (#41937) 2026-08-11 23:21:00 -04:00
Kit Langton d853ff8848 fix(tui): configure new session location (#41929) 2026-08-11 22:55:48 -04:00
opencode-agent[bot] 94bc0fc6fa chore: generate 2026-08-12 02:40:33 +00:00
Kit Langton d3eecf7ba2 feat(tui): compact turn token usage with expandable steps (#41922) 2026-08-12 02:39:19 +00:00
Kit Langton 99166f7c17 feat(tui): add plus button to session tab bar (#41887) 2026-08-11 22:38:08 -04:00
opencode-agent[bot] 08dcf7d731 chore: generate 2026-08-12 02:23:16 +00:00
Kit Langton caae28e0d4 fix(tui): render instruction updates as compact notices (#41900) 2026-08-11 22:21:24 -04:00
Kit Langton c83933d1d4 fix(core): gate tool snapshot on initial MCP registration (#41884) 2026-08-11 22:21:21 -04:00
Kit Langton c86f1c41ff fix(tui): show completed write output (#41883) 2026-08-11 22:20:55 -04:00
Kit Langton 5c0cc8e617 fix(tui): align running shell output (#41880) 2026-08-11 22:20:52 -04:00
Kit Langton 1b45061afb feat(tui): experiments via devtools bar, drafts stay put (#41917) 2026-08-12 02:08:06 +00:00
opencode-agent[bot] 07bcd290c2 chore: generate 2026-08-12 02:06:08 +00:00
Luke Parker 04ad06e2e3 fix(desktop): align local development identity (#41889) 2026-08-12 12:04:57 +10:00
Dax 93965df860 feat(session): record location switches (#41899) 2026-08-11 19:03:57 -07:00
Kit Langton f0333e0eea chore(tui): remove leftover discovery smoke plugin (#41897) 2026-08-11 21:56:55 -04:00
Kit Langton 452335a78d fix(tui): truncate fractional mtimes in fresh plugin specifiers (#41891) 2026-08-11 21:56:44 -04:00
Kit Langton 9322f5d2c9 fix(client): surface managed startup stderr (#41793) 2026-08-11 21:55:43 -04:00
opencode-agent[bot] d6ed520c25 chore: generate 2026-08-12 01:38:26 +00:00
Kit Langton 96a74ffcc6 chore(core): drop obsolete StreamSse hunk from effect patch (#41892) 2026-08-11 21:37:09 -04:00
Kit Langton c858986b08 feat(server): web-standard fetch handler entry (#41896) 2026-08-11 21:37:06 -04:00
Dax Raad f6611a1f56 refactor(core): internalize effect sqlite adapter 2026-08-11 21:33:23 -04:00
Kit Langton 57b636df00 test(tui): resolve autocomplete paths per platform (#41893) 2026-08-11 21:28:23 -04:00
opencode-agent[bot] 3489abedc0 chore: generate 2026-08-12 01:12:18 +00:00
Dax Raad 49a4175a34 fix: make workspace imports NodeNext compatible 2026-08-11 21:09:46 -04:00
Dax Raad b50b8268f2 feat(codemode): configure package publishing 2026-08-11 21:09:46 -04:00
Dax Raad 51512862a5 feat: add V2 curl installer 2026-08-11 21:03:15 -04:00
Kit Langton 3d23c907eb fix(core): restore bundler resolution for source-imported deps (#41885) 2026-08-11 21:01:24 -04:00
opencode-agent[bot] 9c4e6b500e chore: generate 2026-08-12 00:58:51 +00:00
Dax 5da7eaa6eb feat(api): continue pending work after interrupt (#41888) 2026-08-11 17:56:54 -07:00
Kit Langton c254ba8a7f core: embed models.dev snapshot instead of compile-time define (#41838) 2026-08-11 20:55:09 -04:00
Kit Langton d8e126b817 feat(tui): hidden experiments section with per-tab prompt drafts (#41862) 2026-08-11 20:52:56 -04:00
James Long 7987aed8f3 feat(tui): autocomplete cd directories (#41870) 2026-08-11 20:52:16 -04:00
Kit Langton 0df3070d6d test(client): accelerate service lifecycle tests (#41879) 2026-08-11 20:50:25 -04:00
Luke Parker c217ebe2ad fix(desktop): skip web ui build in dev (#41881) 2026-08-12 10:25:32 +10:00
Dax Raad 4acdeffbe9 refactor(core): require explicit import extensions 2026-08-11 20:22:50 -04:00
Kit Langton dc82a446b9 fix(core): guard Deferred waiter cleanup in vendored effect patch (#41858) 2026-08-11 20:16:21 -04:00
Luke Parker c4bea9d558 fix(app): stabilize markdown worker startup (#41487) 2026-08-12 10:05:32 +10:00
Luke Parker 1190ef3818 feat(desktop): support local server builds (#41486) 2026-08-12 09:56:47 +10:00
opencode-agent[bot] 055a380e66 fix(core): parameterize v1 migration messages (#41877)
Co-authored-by: Dax <826656+thdxr@users.noreply.github.com>
2026-08-11 19:51:37 -04:00
Dax Raad 456c26a012 feat(tui): show localhost on pair screen 2026-08-11 19:49:33 -04:00
opencode-agent[bot] 6072fc5c1b chore: generate 2026-08-11 22:44:47 +00:00
Brendan Allan 20df2fdba6 feat(cli): embed web ui (#41525)
Co-authored-by: Dax Raad <d@ironbay.co>
2026-08-11 15:43:07 -07:00
opencode-agent[bot] f1399dcb65 fix(desktop): orchestrate update restarts (#41865)
Co-authored-by: Luke Parker <10430890+Hona@users.noreply.github.com>
2026-08-12 08:38:18 +10:00
Dax Raad 3507bd10b6 fix(www): match OG images to docs theme 2026-08-11 18:28:51 -04:00
Dax Raad 492ae1fdfa feat(www): match legacy docs theme 2026-08-11 18:20:58 -04:00
Kit Langton b00d8d65fe feat(tui): hierarchical slot tree for plugin UI placement (#41189) 2026-08-11 22:00:04 +00:00
Kit Langton f1366d80c8 fix(core): default #sqlite resolution to the node driver (#41834) 2026-08-11 17:50:09 -04:00
opencode-agent[bot] 549a8f826b fix(core): log Plan reminder failures (#41856)
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2026-08-11 16:39:26 -05:00
Kit Langton 59970699d0 test(core): narrow plugin test layer (#41852) 2026-08-11 17:23:47 -04:00
Aiden Cline b35c5fc985 fix(core): detect copilot PDF input support (#41854) 2026-08-11 16:05:36 -05:00
Kit Langton 627e67323c docs: remove ideal-pseudocode project skill (#41851) 2026-08-11 17:03:05 -04:00
opencode-agent[bot] b97d964e5c chore: generate 2026-08-11 20:53:50 +00:00
Aiden Cline 3fd3e28690 fix(core): classify copilot utility requests (#41840) 2026-08-11 15:51:55 -05:00
Kit Langton b4abeb4788 test(core): narrow effect flock harness (#41837) 2026-08-11 16:51:42 -04:00
Kit Langton 14deb6baf7 docs: clarify skill source precedence (#41844) 2026-08-11 20:46:14 +00:00
Aiden Cline ab6e18e4ff fix(core): route Vertex Anthropic natively (#41836) 2026-08-11 15:29:02 -05:00
Kit Langton 2b6dd2559c fix(core): prefer nearest skill source (#41788) 2026-08-11 16:13:32 -04:00
Kit Langton 2b7bd84867 test(core): speed up subagent tool suite (#41835) 2026-08-11 16:13:04 -04:00
Kit Langton baa54a5150 test(core): speed up shell tool suite (#41831) 2026-08-11 16:03:50 -04:00
Kit Langton c5704d3e3b feat(core): sqlite driver for durable object storage (#41659) 2026-08-11 15:45:23 -04:00
Kit Langton e3336725db fix(core): normalize provider endpoint templates (#41816) 2026-08-11 15:42:54 -04:00
opencode-agent[bot] ff3ecefd24 fix(cli): sign macOS preview binaries (#40662)
Co-authored-by: Ryan Vogel <89211796+R44VC0RP@users.noreply.github.com>
2026-08-11 15:37:44 -04:00
Kit Langton 8486bbcdc1 refactor(core): unify fff runtime adapters (#41827) 2026-08-11 15:22:03 -04:00
Kit Langton 2621dde1c9 refactor(core): unify config collection decoding (#41826) 2026-08-11 15:21:52 -04:00
Kit Langton a253c0437e test(core): await shell tool registration (#41825) 2026-08-11 15:21:35 -04:00
opencode-agent[bot] d8b3e528e8 fix(core): use standard web search key dialogs (#41821)
Co-authored-by: Aiden Cline <rekram1-node@users.noreply.github.com>
2026-08-11 14:13:58 -05:00
Aiden Cline a5d6969f6f feat(core): extract plan agent into a plugin (#41665) 2026-08-11 13:49:24 -05:00
opencode-agent[bot] 3954d7979d chore: generate 2026-08-11 18:11:13 +00:00
Kit Langton a69a372f27 fix(util): defer module-scope randomness and import.meta path derivation (#41765) 2026-08-11 14:09:18 -04:00
Kit Langton 7300e7e10f core: write-ahead execution claims replace shutdown-hook suspension (#41800) 2026-08-11 14:09:04 -04:00
James Long eeb870b0cc feat(sdk): configure embedded logging (#41809) 2026-08-11 14:03:55 -04:00
Kit Langton 136bad9f17 feat(core): discover AGENTS.md up to the home directory (#41652) 2026-08-11 13:57:10 -04:00
Kit Langton 17536a9237 fix(core): discover local plugin packages (#41784) 2026-08-11 13:14:46 -04:00
Kit Langton 315bc6ac2e fix(acp): avoid implicit model variant (#41782) 2026-08-11 13:09:59 -04:00
Kit Langton a183fb5615 refactor(core): share SQLite client adapter (#41797) 2026-08-11 13:07:22 -04:00
Kit Langton 2c1596ca80 refactor(core): share AI SDK provider factory (#41796) 2026-08-11 13:07:14 -04:00
Kit Langton 9e082c2d43 refactor(core): centralize project upsert (#41795) 2026-08-11 13:07:05 -04:00
Kit Langton fa5ca85e30 refactor(core): reuse plugin source discovery (#41794) 2026-08-11 13:06:58 -04:00
Kit Langton 37a8c6c916 fix(ai): merge Bedrock tool result messages (#41780) 2026-08-11 13:03:31 -04:00
Kit Langton 23b0688a7f fix(server): await models before listing (#41783) 2026-08-11 12:18:07 -04:00
opencode-agent[bot] d0dd49bd84 chore: generate 2026-08-11 16:12:58 +00:00
Kit Langton db9a3b6c41 refactor(core): compact question tool schema (#41772) 2026-08-11 12:09:37 -04:00
opencode-agent[bot] 961b51b509 fix(core): yield while clearing migration events (#41775)
Co-authored-by: Dax Raad <d@ironbay.co>
2026-08-11 16:02:30 +00:00
opencode-agent[bot] 964f7f4254 chore: generate 2026-08-11 15:53:52 +00:00
Aiden Cline 8c27c8485e feat(session): persist previous selections (#41771) 2026-08-11 10:52:14 -05:00
opencode-agent[bot] 6721ff5328 fix(core): omit deprecated models.dev models (#41769)
Co-authored-by: Aiden Cline <rekram1-node@users.noreply.github.com>
2026-08-11 10:17:04 -05:00
Kit Langton 026e0c634f refactor(core): reuse plugin directory names (#41754) 2026-08-11 10:40:36 -04:00
Kit Langton c581b59b7f refactor(merman): simplify spatial span assembly (#41757) 2026-08-11 10:40:22 -04:00
Kit Langton 5cc7811ec2 refactor(core): defer database global lookup (#41755) 2026-08-11 10:40:03 -04:00
Kit Langton 5d32845b02 refactor(tui): simplify marquee reset (#41758) 2026-08-11 10:39:51 -04:00
Kit Langton e9f5842f29 refactor: simplify attachment identity tracking (#41756) 2026-08-11 10:39:42 -04:00
Kit Langton 7b9734593a refactor(core): generate static imports for the migration manifest (#41657) 2026-08-11 10:35:19 -04:00
opencode-agent[bot] 1dbfc7cfab chore: generate 2026-08-11 14:12:14 +00:00
Kit Langton 3441b95afc fix(core): models.dev catalog population must survive KV cache write failures (#41735) 2026-08-11 10:09:37 -04:00
opencode-agent[bot] 306204bad8 fix(app): handle untitled session tab info (#41700)
Co-authored-by: Luke Parker <10430890+Hona@users.noreply.github.com>
2026-08-11 18:02:10 +10:00
Aiden Cline 47c8d85904 feat(plugin): allow tool hooks to fail with tool errors (#41668) 2026-08-11 00:36:59 -05:00
Aiden Cline c401076b6f fix(core): preserve AI SDK tool media (#41672) 2026-08-11 00:33:33 -05:00
Luke Parker 1aef4de853 chore(desktop): skip unused beta CLI builds (#41673) 2026-08-11 15:07:21 +10:00
opencode-agent[bot] 9c94634515 fix(tui): use active model for compaction (#41608)
Co-authored-by: Aiden Cline <rekram1-node@users.noreply.github.com>
2026-08-10 23:50:51 -05:00
Luke Parker c2b754bb03 feat(desktop): publish v2 beta desktop (#41626) 2026-08-11 14:33:31 +10:00
Aiden Cline 2372edd5eb feat(tui): show previous agent in switch notices (#41661) 2026-08-10 23:09:26 -05:00
Kit Langton a5f7f8d3b5 fix(tui): deduplicate repeated image attachments (#41651) 2026-08-10 23:39:38 -04:00
Aiden Cline 4df276b9e8 feat(session): persist previous agent on switch (#41621) 2026-08-10 22:35:16 -05:00
1457 changed files with 176495 additions and 13612 deletions
+1
View File
@@ -1,3 +1,4 @@
packages/core/migration/**/snapshot.json linguist-generated packages/core/migration/**/snapshot.json linguist-generated
packages/core/src/database/migration.gen.ts linguist-generated packages/core/src/database/migration.gen.ts linguist-generated
packages/core/src/models-dev/snapshot.txt linguist-generated
packages/core/src/**/*.txt text eol=lf packages/core/src/**/*.txt text eol=lf
+1
View File
@@ -10,6 +10,7 @@ kitlangton
kommander kommander
ludvigrask ludvigrask
MrMushrooooom MrMushrooooom
neriousy
nexxeln nexxeln
R44VC0RP R44VC0RP
rekram1-node rekram1-node
+49
View File
@@ -0,0 +1,49 @@
name: deploy-lab-catalog
on:
push:
branches: [v2]
paths:
- ".github/workflows/deploy-lab-catalog.yml"
- "bun.lock"
- "package.json"
- "packages/drive/**"
- "packages/protocol/src/simulation.ts"
- "packages/simulation/**"
- "packages/lab/catalog/**"
workflow_dispatch:
concurrency:
group: deploy-lab-catalog-${{ github.ref_name }}
cancel-in-progress: false
permissions:
contents: read
jobs:
deploy:
if: github.repository == 'anomalyco/opencode' && github.ref_name == 'v2'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- uses: ./.github/actions/setup-bun
- name: Install ffmpeg
run: |
sudo apt-get update
sudo apt-get install --yes ffmpeg
- name: Validate
run: |
bun --cwd packages/protocol typecheck
bun --cwd packages/simulation typecheck
bun --cwd packages/drive run check
bun --cwd packages/drive run test
bun --cwd packages/lab/catalog run check
- name: Deploy
working-directory: packages/lab/catalog
run: bun run deploy
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
+69 -12
View File
@@ -6,7 +6,6 @@ on:
branches: branches:
- ci - ci
- dev - dev
- v2
- beta - beta
- fix/npm-native-binary-install - fix/npm-native-binary-install
- snapshot-* - snapshot-*
@@ -75,7 +74,7 @@ jobs:
build-cli: build-cli:
needs: version needs: version
runs-on: blacksmith-4vcpu-ubuntu-2404 runs-on: blacksmith-4vcpu-ubuntu-2404
if: github.repository == 'anomalyco/opencode' if: github.repository == 'anomalyco/opencode' && github.ref_name != 'beta'
steps: steps:
- uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
with: with:
@@ -91,7 +90,7 @@ jobs:
opencode-app-secret: ${{ secrets.OPENCODE_APP_SECRET }} opencode-app-secret: ${{ secrets.OPENCODE_APP_SECRET }}
- name: Build legacy CLI - name: Build legacy CLI
if: github.ref_name != 'v2' if: github.ref_name != 'v2' && github.ref_name != 'beta'
run: ./packages/opencode/script/build.ts ${{ (github.ref_name == 'beta' && '--sourcemaps') || '' }} run: ./packages/opencode/script/build.ts ${{ (github.ref_name == 'beta' && '--sourcemaps') || '' }}
env: env:
OPENCODE_VERSION: ${{ needs.version.outputs.version }} OPENCODE_VERSION: ${{ needs.version.outputs.version }}
@@ -109,7 +108,7 @@ jobs:
GH_TOKEN: ${{ steps.committer.outputs.token }} GH_TOKEN: ${{ steps.committer.outputs.token }}
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
if: github.ref_name != 'v2' if: github.ref_name != 'v2' && github.ref_name != 'beta'
with: with:
name: opencode-cli name: opencode-cli
path: | path: |
@@ -117,22 +116,76 @@ jobs:
packages/opencode/dist/opencode-linux* packages/opencode/dist/opencode-linux*
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
if: github.ref_name != 'v2' if: github.ref_name != 'v2' && github.ref_name != 'beta'
with: with:
name: opencode-cli-windows name: opencode-cli-windows
path: packages/opencode/dist/opencode-windows* path: packages/opencode/dist/opencode-windows*
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with: with:
name: opencode-preview-cli name: opencode-preview-cli-unsigned
path: packages/cli/dist/cli-* path: packages/cli/dist/cli-*
outputs: outputs:
version: ${{ needs.version.outputs.version }} version: ${{ needs.version.outputs.version }}
sign-cli-macos:
needs: build-cli
runs-on: macos-26
if: github.repository == 'anomalyco/opencode'
steps:
- uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
- uses: apple-actions/import-codesign-certs@8f3fb608891dd2244cdab3d69cd68c0d37a7fe93 # v2.0.0
with:
keychain: build
p12-file-base64: ${{ secrets.APPLE_CERTIFICATE }}
p12-password: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: opencode-preview-cli-unsigned
path: packages/cli/dist
- name: Sign macOS CLI binaries
run: |
identity=$(security find-identity -v -p codesigning build.keychain | sed -n 's/.*"\(Developer ID Application:.*\)"/\1/p' | head -n 1)
if [ -z "$identity" ]; then
echo "Developer ID Application identity not found"
exit 1
fi
found=0
for file in packages/cli/dist/cli-darwin-*/bin/opencode2; do
if [ ! -f "$file" ]; then
continue
fi
found=1
codesign \
--force \
--timestamp \
--options runtime \
--entitlements packages/cli/script/entitlements.plist \
--sign "$identity" \
"$file"
codesign --verify --deep --strict --verbose=4 "$file"
codesign --display --requirements - "$file"
done
if [ "$found" -eq 0 ]; then
echo "No macOS CLI binaries found"
exit 1
fi
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: opencode-preview-cli
path: packages/cli/dist/cli-*
if-no-files-found: error
build-node-cli: build-node-cli:
needs: version needs: version
if: github.repository == 'anomalyco/opencode' if: github.repository == 'anomalyco/opencode' && github.ref_name != 'beta'
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
@@ -184,7 +237,7 @@ jobs:
- build-cli - build-cli
- version - version
runs-on: blacksmith-4vcpu-windows-2025 runs-on: blacksmith-4vcpu-windows-2025
if: github.repository == 'anomalyco/opencode' && github.ref_name != 'v2' if: github.repository == 'anomalyco/opencode' && github.ref_name != 'v2' && github.ref_name != 'beta'
env: env:
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
@@ -377,7 +430,7 @@ jobs:
env: env:
OPENCODE_VERSION: ${{ needs.version.outputs.version }} OPENCODE_VERSION: ${{ needs.version.outputs.version }}
OPENCODE_CHANNEL: ${{ (github.ref_name == 'beta' && 'beta') || 'prod' }} OPENCODE_CHANNEL: ${{ (github.ref_name == 'beta' && 'beta') || 'prod' }}
RUST_TARGET: ${{ matrix.settings.target }} OPENCODE_CLI_TARGET: ${{ matrix.settings.target }}
- name: Build - name: Build
run: bun run build run: bun run build
@@ -393,6 +446,7 @@ jobs:
VITE_SENTRY_DSN: ${{ vars.WEB_SENTRY_DSN }} VITE_SENTRY_DSN: ${{ vars.WEB_SENTRY_DSN }}
VITE_SENTRY_ENVIRONMENT: ${{ (github.ref_name == 'beta' && 'beta') || 'production' }} VITE_SENTRY_ENVIRONMENT: ${{ (github.ref_name == 'beta' && 'beta') || 'production' }}
VITE_SENTRY_RELEASE: desktop@${{ needs.version.outputs.version }} VITE_SENTRY_RELEASE: desktop@${{ needs.version.outputs.version }}
OPENCODE_CLI_TARGET: ${{ matrix.settings.target }}
- name: Package - name: Package
if: needs.version.outputs.release if: needs.version.outputs.release
@@ -467,6 +521,7 @@ jobs:
needs: needs:
- version - version
- build-cli - build-cli
- sign-cli-macos
- build-node-cli - build-node-cli
- sign-cli-windows - sign-cli-windows
- build-electron - build-electron
@@ -496,29 +551,31 @@ jobs:
registry-url: "https://registry.npmjs.org" registry-url: "https://registry.npmjs.org"
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
if: github.ref_name != 'v2' if: github.ref_name != 'v2' && github.ref_name != 'beta'
with: with:
name: opencode-cli name: opencode-cli
path: packages/opencode/dist path: packages/opencode/dist
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
if: github.ref_name != 'v2' if: github.ref_name != 'v2' && github.ref_name != 'beta'
with: with:
name: opencode-cli-windows name: opencode-cli-windows
path: packages/opencode/dist path: packages/opencode/dist
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
if: github.ref_name != 'v2' if: github.ref_name != 'v2' && github.ref_name != 'beta'
with: with:
name: opencode-cli-signed-windows name: opencode-cli-signed-windows
path: packages/opencode/dist path: packages/opencode/dist
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
if: github.ref_name != 'beta'
with: with:
name: opencode-preview-cli name: opencode-preview-cli
path: packages/cli/dist path: packages/cli/dist
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
if: github.ref_name != 'beta'
with: with:
pattern: opencode-node-cli-* pattern: opencode-node-cli-*
path: packages/cli/dist/node path: packages/cli/dist/node
+7 -1
View File
@@ -55,6 +55,12 @@ jobs:
git config --global user.email "bot@opencode.ai" git config --global user.email "bot@opencode.ai"
git config --global user.name "opencode" git config --global user.name "opencode"
- name: Install ffmpeg
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install --yes ffmpeg
- name: Cache Turbo - name: Cache Turbo
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with: with:
@@ -66,7 +72,7 @@ jobs:
- name: Run unit tests - name: Run unit tests
timeout-minutes: 20 timeout-minutes: 20
run: GITHUB_ACTIONS=false bun turbo test run: GITHUB_ACTIONS=false bun turbo test ${{ runner.os == 'Windows' && '--filter=!opencode-drive' || '' }}
env: env:
OPENCODE_EXPERIMENTAL_DISABLE_FILEWATCHER: ${{ runner.os == 'Windows' && 'true' || 'false' }} OPENCODE_EXPERIMENTAL_DISABLE_FILEWATCHER: ${{ runner.os == 'Windows' && 'true' || 'false' }}
-13
View File
@@ -1,13 +0,0 @@
import type { Context } from "../../../packages/plugin/src/tui/context"
export default {
id: "test.tui-discovery-smoke",
setup(_context: Context) {
// context.ui.toast.show({
// title: "TUI plugin discovery works",
// message: "Loaded .opencode/plugins/tui/discovery-smoke.ts",
// variant: "success",
// duration: 30_000,
// })
},
}
@@ -1,68 +0,0 @@
---
name: ideal-pseudocode
description: Function-by-function refactoring loop driven by ideal pseudocode. Use when the user says "ideal pseudocode", asks to make a function read like its pseudocode, or wants a dense module cleaned up one function at a time.
---
# Ideal Pseudocode
Clean up one function at a time by writing the pseudocode it _should_ read as, naming every delta between that and the real code, and closing only the gaps the user approves.
## Loop
One function per round. Never touch code before the user picks a direction.
1. **Pick the target** with the user — usually the next function up or down the call chain from the last round.
2. **Read the current code** fresh from disk. It may have unsaved or parallel edits; ask before overwriting anything unexpected.
3. **Distill.** Write the function's ideal pseudocode in a `ts`-fenced code block — TypeScript-flavored for syntax highlighting, but pseudocode: comments over mechanics, one line per idea, every arm of a loop visible as an arm. For a dense or unfamiliar function, first show the _current_ structure as pseudocode, then the ideal.
4. **Name the deltas.** A numbered list; each delta is one concrete gap: control flow smuggled through combinators, a flag-typed parameter, colliding names, side-effect plumbing drowning a loop body, twin functions differing only by a filter. For each, give an honest cost/benefit and a decisive recommendation — including "leave it" when the density has reasons (state the reasons).
5. **Verify before proposing.** Any API the proposal builds on (a combinator, schedule, matcher) gets checked against the installed version's source, not memory.
6. **Get buy-in, then apply.** Minimal edits. Quarantine irreducible ugliness inside a small helper whose name is the pseudocode line it implements. Collapse shallow sibling functions into one deep function only when the distinguishing argument is a domain noun, not a behavior switch.
7. **Validate.** Typecheck and the focused test suites from the package directory; rerun timing-sensitive tests repeatedly. Flag any semantic change the restructure introduced, even when tests pass.
8. **Offer the next target** and stop.
## Style
- Keep each round tight: pseudocode block, deltas, recommendation, one question.
- State mutation belongs in one visible place (usually the loop tail), never spread across error channels or callback closures.
- Prefer early return and `if` chains for control flow; matchers are for producing values.
- Compare names against the question each caller is actually asking; rename toward the domain noun.
## Example round
Every round should read like this (abridged from a real one, on an Effect step-runner):
> `runStep` it is. The ideal pseudocode:
>
> ```ts
> // One logical step: one settled model call, however many attempts it takes.
> // An attempt can end without settling in two ways:
> // transient provider failure -> retry the same call, same assistant message
> // compaction rewrote history -> rebuild the request and call again
> function runStep(session, promotable, step) {
> while (true) {
> const result = callModel(session, promotable, step)
>
> if (result.completed) return result
>
> if (result.retryable) {
> wait(retrySchedule) // exhausted -> publish Step.Failed, fail drain
> promotable = none // never re-promote on a second attempt
> continue
> }
>
> // compaction restarted the step: fresh request from rewritten history
> promotable = none
> step = result.step
> }
> }
> ```
>
> Comparing against the real thing, three deltas:
>
> 1. **The retry arm doesn't live in the loop.** In the pseudocode, retry is just another loop arm. In the real code it's smuggled through the error channel: `Effect.tapError` mutating loop variables via `Effect.sync` closures, then `Effect.retryOrElse` re-running a suspended call. Four mutable variables mutated from three different places — the state flow is invisible unless you already know the trick.
> 2. **`recoverOverflow: typeof compaction.compact | undefined`** — a function-typed parameter used as a one-shot armed/disarmed flag. The callee already closes over `compaction`; a boolean says what it is.
> 3. **`assistantMessageID`** exists solely to resume the same durable message across retries — fine, but only legible once the retry arm is visible in the loop.
>
> The unifying move for (1): a schedule driver, so a retryable failure becomes a caught value handled in the loop body exactly like the compaction arm. One honest trade: it's more manual than the blessed combinator — but the manual version is the loop the combinator was hiding, and the loop already exists for restarts. Two restart mechanisms, one control structure.
>
> Want me to apply it — unified loop, simplified schedule input, boolean `recoverOverflow`?
+4 -4
View File
@@ -51,7 +51,7 @@ files. If the script is unsuccessful, automatically fix the script and run it ag
Scripts use one typed definition object. `setup` runs before OpenCode starts, Scripts use one typed definition object. `setup` runs before OpenCode starts,
and `fs.writeFile` always writes inside the simulated project. and `fs.writeFile` always writes inside the simulated project.
You can read the full typed API here: https://raw.githubusercontent.com/jlongster/opencode-drive/refs/heads/main/src/script/types.ts You can read the full typed API here: https://raw.githubusercontent.com/anomalyco/opencode/v2/packages/drive/src/script/types.ts
```ts ```ts
import { defineScript } from "opencode-drive" import { defineScript } from "opencode-drive"
@@ -83,7 +83,7 @@ itself (this is extremely rare, do not use this unless explicitly asked). In thi
mode `ui` is typed as `null`; call `server.launch()` exactly mode `ui` is typed as `null`; call `server.launch()` exactly
once before launching clients. Each `clients.launch(name)` result provides the once before launching clients. Each `clients.launch(name)` result provides the
same UI methods as the automatic client. You can see an example of this API same UI methods as the automatic client. You can see an example of this API
here: https://raw.githubusercontent.com/jlongster/opencode-drive/refs/heads/main/examples/multiple-clients.ts here: https://raw.githubusercontent.com/anomalyco/opencode/v2/packages/drive/examples/multiple-clients.ts
Use the exported `wait(milliseconds)` utility for an unconditional delay. Use the exported `wait(milliseconds)` utility for an unconditional delay.
@@ -114,8 +114,8 @@ completion are automatic.
You can see some example scripts here: You can see some example scripts here:
- https://raw.githubusercontent.com/jlongster/opencode-drive/refs/heads/main/examples/simple.ts - https://raw.githubusercontent.com/anomalyco/opencode/v2/packages/drive/examples/simple.ts
- https://raw.githubusercontent.com/jlongster/opencode-drive/refs/heads/main/examples/serve.ts - https://raw.githubusercontent.com/anomalyco/opencode/v2/packages/drive/examples/serve.ts
## Prune ## Prune
+1 -1
View File
@@ -4,7 +4,7 @@ import { tool } from "@opencode-ai/plugin"
const TEAM = { const TEAM = {
tui: ["kommander", "simonklee"], tui: ["kommander", "simonklee"],
desktop_web: ["Hona", "Brendonovich"], desktop_web: ["Hona", "Brendonovich"],
core: ["jlongster", "rekram1-node", "nexxeln", "kitlangton"], core: ["jlongster", "rekram1-node", "neriousy", "nexxeln", "kitlangton"],
inference: ["fwang", "MrMushrooooom", "starptech"], inference: ["fwang", "MrMushrooooom", "starptech"],
windows: ["Hona"], windows: ["Hona"],
} as const } as const
+3 -2
View File
@@ -1,8 +1,9 @@
- After changing the public Protocol or Server `HttpApi`, run `bun run generate` from `packages/client`. Do not edit `src/generated` or `src/generated-effect` directly. - After changing the public Protocol or Server `HttpApi`, run `bun run generate` from `packages/client`. Do not edit `src/generated` or `src/generated-effect` directly.
- Keep runtime dependencies directed from Schema to Core and Protocol, then from Core and Protocol to Server. Client runtime code may depend on Schema and Protocol but never Core or Server; `sdk-next` composes Client, Core, and Server. - Keep runtime dependencies directed from Schema to Core and Protocol, then from Core and Protocol to Server. Client runtime code may depend on Schema and Protocol but never Core or Server; `sdk-next` composes Client, Core, and Server.
- Do not modify `packages/opencode` unless the user explicitly asks for V1 work. `packages/opencode` is the V1 implementation and is present for reference only. New implementation changes should land in the V2 package set: `packages/core`, `packages/cli`, `packages/server`, `packages/protocol`, `packages/schema`, and related generated client surfaces when required. - Do not modify `packages/opencode` unless the user explicitly asks for V1 work. `packages/opencode` is the V1 implementation and is present for reference only. New implementation changes should land in the V2 package set: `packages/core`, `packages/cli`, `packages/server`, `packages/protocol`, `packages/schema`, and related generated client surfaces when required.
- The default branch in this repo is `dev`. - The default branch in this repo is `v2`.
- Local `main` ref may not exist; use `dev` or `origin/dev` for diffs. - Base all new branches and worktrees on `v2`, or `origin/v2` when the local `v2` ref is unavailable. Do not base them on `dev`.
- Local `main` ref may not exist; use `v2` or `origin/v2` for diffs.
## Live V2 TUI Testing ## Live V2 TUI Testing
+1070 -528
View File
File diff suppressed because it is too large Load Diff
+45 -53
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail set -euo pipefail
APP=opencode APP=opencode2
MUTED='\033[0;2m' MUTED='\033[0;2m'
RED='\033[0;31m' RED='\033[0;31m'
@@ -15,14 +15,14 @@ Usage: install.sh [options]
Options: Options:
-h, --help Display this help message -h, --help Display this help message
-v, --version <version> Install a specific version (e.g., 1.0.180) -v, --version <version> Install a specific version (e.g., 0.0.0-next-17236)
-b, --binary <path> Install from a local binary instead of downloading -b, --binary <path> Install from a local binary instead of downloading
--no-modify-path Don't modify shell config files (.zshrc, .bashrc, etc.) --no-modify-path Don't modify shell config files (.zshrc, .bashrc, etc.)
Examples: Examples:
curl -fsSL https://opencode.ai/install | bash curl -fsSL https://raw.githubusercontent.com/anomalyco/opencode/v2/install | bash
curl -fsSL https://opencode.ai/install | bash -s -- --version 1.0.180 curl -fsSL https://raw.githubusercontent.com/anomalyco/opencode/v2/install | bash -s -- --version 0.0.0-next-17236
./install --binary /path/to/opencode ./install --binary /path/to/opencode2
EOF EOF
} }
@@ -109,11 +109,6 @@ else
;; ;;
esac esac
archive_ext=".zip"
if [ "$os" = "linux" ]; then
archive_ext=".tar.gz"
fi
is_musl=false is_musl=false
if [ "$os" = "linux" ]; then if [ "$os" = "linux" ]; then
if [ -f /etc/alpine-release ]; then if [ -f /etc/alpine-release ]; then
@@ -165,42 +160,42 @@ else
target="$target-musl" target="$target-musl"
fi fi
filename="$APP-$target$archive_ext" if ! command -v tar >/dev/null 2>&1; then
echo -e "${RED}Error: 'tar' is required but not installed.${NC}"
exit 1
if [ "$os" = "linux" ]; then
if ! command -v tar >/dev/null 2>&1; then
echo -e "${RED}Error: 'tar' is required but not installed.${NC}"
exit 1
fi
else
if ! command -v unzip >/dev/null 2>&1; then
echo -e "${RED}Error: 'unzip' is required but not installed.${NC}"
exit 1
fi
fi fi
if [ -z "$requested_version" ]; then if [ -z "$requested_version" ]; then
url="https://github.com/anomalyco/opencode/releases/latest/download/$filename" metadata=$(curl -fsSL https://registry.npmjs.org/@opencode-ai%2fcli/next || true)
specific_version=$(curl -s https://api.github.com/repos/anomalyco/opencode/releases/latest | sed -n 's/.*"tag_name": *"v\([^"]*\)".*/\1/p') specific_version=$(echo "$metadata" | sed -n 's/.*"version":"\([^"]*\)".*/\1/p')
if [[ $? -ne 0 || -z "$specific_version" ]]; then if [ -z "$specific_version" ]; then
echo -e "${RED}Failed to fetch version information${NC}" echo -e "${RED}Failed to fetch version information${NC}"
exit 1 exit 1
fi fi
else else
# Strip leading 'v' if present # Strip leading 'v' if present
requested_version="${requested_version#v}" requested_version="${requested_version#v}"
url="https://github.com/anomalyco/opencode/releases/download/v${requested_version}/$filename"
specific_version=$requested_version specific_version=$requested_version
fi
# Verify the release exists before downloading package_name="@opencode-ai/cli-$target"
http_status=$(curl -sI -o /dev/null -w "%{http_code}" "https://github.com/anomalyco/opencode/releases/tag/v${requested_version}") http_status=$(curl -s -o /dev/null -w "%{http_code}" "https://registry.npmjs.org/@opencode-ai%2fcli-$target/$specific_version" || true)
if [ "$http_status" = "404" ]; then if [ "$http_status" = "404" ]; then
echo -e "${RED}Error: Release v${requested_version} not found${NC}" echo -e "${RED}Error: Version ${specific_version} is not available for $target${NC}"
echo -e "${MUTED}Available releases: https://github.com/anomalyco/opencode/releases${NC}" echo -e "${MUTED}Available versions: https://www.npmjs.com/package/$package_name?activeTab=versions${NC}"
exit 1 exit 1
fi fi
if [ "$http_status" != "200" ]; then
echo -e "${RED}Failed to fetch package information${NC}"
exit 1
fi
filename="cli-$target-$specific_version.tgz"
url="https://registry.npmjs.org/$package_name/-/$filename"
binary_name="$APP"
if [ "$os" = "windows" ]; then
binary_name="$APP.exe"
fi fi
fi fi
@@ -219,11 +214,13 @@ print_message() {
} }
check_version() { check_version() {
if command -v opencode >/dev/null 2>&1; then if command -v "$APP" >/dev/null 2>&1; then
opencode_path=$(which opencode) opencode_path=$(which "$APP")
## Check the installed version ## Check the installed version
installed_version=$(opencode --version 2>/dev/null || echo "") installed_version=$("$APP" --version 2>/dev/null || echo "")
installed_version="${installed_version##* }"
installed_version="${installed_version#v}"
if [[ "$installed_version" != "$specific_version" ]]; then if [[ "$installed_version" != "$specific_version" ]]; then
print_message info "${MUTED}Installed version: ${NC}$installed_version." print_message info "${MUTED}Installed version: ${NC}$installed_version."
@@ -287,7 +284,7 @@ download_with_progress() {
trap "trap - RETURN; rm -f \"$tracefile\"; printf '\033[?25h' >&4; exec 4>&-" RETURN trap "trap - RETURN; rm -f \"$tracefile\"; printf '\033[?25h' >&4; exec 4>&-" RETURN
( (
curl --trace-ascii "$tracefile" -s -L -o "$output" "$url" curl --trace-ascii "$tracefile" -fsL -o "$output" "$url"
) & ) &
local curl_pid=$! local curl_pid=$!
@@ -325,30 +322,25 @@ download_with_progress() {
} }
download_and_install() { download_and_install() {
print_message info "\n${MUTED}Installing ${NC}opencode ${MUTED}version: ${NC}$specific_version" print_message info "\n${MUTED}Installing ${NC}$APP ${MUTED}version: ${NC}$specific_version"
local tmp_dir="${TMPDIR:-/tmp}/opencode_install_$$" local tmp_dir="${TMPDIR:-/tmp}/opencode_install_$$"
mkdir -p "$tmp_dir" mkdir -p "$tmp_dir"
if [[ "$os" == "windows" ]] || ! [ -t 2 ] || ! download_with_progress "$url" "$tmp_dir/$filename"; then if [[ "$os" == "windows" ]] || ! [ -t 2 ] || ! download_with_progress "$url" "$tmp_dir/$filename"; then
# Fallback to standard curl on Windows, non-TTY environments, or if custom progress fails # Fallback to standard curl on Windows, non-TTY environments, or if custom progress fails
curl -# -L -o "$tmp_dir/$filename" "$url" curl -f -# -L -o "$tmp_dir/$filename" "$url"
fi fi
if [ "$os" = "linux" ]; then tar -xzf "$tmp_dir/$filename" -C "$tmp_dir"
tar -xzf "$tmp_dir/$filename" -C "$tmp_dir" mv "$tmp_dir/package/bin/$binary_name" "$INSTALL_DIR"
else chmod 755 "${INSTALL_DIR}/$binary_name"
unzip -q "$tmp_dir/$filename" -d "$tmp_dir"
fi
mv "$tmp_dir/opencode" "$INSTALL_DIR"
chmod 755 "${INSTALL_DIR}/opencode"
rm -rf "$tmp_dir" rm -rf "$tmp_dir"
} }
install_from_binary() { install_from_binary() {
print_message info "\n${MUTED}Installing ${NC}opencode ${MUTED}from: ${NC}$binary_path" print_message info "\n${MUTED}Installing ${NC}$APP ${MUTED}from: ${NC}$binary_path"
cp "$binary_path" "${INSTALL_DIR}/opencode" cp "$binary_path" "${INSTALL_DIR}/$APP"
chmod 755 "${INSTALL_DIR}/opencode" chmod 755 "${INSTALL_DIR}/$APP"
} }
if [ -n "$binary_path" ]; then if [ -n "$binary_path" ]; then
@@ -453,8 +445,8 @@ echo -e ""
echo -e "${MUTED}OpenCode includes free models, to start:${NC}" echo -e "${MUTED}OpenCode includes free models, to start:${NC}"
echo -e "" echo -e ""
echo -e "cd <project> ${MUTED}# Open directory${NC}" echo -e "cd <project> ${MUTED}# Open directory${NC}"
echo -e "opencode ${MUTED}# Run command${NC}" echo -e "opencode2 ${MUTED}# Run command${NC}"
echo -e "" echo -e ""
echo -e "${MUTED}For more information visit ${NC}https://opencode.ai/docs" echo -e "${MUTED}For more information visit ${NC}https://opencode.ai/v2/docs"
echo -e "" echo -e ""
echo -e "" echo -e ""
+12 -8
View File
@@ -33,6 +33,7 @@
"packages": [ "packages": [
"packages/*", "packages/*",
"packages/console/*", "packages/console/*",
"packages/lab/*",
"packages/stats/*", "packages/stats/*",
"packages/slack" "packages/slack"
], ],
@@ -46,9 +47,9 @@
"@octokit/rest": "22.0.0", "@octokit/rest": "22.0.0",
"@hono/standard-validator": "0.2.0", "@hono/standard-validator": "0.2.0",
"@hono/zod-validator": "0.4.2", "@hono/zod-validator": "0.4.2",
"@opentui/core": "0.0.0-20260808-9ecf7c0a", "@opentui/core": "0.5.2",
"@opentui/keymap": "0.0.0-20260808-9ecf7c0a", "@opentui/keymap": "0.5.2",
"@opentui/solid": "0.0.0-20260808-9ecf7c0a", "@opentui/solid": "0.5.2",
"@tanstack/solid-virtual": "3.13.32", "@tanstack/solid-virtual": "3.13.32",
"@shikijs/stream": "4.2.0", "@shikijs/stream": "4.2.0",
"@standard-schema/spec": "1.1.0", "@standard-schema/spec": "1.1.0",
@@ -120,7 +121,8 @@
"prettier": "3.6.2", "prettier": "3.6.2",
"semver": "^7.6.0", "semver": "^7.6.0",
"sst": "catalog:", "sst": "catalog:",
"turbo": "2.10.2" "turbo": "2.10.2",
"vitest": "4.1.10"
}, },
"dependencies": { "dependencies": {
"@aws-sdk/client-s3": "3.933.0", "@aws-sdk/client-s3": "3.933.0",
@@ -150,9 +152,9 @@
"electron" "electron"
], ],
"overrides": { "overrides": {
"@opentui/core": "0.0.0-20260808-9ecf7c0a", "@opentui/core": "catalog:",
"@opentui/keymap": "0.0.0-20260808-9ecf7c0a", "@opentui/keymap": "catalog:",
"@opentui/solid": "0.0.0-20260808-9ecf7c0a", "@opentui/solid": "catalog:",
"@types/bun": "catalog:", "@types/bun": "catalog:",
"@types/node": "catalog:", "@types/node": "catalog:",
"effect": "catalog:" "effect": "catalog:"
@@ -171,6 +173,8 @@
"@pierre/trees@1.0.0-beta.4": "patches/@pierre%2Ftrees@1.0.0-beta.4.patch", "@pierre/trees@1.0.0-beta.4": "patches/@pierre%2Ftrees@1.0.0-beta.4.patch",
"@modelcontextprotocol/sdk@1.29.0": "patches/@modelcontextprotocol%2Fsdk@1.29.0.patch", "@modelcontextprotocol/sdk@1.29.0": "patches/@modelcontextprotocol%2Fsdk@1.29.0.patch",
"effect@4.0.0-beta.101": "patches/effect@4.0.0-beta.101.patch", "effect@4.0.0-beta.101": "patches/effect@4.0.0-beta.101.patch",
"@tanstack/virtual-core@3.17.3": "patches/@tanstack%2Fvirtual-core@3.17.3.patch" "@tanstack/virtual-core@3.17.3": "patches/@tanstack%2Fvirtual-core@3.17.3.patch",
"@cloudflare/vitest-pool-workers@0.12.6": "patches/@cloudflare%2Fvitest-pool-workers@0.12.6.patch",
"@ff-labs/fff-bun@0.10.1": "patches/@ff-labs%2Ffff-bun@0.10.1.patch"
} }
} }
+2 -2
View File
@@ -10,8 +10,8 @@
// //
// Manual `cache: CacheHint` placements on individual parts are preserved and // Manual `cache: CacheHint` placements on individual parts are preserved and
// count against the four-breakpoint budget; auto only fills remaining slots. // count against the four-breakpoint budget; auto only fills remaining slots.
import { CacheHint, type CachePolicy, type CachePolicyObject } from "./schema/options" import { CacheHint, type CachePolicy, type CachePolicyObject } from "./schema/options.js"
import { LLMRequest, Message, ToolDefinition, type ContentPart } from "./schema/messages" import { LLMRequest, Message, ToolDefinition, type ContentPart } from "./schema/messages.js"
const AUTO: CachePolicyObject = { const AUTO: CachePolicyObject = {
tools: true, tools: true,
+3 -3
View File
@@ -1,7 +1,7 @@
import { Context, Effect, Layer } from "effect" import { Context, Effect, Layer } from "effect"
import { RequestExecutor } from "./route/executor" import { RequestExecutor } from "./route/executor.js"
import type { ImageOptions, ImageRequest, ImageRequestFor, ImageResponse } from "./image" import type { ImageOptions, ImageRequest, ImageRequestFor, ImageResponse } from "./image.js"
import type { AIError } from "./schema" import type { AIError } from "./schema/index.js"
export type Execute = RequestExecutor.Interface["execute"] export type Execute = RequestExecutor.Interface["execute"]
+10 -2
View File
@@ -1,6 +1,14 @@
import { Effect, Schema } from "effect" import { Effect, Schema } from "effect"
import { HttpOptions, InvalidRequestReason, AIError, ModelID, ProviderID, ProviderMetadata, Usage } from "./schema" import {
import { ImageClient, Service, type Execute as ImageExecute } from "./image-client" HttpOptions,
InvalidRequestReason,
AIError,
ModelID,
ProviderID,
ProviderMetadata,
Usage,
} from "./schema/index.js"
import { ImageClient, Service, type Execute as ImageExecute } from "./image-client.js"
export interface ImageRoute<Options extends ImageOptions = ImageOptions> { export interface ImageRoute<Options extends ImageOptions = ImageOptions> {
readonly id: string readonly id: string
+21 -18
View File
@@ -1,22 +1,22 @@
export { LLMClient } from "./route/client" export { LLMClient } from "./route/client.js"
export { ImageClient } from "./image-client" export { ImageClient } from "./image-client.js"
export { Auth } from "./route/auth" export { Auth } from "./route/auth.js"
export { Provider } from "./provider" export { Provider } from "./provider.js"
export { ProviderPackage } from "./provider-package" export { ProviderPackage } from "./provider-package.js"
export { isContextOverflow, isContextOverflowFailure } from "./provider-error" export { isContextOverflow, isContextOverflowFailure } from "./provider-error.js"
export type { export type {
RouteLanguageModelInput, RouteLanguageModelInput,
RouteRoutedLanguageModelInput, RouteRoutedLanguageModelInput,
Interface as LLMClientShape, Interface as LLMClientShape,
Service as LLMClientService, Service as LLMClientService,
} from "./route/client" } from "./route/client.js"
export * from "./schema" export * from "./schema/index.js"
export { GeneratedImage, ImageInput, ImageInputSchema, ImageModel, ImageRequest, ImageResponse } from "./image" export { GeneratedImage, ImageInput, ImageInputSchema, ImageModel, ImageRequest, ImageResponse } from "./image.js"
export type { ImageModelOptions, ImageOptions, ImageRequestFor, ImageRequestInput, ImageRoute } from "./image" export type { ImageModelOptions, ImageOptions, ImageRequestFor, ImageRequestInput, ImageRoute } from "./image.js"
export { Image } from "./image" export { Image } from "./image.js"
export { Tool, ToolFailure, toDefinitions } from "./tool" export { Tool, ToolFailure, toDefinitions } from "./tool.js"
export { ToolRuntime } from "./tool-runtime" export { ToolRuntime } from "./tool-runtime.js"
export type { DispatchResult as ToolDispatchResult, ToolSettlement } from "./tool-runtime" export type { DispatchResult as ToolDispatchResult, ToolSettlement } from "./tool-runtime.js"
export type { export type {
AnyExecutableTool, AnyExecutableTool,
AnyTool, AnyTool,
@@ -29,11 +29,14 @@ export type {
Tools, Tools,
ToolSchema, ToolSchema,
ToolToModelOutput, ToolToModelOutput,
} from "./tool" } from "./tool.js"
export * as LLM from "./llm" export * as LLM from "./llm.js"
export type { export type {
Definition as ProviderDefinition, Definition as ProviderDefinition,
LanguageModelFactory as ProviderLanguageModelFactory, LanguageModelFactory as ProviderLanguageModelFactory,
LanguageModelOptions as ProviderLanguageModelOptions, LanguageModelOptions as ProviderLanguageModelOptions,
} from "./provider" } from "./provider.js"
export type { Definition as ProviderPackageDefinition, Settings as ProviderPackageSettings } from "./provider-package" export type {
Definition as ProviderPackageDefinition,
Settings as ProviderPackageSettings,
} from "./provider-package.js"
+3 -3
View File
@@ -1,5 +1,5 @@
import { Effect, JsonSchema, Schema } from "effect" import { Effect, JsonSchema, Schema } from "effect"
import { LLMClient, Service } from "./route/client" import { LLMClient, Service } from "./route/client.js"
import { import {
GenerationOptions, GenerationOptions,
HttpOptions, HttpOptions,
@@ -15,8 +15,8 @@ import {
ToolDefinition, ToolDefinition,
type ContentPart, type ContentPart,
type LanguageModelProviderOptions, type LanguageModelProviderOptions,
} from "./schema" } from "./schema/index.js"
import { make as makeTool, toDefinitions, type ToolSchema } from "./tool" import { make as makeTool, toDefinitions, type ToolSchema } from "./tool.js"
/** Input accepted by `LLM.request`, normalized into the canonical `LLMRequest` class. */ /** Input accepted by `LLM.request`, normalized into the canonical `LLMRequest` class. */
export type RequestInput<SelectedLanguageModel extends LanguageModel = LanguageModel> = Omit< export type RequestInput<SelectedLanguageModel extends LanguageModel = LanguageModel> = Omit<
+1 -1
View File
@@ -1 +1 @@
export * from "./protocols/index" export * from "./protocols/index.js"
+17 -14
View File
@@ -1,10 +1,10 @@
import { Effect, Schema } from "effect" import { Effect, Schema } from "effect"
import { Tool } from "@opencode-ai/schema/tool" import { Tool } from "@opencode-ai/schema/tool"
import { Route } from "../route/client" import { Route } from "../route/client.js"
import { Auth } from "../route/auth" import { Auth } from "../route/auth.js"
import { Endpoint } from "../route/endpoint" import { Endpoint } from "../route/endpoint.js"
import { Framing } from "../route/framing" import { Framing } from "../route/framing.js"
import { Protocol } from "../route/protocol" import { Protocol } from "../route/protocol.js"
import { import {
AIError, AIError,
LLMEvent, LLMEvent,
@@ -21,13 +21,13 @@ import {
type ToolCallPart, type ToolCallPart,
type ToolDefinition, type ToolDefinition,
type ToolResultPart, type ToolResultPart,
} from "../schema" } from "../schema/index.js"
import { JsonObject, optionalArray, optionalNull, ProviderShared } from "./shared" import { JsonObject, optionalArray, optionalNull, ProviderShared } from "./shared.js"
import { classifyProviderFailure } from "../provider-error" import { classifyProviderFailure } from "../provider-error.js"
import * as Cache from "./utils/cache" import * as Cache from "./utils/cache.js"
import { Lifecycle } from "./utils/lifecycle" import { Lifecycle } from "./utils/lifecycle.js"
import { ToolSchemaProjection } from "./utils/tool-schema" import { ToolSchemaProjection } from "./utils/tool-schema.js"
import { ToolStream } from "./utils/tool-stream" import { ToolStream } from "./utils/tool-stream.js"
const ADAPTER = "anthropic-messages" const ADAPTER = "anthropic-messages"
const MEDIA_MIMES = new Set<string>([...ProviderShared.IMAGE_MIMES, ...ProviderShared.PDF_MIMES]) const MEDIA_MIMES = new Set<string>([...ProviderShared.IMAGE_MIMES, ...ProviderShared.PDF_MIMES])
@@ -573,7 +573,10 @@ const lowerMessages = Effect.fn("AnthropicMessages.lowerMessages")(function* (
cache_control: cacheControl(breakpoints, part.cache), cache_control: cacheControl(breakpoints, part.cache),
}) })
} }
messages.push({ role: "user", content }) const previous = messages.at(-1)
if (previous?.role === "user" && previous.content.every((block) => block.type === "tool_result"))
messages[messages.length - 1] = { role: "user", content: [...previous.content, ...content] }
else messages.push({ role: "user", content })
} }
return messages return messages
@@ -1029,4 +1032,4 @@ export const route = Route.make({
headers: () => ({ "anthropic-version": "2023-06-01" }), headers: () => ({ "anthropic-version": "2023-06-01" }),
}) })
export * as AnthropicMessages from "./anthropic-messages" export * as AnthropicMessages from "./anthropic-messages.js"
+23 -17
View File
@@ -1,7 +1,7 @@
import { Effect, Schema } from "effect" import { Effect, Schema } from "effect"
import { Route } from "../route/client" import { Route } from "../route/client.js"
import { Endpoint } from "../route/endpoint" import { Endpoint } from "../route/endpoint.js"
import { Protocol } from "../route/protocol" import { Protocol } from "../route/protocol.js"
import { import {
AIError, AIError,
LLMEvent, LLMEvent,
@@ -17,20 +17,20 @@ import {
type ToolCallPart, type ToolCallPart,
type ToolDefinition, type ToolDefinition,
type ToolResultPart, type ToolResultPart,
} from "../schema" } from "../schema/index.js"
import { BedrockEventStream } from "./bedrock-event-stream" import { BedrockEventStream } from "./bedrock-event-stream.js"
import { classifyProviderFailure } from "../provider-error" import { classifyProviderFailure } from "../provider-error.js"
import { JsonObject, optionalArray, ProviderShared } from "./shared" import { JsonObject, optionalArray, ProviderShared } from "./shared.js"
import { BedrockAuth } from "./utils/bedrock-auth" import { BedrockAuth } from "./utils/bedrock-auth.js"
import { BedrockCache } from "./utils/bedrock-cache" import { BedrockCache } from "./utils/bedrock-cache.js"
import { BedrockMedia } from "./utils/bedrock-media" import { BedrockMedia } from "./utils/bedrock-media.js"
import { Lifecycle } from "./utils/lifecycle" import { Lifecycle } from "./utils/lifecycle.js"
import { ToolSchemaProjection } from "./utils/tool-schema" import { ToolSchemaProjection } from "./utils/tool-schema.js"
import { ToolStream } from "./utils/tool-stream" import { ToolStream } from "./utils/tool-stream.js"
const ADAPTER = "bedrock-converse" const ADAPTER = "bedrock-converse"
export type { Credentials as BedrockCredentials } from "./utils/bedrock-auth" export type { Credentials as BedrockCredentials } from "./utils/bedrock-auth.js"
// ============================================================================= // =============================================================================
// Request Body Schema // Request Body Schema
@@ -348,7 +348,10 @@ const lowerMessages = Effect.fn("BedrockConverse.lowerMessages")(function* (
continue continue
} }
} }
messages.push({ role: "user", content }) const previous = messages.at(-1)
if (previous?.role === "user")
messages[messages.length - 1] = { role: "user", content: [...previous.content, ...content] }
else messages.push({ role: "user", content })
continue continue
} }
@@ -392,7 +395,10 @@ const lowerMessages = Effect.fn("BedrockConverse.lowerMessages")(function* (
const cachePoint = BedrockCache.block(breakpoints, part.cache) const cachePoint = BedrockCache.block(breakpoints, part.cache)
if (cachePoint) content.push(cachePoint) if (cachePoint) content.push(cachePoint)
} }
messages.push({ role: "user", content }) const previous = messages.at(-1)
if (previous?.role === "user")
messages[messages.length - 1] = { role: "user", content: [...previous.content, ...content] }
else messages.push({ role: "user", content })
} }
return messages return messages
@@ -731,4 +737,4 @@ export const route = Route.make({
export const sigV4Auth = BedrockAuth.sigV4 export const sigV4Auth = BedrockAuth.sigV4
export * as BedrockConverse from "./bedrock-converse" export * as BedrockConverse from "./bedrock-converse.js"
@@ -1,8 +1,8 @@
import { EventStreamCodec } from "@smithy/eventstream-codec" import { EventStreamCodec } from "@smithy/eventstream-codec"
import { fromUtf8, toUtf8 } from "@smithy/util-utf8" import { fromUtf8, toUtf8 } from "@smithy/util-utf8"
import { Effect, Stream } from "effect" import { Effect, Stream } from "effect"
import { Framing } from "../route/framing" import { Framing } from "../route/framing.js"
import { ProviderShared } from "./shared" import { ProviderShared } from "./shared.js"
// Bedrock streams responses using the AWS event stream binary protocol — each // Bedrock streams responses using the AWS event stream binary protocol — each
// frame is `[length:4][headers-length:4][prelude-crc:4][headers][payload][crc:4]`. // frame is `[length:4][headers-length:4][prelude-crc:4][headers][payload][crc:4]`.
@@ -98,4 +98,4 @@ export const framing = (route: string): Framing.Definition<object> => ({
frame: (bytes) => bytes.pipe(Stream.mapAccumEffect(() => initialFrameBuffer, consumeFrames(route))), frame: (bytes) => bytes.pipe(Stream.mapAccumEffect(() => initialFrameBuffer, consumeFrames(route))),
}) })
export * as BedrockEventStream from "./bedrock-event-stream" export * as BedrockEventStream from "./bedrock-event-stream.js"
+11 -11
View File
@@ -1,10 +1,10 @@
import { Effect, Schema } from "effect" import { Effect, Schema } from "effect"
import { Tool } from "@opencode-ai/schema/tool" import { Tool } from "@opencode-ai/schema/tool"
import { Route } from "../route/client" import { Route } from "../route/client.js"
import { Auth } from "../route/auth" import { Auth } from "../route/auth.js"
import { Endpoint } from "../route/endpoint" import { Endpoint } from "../route/endpoint.js"
import { Framing } from "../route/framing" import { Framing } from "../route/framing.js"
import { Protocol } from "../route/protocol" import { Protocol } from "../route/protocol.js"
import { import {
LLMEvent, LLMEvent,
Usage, Usage,
@@ -17,11 +17,11 @@ import {
type TextPart, type TextPart,
type ToolCallPart, type ToolCallPart,
type ToolDefinition, type ToolDefinition,
} from "../schema" } from "../schema/index.js"
import { JsonObject, optionalArray, ProviderShared } from "./shared" import { JsonObject, optionalArray, ProviderShared } from "./shared.js"
import { GeminiToolSchema } from "./utils/gemini-tool-schema" import { GeminiToolSchema } from "./utils/gemini-tool-schema.js"
import { Lifecycle } from "./utils/lifecycle" import { Lifecycle } from "./utils/lifecycle.js"
import { ToolSchemaProjection } from "./utils/tool-schema" import { ToolSchemaProjection } from "./utils/tool-schema.js"
const ADAPTER = "gemini" const ADAPTER = "gemini"
const MEDIA_MIMES = new Set<string>(ProviderShared.MEDIA_MIMES) const MEDIA_MIMES = new Set<string>(ProviderShared.MEDIA_MIMES)
@@ -643,4 +643,4 @@ export const route = Route.make({
framing: Framing.sse, framing: Framing.sse,
}) })
export * as Gemini from "./gemini" export * as Gemini from "./gemini.js"
+5 -5
View File
@@ -7,8 +7,8 @@ import {
type ImageInput, type ImageInput,
type ImageRequestFor, type ImageRequestFor,
type ImageRoute, type ImageRoute,
} from "../image" } from "../image.js"
import { Auth, type Definition as AuthDefinition } from "../route/auth" import { Auth, type Definition as AuthDefinition } from "../route/auth.js"
import { import {
InvalidProviderOutputReason, InvalidProviderOutputReason,
AIError, AIError,
@@ -17,9 +17,9 @@ import {
mergeJsonRecords, mergeJsonRecords,
type HttpOptions, type HttpOptions,
type ProviderMetadata, type ProviderMetadata,
} from "../schema" } from "../schema/index.js"
import { ProviderShared } from "./shared" import { ProviderShared } from "./shared.js"
import { ImageInputs } from "./utils/image-input" import { ImageInputs } from "./utils/image-input.js"
const ADAPTER = "google-images" const ADAPTER = "google-images"
export const DEFAULT_BASE_URL = "https://generativelanguage.googleapis.com/v1beta" export const DEFAULT_BASE_URL = "https://generativelanguage.googleapis.com/v1beta"
+9 -9
View File
@@ -1,9 +1,9 @@
export * as AnthropicMessages from "./anthropic-messages" export * as AnthropicMessages from "./anthropic-messages.js"
export * as BedrockConverse from "./bedrock-converse" export * as BedrockConverse from "./bedrock-converse.js"
export * as Gemini from "./gemini" export * as Gemini from "./gemini.js"
export * as OpenAIChat from "./openai-chat" export * as OpenAIChat from "./openai-chat.js"
export * as OpenAIImages from "./openai-images" export * as OpenAIImages from "./openai-images.js"
export * as OpenAICompatibleChat from "./openai-compatible-chat" export * as OpenAICompatibleChat from "./openai-compatible-chat.js"
export * as OpenAICompatibleResponses from "./openai-compatible-responses" export * as OpenAICompatibleResponses from "./openai-compatible-responses.js"
export * as OpenAIResponses from "./openai-responses" export * as OpenAIResponses from "./openai-responses.js"
export * as OpenResponses from "./open-responses" export * as OpenResponses from "./open-responses.js"
+10 -10
View File
@@ -1,7 +1,7 @@
import { Effect, Schema } from "effect" import { Effect, Schema } from "effect"
import type { Content } from "@opencode-ai/schema/tool" import type { Content } from "@opencode-ai/schema/tool"
import { HttpTransport } from "../route/transport" import { HttpTransport } from "../route/transport/index.js"
import { Protocol } from "../route/protocol" import { Protocol } from "../route/protocol.js"
import { import {
AIError, AIError,
LLMEvent, LLMEvent,
@@ -16,13 +16,13 @@ import {
type ToolCallPart, type ToolCallPart,
type ToolDefinition, type ToolDefinition,
type ToolResultPart, type ToolResultPart,
} from "../schema" } from "../schema/index.js"
import { JsonObject, optionalArray, optionalNull, ProviderShared } from "./shared" import { JsonObject, optionalArray, optionalNull, ProviderShared } from "./shared.js"
import { classifyProviderFailure } from "../provider-error" import { classifyProviderFailure } from "../provider-error.js"
import { OpenResponsesOptions } from "./utils/open-responses-options" import { OpenResponsesOptions } from "./utils/open-responses-options.js"
import { Lifecycle } from "./utils/lifecycle" import { Lifecycle } from "./utils/lifecycle.js"
import { ToolSchemaProjection } from "./utils/tool-schema" import { ToolSchemaProjection } from "./utils/tool-schema.js"
import { ToolStream } from "./utils/tool-stream" import { ToolStream } from "./utils/tool-stream.js"
const ADAPTER = "open-responses" const ADAPTER = "open-responses"
const NAME = "Open Responses" const NAME = "Open Responses"
@@ -1061,4 +1061,4 @@ export const protocol = Protocol.make({
export const httpTransport = HttpTransport.sseJson.with<OpenResponsesBody>() export const httpTransport = HttpTransport.sseJson.with<OpenResponsesBody>()
export * as OpenResponses from "./open-responses" export * as OpenResponses from "./open-responses.js"
+13 -13
View File
@@ -1,10 +1,10 @@
import { Effect, Schema } from "effect" import { Effect, Schema } from "effect"
import { Tool } from "@opencode-ai/schema/tool" import { Tool } from "@opencode-ai/schema/tool"
import { Route } from "../route/client" import { Route } from "../route/client.js"
import { Auth } from "../route/auth" import { Auth } from "../route/auth.js"
import { Endpoint } from "../route/endpoint" import { Endpoint } from "../route/endpoint.js"
import { HttpTransport } from "../route/transport" import { HttpTransport } from "../route/transport/index.js"
import { Protocol } from "../route/protocol" import { Protocol } from "../route/protocol.js"
import { import {
AIError, AIError,
LLMEvent, LLMEvent,
@@ -19,13 +19,13 @@ import {
type TextPart, type TextPart,
type ToolCallPart, type ToolCallPart,
type ToolDefinition, type ToolDefinition,
} from "../schema" } from "../schema/index.js"
import { classifyProviderFailure } from "../provider-error" import { classifyProviderFailure } from "../provider-error.js"
import { isRecord, JsonObject, optionalArray, optionalNull, ProviderShared } from "./shared" import { isRecord, JsonObject, optionalArray, optionalNull, ProviderShared } from "./shared.js"
import { OpenAIOptions } from "./utils/openai-options" import { OpenAIOptions } from "./utils/openai-options.js"
import { Lifecycle } from "./utils/lifecycle" import { Lifecycle } from "./utils/lifecycle.js"
import { ToolSchemaProjection } from "./utils/tool-schema" import { ToolSchemaProjection } from "./utils/tool-schema.js"
import { ToolStream } from "./utils/tool-stream" import { ToolStream } from "./utils/tool-stream.js"
const ADAPTER = "openai-chat" const ADAPTER = "openai-chat"
const IMAGE_MIMES = new Set<string>(ProviderShared.IMAGE_MIMES) const IMAGE_MIMES = new Set<string>(ProviderShared.IMAGE_MIMES)
@@ -884,4 +884,4 @@ export const route = Route.make({
transport: httpTransport, transport: httpTransport,
}) })
export * as OpenAIChat from "./openai-chat" export * as OpenAIChat from "./openai-chat.js"
@@ -1,7 +1,7 @@
import { Route, type RouteRoutedLanguageModelInput } from "../route/client" import { Route, type RouteRoutedLanguageModelInput } from "../route/client.js"
import { Endpoint } from "../route/endpoint" import { Endpoint } from "../route/endpoint.js"
import { Framing } from "../route/framing" import { Framing } from "../route/framing.js"
import * as OpenAIChat from "./openai-chat" import * as OpenAIChat from "./openai-chat.js"
const ADAPTER = "openai-compatible-chat" const ADAPTER = "openai-compatible-chat"
@@ -22,4 +22,4 @@ export const route = Route.make({
framing: Framing.sse, framing: Framing.sse,
}) })
export * as OpenAICompatibleChat from "./openai-compatible-chat" export * as OpenAICompatibleChat from "./openai-compatible-chat.js"
@@ -1,6 +1,6 @@
import { Route, type RouteRoutedLanguageModelInput } from "../route/client" import { Route, type RouteRoutedLanguageModelInput } from "../route/client.js"
import { Endpoint } from "../route/endpoint" import { Endpoint } from "../route/endpoint.js"
import { OpenResponses } from "./open-responses" import { OpenResponses } from "./open-responses.js"
const ADAPTER = "openai-compatible-responses" const ADAPTER = "openai-compatible-responses"
@@ -19,4 +19,4 @@ export const route = Route.make({
transport: OpenResponses.httpTransport, transport: OpenResponses.httpTransport,
}) })
export * as OpenAICompatibleResponses from "./openai-compatible-responses" export * as OpenAICompatibleResponses from "./openai-compatible-responses.js"
+6 -6
View File
@@ -7,8 +7,8 @@ import {
type ImageInput, type ImageInput,
type ImageRequestFor, type ImageRequestFor,
type ImageRoute, type ImageRoute,
} from "../image" } from "../image.js"
import { Auth, type Definition as AuthDefinition } from "../route/auth" import { Auth, type Definition as AuthDefinition } from "../route/auth.js"
import { import {
InvalidProviderOutputReason, InvalidProviderOutputReason,
AIError, AIError,
@@ -16,10 +16,10 @@ import {
mergeHttpOptions, mergeHttpOptions,
mergeJsonRecords, mergeJsonRecords,
type HttpOptions, type HttpOptions,
} from "../schema" } from "../schema/index.js"
import { ProviderShared } from "./shared" import { ProviderShared } from "./shared.js"
import { ImageInputs } from "./utils/image-input" import { ImageInputs } from "./utils/image-input.js"
import { OpenAIImage } from "./utils/openai-image" import { OpenAIImage } from "./utils/openai-image.js"
const ADAPTER = "openai-images" const ADAPTER = "openai-images"
export const DEFAULT_BASE_URL = "https://api.openai.com/v1" export const DEFAULT_BASE_URL = "https://api.openai.com/v1"
+12 -12
View File
@@ -1,15 +1,15 @@
import { Effect, Encoding, Schema } from "effect" import { Effect, Encoding, Schema } from "effect"
import { Route } from "../route/client" import { Route } from "../route/client.js"
import { Auth } from "../route/auth" import { Auth } from "../route/auth.js"
import { Endpoint } from "../route/endpoint" import { Endpoint } from "../route/endpoint.js"
import { Protocol } from "../route/protocol" import { Protocol } from "../route/protocol.js"
import { HttpTransport, WebSocketTransport } from "../route/transport" import { HttpTransport, WebSocketTransport } from "../route/transport/index.js"
import { LLMEvent, LLMRequest, type JsonSchema, type ToolDefinition } from "../schema" import { LLMEvent, LLMRequest, type JsonSchema, type ToolDefinition } from "../schema/index.js"
import { OpenResponses } from "./open-responses" import { OpenResponses } from "./open-responses.js"
import { optionalArray, ProviderShared } from "./shared" import { optionalArray, ProviderShared } from "./shared.js"
import { Lifecycle } from "./utils/lifecycle" import { Lifecycle } from "./utils/lifecycle.js"
import { OpenAIImage } from "./utils/openai-image" import { OpenAIImage } from "./utils/openai-image.js"
import { ToolSchemaProjection } from "./utils/tool-schema" import { ToolSchemaProjection } from "./utils/tool-schema.js"
const ADAPTER = "openai-responses" const ADAPTER = "openai-responses"
const NAME = "OpenAI Responses" const NAME = "OpenAI Responses"
@@ -290,4 +290,4 @@ export const webSocketRoute = Route.make({
defaults: { providerOptions: { openai: { store: false } } }, defaults: { providerOptions: { openai: { store: false } } },
}) })
export * as OpenAIResponses from "./openai-responses" export * as OpenAIResponses from "./openai-responses.js"
+3 -3
View File
@@ -12,8 +12,8 @@ import {
type MediaPart, type MediaPart,
type TextPart, type TextPart,
type ToolResultPart, type ToolResultPart,
} from "../schema" } from "../schema/index.js"
import { isRecord } from "../utils/record" import { isRecord } from "../utils/record.js"
export { isRecord } export { isRecord }
export const Json = Schema.fromJsonString(Schema.Unknown) export const Json = Schema.fromJsonString(Schema.Unknown)
@@ -324,4 +324,4 @@ export const jsonPost = (input: { readonly url: string; readonly body: string; r
HttpClientRequest.bodyText(input.body, "application/json"), HttpClientRequest.bodyText(input.body, "application/json"),
) )
export * as ProviderShared from "./shared" export * as ProviderShared from "./shared.js"
@@ -1,8 +1,8 @@
import { AwsV4Signer } from "aws4fetch" import { AwsV4Signer } from "aws4fetch"
import { Effect } from "effect" import { Effect } from "effect"
import { Headers } from "effect/unstable/http" import { Headers } from "effect/unstable/http"
import { Auth, type AuthInput } from "../../route/auth" import { Auth, type AuthInput } from "../../route/auth.js"
import { ProviderShared } from "../shared" import { ProviderShared } from "../shared.js"
/** /**
* AWS credentials for SigV4 signing. Bedrock also supports Bearer API key auth, * AWS credentials for SigV4 signing. Bedrock also supports Bearer API key auth,
@@ -74,4 +74,4 @@ export const sigV4 = (
/** Bedrock route auth defaults to SigV4 and expects credentials from route configuration. */ /** Bedrock route auth defaults to SigV4 and expects credentials from route configuration. */
export const auth = sigV4(undefined) export const auth = sigV4(undefined)
export * as BedrockAuth from "./bedrock-auth" export * as BedrockAuth from "./bedrock-auth.js"
@@ -1,6 +1,6 @@
import { Schema } from "effect" import { Schema } from "effect"
import type { CacheHint } from "../../schema" import type { CacheHint } from "../../schema/index.js"
import { newBreakpoints, ttlBucket, type Breakpoints } from "./cache" import { newBreakpoints, ttlBucket, type Breakpoints } from "./cache.js"
// Bedrock cache markers are positional: emit a `cachePoint` block immediately // Bedrock cache markers are positional: emit a `cachePoint` block immediately
// after the content the caller wants treated as a cacheable prefix. Bedrock // after the content the caller wants treated as a cacheable prefix. Bedrock
@@ -18,7 +18,7 @@ export type CachePointBlock = Schema.Schema.Type<typeof CachePointBlock>
// budget is respected across `system`, `messages`, and `tools`. // budget is respected across `system`, `messages`, and `tools`.
export const BEDROCK_BREAKPOINT_CAP = 4 export const BEDROCK_BREAKPOINT_CAP = 4
export type { Breakpoints } from "./cache" export type { Breakpoints } from "./cache.js"
export const breakpoints = () => newBreakpoints(BEDROCK_BREAKPOINT_CAP) export const breakpoints = () => newBreakpoints(BEDROCK_BREAKPOINT_CAP)
const DEFAULT_5M: CachePointBlock = { cachePoint: { type: "default" } } const DEFAULT_5M: CachePointBlock = { cachePoint: { type: "default" } }
@@ -34,4 +34,4 @@ export const block = (breakpoints: Breakpoints, cache: CacheHint | undefined): C
return ttlBucket(cache.ttlSeconds) === "1h" ? DEFAULT_1H : DEFAULT_5M return ttlBucket(cache.ttlSeconds) === "1h" ? DEFAULT_1H : DEFAULT_5M
} }
export * as BedrockCache from "./bedrock-cache" export * as BedrockCache from "./bedrock-cache.js"
@@ -1,6 +1,6 @@
import { Effect, Schema } from "effect" import { Effect, Schema } from "effect"
import type { MediaPart } from "../../schema" import type { MediaPart } from "../../schema/index.js"
import { ProviderShared } from "../shared" import { ProviderShared } from "../shared.js"
// Bedrock Converse accepts image `format` as the file extension and // Bedrock Converse accepts image `format` as the file extension and
// `source.bytes` as base64 in the JSON wire format. // `source.bytes` as base64 in the JSON wire format.
@@ -89,4 +89,4 @@ export const lower = Effect.fn("BedrockMedia.lower")(function* (part: MediaPart)
return yield* ProviderShared.invalidRequest(`Bedrock Converse does not support media type ${part.mediaType}`) return yield* ProviderShared.invalidRequest(`Bedrock Converse does not support media type ${part.mediaType}`)
}) })
export * as BedrockMedia from "./bedrock-media" export * as BedrockMedia from "./bedrock-media.js"
@@ -1,4 +1,4 @@
import { isRecord } from "../../utils/record" import { isRecord } from "../../utils/record.js"
// Gemini accepts a JSON Schema-like dialect for tool parameters, but rejects a // Gemini accepts a JSON Schema-like dialect for tool parameters, but rejects a
// handful of common JSON Schema shapes. Keep this projection isolated so the // handful of common JSON Schema shapes. Keep this projection isolated so the
@@ -116,4 +116,4 @@ const projectNode = (schema: unknown, nested = false): Record<string, unknown> |
export const convert = (schema: unknown) => projectNode(sanitizeNode(schema)) export const convert = (schema: unknown) => projectNode(sanitizeNode(schema))
export * as GeminiToolSchema from "./gemini-tool-schema" export * as GeminiToolSchema from "./gemini-tool-schema.js"
@@ -1,6 +1,6 @@
import { Effect, Encoding } from "effect" import { Effect, Encoding } from "effect"
import type { ImageInput } from "../../image" import type { ImageInput } from "../../image.js"
import { InvalidRequestReason, AIError } from "../../schema" import { InvalidRequestReason, AIError } from "../../schema/index.js"
const invalid = (module: string, message: string) => const invalid = (module: string, message: string) =>
new AIError({ new AIError({
+2 -2
View File
@@ -1,4 +1,4 @@
import { LLMEvent, type FinishReasonDetails, type ProviderMetadata, type Usage } from "../../schema" import { LLMEvent, type FinishReasonDetails, type ProviderMetadata, type Usage } from "../../schema/index.js"
export interface State { export interface State {
readonly stepStarted: boolean readonly stepStarted: boolean
@@ -102,4 +102,4 @@ export const finish = (
return { ...stepped, stepStarted: false } return { ...stepped, stepStarted: false }
} }
export * as Lifecycle from "./lifecycle" export * as Lifecycle from "./lifecycle.js"
@@ -1,5 +1,5 @@
import { Schema } from "effect" import { Schema } from "effect"
import { TextVerbosity, type LLMRequest } from "../../schema" import { TextVerbosity, type LLMRequest } from "../../schema/index.js"
export const ResponseIncludables = [ export const ResponseIncludables = [
"file_search_call.results", "file_search_call.results",
@@ -60,4 +60,4 @@ export const resolve = (request: LLMRequest): Resolved => {
} }
} }
export * as OpenResponsesOptions from "./open-responses-options" export * as OpenResponsesOptions from "./open-responses-options.js"
@@ -1,5 +1,5 @@
import { ReasoningEfforts } from "../../schema" import { ReasoningEfforts } from "../../schema/index.js"
import { OpenResponsesOptions } from "./open-responses-options" import { OpenResponsesOptions } from "./open-responses-options.js"
export const OpenAIReasoningEfforts = ReasoningEfforts export const OpenAIReasoningEfforts = ReasoningEfforts
export type OpenAIReasoningEffort = string export type OpenAIReasoningEffort = string
@@ -20,4 +20,4 @@ export const isReasoningEffort = (effort: unknown): effort is OpenAIReasoningEff
export const resolve = OpenResponsesOptions.resolve export const resolve = OpenResponsesOptions.resolve
export * as OpenAIOptions from "./openai-options" export * as OpenAIOptions from "./openai-options.js"
@@ -1,6 +1,6 @@
import type { JsonSchema, LanguageModelToolSchemaCompatibility } from "../../schema" import type { JsonSchema, LanguageModelToolSchemaCompatibility } from "../../schema/index.js"
import { isRecord } from "../../utils/record" import { isRecord } from "../../utils/record.js"
import { GeminiToolSchema } from "./gemini-tool-schema" import { GeminiToolSchema } from "./gemini-tool-schema.js"
const removeNullSchemas = (value: unknown): unknown => { const removeNullSchemas = (value: unknown): unknown => {
if (Array.isArray(value)) return value.map(removeNullSchemas) if (Array.isArray(value)) return value.map(removeNullSchemas)
@@ -1,6 +1,6 @@
import { Effect } from "effect" import { Effect } from "effect"
import { AIError, LLMEvent, type ProviderMetadata, type ToolCall, type ToolInputError } from "../../schema" import { AIError, LLMEvent, type ProviderMetadata, type ToolCall, type ToolInputError } from "../../schema/index.js"
import { eventError, parseToolInput, type ToolAccumulator } from "../shared" import { eventError, parseToolInput, type ToolAccumulator } from "../shared.js"
type StreamKey = string | number type StreamKey = string | number
@@ -223,4 +223,4 @@ export const finishAll = <K extends StreamKey>(route: string, tools: State<K>) =
} }
}) })
export * as ToolStream from "./tool-stream" export * as ToolStream from "./tool-stream.js"
+5 -5
View File
@@ -1,7 +1,7 @@
import { Effect, Encoding, Schema } from "effect" import { Effect, Encoding, Schema } from "effect"
import { Headers, HttpClientRequest } from "effect/unstable/http" import { Headers, HttpClientRequest } from "effect/unstable/http"
import { GeneratedImage, ImageModel, ImageResponse, type ImageRequestFor, type ImageRoute } from "../image" import { GeneratedImage, ImageModel, ImageResponse, type ImageRequestFor, type ImageRoute } from "../image.js"
import { Auth, type Definition as AuthDefinition } from "../route/auth" import { Auth, type Definition as AuthDefinition } from "../route/auth.js"
import { import {
InvalidProviderOutputReason, InvalidProviderOutputReason,
AIError, AIError,
@@ -9,9 +9,9 @@ import {
mergeHttpOptions, mergeHttpOptions,
mergeJsonRecords, mergeJsonRecords,
type HttpOptions, type HttpOptions,
} from "../schema" } from "../schema/index.js"
import { ProviderShared, optionalNull } from "./shared" import { ProviderShared, optionalNull } from "./shared.js"
import { ImageInputs } from "./utils/image-input" import { ImageInputs } from "./utils/image-input.js"
const ADAPTER = "xai-images" const ADAPTER = "xai-images"
export const DEFAULT_BASE_URL = "https://api.x.ai/v1" export const DEFAULT_BASE_URL = "https://api.x.ai/v1"
+11 -5
View File
@@ -1,10 +1,16 @@
import { Effect, Schema } from "effect" import { Effect, Schema } from "effect"
import { Headers, HttpClientRequest } from "effect/unstable/http" import { Headers, HttpClientRequest } from "effect/unstable/http"
import { GeneratedImage, ImageModel, ImageResponse, type ImageRequestFor, type ImageRoute } from "../image" import { GeneratedImage, ImageModel, ImageResponse, type ImageRequestFor, type ImageRoute } from "../image.js"
import { Auth, type Definition as AuthDefinition } from "../route/auth" import { Auth, type Definition as AuthDefinition } from "../route/auth.js"
import { InvalidProviderOutputReason, AIError, mergeHttpOptions, mergeJsonRecords, type HttpOptions } from "../schema" import {
import { ProviderShared } from "./shared" InvalidProviderOutputReason,
import { ImageInputs } from "./utils/image-input" AIError,
mergeHttpOptions,
mergeJsonRecords,
type HttpOptions,
} from "../schema/index.js"
import { ProviderShared } from "./shared.js"
import { ImageInputs } from "./utils/image-input.js"
const ADAPTER = "zai-images" const ADAPTER = "zai-images"
export const DEFAULT_BASE_URL = "https://api.z.ai/api/paas/v4" export const DEFAULT_BASE_URL = "https://api.z.ai/api/paas/v4"
+1 -1
View File
@@ -12,7 +12,7 @@ import {
type HttpContext, type HttpContext,
type HttpRateLimitDetails, type HttpRateLimitDetails,
type ProviderMetadata, type ProviderMetadata,
} from "./schema" } from "./schema/index.js"
const patterns = [ const patterns = [
/prompt is too long/i, /prompt is too long/i,
+2 -2
View File
@@ -1,4 +1,4 @@
import type { LanguageModel, ProviderOptions } from "./schema" import type { LanguageModel, ProviderOptions } from "./schema/index.js"
export interface Settings extends Readonly<Record<string, unknown>> { export interface Settings extends Readonly<Record<string, unknown>> {
readonly baseURL?: string readonly baseURL?: string
@@ -18,4 +18,4 @@ export interface Definition<
readonly model: (modelID: string, settings: ProviderSettings) => LanguageModel<Options> readonly model: (modelID: string, settings: ProviderSettings) => LanguageModel<Options>
} }
export * as ProviderPackage from "./provider-package" export * as ProviderPackage from "./provider-package.js"
+2 -2
View File
@@ -1,4 +1,4 @@
import type { LanguageModel, ModelID, ProviderID } from "./schema" import type { LanguageModel, ModelID, ProviderID } from "./schema/index.js"
export type LanguageModelOptions = Pick<LanguageModel.Input, "defaults" | "compatibility"> export type LanguageModelOptions = Pick<LanguageModel.Input, "defaults" | "compatibility">
@@ -33,4 +33,4 @@ export const make = <DefinitionType extends DefinitionShape>(
definition: NoExtraFields<DefinitionType, DefinitionShape>, definition: NoExtraFields<DefinitionType, DefinitionShape>,
) => definition ) => definition
export * as Provider from "./provider" export * as Provider from "./provider.js"
+1 -1
View File
@@ -1 +1 @@
export * from "./providers/index" export * from "./providers/index.js"
@@ -1,11 +1,11 @@
import { Auth } from "../route/auth" import { Auth } from "../route/auth.js"
import type { Route as RouteDef, RouteDefaultsInput } from "../route/client" import type { Route as RouteDef, RouteDefaultsInput } from "../route/client.js"
import type { ProviderPackage } from "../provider-package" import type { ProviderPackage } from "../provider-package.js"
import { OpenAIChat } from "../protocols/openai-chat" import { OpenAIChat } from "../protocols/openai-chat.js"
import { OpenAIResponses } from "../protocols/openai-responses" import { OpenAIResponses } from "../protocols/openai-responses.js"
import { BedrockAuth, type Credentials } from "../protocols/utils/bedrock-auth" import { BedrockAuth, type Credentials } from "../protocols/utils/bedrock-auth.js"
import { ProviderID, type ModelID } from "../schema" import { ProviderID, type ModelID } from "../schema/index.js"
import { withOpenAIOptions, type OpenAIProviderOptionsInput } from "./openai-options" import { withOpenAIOptions, type OpenAIProviderOptionsInput } from "./openai-options.js"
export const id = ProviderID.make("amazon-bedrock") export const id = ProviderID.make("amazon-bedrock")
+6 -6
View File
@@ -1,9 +1,9 @@
import type { RouteDefaultsInput } from "../route/client" import type { RouteDefaultsInput } from "../route/client.js"
import { Auth } from "../route/auth" import { Auth } from "../route/auth.js"
import type { ProviderPackage } from "../provider-package" import type { ProviderPackage } from "../provider-package.js"
import { ProviderID, type ModelID } from "../schema" import { ProviderID, type ModelID } from "../schema/index.js"
import * as BedrockConverse from "../protocols/bedrock-converse" import * as BedrockConverse from "../protocols/bedrock-converse.js"
import type { BedrockCredentials } from "../protocols/bedrock-converse" import type { BedrockCredentials } from "../protocols/bedrock-converse.js"
export const id = ProviderID.make("amazon-bedrock") export const id = ProviderID.make("amazon-bedrock")
@@ -1,2 +1,2 @@
export { chatModel as model } from "../amazon-bedrock-mantle" export { chatModel as model } from "../amazon-bedrock-mantle.js"
export type { Settings } from "../amazon-bedrock-mantle" export type { Settings } from "../amazon-bedrock-mantle.js"
@@ -1,2 +1,2 @@
export { chatModel as model } from "../../amazon-bedrock-mantle" export { chatModel as model } from "../../amazon-bedrock-mantle.js"
export type { Settings } from "../../amazon-bedrock-mantle" export type { Settings } from "../../amazon-bedrock-mantle.js"
@@ -1,2 +1,2 @@
export { responsesModel as model } from "../../amazon-bedrock-mantle" export { responsesModel as model } from "../../amazon-bedrock-mantle.js"
export type { Settings } from "../../amazon-bedrock-mantle" export type { Settings } from "../../amazon-bedrock-mantle.js"
@@ -1,9 +1,9 @@
import type { ProviderPackage } from "../provider-package" import type { ProviderPackage } from "../provider-package.js"
import { AnthropicMessages } from "../protocols/anthropic-messages" import { AnthropicMessages } from "../protocols/anthropic-messages.js"
import { Auth } from "../route/auth" import { Auth } from "../route/auth.js"
import type { ProviderAuthOption } from "../route/auth-options" import type { ProviderAuthOption } from "../route/auth-options.js"
import type { RouteDefaultsInput } from "../route/client" import type { RouteDefaultsInput } from "../route/client.js"
import { ProviderID, type ModelID } from "../schema" import { ProviderID, type ModelID } from "../schema/index.js"
export type AnthropicOptionsInput = AnthropicMessages.OptionsInput export type AnthropicOptionsInput = AnthropicMessages.OptionsInput
export type AnthropicProviderOptionsInput = AnthropicMessages.ProviderOptionsInput export type AnthropicProviderOptionsInput = AnthropicMessages.ProviderOptionsInput
@@ -74,4 +74,4 @@ export const model: ProviderPackage.Definition<Settings, AnthropicMessages.Provi
}).model(modelID) }).model(modelID)
} }
export * as AnthropicCompatible from "./anthropic-compatible" export * as AnthropicCompatible from "./anthropic-compatible.js"
+7 -7
View File
@@ -1,10 +1,10 @@
import type { RouteDefaultsInput } from "../route/client" import type { RouteDefaultsInput } from "../route/client.js"
import { Auth } from "../route/auth" import { Auth } from "../route/auth.js"
import type { ProviderAuthOption } from "../route/auth-options" import type { ProviderAuthOption } from "../route/auth-options.js"
import type { ProviderPackage } from "../provider-package" import type { ProviderPackage } from "../provider-package.js"
import { ProviderID, type ModelID } from "../schema" import { ProviderID, type ModelID } from "../schema/index.js"
import { AnthropicMessages } from "../protocols/anthropic-messages" import { AnthropicMessages } from "../protocols/anthropic-messages.js"
import { AnthropicCompatible } from "./anthropic-compatible" import { AnthropicCompatible } from "./anthropic-compatible.js"
export type AnthropicOptionsInput = AnthropicMessages.OptionsInput export type AnthropicOptionsInput = AnthropicMessages.OptionsInput
export type AnthropicProviderOptionsInput = AnthropicMessages.ProviderOptionsInput export type AnthropicProviderOptionsInput = AnthropicMessages.ProviderOptionsInput
+9 -9
View File
@@ -1,12 +1,12 @@
import { Auth } from "../route/auth" import { Auth } from "../route/auth.js"
import { type AtLeastOne, type ProviderAuthOption } from "../route/auth-options" import { type AtLeastOne, type ProviderAuthOption } from "../route/auth-options.js"
import type { Route as RouteDef, RouteDefaultsInput } from "../route/client" import type { Route as RouteDef, RouteDefaultsInput } from "../route/client.js"
import type { ProviderPackage } from "../provider-package" import type { ProviderPackage } from "../provider-package.js"
import { ProviderID, type ModelID } from "../schema" import { ProviderID, type ModelID } from "../schema/index.js"
import * as OpenAIChat from "../protocols/openai-chat" import * as OpenAIChat from "../protocols/openai-chat.js"
import * as OpenAIResponses from "../protocols/openai-responses" import * as OpenAIResponses from "../protocols/openai-responses.js"
import { ProviderShared } from "../protocols/shared" import { ProviderShared } from "../protocols/shared.js"
import { withOpenAIOptions, type OpenAIProviderOptionsInput } from "./openai-options" import { withOpenAIOptions, type OpenAIProviderOptionsInput } from "./openai-options.js"
export const id = ProviderID.make("azure") export const id = ProviderID.make("azure")
const routeAuth = Auth.remove("authorization") const routeAuth = Auth.remove("authorization")
+2 -2
View File
@@ -1,2 +1,2 @@
export { chatModel as model } from "../azure" export { chatModel as model } from "../azure.js"
export type { Settings } from "../azure" export type { Settings } from "../azure.js"
+2 -2
View File
@@ -1,2 +1,2 @@
export { responsesModel as model } from "../azure" export { responsesModel as model } from "../azure.js"
export type { Settings } from "../azure" export type { Settings } from "../azure.js"
+6 -6
View File
@@ -1,10 +1,10 @@
import type { Config, Redacted } from "effect" import type { Config, Redacted } from "effect"
import * as OpenAICompatibleChat from "../protocols/openai-compatible-chat" import * as OpenAICompatibleChat from "../protocols/openai-compatible-chat.js"
import { Auth } from "../route/auth" import { Auth } from "../route/auth.js"
import { AuthOptions, type AtLeastOne, type ProviderAuthOption } from "../route/auth-options" import { AuthOptions, type AtLeastOne, type ProviderAuthOption } from "../route/auth-options.js"
import type { RouteDefaultsInput } from "../route/client" import type { RouteDefaultsInput } from "../route/client.js"
import { ProviderID, type ModelID } from "../schema" import { ProviderID, type ModelID } from "../schema/index.js"
import type { OpenAIProviderOptionsInput } from "./openai-options" import type { OpenAIProviderOptionsInput } from "./openai-options.js"
export const aiGatewayID = ProviderID.make("cloudflare-ai-gateway") export const aiGatewayID = ProviderID.make("cloudflare-ai-gateway")
export const workersAIID = ProviderID.make("cloudflare-workers-ai") export const workersAIID = ProviderID.make("cloudflare-workers-ai")
@@ -1,9 +1,9 @@
import type { ProviderPackage } from "../provider-package" import type { ProviderPackage } from "../provider-package.js"
import { OpenAICompatibleChat } from "../protocols/openai-compatible-chat" import { OpenAICompatibleChat } from "../protocols/openai-compatible-chat.js"
import type { RouteDefaultsInput } from "../route/client" import type { RouteDefaultsInput } from "../route/client.js"
import { ProviderID, type ModelID } from "../schema" import { ProviderID, type ModelID } from "../schema/index.js"
import { GoogleVertexShared } from "./google-vertex-shared" import { GoogleVertexShared } from "./google-vertex-shared.js"
import type { OpenAIProviderOptionsInput } from "./openai-options" import type { OpenAIProviderOptionsInput } from "./openai-options.js"
export const id = ProviderID.make("google-vertex") export const id = ProviderID.make("google-vertex")
@@ -1,13 +1,13 @@
import { Effect, Schema, Struct } from "effect" import { Effect, Schema, Struct } from "effect"
import type { ProviderPackage } from "../provider-package" import type { ProviderPackage } from "../provider-package.js"
import { AnthropicMessages } from "../protocols/anthropic-messages" import { AnthropicMessages } from "../protocols/anthropic-messages.js"
import { Auth } from "../route/auth" import { Auth } from "../route/auth.js"
import { Route, type RouteDefaultsInput } from "../route/client" import { Route, type RouteDefaultsInput } from "../route/client.js"
import { Endpoint } from "../route/endpoint" import { Endpoint } from "../route/endpoint.js"
import { Framing } from "../route/framing" import { Framing } from "../route/framing.js"
import { Protocol } from "../route/protocol" import { Protocol } from "../route/protocol.js"
import { ProviderID, type ModelID } from "../schema" import { ProviderID, type ModelID } from "../schema/index.js"
import { GoogleVertexShared } from "./google-vertex-shared" import { GoogleVertexShared } from "./google-vertex-shared.js"
export type AnthropicOptionsInput = AnthropicMessages.OptionsInput export type AnthropicOptionsInput = AnthropicMessages.OptionsInput
export type AnthropicProviderOptionsInput = AnthropicMessages.ProviderOptionsInput export type AnthropicProviderOptionsInput = AnthropicMessages.ProviderOptionsInput
@@ -1,9 +1,9 @@
import type { ProviderPackage } from "../provider-package" import type { ProviderPackage } from "../provider-package.js"
import { OpenAICompatibleResponses } from "../protocols/openai-compatible-responses" import { OpenAICompatibleResponses } from "../protocols/openai-compatible-responses.js"
import type { RouteDefaultsInput } from "../route/client" import type { RouteDefaultsInput } from "../route/client.js"
import { ProviderID, type ModelID } from "../schema" import { ProviderID, type ModelID } from "../schema/index.js"
import { GoogleVertexShared } from "./google-vertex-shared" import { GoogleVertexShared } from "./google-vertex-shared.js"
import type { OpenResponsesProviderOptionsInput } from "./open-responses-options" import type { OpenResponsesProviderOptionsInput } from "./open-responses-options.js"
export const id = ProviderID.make("google-vertex") export const id = ProviderID.make("google-vertex")
@@ -1,6 +1,6 @@
import type { AnyAuthClient } from "google-auth-library" import type { AnyAuthClient } from "google-auth-library"
import { Effect, Redacted } from "effect" import { Effect, Redacted } from "effect"
import { Auth, MissingCredentialError } from "../route/auth" import { Auth, MissingCredentialError } from "../route/auth.js"
const SCOPE = "https://www.googleapis.com/auth/cloud-platform" const SCOPE = "https://www.googleapis.com/auth/cloud-platform"
@@ -74,4 +74,4 @@ export const oauth = (input: OAuthOptions, project?: string) => {
return adc(project) return adc(project)
} }
export * as GoogleVertexShared from "./google-vertex-shared" export * as GoogleVertexShared from "./google-vertex-shared.js"
+8 -8
View File
@@ -1,11 +1,11 @@
import type { ProviderPackage } from "../provider-package" import type { ProviderPackage } from "../provider-package.js"
import { Gemini } from "../protocols/gemini" import { Gemini } from "../protocols/gemini.js"
import { Auth } from "../route/auth" import { Auth } from "../route/auth.js"
import { Route, type RouteDefaultsInput } from "../route/client" import { Route, type RouteDefaultsInput } from "../route/client.js"
import { Endpoint } from "../route/endpoint" import { Endpoint } from "../route/endpoint.js"
import { Framing } from "../route/framing" import { Framing } from "../route/framing.js"
import { ProviderID, type ModelID } from "../schema" import { ProviderID, type ModelID } from "../schema/index.js"
import { GoogleVertexShared } from "./google-vertex-shared" import { GoogleVertexShared } from "./google-vertex-shared.js"
export type GeminiOptionsInput = Gemini.OptionsInput export type GeminiOptionsInput = Gemini.OptionsInput
export type GeminiProviderOptionsInput = Gemini.ProviderOptionsInput export type GeminiProviderOptionsInput = Gemini.ProviderOptionsInput
@@ -1,2 +1,2 @@
export { model } from "../google-vertex-chat" export { model } from "../google-vertex-chat.js"
export type { Settings } from "../google-vertex-chat" export type { Settings } from "../google-vertex-chat.js"
@@ -1,2 +1,2 @@
export { model } from "../google-vertex" export { model } from "../google-vertex.js"
export type { Settings } from "../google-vertex" export type { Settings } from "../google-vertex.js"
@@ -1,2 +1,2 @@
export { model } from "../google-vertex-messages" export { model } from "../google-vertex-messages.js"
export type { Settings } from "../google-vertex-messages" export type { Settings } from "../google-vertex-messages.js"
@@ -1,2 +1,2 @@
export { model } from "../google-vertex-responses" export { model } from "../google-vertex-responses.js"
export type { Settings } from "../google-vertex-responses" export type { Settings } from "../google-vertex-responses.js"
+8 -8
View File
@@ -1,12 +1,12 @@
import type { RouteDefaultsInput } from "../route/client" import type { RouteDefaultsInput } from "../route/client.js"
import { Auth } from "../route/auth" import { Auth } from "../route/auth.js"
import type { ProviderAuthOption } from "../route/auth-options" import type { ProviderAuthOption } from "../route/auth-options.js"
import type { ProviderPackage } from "../provider-package" import type { ProviderPackage } from "../provider-package.js"
import { HttpOptions, ProviderID, mergeHttpOptions, type ModelID } from "../schema" import { HttpOptions, ProviderID, mergeHttpOptions, type ModelID } from "../schema/index.js"
import { Gemini } from "../protocols/gemini" import { Gemini } from "../protocols/gemini.js"
import { GoogleImages } from "../protocols/google-images" import { GoogleImages } from "../protocols/google-images.js"
export type { GoogleImageOptions } from "../protocols/google-images" export type { GoogleImageOptions } from "../protocols/google-images.js"
export type GeminiOptionsInput = Gemini.OptionsInput export type GeminiOptionsInput = Gemini.OptionsInput
export type GeminiProviderOptionsInput = Gemini.ProviderOptionsInput export type GeminiProviderOptionsInput = Gemini.ProviderOptionsInput
+18 -18
View File
@@ -1,18 +1,18 @@
export * as Anthropic from "./anthropic" export * as Anthropic from "./anthropic.js"
export * as AnthropicCompatible from "./anthropic-compatible" export * as AnthropicCompatible from "./anthropic-compatible.js"
export * as AmazonBedrock from "./amazon-bedrock" export * as AmazonBedrock from "./amazon-bedrock.js"
export * as AmazonBedrockMantle from "./amazon-bedrock-mantle" export * as AmazonBedrockMantle from "./amazon-bedrock-mantle.js"
export * as Azure from "./azure" export * as Azure from "./azure.js"
export * as Cloudflare from "./cloudflare" export * as Cloudflare from "./cloudflare.js"
export { CloudflareAIGateway, CloudflareWorkersAI } from "./cloudflare" export { CloudflareAIGateway, CloudflareWorkersAI } from "./cloudflare.js"
export * as Google from "./google" export * as Google from "./google.js"
export * as GoogleVertex from "./google-vertex" export * as GoogleVertex from "./google-vertex.js"
export * as GoogleVertexChat from "./google-vertex-chat" export * as GoogleVertexChat from "./google-vertex-chat.js"
export * as GoogleVertexMessages from "./google-vertex-messages" export * as GoogleVertexMessages from "./google-vertex-messages.js"
export * as GoogleVertexResponses from "./google-vertex-responses" export * as GoogleVertexResponses from "./google-vertex-responses.js"
export * as OpenAI from "./openai" export * as OpenAI from "./openai.js"
export * as OpenAICompatible from "./openai-compatible" export * as OpenAICompatible from "./openai-compatible.js"
export * as OpenAICompatibleResponses from "./openai-compatible-responses" export * as OpenAICompatibleResponses from "./openai-compatible-responses.js"
export * as OpenRouter from "./openrouter" export * as OpenRouter from "./openrouter.js"
export * as XAI from "./xai" export * as XAI from "./xai.js"
export * as ZAI from "./zai" export * as ZAI from "./zai.js"
@@ -1,5 +1,5 @@
import type { ResponseIncludable, ServiceTier } from "../protocols/utils/open-responses-options" import type { ResponseIncludable, ServiceTier } from "../protocols/utils/open-responses-options.js"
import type { ProviderOptions, ReasoningEffort, TextVerbosity } from "../schema" import type { ProviderOptions, ReasoningEffort, TextVerbosity } from "../schema/index.js"
export interface OpenResponsesOptionsInput { export interface OpenResponsesOptionsInput {
readonly [key: string]: unknown readonly [key: string]: unknown
@@ -16,4 +16,4 @@ export type OpenResponsesProviderOptionsInput = ProviderOptions & {
readonly openresponses?: OpenResponsesOptionsInput readonly openresponses?: OpenResponsesOptionsInput
} }
export * as OpenResponsesProviderOptions from "./open-responses-options" export * as OpenResponsesProviderOptions from "./open-responses-options.js"
@@ -1,11 +1,11 @@
import type { ProviderPackage } from "../provider-package" import type { ProviderPackage } from "../provider-package.js"
import { OpenAICompatibleResponses } from "../protocols/openai-compatible-responses" import { OpenAICompatibleResponses } from "../protocols/openai-compatible-responses.js"
import { AuthOptions, type ProviderAuthOption } from "../route/auth-options" import { AuthOptions, type ProviderAuthOption } from "../route/auth-options.js"
import type { RouteDefaultsInput } from "../route/client" import type { RouteDefaultsInput } from "../route/client.js"
import { ProviderID, type ModelID } from "../schema" import { ProviderID, type ModelID } from "../schema/index.js"
import type { OpenResponsesProviderOptionsInput } from "./open-responses-options" import type { OpenResponsesProviderOptionsInput } from "./open-responses-options.js"
export type { OpenResponsesOptionsInput, OpenResponsesProviderOptionsInput } from "./open-responses-options" export type { OpenResponsesOptionsInput, OpenResponsesProviderOptionsInput } from "./open-responses-options.js"
export const id = ProviderID.make("openai-compatible") export const id = ProviderID.make("openai-compatible")
@@ -1,10 +1,10 @@
import { ProviderID, type ModelID } from "../schema" import { ProviderID, type ModelID } from "../schema/index.js"
import * as OpenAICompatibleChat from "../protocols/openai-compatible-chat" import * as OpenAICompatibleChat from "../protocols/openai-compatible-chat.js"
import type { RouteDefaultsInput } from "../route/client" import type { RouteDefaultsInput } from "../route/client.js"
import { AuthOptions, type ProviderAuthOption } from "../route/auth-options" import { AuthOptions, type ProviderAuthOption } from "../route/auth-options.js"
import type { ProviderPackage } from "../provider-package" import type { ProviderPackage } from "../provider-package.js"
import { profiles, type OpenAICompatibleProfile } from "./openai-compatible-profile" import { profiles, type OpenAICompatibleProfile } from "./openai-compatible-profile.js"
import type { OpenAIProviderOptionsInput } from "./openai-options" import type { OpenAIProviderOptionsInput } from "./openai-options.js"
export const id = ProviderID.make("openai-compatible") export const id = ProviderID.make("openai-compatible")
@@ -1 +1 @@
export * from "../openai-compatible-responses" export * from "../openai-compatible-responses.js"
+5 -5
View File
@@ -1,8 +1,8 @@
import type { ProviderOptions } from "../schema" import type { ProviderOptions } from "../schema/index.js"
import { mergeProviderOptions } from "../schema" import { mergeProviderOptions } from "../schema/index.js"
import type { OpenResponsesOptionsInput } from "./open-responses-options" import type { OpenResponsesOptionsInput } from "./open-responses-options.js"
export type { OpenAIResponseIncludable, OpenAIServiceTier } from "../protocols/utils/openai-options" export type { OpenAIResponseIncludable, OpenAIServiceTier } from "../protocols/utils/openai-options.js"
export type OpenAIOptionsInput = OpenResponsesOptionsInput export type OpenAIOptionsInput = OpenResponsesOptionsInput
@@ -67,4 +67,4 @@ export const withOpenAIOptions = <Options extends { readonly providerOptions?: O
} }
} }
export * as OpenAIProviderOptions from "./openai-options" export * as OpenAIProviderOptions from "./openai-options.js"
+10 -10
View File
@@ -1,14 +1,14 @@
import { AuthOptions, type ProviderAuthOption } from "../route/auth-options" import { AuthOptions, type ProviderAuthOption } from "../route/auth-options.js"
import type { Route, RouteDefaultsInput } from "../route/client" import type { Route, RouteDefaultsInput } from "../route/client.js"
import type { ProviderPackage } from "../provider-package" import type { ProviderPackage } from "../provider-package.js"
import { HttpOptions, ProviderID, ToolDefinition, mergeHttpOptions, type ModelID } from "../schema" import { HttpOptions, ProviderID, ToolDefinition, mergeHttpOptions, type ModelID } from "../schema/index.js"
import * as OpenAIChat from "../protocols/openai-chat" import * as OpenAIChat from "../protocols/openai-chat.js"
import * as OpenAIResponses from "../protocols/openai-responses" import * as OpenAIResponses from "../protocols/openai-responses.js"
import { withOpenAIOptions, type OpenAIProviderOptionsInput } from "./openai-options" import { withOpenAIOptions, type OpenAIProviderOptionsInput } from "./openai-options.js"
import { OpenAIImages, type OpenAIImageString } from "../protocols/openai-images" import { OpenAIImages, type OpenAIImageString } from "../protocols/openai-images.js"
export type { OpenAIOptionsInput, OpenAIResponseIncludable } from "./openai-options" export type { OpenAIOptionsInput, OpenAIResponseIncludable } from "./openai-options.js"
export type { OpenAIImageOptions } from "../protocols/openai-images" export type { OpenAIImageOptions } from "../protocols/openai-images.js"
export const id = ProviderID.make("openai") export const id = ProviderID.make("openai")
+2 -2
View File
@@ -1,2 +1,2 @@
export { chatModel as model } from "../openai" export { chatModel as model } from "../openai.js"
export type { Settings } from "../openai" export type { Settings } from "../openai.js"
@@ -1,2 +1,2 @@
export { model } from "../openai" export { model } from "../openai.js"
export type { Settings } from "../openai" export type { Settings } from "../openai.js"
+11 -11
View File
@@ -1,15 +1,15 @@
import { Effect, Schema } from "effect" import { Effect, Schema } from "effect"
import { Route, type RouteDefaultsInput } from "../route/client" import { Route, type RouteDefaultsInput } from "../route/client.js"
import { Endpoint } from "../route/endpoint" import { Endpoint } from "../route/endpoint.js"
import { Framing } from "../route/framing" import { Framing } from "../route/framing.js"
import { Protocol } from "../route/protocol" import { Protocol } from "../route/protocol.js"
import { AuthOptions, type ProviderAuthOption } from "../route/auth-options" import { AuthOptions, type ProviderAuthOption } from "../route/auth-options.js"
import { ProviderID, type CacheHint, type ModelID, type ProviderOptions } from "../schema" import { ProviderID, type CacheHint, type ModelID, type ProviderOptions } from "../schema/index.js"
import type { ProviderPackage } from "../provider-package" import type { ProviderPackage } from "../provider-package.js"
import * as OpenAICompatibleProfiles from "./openai-compatible-profile" import * as OpenAICompatibleProfiles from "./openai-compatible-profile.js"
import * as OpenAIChat from "../protocols/openai-chat" import * as OpenAIChat from "../protocols/openai-chat.js"
import { newBreakpoints, ttlBucket } from "../protocols/utils/cache" import { newBreakpoints, ttlBucket } from "../protocols/utils/cache.js"
import { isRecord } from "../protocols/shared" import { isRecord } from "../protocols/shared.js"
export const profile = OpenAICompatibleProfiles.profiles.openrouter export const profile = OpenAICompatibleProfiles.profiles.openrouter
export const id = ProviderID.make(profile.provider) export const id = ProviderID.make(profile.provider)
+12 -12
View File
@@ -1,14 +1,14 @@
import { AuthOptions, type ProviderAuthOption } from "../route/auth-options" import { AuthOptions, type ProviderAuthOption } from "../route/auth-options.js"
import { Route, type RouteDefaultsInput } from "../route/client" import { Route, type RouteDefaultsInput } from "../route/client.js"
import { Endpoint } from "../route/endpoint" import { Endpoint } from "../route/endpoint.js"
import { HttpOptions, ProviderID, type ModelID, type ProviderOptions } from "../schema" import { HttpOptions, ProviderID, type ModelID, type ProviderOptions } from "../schema/index.js"
import * as OpenAICompatibleProfiles from "./openai-compatible-profile" import * as OpenAICompatibleProfiles from "./openai-compatible-profile.js"
import * as OpenAICompatibleChat from "../protocols/openai-compatible-chat" import * as OpenAICompatibleChat from "../protocols/openai-compatible-chat.js"
import * as OpenAIChat from "../protocols/openai-chat" import * as OpenAIChat from "../protocols/openai-chat.js"
import * as OpenAIResponses from "../protocols/openai-responses" import * as OpenAIResponses from "../protocols/openai-responses.js"
import { XAIImages } from "../protocols/xai-images" import { XAIImages } from "../protocols/xai-images.js"
import type { OpenAIOptionsInput } from "./openai-options" import type { OpenAIOptionsInput } from "./openai-options.js"
import type { ProviderPackage } from "../provider-package" import type { ProviderPackage } from "../provider-package.js"
export const id = ProviderID.make("xai") export const id = ProviderID.make("xai")
@@ -28,7 +28,7 @@ export interface Settings extends ProviderPackage.Settings {
readonly providerOptions?: XAIProviderOptionsInput readonly providerOptions?: XAIProviderOptionsInput
} }
export type { XAIImageOptions } from "../protocols/xai-images" export type { XAIImageOptions } from "../protocols/xai-images.js"
const responsesRoute = Route.make({ const responsesRoute = Route.make({
id: "openai-responses", id: "openai-responses",
+4 -4
View File
@@ -1,6 +1,6 @@
import { ZAIImages } from "../protocols/zai-images" import { ZAIImages } from "../protocols/zai-images.js"
import { AuthOptions, type ProviderAuthOption } from "../route/auth-options" import { AuthOptions, type ProviderAuthOption } from "../route/auth-options.js"
import { HttpOptions, ProviderID, type ModelID } from "../schema" import { HttpOptions, ProviderID, type ModelID } from "../schema/index.js"
export const id = ProviderID.make("zai") export const id = ProviderID.make("zai")
@@ -10,7 +10,7 @@ export type Config = ProviderAuthOption<"optional"> & {
readonly http?: HttpOptions.Input readonly http?: HttpOptions.Input
} }
export type { ZAIImageOptions } from "../protocols/zai-images" export type { ZAIImageOptions } from "../protocols/zai-images.js"
const auth = (options: ProviderAuthOption<"optional">) => AuthOptions.bearer(options, "ZAI_API_KEY") const auth = (options: ProviderAuthOption<"optional">) => AuthOptions.bearer(options, "ZAI_API_KEY")
+1 -1
View File
@@ -1 +1 @@
export * from "./route/index" export * from "./route/index.js"
+2 -2
View File
@@ -1,5 +1,5 @@
import type { Config, Redacted } from "effect" import type { Config, Redacted } from "effect"
import { Auth } from "./auth" import { Auth } from "./auth.js"
export type ApiKeyMode = "optional" | "required" export type ApiKeyMode = "optional" | "required"
@@ -61,4 +61,4 @@ export const bearer = (
.bearer() .bearer()
} }
export * as AuthOptions from "./auth-options" export * as AuthOptions from "./auth-options.js"
+2 -2
View File
@@ -1,6 +1,6 @@
import { Config, Effect, Redacted } from "effect" import { Config, Effect, Redacted } from "effect"
import { Headers } from "effect/unstable/http" import { Headers } from "effect/unstable/http"
import { AuthenticationReason, InvalidRequestReason, AIError, type HttpOptions } from "../schema" import { AuthenticationReason, InvalidRequestReason, AIError, type HttpOptions } from "../schema/index.js"
export class MissingCredentialError extends Error { export class MissingCredentialError extends Error {
readonly _tag = "MissingCredentialError" readonly _tag = "MissingCredentialError"
@@ -153,4 +153,4 @@ export const toEffect =
(authInput: AuthInput): Effect.Effect<Headers.Headers, AIError> => (authInput: AuthInput): Effect.Effect<Headers.Headers, AIError> =>
input.apply(authInput).pipe(Effect.mapError(toAIError)) input.apply(authInput).pipe(Effect.mapError(toAIError))
export * as Auth from "./auth" export * as Auth from "./auth.js"
+12 -12
View File
@@ -1,16 +1,16 @@
import { Cause, Context, Effect, Layer, Schema, Stream } from "effect" import { Cause, Context, Effect, Layer, Schema, Stream } from "effect"
import * as Option from "effect/Option" import * as Option from "effect/Option"
import { Auth } from "./auth" import { Auth } from "./auth.js"
import { Endpoint, type EndpointPatch } from "./endpoint" import { Endpoint, type EndpointPatch } from "./endpoint.js"
import { RequestExecutor } from "./executor" import { RequestExecutor } from "./executor.js"
import { Framing } from "./framing" import { Framing } from "./framing.js"
import { HttpTransport } from "./transport" import { HttpTransport } from "./transport/index.js"
import type { HttpMiddleware, Transport, TransportRuntime } from "./transport" import type { HttpMiddleware, Transport, TransportRuntime } from "./transport/index.js"
import { WebSocketExecutor } from "./transport" import { WebSocketExecutor } from "./transport/index.js"
import type { Protocol } from "./protocol" import type { Protocol } from "./protocol.js"
import { applyCachePolicy } from "../cache-policy" import { applyCachePolicy } from "../cache-policy.js"
import * as ProviderShared from "../protocols/shared" import * as ProviderShared from "../protocols/shared.js"
import type { ProtocolID, ProviderOptions } from "../schema" import type { ProtocolID, ProviderOptions } from "../schema/index.js"
import { import {
AIError, AIError,
GenerationOptions, GenerationOptions,
@@ -25,7 +25,7 @@ import {
mergeGenerationOptions, mergeGenerationOptions,
mergeHttpOptions, mergeHttpOptions,
mergeProviderOptions, mergeProviderOptions,
} from "../schema" } from "../schema/index.js"
export interface RouteBody<Body> { export interface RouteBody<Body> {
/** Schema for the validated provider-native body sent as the JSON request. */ /** Schema for the validated provider-native body sent as the JSON request. */
+3 -3
View File
@@ -1,5 +1,5 @@
import type { LLMRequest } from "../schema" import type { LLMRequest } from "../schema/index.js"
import * as ProviderShared from "../protocols/shared" import * as ProviderShared from "../protocols/shared.js"
export interface EndpointInput<Body> { export interface EndpointInput<Body> {
readonly request: LLMRequest readonly request: LLMRequest
@@ -53,4 +53,4 @@ export const render = <Body>(endpoint: Definition<Body>, input: EndpointInput<Bo
return url return url
} }
export * as Endpoint from "./endpoint" export * as Endpoint from "./endpoint.js"
+76 -33
View File
@@ -1,4 +1,4 @@
import { Cause, Context, Effect, Layer, Option, Schema } from "effect" import { Cause, Context, Effect, Layer, Option, Schema, Stream } from "effect"
import { import {
FetchHttpClient, FetchHttpClient,
Headers, Headers,
@@ -14,8 +14,8 @@ import {
HttpResponseDetails, HttpResponseDetails,
AIError, AIError,
TransportReason, TransportReason,
} from "../schema" } from "../schema/index.js"
import { classifyProviderFailure } from "../provider-error" import { classifyProviderFailure } from "../provider-error.js"
export interface Interface { export interface Interface {
readonly execute: ( readonly execute: (
@@ -297,44 +297,86 @@ export const classifyHttpFailure = (input: {
}) })
} }
const toHttpError = (redactedNames: ReadonlyArray<string | RegExp>) => (error: unknown) => { type HttpOperation = "request" | "read"
const transportError = (input: {
readonly message: string const NativeTransportFailure = Schema.Struct({
readonly kind?: string | undefined message: Schema.String,
readonly request?: HttpClientRequest.HttpClientRequest | undefined code: Schema.optionalKey(Schema.String),
}) => cause: Schema.optionalKey(Schema.Unknown),
})
const decodeNativeTransportFailure = Schema.decodeUnknownOption(NativeTransportFailure)
const nativeTransportFailure = (error: unknown) => {
const failure = Option.getOrUndefined(decodeNativeTransportFailure(error))
if (!failure) return undefined
if (failure.code !== undefined) return failure
const cause = Option.getOrUndefined(decodeNativeTransportFailure(failure.cause))
if (cause?.code !== undefined) return cause
return failure
}
const httpError = (input: {
readonly error: unknown
readonly request: HttpClientRequest.HttpClientRequest
readonly operation: HttpOperation
readonly redactedNames: ReadonlyArray<string | RegExp>
}) => {
const request = HttpClientError.isHttpClientError(input.error) ? input.error.request : input.request
const transportError = (failure: { readonly message: string; readonly code?: string | undefined }) =>
new AIError({ new AIError({
module: "RequestExecutor", module: "RequestExecutor",
method: "execute", method: input.operation,
reason: new TransportReason({ reason: new TransportReason({
message: input.message, message: failure.message,
kind: input.kind, transport: "http",
url: input.request ? redactUrl(input.request.url) : undefined, operation: input.operation,
http: input.request ? new HttpContext({ request: requestDetails(input.request, redactedNames) }) : undefined, code: failure.code,
url: redactUrl(request.url),
http: new HttpContext({ request: requestDetails(request, input.redactedNames) }),
}), }),
}) })
if (Cause.isTimeoutError(error)) { const source =
return transportError({ message: error.message, kind: "Timeout" }) HttpClientError.isHttpClientError(input.error) && "cause" in input.error.reason
} ? input.error.reason.cause
if (!HttpClientError.isHttpClientError(error)) { : input.error
return transportError({ message: error instanceof Error ? error.message : "HTTP transport failed" }) const native = nativeTransportFailure(source)
} const code = native?.code
const request = "request" in error ? error.request : undefined const raw = native?.message ?? (input.error instanceof Error ? input.error.message : undefined)
if (error.reason._tag === "TransportError") { const detail = raw ? redactBody(raw, secretValues(request)) : undefined
const message = code && detail && !detail.includes(code) ? `${code}: ${detail}` : detail
if (Cause.isTimeoutError(input.error) || Cause.isTimeoutError(source))
return transportError({ message: message ?? "HTTP transport timed out", code: code ?? "Timeout" })
if (!HttpClientError.isHttpClientError(input.error))
return transportError({ message: message ?? "HTTP transport failed", code })
if (input.error.reason._tag === "TransportError") {
return transportError({ return transportError({
message: error.reason.description ?? "HTTP transport failed", message: message ?? input.error.reason.description ?? "HTTP transport failed",
kind: error.reason._tag, code: code ?? input.error.reason._tag,
request,
}) })
} }
return transportError({ return transportError({
message: `HTTP transport failed: ${error.reason._tag}`, message: message ?? `HTTP transport failed: ${input.error.reason._tag}`,
kind: error.reason._tag, code: code ?? input.error.reason._tag,
request,
}) })
} }
export const stream = (
executor: Interface,
request: HttpClientRequest.HttpClientRequest,
middleware?: HttpMiddleware,
): Stream.Stream<Uint8Array, AIError> =>
Stream.unwrap(
Effect.gen(function* () {
const redactedNames = yield* Headers.CurrentRedactedNames
const response = yield* executor.execute(request, middleware)
return response.stream.pipe(
Stream.mapError((error) => httpError({ error, request: response.request, operation: "read", redactedNames })),
)
}),
)
export const layer: Layer.Layer<Service, never, HttpClient.HttpClient> = Layer.effect( export const layer: Layer.Layer<Service, never, HttpClient.HttpClient> = Layer.effect(
Service, Service,
Effect.gen(function* () { Effect.gen(function* () {
@@ -343,15 +385,16 @@ export const layer: Layer.Layer<Service, never, HttpClient.HttpClient> = Layer.e
Effect.gen(function* () { Effect.gen(function* () {
const redactedNames = yield* Headers.CurrentRedactedNames const redactedNames = yield* Headers.CurrentRedactedNames
if (!middleware) if (!middleware)
return yield* http return yield* http.execute(request).pipe(
.execute(request) Effect.mapError((error) => httpError({ error, request, operation: "request", redactedNames })),
.pipe(Effect.mapError(toHttpError(redactedNames)), Effect.flatMap(statusError(request, redactedNames))) Effect.flatMap(statusError(request, redactedNames)),
)
const response = yield* middleware(request, (input) => const response = yield* middleware(request, (input) =>
http http
.execute(input) .execute(input)
.pipe(Effect.mapError((cause) => (cause instanceof Error ? cause : new Error(String(cause))))), .pipe(Effect.mapError((cause) => (cause instanceof Error ? cause : new Error(String(cause))))),
).pipe(Effect.mapError(toHttpError(redactedNames))) ).pipe(Effect.mapError((error) => httpError({ error, request, operation: "request", redactedNames })))
return yield* statusError(response.request, redactedNames)(response) return yield* statusError(response.request, redactedNames)(response)
}) })
return Service.of({ return Service.of({
@@ -362,4 +405,4 @@ export const layer: Layer.Layer<Service, never, HttpClient.HttpClient> = Layer.e
export const fetchLayer = layer.pipe(Layer.provide(FetchHttpClient.layer)) export const fetchLayer = layer.pipe(Layer.provide(FetchHttpClient.layer))
export * as RequestExecutor from "./executor" export * as RequestExecutor from "./executor.js"
+3 -3
View File
@@ -1,6 +1,6 @@
import type { Stream } from "effect" import type { Stream } from "effect"
import * as ProviderShared from "../protocols/shared" import * as ProviderShared from "../protocols/shared.js"
import type { AIError } from "../schema" import type { AIError } from "../schema/index.js"
/** /**
* Decode a streaming HTTP response body into provider-protocol frames. * Decode a streaming HTTP response body into provider-protocol frames.
@@ -24,4 +24,4 @@ export interface Definition<Frame> {
/** Server-Sent Events framing. Used by every JSON-streaming HTTP provider. */ /** Server-Sent Events framing. Used by every JSON-streaming HTTP provider. */
export const sse: Definition<string> = { id: "sse", frame: ProviderShared.sseFraming } export const sse: Definition<string> = { id: "sse", frame: ProviderShared.sseFraming }
export * as Framing from "./framing" export * as Framing from "./framing.js"
+16 -16
View File
@@ -1,4 +1,4 @@
export { Route, LLMClient } from "./client" export { Route, LLMClient } from "./client.js"
export type { export type {
Route as RouteShape, Route as RouteShape,
RouteLanguageModelInput, RouteLanguageModelInput,
@@ -9,18 +9,18 @@ export type {
Interface as LLMClientShape, Interface as LLMClientShape,
Service as LLMClientService, Service as LLMClientService,
StreamOptions, StreamOptions,
} from "./client" } from "./client.js"
export * from "./executor" export * from "./executor.js"
export { Auth } from "./auth" export { Auth } from "./auth.js"
export { AuthOptions } from "./auth-options" export { AuthOptions } from "./auth-options.js"
export { Endpoint } from "./endpoint" export { Endpoint } from "./endpoint.js"
export { Framing } from "./framing" export { Framing } from "./framing.js"
export { Protocol } from "./protocol" export { Protocol } from "./protocol.js"
export { HttpTransport, WebSocketExecutor, WebSocketTransport } from "./transport" export { HttpTransport, WebSocketExecutor, WebSocketTransport } from "./transport/index.js"
export * as Transport from "./transport" export * as Transport from "./transport/index.js"
export type { Definition as AuthShape, AuthInput, Credential, CredentialError } from "./auth" export type { Definition as AuthShape, AuthInput, Credential, CredentialError } from "./auth.js"
export type { ApiKeyMode, AuthOverride, ProviderAuthOption } from "./auth-options" export type { ApiKeyMode, AuthOverride, ProviderAuthOption } from "./auth-options.js"
export type { Definition as EndpointFn, EndpointInput } from "./endpoint" export type { Definition as EndpointFn, EndpointInput } from "./endpoint.js"
export type { Definition as FramingDef } from "./framing" export type { Definition as FramingDef } from "./framing.js"
export type { Protocol as ProtocolDef } from "./protocol" export type { Protocol as ProtocolDef } from "./protocol.js"
export type { HttpHandler, HttpMiddleware, Transport as TransportDef, TransportRuntime } from "./transport" export type { HttpHandler, HttpMiddleware, Transport as TransportDef, TransportRuntime } from "./transport/index.js"
+2 -2
View File
@@ -1,5 +1,5 @@
import { Schema, type Effect } from "effect" import { Schema, type Effect } from "effect"
import type { AIError, LLMEvent, LLMRequest, ProtocolID } from "../schema" import type { AIError, LLMEvent, LLMRequest, ProtocolID } from "../schema/index.js"
/** /**
* The semantic API contract of one model server family. * The semantic API contract of one model server family.
@@ -82,4 +82,4 @@ export const make = <Body, Frame, Event, State>(
export const jsonEvent = <const S extends Schema.Top>(schema: S) => Schema.fromJsonString(schema) export const jsonEvent = <const S extends Schema.Top>(schema: S) => Schema.fromJsonString(schema)
export * as Protocol from "./protocol" export * as Protocol from "./protocol.js"
+10 -27
View File
@@ -1,11 +1,12 @@
import { Effect, Stream } from "effect" import { Effect } from "effect"
import { Headers, HttpClientRequest } from "effect/unstable/http" import { Headers, HttpClientRequest } from "effect/unstable/http"
import { Auth } from "../auth" import { Auth } from "../auth.js"
import { render as renderEndpoint } from "../endpoint" import { render as renderEndpoint } from "../endpoint.js"
import { Framing } from "../framing" import { Framing } from "../framing.js"
import type { HttpMiddleware, Transport, TransportPrepareInput } from "./index" import type { HttpMiddleware, Transport, TransportPrepareInput } from "./index.js"
import * as ProviderShared from "../../protocols/shared" import * as ProviderShared from "../../protocols/shared.js"
import { mergeJsonRecords, type LLMRequest } from "../../schema" import { mergeJsonRecords, type LLMRequest } from "../../schema/index.js"
import { RequestExecutor } from "../executor.js"
export type JsonRequestInput<Body> = TransportPrepareInput<Body> export type JsonRequestInput<Body> = TransportPrepareInput<Body>
@@ -86,26 +87,8 @@ export const httpJson = <Body, Frame>(input: HttpJsonInput<Body, Frame>): HttpJs
middleware: prepareInput.middleware, middleware: prepareInput.middleware,
} }
}), }),
frames: (prepared, request, runtime) => frames: (prepared, _request, runtime) =>
Stream.unwrap( prepared.framing.frame(RequestExecutor.stream(runtime.http, prepared.request, prepared.middleware)),
runtime.http
.execute(prepared.request, prepared.middleware)
.pipe(
Effect.map((response) =>
prepared.framing.frame(
response.stream.pipe(
Stream.mapError((error) =>
ProviderShared.eventError(
`${request.model.provider}/${request.model.route.id}`,
`Failed to read ${request.model.provider}/${request.model.route.id} stream`,
ProviderShared.errorText(error),
),
),
),
),
),
),
),
}) })
export const sseJson = { export const sseJson = {
+8 -8
View File
@@ -1,9 +1,9 @@
import type { Effect, Stream } from "effect" import type { Effect, Stream } from "effect"
import { Endpoint } from "../endpoint" import { Endpoint } from "../endpoint.js"
import { Auth } from "../auth" import { Auth } from "../auth.js"
import type { HttpMiddleware, Interface as RequestExecutorInterface } from "../executor" import type { HttpMiddleware, Interface as RequestExecutorInterface } from "../executor.js"
import type { Interface as WebSocketExecutorInterface } from "./websocket" import type { Interface as WebSocketExecutorInterface } from "./websocket.js"
import type { AIError, LLMRequest } from "../../schema" import type { AIError, LLMRequest } from "../../schema/index.js"
export interface TransportRuntime { export interface TransportRuntime {
readonly http: RequestExecutorInterface readonly http: RequestExecutorInterface
@@ -26,6 +26,6 @@ export interface TransportPrepareInput<Body> {
readonly middleware?: HttpMiddleware readonly middleware?: HttpMiddleware
} }
export * as HttpTransport from "./http" export * as HttpTransport from "./http.js"
export type { HttpHandler, HttpMiddleware } from "../executor" export type { HttpHandler, HttpMiddleware } from "../executor.js"
export { WebSocketExecutor, WebSocketTransport } from "./websocket" export { WebSocketExecutor, WebSocketTransport } from "./websocket.js"
+38 -15
View File
@@ -1,8 +1,8 @@
import { Cause, Context, Effect, Layer, Queue, Stream } from "effect" import { Cause, Context, Effect, Layer, Queue, Stream } from "effect"
import { Headers } from "effect/unstable/http" import { Headers } from "effect/unstable/http"
import { AIError, TransportReason } from "../../schema" import { AIError, TransportReason, type TransportOperation } from "../../schema/index.js"
import * as HttpTransport from "./http" import * as HttpTransport from "./http.js"
import type { Transport } from "./index" import type { Transport } from "./index.js"
export interface WebSocketRequest { export interface WebSocketRequest {
readonly url: string readonly url: string
@@ -29,12 +29,18 @@ export class Service extends Context.Service<Service, Interface>()("@opencode/AI
const transportError = ( const transportError = (
method: string, method: string,
message: string, message: string,
input: { readonly url?: string; readonly kind?: string } = {}, input: { readonly operation: TransportOperation; readonly url?: string; readonly code?: string },
) => ) =>
new AIError({ new AIError({
module: "WebSocketExecutor", module: "WebSocketExecutor",
method, method,
reason: new TransportReason({ message, url: input.url, kind: input.kind }), reason: new TransportReason({
message,
transport: "websocket",
operation: input.operation,
url: input.url,
code: input.code,
}),
}) })
const eventMessage = (event: Event) => { const eventMessage = (event: Event) => {
@@ -55,7 +61,8 @@ const waitOpen = (ws: globalThis.WebSocket, input: WebSocketRequest) => {
return Effect.fail( return Effect.fail(
transportError("open", `WebSocket closed before opening (state ${ws.readyState})`, { transportError("open", `WebSocket closed before opening (state ${ws.readyState})`, {
url: input.url, url: input.url,
kind: "open", operation: "request",
code: "closed",
}), }),
) )
} }
@@ -79,7 +86,10 @@ const waitOpen = (ws: globalThis.WebSocket, input: WebSocketRequest) => {
cleanup() cleanup()
resume( resume(
Effect.fail( Effect.fail(
transportError("open", `Failed to open WebSocket: ${eventMessage(event)}`, { url: input.url, kind: "open" }), transportError("open", `Failed to open WebSocket: ${eventMessage(event)}`, {
url: input.url,
operation: "request",
}),
), ),
) )
} }
@@ -89,7 +99,8 @@ const waitOpen = (ws: globalThis.WebSocket, input: WebSocketRequest) => {
Effect.fail( Effect.fail(
transportError("open", `WebSocket closed before opening with code ${event.code}`, { transportError("open", `WebSocket closed before opening with code ${event.code}`, {
url: input.url, url: input.url,
kind: "open", operation: "request",
code: String(event.code),
}), }),
), ),
) )
@@ -118,7 +129,8 @@ const webSocketUrl = (value: string) =>
catch: (error) => catch: (error) =>
transportError("prepare", error instanceof Error ? error.message : "Invalid WebSocket URL", { transportError("prepare", error instanceof Error ? error.message : "Invalid WebSocket URL", {
url: value, url: value,
kind: "websocket", operation: "request",
code: "invalid-url",
}), }),
}) })
@@ -129,7 +141,7 @@ export const open = (input: WebSocketRequest) =>
catch: (error) => catch: (error) =>
transportError("open", error instanceof Error ? error.message : "Failed to construct WebSocket", { transportError("open", error instanceof Error ? error.message : "Failed to construct WebSocket", {
url: input.url, url: input.url,
kind: "open", operation: "request",
}), }),
}).pipe(Effect.flatMap((ws) => fromWebSocket(ws, input))) }).pipe(Effect.flatMap((ws) => fromWebSocket(ws, input)))
@@ -150,7 +162,10 @@ export const fromWebSocket = (
Queue.failCauseUnsafe( Queue.failCauseUnsafe(
messages, messages,
Cause.fail( Cause.fail(
transportError("message", "Unsupported WebSocket message payload", { url: input.url, kind: "message" }), transportError("message", "Unsupported WebSocket message payload", {
url: input.url,
operation: "read",
}),
), ),
) )
} }
@@ -158,7 +173,10 @@ export const fromWebSocket = (
Queue.failCauseUnsafe( Queue.failCauseUnsafe(
messages, messages,
Cause.fail( Cause.fail(
transportError("message", `WebSocket error: ${eventMessage(event)}`, { url: input.url, kind: "message" }), transportError("message", `WebSocket error: ${eventMessage(event)}`, {
url: input.url,
operation: "read",
}),
), ),
) )
} }
@@ -167,7 +185,11 @@ export const fromWebSocket = (
Queue.failCauseUnsafe( Queue.failCauseUnsafe(
messages, messages,
Cause.fail( Cause.fail(
transportError("message", `WebSocket closed with code ${event.code}`, { url: input.url, kind: "close" }), transportError("message", `WebSocket closed with code ${event.code}`, {
url: input.url,
operation: "read",
code: String(event.code),
}),
), ),
) )
} }
@@ -188,7 +210,7 @@ export const fromWebSocket = (
catch: (error) => catch: (error) =>
transportError("sendText", error instanceof Error ? error.message : "Failed to send WebSocket message", { transportError("sendText", error instanceof Error ? error.message : "Failed to send WebSocket message", {
url: input.url, url: input.url,
kind: "write", operation: "write",
}), }),
}), }),
messages: Stream.fromQueue(messages), messages: Stream.fromQueue(messages),
@@ -243,7 +265,8 @@ export const json = <Body, Message>(input: JsonInput<Body, Message>): JsonTransp
return Stream.fail( return Stream.fail(
transportError("json", "WebSocket JSON transport requires WebSocketExecutor.Service", { transportError("json", "WebSocket JSON transport requires WebSocketExecutor.Service", {
url: prepared.url, url: prepared.url,
kind: "websocket", operation: "request",
code: "unavailable",
}), }),
) )
} }
+10 -2
View File
@@ -1,6 +1,6 @@
import { Schema } from "effect" import { Schema } from "effect"
import { Tool } from "@opencode-ai/schema/tool" import { Tool } from "@opencode-ai/schema/tool"
import { ModelID, ProviderID, ProviderMetadata, RouteID } from "./ids" import { ModelID, ProviderID, ProviderMetadata, RouteID } from "./ids.js"
export const ProviderFailureClassification = Schema.Literals(["context-overflow", "payload-too-large"]) export const ProviderFailureClassification = Schema.Literals(["context-overflow", "payload-too-large"])
export type ProviderFailureClassification = typeof ProviderFailureClassification.Type export type ProviderFailureClassification = typeof ProviderFailureClassification.Type
@@ -92,10 +92,18 @@ export class ProviderInternalReason extends Schema.Class<ProviderInternalReason>
http: Schema.optional(HttpContext), http: Schema.optional(HttpContext),
}) {} }) {}
export const TransportType = Schema.Literals(["http", "websocket"])
export type TransportType = typeof TransportType.Type
export const TransportOperation = Schema.Literals(["request", "read", "write"])
export type TransportOperation = typeof TransportOperation.Type
export class TransportReason extends Schema.Class<TransportReason>("AI.Error.Transport")({ export class TransportReason extends Schema.Class<TransportReason>("AI.Error.Transport")({
_tag: Schema.tag("Transport"), _tag: Schema.tag("Transport"),
message: Schema.String, message: Schema.String,
kind: Schema.optional(Schema.String), transport: TransportType,
operation: TransportOperation,
code: Schema.optional(Schema.String),
url: Schema.optional(Schema.String), url: Schema.optional(Schema.String),
http: Schema.optional(HttpContext), http: Schema.optional(HttpContext),
}) {} }) {}
+3 -3
View File
@@ -1,7 +1,7 @@
import { Schema } from "effect" import { Schema } from "effect"
import { ContentBlockID, FinishReason, ProviderMetadata, ToolCallID } from "./ids" import { ContentBlockID, FinishReason, ProviderMetadata, ToolCallID } from "./ids.js"
import { Message, ToolCallPart, ToolOutput, ToolResultPart, ToolResultValue, type ContentPart } from "./messages" import { Message, ToolCallPart, ToolOutput, ToolResultPart, ToolResultValue, type ContentPart } from "./messages.js"
import { ProviderFailureClassification } from "./errors" import { ProviderFailureClassification } from "./errors.js"
/** /**
* Token usage reported by an LLM provider. * Token usage reported by an LLM provider.
+5 -5
View File
@@ -1,5 +1,5 @@
export * from "./ids" export * from "./ids.js"
export * from "./options" export * from "./options.js"
export * from "./messages" export * from "./messages.js"
export * from "./events" export * from "./events.js"
export * from "./errors" export * from "./errors.js"
+10 -3
View File
@@ -1,8 +1,15 @@
import { Schema } from "effect" import { Schema } from "effect"
import { Tool } from "@opencode-ai/schema/tool" import { Tool } from "@opencode-ai/schema/tool"
import { JsonSchema, MessageRole, ProviderMetadata } from "./ids" import { JsonSchema, MessageRole, ProviderMetadata } from "./ids.js"
import { CacheHint, CachePolicy, GenerationOptions, HttpOptions, LanguageModelSchema, ProviderOptions } from "./options" import {
import { isRecord } from "../utils/record" CacheHint,
CachePolicy,
GenerationOptions,
HttpOptions,
LanguageModelSchema,
ProviderOptions,
} from "./options.js"
import { isRecord } from "../utils/record.js"
const systemPartSchema = Schema.Struct({ const systemPartSchema = Schema.Struct({
type: Schema.Literal("text"), type: Schema.Literal("text"),
+3 -3
View File
@@ -1,7 +1,7 @@
import { Schema } from "effect" import { Schema } from "effect"
import { JsonSchema, ModelID, ProviderID } from "./ids" import { JsonSchema, ModelID, ProviderID } from "./ids.js"
import type { AnyRoute } from "../route/client" import type { AnyRoute } from "../route/client.js"
import { isRecord } from "../utils/record" import { isRecord } from "../utils/record.js"
export const mergeJsonRecords = ( export const mergeJsonRecords = (
...items: ReadonlyArray<Record<string, unknown> | undefined> ...items: ReadonlyArray<Record<string, unknown> | undefined>
+3 -3
View File
@@ -1,6 +1,6 @@
export * as TestLLM from "./testing" export * as TestLLM from "./testing.js"
import { LLMClient, type Interface as LLMClientShape } from "./route/client" import { LLMClient, type Interface as LLMClientShape } from "./route/client.js"
import { import {
LLMEvent, LLMEvent,
LLMResponse, LLMResponse,
@@ -8,7 +8,7 @@ import {
type AIError, type AIError,
type LLMRequest, type LLMRequest,
type UsageInput, type UsageInput,
} from "./schema" } from "./schema/index.js"
import { Context, Deferred, Effect, Latch, Layer, Queue, Scope, Stream } from "effect" import { Context, Deferred, Effect, Latch, Layer, Queue, Scope, Stream } from "effect"
export type Response = readonly LLMEvent[] | Stream.Stream<LLMEvent, AIError> export type Response = readonly LLMEvent[] | Stream.Stream<LLMEvent, AIError>
+2 -2
View File
@@ -7,8 +7,8 @@ import {
ToolResultValue, ToolResultValue,
type ToolOutput as ToolOutputType, type ToolOutput as ToolOutputType,
type ToolResultValue as ToolResultValueType, type ToolResultValue as ToolResultValueType,
} from "./schema" } from "./schema/index.js"
import { type AnyTool, type Tools } from "./tool" import { type AnyTool, type Tools } from "./tool.js"
export interface ToolSettlement { export interface ToolSettlement {
readonly result: ToolResultValueType readonly result: ToolResultValueType

Some files were not shown because too many files have changed in this diff Show More