@knightcodeai/cli 0.19.0 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/dist/bundle/chunks/anthropic-messages-5ZM7Z2XQ.js +6 -0
  3. package/dist/bundle/chunks/{azure-openai-responses-ZZQJJY7B.js → azure-openai-responses-OUS4VJNA.js} +1 -1
  4. package/dist/bundle/chunks/bedrock-converse-stream.js +1 -1
  5. package/dist/bundle/chunks/chunk-5ZES34QN.js +2 -0
  6. package/dist/bundle/chunks/{chunk-T47GKPON.js → chunk-6GRVIFTN.js} +1 -1
  7. package/dist/bundle/chunks/{chunk-IIS4JDFO.js → chunk-6KWPS3LA.js} +23 -22
  8. package/dist/bundle/chunks/{chunk-R7X2RAGR.js → chunk-6X7IXVQH.js} +4 -4
  9. package/dist/bundle/chunks/{chunk-RZDCZFG7.js → chunk-B7A4GIBK.js} +1 -1
  10. package/dist/bundle/chunks/{chunk-JSLSFYBX.js → chunk-BWGDAXFH.js} +1 -1
  11. package/dist/bundle/chunks/{chunk-3QKQRXCZ.js → chunk-DFRYMT3C.js} +3 -3
  12. package/dist/bundle/chunks/{chunk-BTRAS32C.js → chunk-IIWY7U5S.js} +1 -1
  13. package/dist/bundle/chunks/{chunk-OQHO44L6.js → chunk-J63X3FRT.js} +1 -1
  14. package/dist/bundle/chunks/{chunk-KRDGPCZM.js → chunk-JMJS643J.js} +1 -1
  15. package/dist/bundle/chunks/{chunk-C62RVFZT.js → chunk-L6QGGIZG.js} +2 -2
  16. package/dist/bundle/chunks/{chunk-AA6MLWWY.js → chunk-MZ5UVOTP.js} +1 -1
  17. package/dist/bundle/chunks/chunk-N5M3XNPN.js +2 -0
  18. package/dist/bundle/chunks/{chunk-GPHSXYPT.js → chunk-RKNFSQUG.js} +1 -1
  19. package/dist/bundle/chunks/{chunk-2QXPCKUQ.js → chunk-VQBEVSZH.js} +1 -1
  20. package/dist/bundle/chunks/{cli-SIPOI5P3.js → cli-72YA573P.js} +1 -1
  21. package/dist/bundle/chunks/cloudflare-workers-ai-system-one-AWHNPEAT.js +2 -0
  22. package/dist/bundle/chunks/{easter-egg-3d-BVWLABZN.js → easter-egg-3d-XXPCDCZV.js} +1 -1
  23. package/dist/bundle/chunks/{execute-6UIHV2DR.js → execute-KWIUMPDE.js} +1 -1
  24. package/dist/bundle/chunks/{google-generative-ai-RFCCCXK6.js → google-generative-ai-HPLZ2ABU.js} +1 -1
  25. package/dist/bundle/chunks/{google-vertex-WN3JAZCD.js → google-vertex-E3UTTTIG.js} +1 -1
  26. package/dist/bundle/chunks/{mistral-conversations-GIGX6BW6.js → mistral-conversations-KT4C4XTZ.js} +1 -1
  27. package/dist/bundle/chunks/{openai-codex-responses-3Y7R752C.js → openai-codex-responses-BCLSSOJ6.js} +1 -1
  28. package/dist/bundle/chunks/openai-completions-GTNAEXX6.js +7 -0
  29. package/dist/bundle/chunks/{openai-decisions-MLI3SMFN.js → openai-decisions-CSQTW3S4.js} +1 -1
  30. package/dist/bundle/chunks/{openai-responses-BNRA5YTD.js → openai-responses-ILFXLMJW.js} +1 -1
  31. package/dist/bundle/chunks/{openrouter-images-JWVDFQJ4.js → openrouter-images-J66HXAMB.js} +1 -1
  32. package/dist/bundle/chunks/{runtime-AROIXIAV.js → runtime-OW5JKWBE.js} +1 -1
  33. package/dist/bundle/chunks/typesafe-system-one-QA5VXBEZ.js +2 -0
  34. package/dist/bundle/chunks/{virtual-modules-IYKQM2DL.js → virtual-modules-5DLTLPP7.js} +1 -1
  35. package/dist/bundle/cli-runtime.js +1 -1
  36. package/dist/core/export-html/template.css +9 -0
  37. package/dist/core/export-html/template.js +165 -45
  38. package/docs/app-server.md +66 -8
  39. package/docs/app-server.schema.json +6200 -1048
  40. package/docs/containerization.md +2 -2
  41. package/docs/declarative-views.md +118 -0
  42. package/docs/desktop.md +26 -0
  43. package/docs/docs.json +4 -0
  44. package/docs/extensions.md +4 -2
  45. package/docs/models-configuration.md +9 -0
  46. package/docs/models.md +8 -4
  47. package/examples/extensions/README.md +1 -0
  48. package/examples/extensions/desktop-view.ts +72 -0
  49. package/examples/extensions/gondolin/index.ts +8 -10
  50. package/examples/extensions/gondolin/package-lock.json +20 -30
  51. package/examples/extensions/gondolin/package.json +1 -1
  52. package/package.json +1 -1
  53. package/dist/bundle/chunks/anthropic-messages-34POCEPV.js +0 -6
  54. package/dist/bundle/chunks/chunk-2QXUEOVY.js +0 -2
  55. package/dist/bundle/chunks/cloudflare-workers-ai-system-one-QHP5M5HC.js +0 -2
  56. package/dist/bundle/chunks/openai-completions-HBXENZUJ.js +0 -7
  57. package/dist/bundle/chunks/typesafe-system-one-5LELZY2F.js +0 -2
@@ -112,11 +112,11 @@ OpenShell inference routing can keep raw model credentials outside the sandbox.
112
112
 
113
113
  ## Route tools through Gondolin
114
114
 
115
- [Gondolin](https://github.com/KnightCodeAI/gondolin) is a local Linux micro-VM. Its example extension keeps the KnightCode process and file-based provider credentials on the host while routing the built-in tools and user `!` commands into the VM.
115
+ [Gondolin](https://github.com/earendil-works/gondolin) is a local Linux micro-VM. Its example extension keeps the KnightCode process and file-based provider credentials on the host while routing the built-in tools and user `!` commands into the VM.
116
116
 
117
117
  Commands inside the VM inherit the host process environment. Provider keys supplied through environment variables can therefore be visible inside the VM. Do not use this pattern as a credential boundary unless you remove sensitive variables or change the extension's environment handling.
118
118
 
119
- Gondolin requires Node.js 23.6 or newer and QEMU installed through your operating-system package manager.
119
+ Gondolin requires Node.js 22.19 or newer and QEMU installed through your operating-system package manager.
120
120
 
121
121
  ### Install the extension
122
122
 
@@ -0,0 +1,118 @@
1
+ # Declarative Views
2
+
3
+ A view is a small tree of data that describes what to draw: text, markdown, a
4
+ table, a progress bar, a button. An extension hands one to a widget, a tool
5
+ result or an extension message, and every client draws it with its own
6
+ components: the terminal with TUI components, the [desktop app](desktop.md) with
7
+ its own. A view is data only. It never holds HTML, scripts or URLs that load.
8
+
9
+ ```ts
10
+ import type { View } from "@knightcodeai/cli";
11
+
12
+ const view: View = {
13
+ type: "stack",
14
+ children: [
15
+ { type: "text", text: "2 checks failed", tone: "error" },
16
+ { type: "table", columns: ["check", "result"], rows: [["parser", "failed"], ["lexer", "failed"]] },
17
+ { type: "button", id: "rerun", label: "Run again" },
18
+ ],
19
+ };
20
+ ```
21
+
22
+ ## Nodes
23
+
24
+ | Node | Fields | Example |
25
+ | --- | --- | --- |
26
+ | `text` | `text`, optional `tone`: `muted`, `accent`, `success`, `warning`, `error` | `{type: "text", text: "Saved", tone: "success"}` |
27
+ | `markdown` | `markdown`; raw HTML shows as text, images do not load | `{type: "markdown", markdown: "**3** files"}` |
28
+ | `stack` | `children`, optional `direction`: `vertical` (default) or `horizontal` | `{type: "stack", direction: "horizontal", children: [...]}` |
29
+ | `list` | `items`, each a view, drawn as bullets | `{type: "list", items: [{type: "text", text: "one"}]}` |
30
+ | `table` | `columns` (at least one), `rows` of strings, each row as wide as `columns` | `{type: "table", columns: ["a"], rows: [["1"]]}` |
31
+ | `progress` | `value` (0 or more), `max` (over 0), optional `label`; a value over `max` draws full | `{type: "progress", value: 3, max: 10, label: "tests"}` |
32
+ | `button` | `id` (1 to 128 characters), `label` | `{type: "button", id: "reset", label: "Reset"}` |
33
+ | `field` | `id`, `kind`: `text`, `toggle` or `select`, `label`, `value`, and `options` for a select | `{type: "field", id: "mode", kind: "select", label: "Mode", value: "fast", options: ["fast", "slow"]}` |
34
+
35
+ ## Rules and limits
36
+
37
+ A view that breaks a rule is refused as a whole:
38
+
39
+ - No members other than those above, and no other node types.
40
+ - Stacks and lists nest at most 8 levels.
41
+ - Control ids (`button` and `field`) are unique within the view.
42
+ - A toggle's `value` is a boolean; a text or select field's is a string. A
43
+ select has `options` and its `value` is one of them; a text field or toggle
44
+ has no `options`.
45
+ - Its JSON is at most 64 KiB in a tool result or an extension message, and at
46
+ most 16 KiB in a widget.
47
+
48
+ The view is copied through JSON when it is used, so a getter, a `toJSON` or a
49
+ change you make to the object later does not change what was drawn. Call
50
+ `setWidget` again to change a widget.
51
+
52
+ Terminal escape sequences and control characters other than tab and newline
53
+ are removed from every string of the copy, so colour a view with `tone`, not
54
+ ANSI codes.
55
+
56
+ A thread shows at most 16 widgets and 50 statuses, with keys of 1 to 128
57
+ characters. Status text, the title and the working message are cut to 200
58
+ characters and kept on one line.
59
+
60
+ ## Widgets
61
+
62
+ ```ts
63
+ ctx.ui.setWidget("checks:summary", view, { placement: "aboveEditor" });
64
+ ctx.ui.setWidget("checks:summary", undefined); // remove it
65
+ ```
66
+
67
+ `placement` is `aboveEditor` (the default) or `belowEditor`. Widget and status
68
+ keys are shared by every extension of a thread, so prefix them with your
69
+ extension's name. In the terminal, an invalid view draws one warning line in
70
+ its place; the app server sends nothing and reports it once per key as a
71
+ warning.
72
+
73
+ ## Tool results and extension messages
74
+
75
+ Put a view in `details.view` and clients draw it in place of the text output:
76
+
77
+ ```ts
78
+ return { content: [{ type: "text", text: "parser: failed" }], details: { view } };
79
+
80
+ knightcode.sendMessage({ customType: "checks", content: "2 failed", display: true, details: { view } });
81
+ ```
82
+
83
+ Keep the text content meaningful: the model reads it, not the view, and a client
84
+ that cannot draw the view shows the text. An invalid view is ignored and the
85
+ text shows. In the terminal a tool's own `renderResult`, or a registered message
86
+ renderer, still wins over the view.
87
+
88
+ ## Buttons and fields
89
+
90
+ In a widget on the desktop app, buttons and fields are live. A press or a
91
+ change reaches every handler the thread's extensions registered:
92
+
93
+ ```ts
94
+ ctx.ui.onViewEvent?.((event) => {
95
+ if (event.widgetKey !== "checks:summary") return;
96
+ if (event.id === "rerun") runChecks();
97
+ if (event.id === "verbose" && typeof event.value === "boolean") verbose = event.value;
98
+ ctx.ui.setWidget("checks:summary", render());
99
+ });
100
+ ```
101
+
102
+ `event` is `{widgetKey, id, value?}`: `value` is a string for a text or select
103
+ field, a boolean for a toggle, absent for a button. A field shows the value in
104
+ the view, so answer a change by setting the widget again. `onViewEvent` is
105
+ present only where widgets are interactive; the `?.` keeps the code working
106
+ elsewhere. A handler that throws becomes a warning in the app.
107
+
108
+ ## Where views draw
109
+
110
+ | Client | Widgets | Tool results and messages | Controls |
111
+ | --- | --- | --- | --- |
112
+ | Terminal | yes | yes | drawn as text |
113
+ | Desktop app | yes | yes | live in widgets, disabled elsewhere |
114
+ | RPC, JSON and print modes | no | no | no |
115
+
116
+ [`examples/extensions/desktop-view.ts`](../examples/extensions/desktop-view.ts)
117
+ puts all of it together. For the protocol side see
118
+ [app-server.md](app-server.md#dialogs-and-extension-ui).
package/docs/desktop.md CHANGED
@@ -68,6 +68,32 @@ on Windows, `~/Library/Logs/KnightCode` on macOS, `~/.config/KnightCode/logs` on
68
68
  - Command palette (Ctrl+K): actions and threads in one search.
69
69
  - Quitting while threads run asks first.
70
70
 
71
+ ## Extensions
72
+
73
+ Extensions load in desktop threads as they do in the terminal (`ctx.mode` is
74
+ `"desktop"`). What they show appears in the app:
75
+
76
+ - Dialogs (`select`, `confirm`, `input`, `editor`) and `ask_user`'s questions take
77
+ the composer's place; notices (`notify`) show as toasts.
78
+ - Statuses (`setStatus`) are chips in the thread's header, with the full text on
79
+ hover.
80
+ - `setTitle` names the window while the thread is open; the thread's own name stays.
81
+ - `setWorkingMessage` replaces the "Thinking" text while a turn waits on the model,
82
+ and `setWorkingVisible(false)` hides it.
83
+ - Widgets (`setWidget`) show in a panel above the composer, lines in a monospace
84
+ font, at most 10 per widget. A widget can be a
85
+ [declarative view](declarative-views.md) whose buttons and fields work: a press
86
+ reaches the extension's `ctx.ui.onViewEvent` handler.
87
+ - A tool result or an extension message with a view in `details.view` shows the view
88
+ in its row.
89
+
90
+ Terminal-only UI does not draw: widget factories, `custom()` components, header
91
+ and footer components, raw terminal input, the working indicator and editor access
92
+ (`setEditorText`, `getEditorText`, `pasteToEditor`, custom editors and autocomplete).
93
+ The app server notes each such call once per thread in `app-server.log`. A widget or
94
+ status the server refuses (an invalid view, too large, too many) shows a warning
95
+ once. The protocol side is in [app-server.md](app-server.md#dialogs-and-extension-ui).
96
+
71
97
  ## Keyboard shortcuts
72
98
 
73
99
  | Action | Default |
package/docs/docs.json CHANGED
@@ -121,6 +121,10 @@
121
121
  "title": "Build Extensions",
122
122
  "path": "extensions.md"
123
123
  },
124
+ {
125
+ "title": "Draw Declarative Views",
126
+ "path": "declarative-views.md"
127
+ },
124
128
  {
125
129
  "title": "Add Custom Providers",
126
130
  "path": "custom-provider.md"
@@ -387,8 +387,10 @@ Use `ctx.ui.custom()` only when the interaction needs its own rendering and inpu
387
387
  See [Terminal UI](tui.md) for component, focus, overlay, theme, and performance guidance.
388
388
 
389
389
  Extensions load in interactive, RPC, JSON, print, and desktop modes.
390
- Desktop mode currently has no extension UI (`hasUI` is false); terminal
391
- components remain unavailable even when desktop dialogs are added.
390
+ Desktop mode draws dialogs, notices, statuses, titles, working text and widgets,
391
+ including [declarative views](declarative-views.md) with live buttons and
392
+ fields; terminal components (widget factories, `custom()`, header, footer) do not
393
+ draw there. See [Desktop App](desktop.md#extensions).
392
394
  Interactive mode provides the complete terminal UI. The [DOOM overlay example](../examples/extensions/doom-overlay/) uses a custom overlay component to render a game frame by frame:
393
395
 
394
396
  <p align="center"><img src="images/doom-extension.png" alt="The DOOM overlay example running over a KnightCode session" width="750"></p>
@@ -185,3 +185,12 @@ Also applies to `azure-openai-responses` and `openai-codex-responses`.
185
185
  | Field | Type | Default | Description |
186
186
  |---|---|---|---|
187
187
  | `supportsMidConvoSystemMessages` | boolean | `false` | Whether the exact model accepts system messages after the conversation has started. When false, later system messages are folded into the leading system message. |
188
+
189
+ ### `typesafe-system-one`
190
+
191
+ Also applies to `cloudflare-workers-ai-system-one`.
192
+
193
+ | Field | Type | Default | Description |
194
+ |---|---|---|---|
195
+ | `imageInput` | `"assets" \| "images" \| "state-image-urls"` | | How the service takes classifier images. "assets" sends TypeSafe's assets map (PNG and JPEG) and references the images from the state, as TypeSafe and Jev on Workers AI expect. "images" sends Clef's images list of data URLs (PNG, JPEG, and WebP, at most four), as Clef on Workers AI expects. "state-image-urls" places image_url parts before the state, as OpenRouter expects; OpenRouter drops assets without an error. Auto-detected from the provider, baseUrl, and model ID. Classifier models read it from the provider-level compat; models and modelOverrides entries apply to chat models only. |
196
+ | `sendImageOptInHeader` | boolean | | Whether requests with images send the TypeSafe-Danger-Zone-Images: true header, which TypeSafe's own API requires for its experimental image input. Defaults to true for the typesafe-system-one API unless the model is detected as OpenRouter. |
package/docs/models.md CHANGED
@@ -141,20 +141,20 @@ Compatibility settings should describe verified differences in the endpoint's re
141
141
 
142
142
  ## Use classifier models
143
143
 
144
- Classifier models do not chat. They answer typed questions about JSON state: pick one of several choices, answer yes or no, or give a score, each with probabilities. KnightCode includes TypeSafe's Jev model from these providers, Cloudflare's Clef and Clef Flash models from Workers AI, and OpenAI's GPT-6 Luna through the [Decisions API](https://developers.openai.com/api/docs/guides/decisions):
144
+ Classifier models do not chat. They answer typed questions about JSON state: pick one of several choices, answer yes or no, or give a score, each with probabilities. KnightCode includes TypeSafe's Jev model from these providers, Cloudflare's Clef, Clef Flash, and Clef Omni models from Workers AI and OpenRouter, and OpenAI's GPT-6 Luna through the [Decisions API](https://developers.openai.com/api/docs/guides/decisions):
145
145
 
146
146
  | Provider | Model IDs | Authentication |
147
147
  |---|---|---|
148
148
  | `typesafe` | `jev-latest` | `TYPESAFE_API_KEY` |
149
- | `openrouter` | `typesafe/jev-1.13`, `~typesafe/jev-latest` | `OPENROUTER_API_KEY` or `/login` |
150
- | `cloudflare-workers-ai` | `typesafe/jev`, `@cf/cloudflare/clef`, `@cf/cloudflare/clef-flash` | `CLOUDFLARE_API_KEY` and `CLOUDFLARE_ACCOUNT_ID` |
149
+ | `openrouter` | `typesafe/jev-1.13`, `typesafe/jev-1.14`, `~typesafe/jev-latest`, `cloudflare/clef`, `cloudflare/clef-flash`, `cloudflare/clef-omni` | `OPENROUTER_API_KEY` or `/login` |
150
+ | `cloudflare-workers-ai` | `typesafe/jev`, `typesafe/jev-1.14.0`, `@cf/cloudflare/clef`, `@cf/cloudflare/clef-flash`, `@cf/cloudflare/clef-omni` | `CLOUDFLARE_API_KEY` and `CLOUDFLARE_ACCOUNT_ID` |
151
151
  | `vercel-ai-gateway` | `typesafe-ai/jev` | `AI_GATEWAY_API_KEY` |
152
152
  | `opencode` | `jev-1.13`, `jev-1.13-free` | `OPENCODE_API_KEY` |
153
153
  | `openai` | `gpt-6-luna` | `OPENAI_API_KEY` |
154
154
 
155
155
  A [llama.cpp router](llama-cpp.md#classification) lists decision models as native classifiers, and also lists each chat model as a classifier.
156
156
 
157
- OpenAI's Decisions API needs an API key. ChatGPT sign-in does not work with it, so `gpt-6-luna` is not listed as available while `openai` is logged in through `/login`, even when `OPENAI_API_KEY` is set; log out of `openai` to use the key. GPT-6 Luna also judges images passed in `images` (see [Codemode](codemode.md#classify)); other classifier models return an error for them. The API rejects inputs above 922K tokens, but requests that run longer than about five seconds, currently above roughly 600K input tokens, fail with a gateway timeout.
157
+ OpenAI's Decisions API needs an API key. ChatGPT sign-in does not work with it, so `gpt-6-luna` is not listed as available while `openai` is logged in through `/login`, even when `OPENAI_API_KEY` is set; log out of `openai` to use the key. The API rejects inputs above 922K tokens, but requests that run longer than about five seconds, currently above roughly 600K input tokens, fail with a gateway timeout.
158
158
 
159
159
  Classifier models do not appear in `/model`. The [classifier gate](usage.md#classifier-gate) uses one to screen risky tool calls. The model reaches them through the [`codemode`](cli.md#enable-codemode) tool, which is off unless an MCP server turned it on. Enable it with `"defaultTools": ["+codemode"]` in [settings](settings.md#tools). Scripts then list classifier models with `models.getAvailableOfType("classifier")` and call `models.classify(model, { state, questions })`:
160
160
 
@@ -175,6 +175,10 @@ return result.answers;
175
175
 
176
176
  [Codemode](codemode.md#classify) describes the question and answer types.
177
177
 
178
+ Classifier models whose `input` includes `"image"` also judge images passed in `images`; other classifier models return an error for them. Image input works with GPT-6 Luna, with Jev from `typesafe` and `cloudflare-workers-ai`, with Clef from `cloudflare-workers-ai`, and with the OpenRouter models whose catalog lists image input. Jev takes PNG and JPEG images, and image input is experimental on TypeSafe's own API. Clef takes PNG, JPEG, and WebP images, at most four per request. Jev from `vercel-ai-gateway` and `opencode` takes text only.
179
+
180
+ Each System One service takes images in its own request format, set by the model's `compat.imageInput`. Built-in models set it, so overriding a provider's `baseUrl` keeps it. A custom provider that proxies another service's classifiers sets it in its provider-level `compat` in `models.json` (see [System One compatibility](models-configuration.md#typesafe-system-one)). OpenRouter in particular answers requests in TypeSafe's format without an error but drops the images.
181
+
178
182
  When the service reports token counts, as all System One services do, `result.usage` carries them with their cost. KnightCode adds the usage of a script's classifier calls to the `codemode` tool result, so it counts toward the session cost in the footer and `/session`. The cost uses the model's catalog price; models without one, such as TypeSafe's direct `jev-latest`, report tokens at no cost.
179
183
 
180
184
  Extensions call classifiers through `ctx.modelRegistry.classify()`, without codemode. [Virtual models](virtual-models.md#route-requests) can use them to route requests; see the `jev-router.ts` example.
@@ -32,6 +32,7 @@ cp permission-gate.ts ~/.knightcode/agent/extensions/
32
32
  |-----------|-------------|
33
33
  | `todo.ts` | Todo list tool + `/todos` command with custom rendering and state persistence |
34
34
  | `hello.ts` | Minimal custom tool example |
35
+ | `desktop-view.ts` | Declarative views: a widget with a live toggle and button (`ctx.ui.onViewEvent`), and a tool result drawn as a table |
35
36
  | `question.ts` | Demonstrates `ctx.ui.select()` for asking the user questions with custom UI |
36
37
  | `questionnaire.ts` | Multi-question input with tab bar navigation between questions |
37
38
  | `tool-override.ts` | Override built-in tools (e.g., add logging/access control to `read`) |
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Desktop View - declarative views in a widget, answering presses, and in a tool result
3
+ *
4
+ * Shows a widget with a toggle and a button, drawn by the terminal and by the desktop app from the same data
5
+ * (docs/declarative-views.md). In the desktop app the controls are live: a press reaches `ctx.ui.onViewEvent`, and
6
+ * the extension answers by setting the widget again. The `check_summary` tool returns a table view, which both
7
+ * clients draw in place of the tool's text output.
8
+ *
9
+ * Usage:
10
+ * knightcode --extension examples/extensions/desktop-view.ts
11
+ */
12
+
13
+ import { Type } from "@knightcode/ai";
14
+ import { defineTool, type ExtensionAPI, type ExtensionContext, type View } from "@knightcodeai/cli";
15
+
16
+ const WIDGET = "desktop-view:checks";
17
+
18
+ const checkSummary = defineTool({
19
+ name: "check_summary",
20
+ label: "Check summary",
21
+ description: "Summarize the project's checks as a table",
22
+ parameters: Type.Object({}),
23
+ async execute() {
24
+ const view: View = {
25
+ type: "table",
26
+ columns: ["check", "result"],
27
+ rows: [
28
+ ["types", "passed"],
29
+ ["tests", "2 failed"],
30
+ ],
31
+ };
32
+ return { content: [{ type: "text", text: "types: passed\ntests: 2 failed" }], details: { view } };
33
+ },
34
+ });
35
+
36
+ export default function (knightcode: ExtensionAPI) {
37
+ // Per session: the factory runs again for every thread that loads the extension.
38
+ let verbose = false;
39
+ let resets = 0;
40
+
41
+ const show = (ctx: ExtensionContext) => {
42
+ const view: View = {
43
+ type: "stack",
44
+ children: [
45
+ { type: "text", text: `Checks reset ${resets} time${resets === 1 ? "" : "s"}`, tone: "muted" },
46
+ {
47
+ type: "stack",
48
+ direction: "horizontal",
49
+ children: [
50
+ { type: "field", id: "verbose", kind: "toggle", label: "Verbose", value: verbose },
51
+ { type: "button", id: "reset", label: "Reset" },
52
+ ],
53
+ },
54
+ ],
55
+ };
56
+ ctx.ui.setWidget(WIDGET, view);
57
+ };
58
+
59
+ knightcode.registerTool(checkSummary);
60
+
61
+ knightcode.on("session_start", (_event, ctx) => {
62
+ if (!ctx.hasUI) return;
63
+ show(ctx);
64
+ // Only where widgets are interactive (the desktop app); the terminal draws the controls as text.
65
+ ctx.ui.onViewEvent?.((event) => {
66
+ if (event.widgetKey !== WIDGET) return;
67
+ if (event.id === "verbose" && typeof event.value === "boolean") verbose = event.value;
68
+ if (event.id === "reset") resets++;
69
+ show(ctx);
70
+ });
71
+ });
72
+ }
@@ -7,20 +7,20 @@
7
7
  * isolated to the VM.
8
8
  *
9
9
  * Setup:
10
- * cd packages/coding-agent/examples/extensions/gondolin
10
+ * cd packages/cli/examples/extensions/gondolin
11
11
  * npm install --ignore-scripts
12
12
  *
13
13
  * Usage:
14
14
  * cd /path/to/project
15
- * knightcode -e /path/to/knightcode/packages/coding-agent/examples/extensions/gondolin
15
+ * knightcode -e /path/to/knightcode/packages/cli/examples/extensions/gondolin
16
16
  *
17
17
  * Requirements:
18
- * - Node.js >= 23.6.0 for @KnightCodeAI/gondolin
18
+ * - Node.js >= 22.19.0 for @earendil-works/gondolin
19
19
  * - QEMU installed (for example, `brew install qemu` on macOS)
20
20
  */
21
21
 
22
22
  import path from "node:path";
23
- import { RealFSProvider, VM } from "@KnightCodeAI/gondolin";
23
+ import { RealFSProvider, VM } from "@earendil-works/gondolin";
24
24
  import type { ExtensionAPI, ExtensionContext } from "@knightcodeai/cli";
25
25
  import {
26
26
  type BashOperations,
@@ -249,7 +249,6 @@ async function executeGondolinGrep(
249
249
  const contextLines = params.context && params.context > 0 ? params.context : 0;
250
250
  const effectiveLimit = Math.max(1, params.limit ?? DEFAULT_GREP_LIMIT);
251
251
  const outputLines: string[] = [];
252
- const details: GrepToolDetails = {};
253
252
  let matchCount = 0;
254
253
  let matchLimitReached = false;
255
254
  let linesTruncated = false;
@@ -285,12 +284,14 @@ async function executeGondolinGrep(
285
284
  signal,
286
285
  );
287
286
 
288
- if (matchCount === 0) return { content: [{ type: "text", text: "No matches found" }], details: undefined };
287
+ if (matchCount === 0) return { content: [{ type: "text", text: "No matches found" }], details: { matchCount: 0 } };
289
288
 
290
289
  const rawOutput = outputLines.join("\n");
291
290
  const truncation = truncateHead(rawOutput, { maxLines: Number.MAX_SAFE_INTEGER });
292
291
  const notices: string[] = [];
293
292
  let output = truncation.content;
293
+ // The renderer counts output lines when this is missing, and context lines are not matches.
294
+ const details: GrepToolDetails = { matchCount };
294
295
 
295
296
  if (matchLimitReached) {
296
297
  details.matchLimitReached = effectiveLimit;
@@ -306,10 +307,7 @@ async function executeGondolinGrep(
306
307
  }
307
308
  if (notices.length > 0) output += `\n\n[${notices.join(". ")}]`;
308
309
 
309
- return {
310
- content: [{ type: "text", text: output }],
311
- details: Object.keys(details).length > 0 ? details : undefined,
312
- };
310
+ return { content: [{ type: "text", text: output }], details };
313
311
  }
314
312
 
315
313
  function sanitizeEnv(env: NodeJS.ProcessEnv | undefined): Record<string, string> | undefined {
@@ -8,7 +8,7 @@
8
8
  "name": "knightcode-extension-gondolin",
9
9
  "version": "0.84.3",
10
10
  "dependencies": {
11
- "@KnightCodeAI/gondolin": "0.12.0"
11
+ "@earendil-works/gondolin": "0.14.0"
12
12
  }
13
13
  },
14
14
  "node_modules/@cto.af/wtf8": {
@@ -20,32 +20,31 @@
20
20
  "node": ">=20"
21
21
  }
22
22
  },
23
- "node_modules/@KnightCodeAI/gondolin": {
24
- "version": "0.12.0",
25
- "resolved": "https://registry.npmjs.org/@KnightCodeAI/gondolin/-/gondolin-0.12.0.tgz",
26
- "integrity": "sha512-BXbvzQKb5QmxY5NtthRDONJTu7+IDKbzqWGrJyyNXMP7N681Tx0Q9TK8pK1ba8nUvYQTipNJyGZOsJfYiZll1A==",
23
+ "node_modules/@earendil-works/gondolin": {
24
+ "version": "0.14.0",
25
+ "resolved": "https://registry.npmjs.org/@earendil-works/gondolin/-/gondolin-0.14.0.tgz",
26
+ "integrity": "sha512-MSw+EGcqFJeyKHdXEfm+nMRr74ytT4x8DGIohav0oPCHeVQAB9AajquM94cv2xqvIU3VDAaZZVC07IXsOY6OVQ==",
27
27
  "license": "Apache-2.0",
28
28
  "dependencies": {
29
29
  "cbor2": "^2.3.0",
30
- "node-forge": "^1.3.3",
31
30
  "ssh2": "^1.17.0",
32
- "undici": "^6.21.0"
31
+ "undici": "^6.29.0"
33
32
  },
34
33
  "bin": {
35
34
  "gondolin": "dist/bin/gondolin.js"
36
35
  },
37
36
  "engines": {
38
- "node": ">=23.6.0"
37
+ "node": ">=22.19.0"
39
38
  },
40
39
  "optionalDependencies": {
41
- "@KnightCodeAI/gondolin-krun-runner-darwin-arm64": "0.12.0",
42
- "@KnightCodeAI/gondolin-krun-runner-linux-x64": "0.12.0"
40
+ "@earendil-works/gondolin-krun-runner-darwin-arm64": "0.14.0",
41
+ "@earendil-works/gondolin-krun-runner-linux-x64": "0.14.0"
43
42
  }
44
43
  },
45
- "node_modules/@KnightCodeAI/gondolin-krun-runner-darwin-arm64": {
46
- "version": "0.12.0",
47
- "resolved": "https://registry.npmjs.org/@KnightCodeAI/gondolin-krun-runner-darwin-arm64/-/gondolin-krun-runner-darwin-arm64-0.12.0.tgz",
48
- "integrity": "sha512-ftDlusht4PcT7Y3TuPrZIKrCXy3isiBTVMvlXYK0pcud2uXY6uwFTGeunYgP+8ND/60ddb+MImqbfmkcK8B84A==",
44
+ "node_modules/@earendil-works/gondolin-krun-runner-darwin-arm64": {
45
+ "version": "0.14.0",
46
+ "resolved": "https://registry.npmjs.org/@earendil-works/gondolin-krun-runner-darwin-arm64/-/gondolin-krun-runner-darwin-arm64-0.14.0.tgz",
47
+ "integrity": "sha512-fvuhz1CZPaEbmXC0luZoXd1obAb0bjaaGJz33Jo63bhMlosYunAahItakh7cU0dnTlNFzufOSnpGOPqtuYjnpQ==",
49
48
  "cpu": [
50
49
  "arm64"
51
50
  ],
@@ -58,10 +57,10 @@
58
57
  "gondolin-krun-runner": "bin/gondolin-krun-runner"
59
58
  }
60
59
  },
61
- "node_modules/@KnightCodeAI/gondolin-krun-runner-linux-x64": {
62
- "version": "0.12.0",
63
- "resolved": "https://registry.npmjs.org/@KnightCodeAI/gondolin-krun-runner-linux-x64/-/gondolin-krun-runner-linux-x64-0.12.0.tgz",
64
- "integrity": "sha512-RRYsgwe2r5ApKmFNy469QgwnyjAHpAs9XANdWpTd9ol4iUYOY3sX7e0xIooAKxd+ktxGI4N/xRWicwGen3D/Ow==",
60
+ "node_modules/@earendil-works/gondolin-krun-runner-linux-x64": {
61
+ "version": "0.14.0",
62
+ "resolved": "https://registry.npmjs.org/@earendil-works/gondolin-krun-runner-linux-x64/-/gondolin-krun-runner-linux-x64-0.14.0.tgz",
63
+ "integrity": "sha512-91iw4smoOH/yOoAuQS6N2UZOxqSFwRhtk8rTyrokXBoWiWcveAzg4kHvnLcm40ZlNrWwzIDFrere4bkZeGDQcA==",
65
64
  "cpu": [
66
65
  "x64"
67
66
  ],
@@ -134,15 +133,6 @@
134
133
  "license": "MIT",
135
134
  "optional": true
136
135
  },
137
- "node_modules/node-forge": {
138
- "version": "1.4.0",
139
- "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.4.0.tgz",
140
- "integrity": "sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==",
141
- "license": "(BSD-3-Clause OR GPL-2.0)",
142
- "engines": {
143
- "node": ">= 6.13.0"
144
- }
145
- },
146
136
  "node_modules/safer-buffer": {
147
137
  "version": "2.1.2",
148
138
  "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
@@ -173,9 +163,9 @@
173
163
  "license": "Unlicense"
174
164
  },
175
165
  "node_modules/undici": {
176
- "version": "6.26.0",
177
- "resolved": "https://registry.npmjs.org/undici/-/undici-6.26.0.tgz",
178
- "integrity": "sha512-4yqz8a3n5HmGTlsbADNtr/dJlhkh/55Rq798G6ibiULcXbDtaLpTl1pvdqcbFfeoj3iSi52lePFM7h9H21cw/A==",
166
+ "version": "6.29.1",
167
+ "resolved": "https://registry.npmjs.org/undici/-/undici-6.29.1.tgz",
168
+ "integrity": "sha512-EvRlUt/QqAEvXdQeRMYUfwOQsfzpDztykrG2owGEE/rBOBoYv9uF39ZzsNDh4lTFHhTr2XOZjKkYgueD12ZmGA==",
179
169
  "license": "MIT",
180
170
  "engines": {
181
171
  "node": ">=18.17"
@@ -14,6 +14,6 @@
14
14
  ]
15
15
  },
16
16
  "dependencies": {
17
- "@KnightCodeAI/gondolin": "0.12.0"
17
+ "@earendil-works/gondolin": "0.14.0"
18
18
  }
19
19
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@knightcodeai/cli",
3
- "version": "0.19.0",
3
+ "version": "0.20.0",
4
4
  "description": "KnightCode — a local, BYOK terminal coding agent powered by OpenRouter.",
5
5
  "type": "module",
6
6
  "repository": {
@@ -1,6 +0,0 @@
1
- import { createRequire as __kcCreateRequire } from "node:module"; const require = __kcCreateRequire(import.meta.url);
2
- import{buildCopilotDynamicHeaders,hasCopilotVisionInput}from"./chunk-HWXGL4YB.js";import{ANTHROPIC_FEDERATION_RULE_ID_ENV,ANTHROPIC_IDENTITY_TOKEN_FILE_ENV,ANTHROPIC_ORGANIZATION_ID_ENV,ANTHROPIC_SERVICE_ACCOUNT_ID_ENV,ANTHROPIC_WORKSPACE_ID_ENV}from"./chunk-HSL72VZI.js";import{Anthropic}from"./chunk-VJJ6J35N.js";import{retryProviderRequest}from"./chunk-57BPR4HF.js";import{MIN_ANSWER_TOKENS,adjustMaxTokensForThinking,buildBaseOptions,clampMaxTokensToContext,clampThinkingBudgetToAnswerRoom,getJsonSchemaToolParameters,resolveJsonSchemaStrictSampling,splitErrorToolResultImages,transformMessages}from"./chunk-GPHSXYPT.js";import{sanitizeSurrogates}from"./chunk-B2FT7RH5.js";import{headersToRecord}from"./chunk-DVFCXHT4.js";import{calculateCost,getCurrentTools,getInitialSystemMessage,getSystemMessageText,renderSystemMessageUpdate,resolveTranscript}from"./chunk-T47GKPON.js";import{getProviderEnvValue}from"./chunk-BRKUE2MD.js";import{parseJsonWithRepair,parseStreamingJson,parseToolCallArguments}from"./chunk-FA32X2UJ.js";import{AssistantMessageEventStream,appendAssistantMessageDiagnostic}from"./chunk-ETOMYYZC.js";import{getIdentityHeaders}from"./chunk-UU4A4NV5.js";import"./chunk-AHB7JV5B.js";function resolveCacheRetention(cacheRetention,env){return cacheRetention||(getProviderEnvValue("KNIGHTCODE_CACHE_RETENTION",env)==="long"?"long":"short")}function getCacheControl(model,cacheRetention,env){let retention=resolveCacheRetention(cacheRetention,env);if(retention==="none")return{retention};let ttl=retention==="long"&&getAnthropicCompat(model).supportsLongCacheRetention?"1h":void 0;return{retention,cacheControl:{type:"ephemeral",...ttl&&{ttl}}}}var claudeCodeVersion="2.1.280",claudeCodeTools=["Read","Write","Edit","Bash","Grep","Glob","AskUserQuestion","EnterPlanMode","ExitPlanMode","KillShell","NotebookEdit","Skill","Task","TaskOutput","TodoWrite","WebFetch","WebSearch"],ccToolLookup=new Map(claudeCodeTools.map(t=>[t.toLowerCase(),t])),toClaudeCodeName=name=>ccToolLookup.get(name.toLowerCase())??name,fromClaudeCodeName=(name,tools)=>{if(tools&&tools.length>0){let lowerName=name.toLowerCase(),matchedTool=tools.find(tool=>tool.name.toLowerCase()===lowerName);if(matchedTool)return matchedTool.name}return name};function convertImageBlock(image){return{type:"image",source:{type:"base64",media_type:image.mimeType,data:image.data}}}function convertContentBlocks(content){if(!content.some(c=>c.type==="image"))return sanitizeSurrogates(content.map(c=>c.text).join(`
3
- `));let blocks=content.map(block=>block.type==="text"?{type:"text",text:sanitizeSurrogates(block.text)}:convertImageBlock(block));return blocks.some(b=>b.type==="text")||blocks.unshift({type:"text",text:"(see attached image)"}),blocks}var FINE_GRAINED_TOOL_STREAMING_BETA="fine-grained-tool-streaming-2025-05-14",INTERLEAVED_THINKING_BETA="interleaved-thinking-2025-05-14",SERVER_SIDE_FALLBACK_BETA="server-side-fallback-2026-07-01",MID_CONVERSATION_OUTPUT_CONFIG_BETA="mid-conversation-output-config-2026-07-01",THINKING_BINDING_CONTROLS_BETA="thinking-binding-controls-2026-08-01",INLINE_TOOLS_BETA="inline-tools-2026-09-15",DEFERRED_TOOL_PLACEHOLDER={name:"__knightcode_deferred_placeholder__",description:"Reserved placeholder. Never available. Never call this.",input_schema:{type:"object",properties:{},required:[]},defer_loading:!0};function shouldUseServerSideFallbackBeta(model){return(model.compat?.allowedFallbackModels?.length??0)>0}function getAnthropicCompat(model){let isOpenRouter=model.provider==="openrouter"||model.baseUrl.includes("openrouter.ai");return{supportsEagerToolInputStreaming:model.compat?.supportsEagerToolInputStreaming??!0,supportsLongCacheRetention:model.compat?.supportsLongCacheRetention??!0,sendSessionAffinityHeaders:model.compat?.sendSessionAffinityHeaders??isOpenRouter,sessionAffinityFormat:model.compat?.sessionAffinityFormat??(isOpenRouter?"openrouter":void 0),supportsCacheControlOnTools:model.compat?.supportsCacheControlOnTools??!0,supportsTemperature:model.compat?.supportsTemperature??!0,allowEmptySignature:model.compat?.allowEmptySignature??!1,supportsStrictTools:model.compat?.supportsStrictTools??!1,supportsMidConvoSystemMessages:model.compat?.supportsMidConvoSystemMessages??!1,supportsMidConvoToolChanges:model.compat?.supportsMidConvoToolChanges??!1}}function mergeHeaders(...headerSources){let merged={};for(let headers of headerSources)headers&&Object.assign(merged,headers);return merged}function hasHeader(headers,name){if(!headers)return!1;let expected=name.toLowerCase();for(let[key,value]of Object.entries(headers))if(key.toLowerCase()===expected&&value!==null&&value.trim().length>0)return!0;return!1}function hasRequestAuth(apiKey,headers){return!!apiKey||hasHeader(headers,"authorization")||hasHeader(headers,"x-api-key")||hasHeader(headers,"cf-aig-authorization")}function assertRequestAuth(provider,apiKey,headers){if(!hasRequestAuth(apiKey,headers))throw new Error(`No API key for provider: ${provider}`)}var KnightCodeAnthropic=class extends Anthropic{_shouldResolveDefaultCredentials(){return!1}};function getAnthropicFederation(model,apiKey,headers,env){if(model.provider!=="anthropic"||hasRequestAuth(apiKey,headers))return;let federationRuleId=getProviderEnvValue(ANTHROPIC_FEDERATION_RULE_ID_ENV,env),organizationId=getProviderEnvValue(ANTHROPIC_ORGANIZATION_ID_ENV,env),identityTokenFile=getProviderEnvValue(ANTHROPIC_IDENTITY_TOKEN_FILE_ENV,env);if(!(!federationRuleId||!organizationId||!identityTokenFile))return{organization_id:organizationId,workspace_id:getProviderEnvValue(ANTHROPIC_WORKSPACE_ID_ENV,env),authentication:{type:"oidc_federation",federation_rule_id:federationRuleId,service_account_id:getProviderEnvValue(ANTHROPIC_SERVICE_ACCOUNT_ID_ENV,env),identity_token:{source:"file",path:identityTokenFile}}}}var defaultFetchKey={},federationClients=new WeakMap,ANTHROPIC_MESSAGE_EVENTS=new Set(["message_start","message_delta","message_stop","content_block_start","content_block_delta","content_block_stop"]);function flushSseEvent(state){if(!state.event&&state.data.length===0)return null;let event={event:state.event,data:state.data.join(`
4
- `),raw:[...state.raw]};return state.event=null,state.data=[],state.raw=[],event}function decodeSseLine(line,state){if(line==="")return flushSseEvent(state);if(state.raw.push(line),line.startsWith(":"))return null;let delimiterIndex=line.indexOf(":"),fieldName=delimiterIndex===-1?line:line.slice(0,delimiterIndex),value=delimiterIndex===-1?"":line.slice(delimiterIndex+1);return value.startsWith(" ")&&(value=value.slice(1)),fieldName==="event"?state.event=value:fieldName==="data"&&state.data.push(value),null}function nextLineBreakIndex(text){let carriageReturnIndex=text.indexOf("\r"),newlineIndex=text.indexOf(`
5
- `);return carriageReturnIndex===-1?newlineIndex:newlineIndex===-1?carriageReturnIndex:Math.min(carriageReturnIndex,newlineIndex)}function consumeLine(text){let lineBreakIndex=nextLineBreakIndex(text);if(lineBreakIndex===-1)return null;let nextIndex=lineBreakIndex+1;return text[lineBreakIndex]==="\r"&&text[nextIndex]===`
6
- `&&(nextIndex+=1),{line:text.slice(0,lineBreakIndex),rest:text.slice(nextIndex)}}async function*iterateSseMessages(body,signal){let reader=body.getReader(),decoder=new TextDecoder,state={event:null,data:[],raw:[]},buffer="";try{for(;;){if(signal?.aborted)throw new Error("Request was aborted");let{value,done}=await reader.read();if(done)break;buffer+=decoder.decode(value,{stream:!0});let consumed2=consumeLine(buffer);for(;consumed2;){buffer=consumed2.rest;let event=decodeSseLine(consumed2.line,state);event&&(yield event),consumed2=consumeLine(buffer)}}buffer+=decoder.decode();let consumed=consumeLine(buffer);for(;consumed;){buffer=consumed.rest;let event=decodeSseLine(consumed.line,state);event&&(yield event),consumed=consumeLine(buffer)}if(buffer.length>0){let event=decodeSseLine(buffer,state);event&&(yield event)}let trailingEvent=flushSseEvent(state);trailingEvent&&(yield trailingEvent)}finally{reader.releaseLock()}}async function*iterateAnthropicEvents(response,signal){if(!response.body)throw new Error("Attempted to iterate over an Anthropic response with no body");let sawMessageStart=!1,sawMessageEnd=!1;for await(let sse of iterateSseMessages(response.body,signal)){if(sse.event==="error")throw new Error(sse.data);if(ANTHROPIC_MESSAGE_EVENTS.has(sse.event??""))try{let event=parseJsonWithRepair(sse.data);event.type==="message_start"?sawMessageStart=!0:event.type==="message_stop"&&(sawMessageEnd=!0),yield event}catch(error){let message=error instanceof Error?error.message:String(error);throw new Error(`Could not parse Anthropic SSE event ${sse.event}: ${message}; data=${sse.data}; raw=${sse.raw.join("\\n")}`)}}if(sawMessageStart&&!sawMessageEnd)throw new Error("Anthropic stream ended before message_stop")}var stream=(model,context,options)=>{let stream2=new AssistantMessageEventStream,normalizedContext=resolveTranscript(context,getAnthropicCompat(model).supportsMidConvoSystemMessages),currentTools=getCurrentTools(normalizedContext.messages);return(async()=>{let output={role:"assistant",content:[],api:model.api,provider:model.provider,model:model.id,usage:{input:0,output:0,cacheRead:0,cacheWrite:0,totalTokens:0,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:0}},stopReason:"pending",timestamp:Date.now()};try{let client,isOAuth,usageModel=model,inputTransformations;if(options?.client)client=options.client,isOAuth=!1;else{let apiKey=options?.apiKey,federation=getAnthropicFederation(model,apiKey,options?.headers,options?.env);federation||assertRequestAuth(model.provider,apiKey,options?.headers);let copilotDynamicHeaders;if(model.provider==="github-copilot"){let hasImages=hasCopilotVisionInput(normalizedContext.messages);copilotDynamicHeaders=buildCopilotDynamicHeaders({messages:normalizedContext.messages,hasImages})}let cacheSessionId=resolveCacheRetention(options?.cacheRetention,options?.env)==="none"?void 0:options?.sessionId,created=createClient(model,apiKey,options?.headers,options?.fetch,copilotDynamicHeaders,cacheSessionId,federation,options?.identity);client=created.client,isOAuth=created.isOAuthToken}let requestOptions={...options?.signal?{signal:options.signal}:{},...options?.timeoutMs!==void 0?{timeout:options.timeoutMs}:{},maxRetries:0},disablePreferredStrictTools=!1,response;for(;;){options?.signal?.throwIfAborted();let built=buildParams(model,normalizedContext,isOAuth,options,disablePreferredStrictTools);built.providerThinkingLevel&&(output.providerThinkingLevel=built.providerThinkingLevel);let params=built.params,nextParams=await options?.onPayload?.(params,model);nextParams!==void 0&&(params={...nextParams,stream:!0});try{response=await retryProviderRequest(()=>client.beta.messages.create(params,requestOptions).asResponse(),{maxRetries:options?.maxRetries,maxRetryDelayMs:options?.maxRetryDelayMs,signal:options?.signal});break}catch(error){if(options?.signal?.aborted||disablePreferredStrictTools||!built.hasPreferredStrictTools||!(error instanceof Anthropic.APIError)||error.status!==400||error.type!=="invalid_request_error"||!["The compiled grammar is too large","Too many strict tools (","Schemas contains too many parameters with union types ("].some(message=>error.message.includes(message)))throw error;disablePreferredStrictTools=!0,appendAssistantMessageDiagnostic(output,{type:"anthropic_strict_tools_fallback",timestamp:Date.now(),error:{message:error.message},details:{reason:"strict_tool_limits_exceeded"}})}}await options?.onResponse?.({status:response.status,headers:headersToRecord(response.headers)},model),stream2.push({type:"start",partial:output});let blocks=output.content;for await(let event of iterateAnthropicEvents(response,options?.signal))if(await options?.onProviderStreamEvent?.(event,model),event.type==="message_start"){output.responseId=event.message.id;let transformations=event.message.input_transformations;Array.isArray(transformations)&&(inputTransformations=transformations);let responseModel=event.message.model;responseModel!==model.id&&(output.responseModel=responseModel);let fallbackCost=responseModel===model.id?void 0:model.compat?.allowedFallbackModels?.find(fallback=>fallback.provider===model.provider&&fallback.model===responseModel)?.cost;usageModel=fallbackCost?{...model,id:responseModel,cost:fallbackCost}:model,output.usage.input=event.message.usage.input_tokens||0,output.usage.output=event.message.usage.output_tokens||0,output.usage.cacheRead=event.message.usage.cache_read_input_tokens||0,output.usage.cacheWrite=event.message.usage.cache_creation_input_tokens||0,output.usage.cacheWrite1h=event.message.usage.cache_creation?.ephemeral_1h_input_tokens||0,output.usage.totalTokens=output.usage.input+output.usage.output+output.usage.cacheRead+output.usage.cacheWrite,calculateCost(usageModel,output.usage)}else if(event.type==="content_block_start"){if(event.content_block.type==="fallback"){if(output.content.length>0)throw new Error("Anthropic performed an unsupported mid-output model fallback");continue}if(event.content_block.type==="text"){let block={type:"text",text:event.content_block.text??"",index:event.index};output.content.push(block),stream2.push({type:"text_start",contentIndex:output.content.length-1,partial:output})}else if(event.content_block.type==="thinking"){let block={type:"thinking",thinking:event.content_block.thinking??"",thinkingSignature:event.content_block.signature??"",index:event.index};output.content.push(block),stream2.push({type:"thinking_start",contentIndex:output.content.length-1,partial:output})}else if(event.content_block.type==="redacted_thinking"){let block={type:"thinking",thinking:"[Reasoning redacted]",thinkingSignature:event.content_block.data,redacted:!0,index:event.index};output.content.push(block),stream2.push({type:"thinking_start",contentIndex:output.content.length-1,partial:output})}else if(event.content_block.type==="tool_use"){let block={type:"toolCall",id:event.content_block.id,name:isOAuth?fromClaudeCodeName(event.content_block.name,currentTools):event.content_block.name,arguments:event.content_block.input??{},partialJson:"",index:event.index};output.content.push(block),stream2.push({type:"toolcall_start",contentIndex:output.content.length-1,partial:output})}}else if(event.type==="content_block_delta"){if(event.delta.type==="text_delta"){let index=blocks.findIndex(b=>b.index===event.index),block=blocks[index];block&&block.type==="text"&&(block.text+=event.delta.text,stream2.push({type:"text_delta",contentIndex:index,delta:event.delta.text,partial:output}))}else if(event.delta.type==="thinking_delta"){let index=blocks.findIndex(b=>b.index===event.index),block=blocks[index];block&&block.type==="thinking"&&(block.thinking+=event.delta.thinking,stream2.push({type:"thinking_delta",contentIndex:index,delta:event.delta.thinking,partial:output}))}else if(event.delta.type==="input_json_delta"){let index=blocks.findIndex(b=>b.index===event.index),block=blocks[index];block&&block.type==="toolCall"&&(block.partialJson+=event.delta.partial_json,block.arguments=parseStreamingJson(block.partialJson),stream2.push({type:"toolcall_delta",contentIndex:index,delta:event.delta.partial_json,partial:output}))}else if(event.delta.type==="signature_delta"){let index=blocks.findIndex(b=>b.index===event.index),block=blocks[index];block&&block.type==="thinking"&&(block.thinkingSignature=block.thinkingSignature||"",block.thinkingSignature+=event.delta.signature)}}else if(event.type==="content_block_stop"){let index=blocks.findIndex(b=>b.index===event.index),block=blocks[index];block&&(delete block.index,block.type==="text"?stream2.push({type:"text_end",contentIndex:index,content:block.text,partial:output}):block.type==="thinking"?stream2.push({type:"thinking_end",contentIndex:index,content:block.thinking,partial:output}):block.type==="toolCall"&&(Object.assign(block,parseToolCallArguments(block.partialJson)),delete block.partialJson,stream2.push({type:"toolcall_end",contentIndex:index,toolCall:block,partial:output})))}else if(event.type==="message_delta"){let transformations=event.input_transformations;if(Array.isArray(transformations)&&(inputTransformations=transformations),event.delta.stop_reason){output.rawStopReason=event.delta.stop_reason;let stopReasonResult=mapStopReason(event.delta.stop_reason,event.delta.stop_details);output.stopReason=stopReasonResult.stopReason,stopReasonResult.errorMessage&&(output.errorMessage=stopReasonResult.errorMessage)}if(event.usage){event.usage.input_tokens!=null&&(output.usage.input=event.usage.input_tokens),event.usage.output_tokens!=null&&(output.usage.output=event.usage.output_tokens),event.usage.cache_read_input_tokens!=null&&(output.usage.cacheRead=event.usage.cache_read_input_tokens),event.usage.cache_creation_input_tokens!=null&&(output.usage.cacheWrite=event.usage.cache_creation_input_tokens);let cacheCreation=event.usage.cache_creation;cacheCreation?.ephemeral_1h_input_tokens!=null&&(output.usage.cacheWrite1h=cacheCreation.ephemeral_1h_input_tokens);let thinkingTokens=event.usage.output_tokens_details?.thinking_tokens;thinkingTokens!=null&&(output.usage.reasoning=thinkingTokens)}output.usage.totalTokens=output.usage.input+output.usage.output+output.usage.cacheRead+output.usage.cacheWrite,calculateCost(usageModel,output.usage)}if(options?.signal?.aborted)throw new Error("Request was aborted");if(output.stopReason==="pending")throw new Error("Anthropic stream ended without a stop reason");if(output.stopReason==="aborted"||output.stopReason==="error")throw new Error(output.errorMessage||"An unknown error occurred");inputTransformations&&inputTransformations.length>0&&appendAssistantMessageDiagnostic(output,{type:"anthropic_input_transformations",timestamp:Date.now(),details:{transformations:inputTransformations.map(transformation=>({type:transformation.type??void 0,path:transformation.path??void 0,reason:transformation.reason??void 0}))}}),stream2.push({type:"done",reason:output.stopReason,message:output}),stream2.end()}catch(error){for(let block of output.content)delete block.index,delete block.partialJson;output.stopReason=options?.signal?.aborted?"aborted":"error",output.errorMessage=error instanceof Error?error.message:JSON.stringify(error),stream2.push({type:"error",reason:output.stopReason,error:output}),stream2.end()}})(),stream2};function mapThinkingLevelToEffort(model,level){let mapped=level?model.thinkingLevelMap?.[level]:void 0;if(typeof mapped=="string")return mapped;switch(level){case"minimal":case"low":return"low";case"medium":return"medium";case"high":return"high";default:return"high"}}var streamSimple=(model,context,options)=>{getAnthropicFederation(model,options?.apiKey,options?.headers,options?.env)||assertRequestAuth(model.provider,options?.apiKey,options?.headers);let base={...buildBaseOptions(model,context,options,options?.apiKey),toolChoice:options?.toolChoice};if(!options?.reasoning)return stream(model,context,{...base,thinkingEnabled:!1});if(model.compat?.forceAdaptiveThinking===!0){let effort=mapThinkingLevelToEffort(model,options.reasoning);return stream(model,context,{...base,thinkingEnabled:!0,effort})}let adjusted=adjustMaxTokensForThinking(base.maxTokens,model.maxTokens,options.reasoning,options.thinkingBudgets),maxTokens=clampMaxTokensToContext(model,context,adjusted.maxTokens),thinkingBudgetTokens=clampThinkingBudgetToAnswerRoom(adjusted.thinkingBudget,maxTokens);return thinkingBudgetTokens<MIN_ANSWER_TOKENS?stream(model,context,{...base,maxTokens,thinkingEnabled:!1}):stream(model,context,{...base,maxTokens,thinkingEnabled:!0,thinkingBudgetTokens})};function isOAuthToken(apiKey){return apiKey.includes("sk-ant-oat")}function createClient(model,apiKey,optionsHeaders,fetch,dynamicHeaders,sessionId,federation,identity){if(model.provider==="github-copilot")return{client:new KnightCodeAnthropic({apiKey:null,authToken:apiKey??null,baseURL:model.baseUrl,dangerouslyAllowBrowser:!0,fetch,defaultHeaders:mergeHeaders(getIdentityHeaders(model,identity),{accept:"application/json","anthropic-dangerous-direct-browser-access":"true"},model.headers,dynamicHeaders,optionsHeaders)}),isOAuthToken:!1};if(apiKey&&isOAuthToken(apiKey))return{client:new KnightCodeAnthropic({apiKey:null,authToken:apiKey,baseURL:model.baseUrl,dangerouslyAllowBrowser:!0,fetch,defaultHeaders:mergeHeaders({accept:"application/json","anthropic-dangerous-direct-browser-access":"true","user-agent":`claude-cli/${claudeCodeVersion}`,"x-app":"cli"},model.headers,optionsHeaders)}),isOAuthToken:!0};let compat=getAnthropicCompat(model),sessionAffinityHeaders={};if(sessionId&&compat.sendSessionAffinityHeaders){let header=compat.sessionAffinityFormat==="openrouter"?"x-session-id":"x-session-affinity";sessionAffinityHeaders[header]=sessionId}let defaultHeaders=mergeHeaders(getIdentityHeaders(model,identity),{accept:"application/json","anthropic-dangerous-direct-browser-access":"true"},sessionAffinityHeaders,model.headers,optionsHeaders);if(federation){let fetchKey=fetch??defaultFetchKey,clients=federationClients.get(fetchKey);clients||(clients=new Map,federationClients.set(fetchKey,clients));let key=JSON.stringify([model.baseUrl,federation]),client2=clients.get(key);return client2||(client2=new KnightCodeAnthropic({apiKey:null,authToken:null,config:federation,baseURL:model.baseUrl,dangerouslyAllowBrowser:!0,fetch}),clients.set(key,client2)),{client:client2.withOptions({defaultHeaders}),isOAuthToken:!1}}return{client:new KnightCodeAnthropic({apiKey:apiKey??null,authToken:null,baseURL:model.baseUrl,dangerouslyAllowBrowser:!0,fetch,defaultHeaders}),isOAuthToken:!1}}function getBetaFeatures(model,context,isOAuthToken2,nativeToolChanges,options){let configuredFeatures;for(let headers of[model.headers,options?.headers])for(let[name,value]of Object.entries(headers??{}))name.toLowerCase()==="anthropic-beta"&&(configuredFeatures=value);if(configuredFeatures===null)return[];if(configuredFeatures!==void 0)return[...new Set(configuredFeatures.split(",").map(feature=>feature.trim()).filter(feature=>feature.length>0))];let features=[];return isOAuthToken2&&features.push("claude-code-20250219","oauth-2025-04-20"),shouldUseFineGrainedToolStreamingBeta(model,context)&&features.push(FINE_GRAINED_TOOL_STREAMING_BETA),model.reasoning&&options?.thinkingEnabled===!0&&(options.interleavedThinking??!0)&&model.compat?.forceAdaptiveThinking!==!0&&features.push(INTERLEAVED_THINKING_BETA),shouldUseServerSideFallbackBeta(model)&&features.push(SERVER_SIDE_FALLBACK_BETA),model.compat?.supportsMidConvoEffort===!0&&features.push(MID_CONVERSATION_OUTPUT_CONFIG_BETA,THINKING_BINDING_CONTROLS_BETA),nativeToolChanges&&features.push(INLINE_TOOLS_BETA),[...new Set(features)]}function buildParams(model,context,isOAuthToken2,options,disablePreferredStrictTools=!1){let{cacheControl}=getCacheControl(model,options?.cacheRetention,options?.env),compat=getAnthropicCompat(model),initialSystemMessage=getInitialSystemMessage(context.messages),initialSystemText=initialSystemMessage?getSystemMessageText(initialSystemMessage):"",transformedMessages=transformMessages(context.messages,model,normalizeToolCallId),conversationMessages=initialSystemMessage?transformedMessages.slice(1):transformedMessages,hasPreferredStrictTools=!1,convertToolDefinitions=(tools,toolCacheControl2)=>{let definitions=convertTools(tools,isOAuthToken2,compat.supportsEagerToolInputStreaming,compat.supportsStrictTools,toolCacheControl2,disablePreferredStrictTools);return definitions.some((definition,index)=>{let config=tools[index].constrainedSampling;return definition.strict===!0&&config&&config.type==="json_schema"&&config.strict==="prefer"})&&(hasPreferredStrictTools=!0),definitions},initialTools=initialSystemMessage?.toolsAdded??[],nativeToolChanges=compat.supportsMidConvoSystemMessages&&compat.supportsMidConvoToolChanges&&initialTools.length>0,thinkingDisabled=model.reasoning&&options?.thinkingEnabled===!1&&model.thinkingLevelMap?.off!==null,effortMessages=model.compat?.supportsMidConvoEffort===!0&&!thinkingDisabled,converted=convertMessages(conversationMessages,isOAuthToken2,cacheControl,compat.allowEmptySignature,effortMessages?model.provider:void 0,nativeToolChanges?convertToolDefinitions:void 0),activeEffort=options?.effort??"high",betaFeatures=getBetaFeatures(model,context,isOAuthToken2,nativeToolChanges,options),params={model:model.id,messages:effortMessages?insertThinkingLevelMessages(converted,activeEffort):converted.messages,max_tokens:options?.maxTokens??model.maxTokens,stream:!0,...betaFeatures.length>0?{betas:betaFeatures}:{}};isOAuthToken2?(params.system=[{type:"text",text:"You are Claude Code, Anthropic's official CLI for Claude.",...cacheControl?{cache_control:cacheControl}:{}}],initialSystemText&&params.system.push({type:"text",text:sanitizeSurrogates(initialSystemText),...cacheControl?{cache_control:cacheControl}:{}})):initialSystemText&&(params.system=[{type:"text",text:sanitizeSurrogates(initialSystemText),...cacheControl?{cache_control:cacheControl}:{}}]),options?.temperature!==void 0&&!options?.thinkingEnabled&&model.compat?.supportsMidConvoEffort!==!0&&compat.supportsTemperature&&(params.temperature=options.temperature);let toolCacheControl=compat.supportsCacheControlOnTools?cacheControl:void 0;if(nativeToolChanges)params.tools=[...convertToolDefinitions(initialTools,toolCacheControl),DEFERRED_TOOL_PLACEHOLDER];else{let tools=getCurrentTools(context.messages);tools.length>0&&(params.tools=convertToolDefinitions(tools,toolCacheControl))}if(thinkingDisabled)params.thinking={type:"disabled"};else if(effortMessages)params.thinking={type:"adaptive",display:options?.thinkingDisplay??"summarized",block_binding:{prefix_mismatch_behavior:"drop_block"}},params.output_config={effort:"high"};else if(model.reasoning&&options?.thinkingEnabled){let display=options.thinkingDisplay??"summarized";model.compat?.forceAdaptiveThinking===!0?(params.thinking={type:"adaptive",display},options.effort&&(params.output_config={effort:options.effort})):params.thinking={type:"enabled",budget_tokens:options.thinkingBudgetTokens||1024,display}}if(options?.metadata){let userId=options.metadata.user_id;typeof userId=="string"&&(params.metadata={user_id:userId})}options?.toolChoice&&(typeof options.toolChoice=="string"?params.tool_choice={type:options.toolChoice}:params.tool_choice=options.toolChoice);let allowedFallbackModels=model.compat?.allowedFallbackModels;return allowedFallbackModels&&allowedFallbackModels.length>0&&(params.fallbacks=allowedFallbackModels.map(fallback=>({model:fallback.model}))),{params,hasPreferredStrictTools,providerThinkingLevel:effortMessages?activeEffort:void 0}}function normalizeToolCallId(id){return id.replace(/[^a-zA-Z0-9_-]/g,"_").slice(0,64)}function convertToolResult(msg,content=msg.content){return{type:"tool_result",tool_use_id:msg.toolCallId,content:convertContentBlocks(content),is_error:msg.isError}}function convertMessages(transformedMessages,isOAuthToken2,cacheControl,allowEmptySignature=!1,managedProvider,convertToolDefinitions){let params=[],assistantLevels=new Map,pendingSystemMessages=[],flushPendingSystemMessages=()=>{params.push(...pendingSystemMessages),pendingSystemMessages.length=0};for(let i=0;i<transformedMessages.length;i++){let msg=transformedMessages[i];if(msg.role==="system"){let text=renderSystemMessageUpdate(msg),blocks=[];if(text.length>0&&blocks.push({type:"text",text:sanitizeSurrogates(text)}),convertToolDefinitions){let added=msg.toolsAdded??[],redefined=new Set(added.map(tool=>tool.name));for(let tool of msg.toolsRemoved??[])redefined.has(tool.name)||blocks.push({type:"tool_removal",tool:{type:"tool_reference",name:isOAuthToken2?toClaudeCodeName(tool.name):tool.name}});for(let definition of convertToolDefinitions(added))blocks.push({type:"tool_addition",tool:{type:"tool_definition",definition}})}blocks.length>0&&pendingSystemMessages.push({role:"system",content:blocks})}else if(msg.role==="user")if(typeof msg.content=="string")msg.content.trim().length>0&&params.push({role:"user",content:sanitizeSurrogates(msg.content)});else{let filteredBlocks=msg.content.map(item=>item.type==="text"?{type:"text",text:sanitizeSurrogates(item.text)}:convertImageBlock(item)).filter(b=>b.type==="text"?b.text.trim().length>0:!0);if(filteredBlocks.length===0)continue;params.push({role:"user",content:filteredBlocks})}else if(msg.role==="assistant"){flushPendingSystemMessages();let blocks=[];for(let block of msg.content)if(block.type==="text"){if(block.text.trim().length===0)continue;blocks.push({type:"text",text:sanitizeSurrogates(block.text)})}else if(block.type==="thinking"){if(block.redacted){blocks.push({type:"redacted_thinking",data:block.thinkingSignature});continue}let thinkingSignature=block.thinkingSignature,hasThinkingSignature=!!thinkingSignature&&thinkingSignature.trim().length>0;if(block.thinking.trim().length===0&&!hasThinkingSignature)continue;hasThinkingSignature?blocks.push({type:"thinking",thinking:sanitizeSurrogates(block.thinking),signature:thinkingSignature}):blocks.push(allowEmptySignature?{type:"thinking",thinking:sanitizeSurrogates(block.thinking),signature:""}:{type:"text",text:sanitizeSurrogates(block.thinking)})}else block.type==="toolCall"&&blocks.push({type:"tool_use",id:block.id,name:isOAuthToken2?toClaudeCodeName(block.name):block.name,input:block.arguments??{}});if(blocks.length===0)continue;let messageIndex=params.length;params.push({role:"assistant",content:blocks}),managedProvider!==void 0&&msg.api==="anthropic-messages"&&msg.provider===managedProvider&&isAnthropicEffort(msg.providerThinkingLevel)&&assistantLevels.set(messageIndex,msg.providerThinkingLevel)}else if(msg.role==="toolResult"){let toolResults=[],errorImages=[],j=i;for(;j<transformedMessages.length&&transformedMessages[j].role==="toolResult";){let toolMsg=transformedMessages[j],split=splitErrorToolResultImages(toolMsg);toolResults.push(convertToolResult(toolMsg,split?.content)),split&&errorImages.push({type:"text",text:split.caption},...split.images.map(convertImageBlock)),j++}i=j-1,params.push({role:"user",content:[...toolResults,...errorImages]})}}if(flushPendingSystemMessages(),cacheControl&&params.length>0){let lastMessage=params[params.length-1];if(lastMessage.role==="user"||lastMessage.role==="system")if(Array.isArray(lastMessage.content)){let lastBlock=lastMessage.content[lastMessage.content.length-1];lastBlock&&(lastBlock.type==="text"||lastBlock.type==="image"||lastBlock.type==="tool_result"||lastBlock.type==="tool_addition"||lastBlock.type==="tool_removal")&&(lastBlock.cache_control=cacheControl)}else typeof lastMessage.content=="string"&&(lastMessage.content=[{type:"text",text:lastMessage.content,cache_control:cacheControl}])}return{messages:params,assistantLevels}}function isAnthropicEffort(value){return value==="low"||value==="medium"||value==="high"||value==="xhigh"||value==="max"}function insertThinkingLevelMessages(converted,activeEffort){let messages=[];for(let index=0;index<converted.messages.length;index++){let historicalEffort=converted.assistantLevels.get(index);historicalEffort!==void 0&&messages.push({role:"system",content:[],output_config:{effort:historicalEffort}}),messages.push(converted.messages[index])}return messages.push({role:"system",content:[],output_config:{effort:activeEffort}}),messages}function shouldUseFineGrainedToolStreamingBeta(model,context){return getCurrentTools(context.messages).length>0&&!getAnthropicCompat(model).supportsEagerToolInputStreaming}var ANTHROPIC_STRICT_UNSUPPORTED_KEYWORDS=new Set(["minimum","maximum","exclusiveMinimum","exclusiveMaximum","multipleOf","maxItems","uniqueItems","minContains","maxContains","minProperties","maxProperties"]),ANTHROPIC_STRICT_STRING_FORMATS=new Set(["date-time","time","date","duration","email","hostname","uri","ipv4","ipv6","uuid"]),isAnthropicStrictUnsupportedKeyword=(key,value)=>ANTHROPIC_STRICT_UNSUPPORTED_KEYWORDS.has(key)?!0:key==="minItems"?value!==0&&value!==1:key==="format"?typeof value!="string"||!ANTHROPIC_STRICT_STRING_FORMATS.has(value):!1;function convertTools(tools,isOAuthToken2,supportsEagerToolInputStreaming,supportsStrictTools,cacheControl,disablePreferredStrictTools=!1){return tools?tools.map((tool,index)=>{let config=tool.constrainedSampling,strict=disablePreferredStrictTools&&config&&config.type==="json_schema"&&config.strict==="prefer"?void 0:resolveJsonSchemaStrictSampling(tool,supportsStrictTools,isAnthropicStrictUnsupportedKeyword),parameters=getJsonSchemaToolParameters(tool,strict),schema=parameters,legacyInputSchema={type:"object",properties:schema.properties??{},required:schema.required??[]},inputSchema=strict===!0?{...parameters,...legacyInputSchema}:legacyInputSchema;return{name:isOAuthToken2?toClaudeCodeName(tool.name):tool.name,description:tool.description,...supportsEagerToolInputStreaming?{eager_input_streaming:!0}:{},...strict===!0?{strict:!0}:{},input_schema:inputSchema,...cacheControl&&index===tools.length-1?{cache_control:cacheControl}:{}}}):[]}function mapStopReason(reason,stopDetails){switch(reason){case"end_turn":return{stopReason:"stop"};case"max_tokens":return{stopReason:"length"};case"tool_use":return{stopReason:"toolUse"};case"refusal":return{stopReason:"error",errorMessage:stopDetails?.explanation||"The model refused to complete the request"};case"pause_turn":return{stopReason:"stop"};case"stop_sequence":return{stopReason:"stop"};case"sensitive":return{stopReason:"error",errorMessage:"Provider stopped with: sensitive"};default:throw new Error(`Unhandled stop reason: ${reason}`)}}export{stream,streamSimple};
@@ -1,2 +0,0 @@
1
- import { createRequire as __kcCreateRequire } from "node:module"; const require = __kcCreateRequire(import.meta.url);
2
- import{isRecord,parseClassifierUsage,postClassifierRequest,requiredNumber}from"./chunk-AA6MLWWY.js";import{formatProviderError,normalizeProviderError}from"./chunk-4CRI7BMY.js";function probabilities(label,value,id){if(!isRecord(value))throw new Error(`${label} returned invalid probabilities for ${id}`);return Object.fromEntries(Object.entries(value).map(([key,probability])=>[key,requiredNumber(label,probability,`probability for ${id}.${key}`)]))}function parseAnswers(label,value,context){if(!isRecord(value))throw new Error(`${label} returned an unexpected response`);let answers=[];for(let[id,question]of Object.entries(context.questions)){let answer=value[id];if(!isRecord(answer))throw new Error(`${label} did not return an answer for ${id}`);if(question.type==="choice"){if(answer.type!=="choice"||typeof answer.choice!="string")throw new Error(`${label} did not return a choice answer for ${id}`);answers.push([id,{type:"choice",choice:answer.choice,probabilities:probabilities(label,answer.probabilities,id),confidence:requiredNumber(label,answer.confidence,`confidence for ${id}`)}])}else if(question.type==="score"){if(answer.type!=="score")throw new Error(`${label} did not return a score answer for ${id}`);answers.push([id,{type:"score",score:requiredNumber(label,answer.score,`score for ${id}`),confidence:requiredNumber(label,answer.confidence,`confidence for ${id}`)}])}else{if(answer.type!=="noul")throw new Error(`${label} did not return a bool answer for ${id}`);answers.push([id,{type:"bool",probability:requiredNumber(label,answer.noul,`probability for ${id}`)}])}}return Object.fromEntries(answers)}function wireRequest(context){return{state:context.state,questions:Object.fromEntries(Object.entries(context.questions).map(([id,question])=>[id,question.type==="bool"?{...question,type:"noul"}:question]))}}async function classifySystemOne(transport,model,context,options){let output={api:model.api,provider:model.provider,model:model.id,answers:{},stopReason:"stop",timestamp:Date.now()};try{if(model.api!==transport.api)throw new Error(`Unsupported classifier API: ${model.api}`);if(context.images?.length)throw new Error(`${transport.label} does not support image input`);let body=await postClassifierRequest(transport.label,transport.url(model),model,transport.payload(model,wireRequest(context)),options),result=transport.output(body),usage=parseClassifierUsage(result.usage,model);return usage&&(output.usage=usage),output.answers=parseAnswers(transport.label,result.answers,context),output}catch(error){return output.stopReason=options?.signal?.aborted?"aborted":"error",output.errorMessage=formatProviderError(normalizeProviderError(error),`${transport.label} error`),output}}export{classifySystemOne};
@@ -1,2 +0,0 @@
1
- import { createRequire as __kcCreateRequire } from "node:module"; const require = __kcCreateRequire(import.meta.url);
2
- import{classifySystemOne}from"./chunk-2QXUEOVY.js";import{isRecord}from"./chunk-AA6MLWWY.js";import"./chunk-4CRI7BMY.js";import"./chunk-57BPR4HF.js";import"./chunk-DVFCXHT4.js";import"./chunk-T47GKPON.js";import"./chunk-ETOMYYZC.js";import"./chunk-UU4A4NV5.js";import"./chunk-AHB7JV5B.js";var LABEL="Cloudflare Workers AI";function cloudflareErrorMessage(errors){if(Array.isArray(errors)){let messages=errors.map(error=>isRecord(error)&&typeof error.message=="string"?error.message:void 0).filter(message=>message!==void 0);if(messages.length>0)return`${LABEL} error: ${messages.join("; ")}`}return`${LABEL} request failed`}var transport={api:"cloudflare-workers-ai-system-one",label:LABEL,url:model=>new URL("run",`${model.baseUrl.replace(/\/+$/u,"")}/`),payload:(model,request)=>({model:model.id,input:request}),output:body=>{if(!isRecord(body))throw new Error(`${LABEL} returned an unexpected response`);if(body.success===!1)throw new Error(cloudflareErrorMessage(body.errors));let result=body.result;if(!isRecord(result))throw new Error(`${LABEL} returned an unexpected response`);if("answers"in result)return result;if(result.state!=="Completed")throw new Error(`${LABEL} run did not complete (state: ${String(result.state)})`);if(!isRecord(result.result))throw new Error(`${LABEL} returned an unexpected response`);return result.result}},classify=(model,context,options)=>classifySystemOne(transport,model,context,options);export{classify};