@psnext/slingcli 3.0.20260810-3 → 3.0.20260811-2

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 (47) hide show
  1. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl/codec.js +4 -1
  2. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/testing/conformance.js +19 -0
  3. package/node_modules/@earendil-works/pi-ai/dist/api/bedrock-converse-stream.js +12 -1
  4. package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js +2 -1
  5. package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
  6. package/node_modules/@earendil-works/pi-ai/dist/providers/data/amazon-bedrock.json +1 -1
  7. package/node_modules/@earendil-works/pi-ai/dist/providers/data/cloudflare-workers-ai.json +1 -1
  8. package/node_modules/@earendil-works/pi-ai/dist/providers/data/deepseek.json +1 -1
  9. package/node_modules/@earendil-works/pi-ai/dist/providers/data/google.json +1 -1
  10. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode.json +1 -1
  11. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
  12. package/node_modules/@earendil-works/pi-coding-agent/dist/core/project-trust.js +1 -1
  13. package/node_modules/@earendil-works/pi-coding-agent/dist/core/system-prompt.js +1 -1
  14. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.js +3 -3
  15. package/node_modules/@earendil-works/pi-coding-agent/docs/compaction.md +9 -9
  16. package/node_modules/@earendil-works/pi-coding-agent/docs/containerization.md +8 -8
  17. package/node_modules/@earendil-works/pi-coding-agent/docs/custom-provider.md +20 -12
  18. package/node_modules/@earendil-works/pi-coding-agent/docs/development.md +71 -0
  19. package/node_modules/@earendil-works/pi-coding-agent/docs/docs.json +14 -1
  20. package/node_modules/@earendil-works/pi-coding-agent/docs/environment-variables.md +9 -9
  21. package/node_modules/@earendil-works/pi-coding-agent/docs/extensions.md +53 -26
  22. package/node_modules/@earendil-works/pi-coding-agent/docs/index.md +10 -9
  23. package/node_modules/@earendil-works/pi-coding-agent/docs/json.md +21 -16
  24. package/node_modules/@earendil-works/pi-coding-agent/docs/keybindings.md +46 -14
  25. package/node_modules/@earendil-works/pi-coding-agent/docs/llama-cpp.md +3 -3
  26. package/node_modules/@earendil-works/pi-coding-agent/docs/mcp.md +2 -2
  27. package/node_modules/@earendil-works/pi-coding-agent/docs/models.md +25 -4
  28. package/node_modules/@earendil-works/pi-coding-agent/docs/packages.md +13 -13
  29. package/node_modules/@earendil-works/pi-coding-agent/docs/prompt-templates.md +1 -1
  30. package/node_modules/@earendil-works/pi-coding-agent/docs/providers.md +317 -0
  31. package/node_modules/@earendil-works/pi-coding-agent/docs/quickstart.md +11 -9
  32. package/node_modules/@earendil-works/pi-coding-agent/docs/rpc.md +15 -13
  33. package/node_modules/@earendil-works/pi-coding-agent/docs/sdk.md +33 -14
  34. package/node_modules/@earendil-works/pi-coding-agent/docs/security.md +5 -5
  35. package/node_modules/@earendil-works/pi-coding-agent/docs/session-format.md +3 -1
  36. package/node_modules/@earendil-works/pi-coding-agent/docs/sessions.md +1 -1
  37. package/node_modules/@earendil-works/pi-coding-agent/docs/settings.md +6 -2
  38. package/node_modules/@earendil-works/pi-coding-agent/docs/shell-aliases.md +1 -1
  39. package/node_modules/@earendil-works/pi-coding-agent/docs/skills.md +5 -5
  40. package/node_modules/@earendil-works/pi-coding-agent/docs/terminal-setup.md +3 -3
  41. package/node_modules/@earendil-works/pi-coding-agent/docs/termux.md +5 -5
  42. package/node_modules/@earendil-works/pi-coding-agent/docs/themes.md +7 -5
  43. package/node_modules/@earendil-works/pi-coding-agent/docs/tmux.md +3 -3
  44. package/node_modules/@earendil-works/pi-coding-agent/docs/usage.md +17 -10
  45. package/node_modules/@earendil-works/pi-coding-agent/docs/windows.md +1 -1
  46. package/package.json +2 -2
  47. package/slingshot/index.js +9 -7
@@ -1,4 +1,4 @@
1
- > pi can create extensions. Ask it to build one for your use case.
1
+ > sling can create extensions. Ask it to build one for your use case.
2
2
 
3
3
  # Extensions
4
4
 
@@ -103,7 +103,7 @@ export default function (pi: ExtensionAPI) {
103
103
  Test with `--extension` (or `-e`) flag:
104
104
 
105
105
  ```bash
106
- sling -e ./my-extension.ts
106
+ pi -e ./my-extension.ts
107
107
  ```
108
108
 
109
109
  ## Extension Locations
@@ -134,7 +134,7 @@ Additional paths via `settings.json`:
134
134
  }
135
135
  ```
136
136
 
137
- To share extensions via npm or git as sling packages, see [packages.md](packages.md).
137
+ To share extensions via npm or git as sling/pi packages, see [packages.md](packages.md).
138
138
 
139
139
  ## Available Imports
140
140
 
@@ -178,7 +178,7 @@ export default function (pi: ExtensionAPI) {
178
178
 
179
179
  Extensions are loaded via [jiti](https://github.com/unjs/jiti), so TypeScript works without compilation.
180
180
 
181
- If the factory returns a `Promise`, sling awaits it before continuing startup. That means async initialization completes before `session_start`, before `resources_discover`, and before provider registrations queued via `sling.registerProvider()` are flushed.
181
+ If the factory returns a `Promise`, sling awaits it before continuing startup. That means async initialization completes before `session_start`, before `resources_discover`, and before provider registrations queued via `pi.registerProvider()` are flushed.
182
182
 
183
183
  ### Async factory functions
184
184
 
@@ -275,7 +275,7 @@ Run `npm install` in the extension directory, then imports from `node_modules/`
275
275
  ### Lifecycle Overview
276
276
 
277
277
  ```
278
- sling starts
278
+ pi starts
279
279
 
280
280
  ├─► project_trust (user/global and CLI extensions only, before project resources load)
281
281
  ├─► session_start { reason: "startup" }
@@ -551,13 +551,13 @@ pi.on("before_agent_start", async (event, ctx) => {
551
551
  });
552
552
  ```
553
553
 
554
- The `systemPromptOptions` field gives extensions access to the same structured data Pi uses to build the system prompt. This lets you inspect what Pi has loaded — custom prompts, guidelines, tool snippets, context files, skills — without re-discovering resources or re-parsing flags. Use it when your extension needs to make deep, informed changes to the system prompt while respecting user-provided configuration.
554
+ The `systemPromptOptions` field gives extensions access to the same structured data Sling uses to build the system prompt. This lets you inspect what Sling has loaded — custom prompts, guidelines, tool snippets, context files, skills — without re-discovering resources or re-parsing flags. Use it when your extension needs to make deep, informed changes to the system prompt while respecting user-provided configuration.
555
555
 
556
556
  Inside `before_agent_start`, `event.systemPrompt` and `ctx.getSystemPrompt()` both reflect the chained system prompt as of the current handler. Later `before_agent_start` handlers can still modify it again.
557
557
 
558
558
  #### agent_start / agent_end / agent_settled
559
559
 
560
- `agent_start` fires when a low-level agent run begins. `agent_end` fires when that run ends, but Pi may still auto-retry, auto-compact and retry, or continue with queued follow-up messages. Use `agent_settled` for status integrations that need to know Pi will not continue running automatically.
560
+ `agent_start` fires when a low-level agent run begins. `agent_end` fires when that run ends, but Sling may still auto-retry, auto-compact and retry, or continue with queued follow-up messages. Use `agent_settled` for status integrations that need to know Sling will not continue running automatically.
561
561
 
562
562
  ```typescript
563
563
  pi.on("agent_start", async (_event, ctx) => {});
@@ -668,7 +668,7 @@ pi.on("before_provider_headers", (event, ctx) => {
668
668
  // Add or override — e.g. a session id for gateway tracing/attribution
669
669
  event.headers["x-session-id"] = ctx.sessionManager.getSessionId();
670
670
 
671
- // Drop a tracking header sling adds for this call
671
+ // Drop a tracking header pi adds for this call
672
672
  event.headers["X-OpenRouter-Title"] = null;
673
673
  });
674
674
  ```
@@ -679,7 +679,7 @@ Runs once per provider request; retries reuse the same headers rather than re-fi
679
679
 
680
680
  Fired after the provider-specific payload is built, right before the request is sent. Handlers run in extension load order. Returning `undefined` keeps the payload unchanged. Returning any other value replaces the payload for later handlers and for the actual request.
681
681
 
682
- This hook can rewrite provider-level system instructions or remove them entirely. Those payload-level changes are not reflected by `ctx.getSystemPrompt()`, which reports Pi's system prompt string rather than the final serialized provider payload.
682
+ This hook can rewrite provider-level system instructions or remove them entirely. Those payload-level changes are not reflected by `ctx.getSystemPrompt()`, which reports Sling's system prompt string rather than the final serialized provider payload.
683
683
 
684
684
  ```typescript
685
685
  pi.on("before_provider_request", (event, ctx) => {
@@ -762,7 +762,8 @@ Behavior guarantees:
762
762
  - Mutations to `event.input` affect the actual tool execution
763
763
  - Later `tool_call` handlers see mutations made by earlier handlers
764
764
  - No re-validation is performed after your mutation
765
- - Return values from `tool_call` only control blocking via `{ block: true, reason?: string }`
765
+ - Return values from `tool_call` control blocking via `{ block: true, reason?: string, terminate?: boolean }`
766
+ - `terminate` only applies to a blocked call; the agent stops early only when every finalized result in the batch is terminating
766
767
 
767
768
  ```typescript
768
769
  import { isToolCallEventType } from "@earendil-works/pi-coding-agent";
@@ -778,7 +779,7 @@ pi.on("tool_call", async (event, ctx) => {
778
779
  event.input.command = `source ~/.profile\n${event.input.command}`;
779
780
 
780
781
  if (event.input.command.includes("rm -rf")) {
781
- return { block: true, reason: "Dangerous command" };
782
+ return { block: true, reason: "Dangerous command", terminate: true };
782
783
  }
783
784
  }
784
785
 
@@ -982,10 +983,12 @@ ctx.sessionManager.buildContextEntries() // Active branch entries with compac
982
983
  ctx.sessionManager.getLeafId() // Current leaf entry ID
983
984
  ```
984
985
 
985
- ### ctx.modelRegistry / ctx.model / ctx.thinkingLevel
986
+ ### ctx.modelRegistry / ctx.model / ctx.thinkingLevel / ctx.scopedModels
986
987
 
987
988
  Access to models, providers, and resolved authentication. `ctx.modelRegistry.getProvider(id)` returns the effective pi-ai provider, while `getProviderAuth(id)` resolves its current API key, headers, base URL, and provider-scoped environment without requiring a loaded model. `ctx.model` is the active model, and `ctx.thinkingLevel` is its current effective thinking level.
988
989
 
990
+ `ctx.scopedModels` is the read-only list of models scoped to the current session — the same set the `/scoped-models` command shows. It is resolved at session start from the `--models` CLI flag and the `enabledModels` setting (matched against the available catalogue with minimatch on `provider/modelId` or a bare `modelId`). It is empty when no scoping is configured, meaning every available model is usable. Each entry is `{ model, thinkingLevel? }`, where `thinkingLevel` is set only when a pattern pinned it (e.g. `anthropic/*:high`). Use it to populate a model picker that mirrors the built-in one instead of enumerating the whole catalogue via `ctx.modelRegistry.getAvailable()`.
991
+
989
992
  ### ctx.signal
990
993
 
991
994
  The current agent abort signal, or `undefined` when no agent turn is active.
@@ -1013,7 +1016,7 @@ pi.on("tool_result", async (event, ctx) => {
1013
1016
 
1014
1017
  ### ctx.isIdle() / ctx.abort() / ctx.hasPendingMessages()
1015
1018
 
1016
- Control flow helpers. `ctx.isIdle()` is false while Pi is processing an agent run, automatic retry, auto-compaction retry, or queued continuation.
1019
+ Control flow helpers. `ctx.isIdle()` is false while Sling is processing an agent run, automatic retry, auto-compaction retry, or queued continuation.
1017
1020
 
1018
1021
  ### ctx.shutdown()
1019
1022
 
@@ -1062,7 +1065,7 @@ ctx.compact({
1062
1065
 
1063
1066
  ### ctx.getSystemPrompt()
1064
1067
 
1065
- Returns Pi's current system prompt string.
1068
+ Returns Sling's current system prompt string.
1066
1069
 
1067
1070
  - During `before_agent_start`, this reflects chained system-prompt changes made so far for the current turn.
1068
1071
  - It does not include later `context` message mutations.
@@ -1082,7 +1085,7 @@ Command handlers receive `ExtensionCommandContext`, which extends `ExtensionCont
1082
1085
 
1083
1086
  ### ctx.getSystemPromptOptions()
1084
1087
 
1085
- Returns the base inputs Pi currently uses to build the system prompt.
1088
+ Returns the base inputs Sling currently uses to build the system prompt.
1086
1089
 
1087
1090
  ```typescript
1088
1091
  const options = ctx.getSystemPromptOptions();
@@ -1560,6 +1563,27 @@ mode and would not execute if sent via `prompt`.
1560
1563
 
1561
1564
  Register a custom TUI renderer for custom messages with your `customType`. Custom messages are created with `pi.sendMessage()` and participate in LLM context. See [Custom UI](#custom-ui).
1562
1565
 
1566
+ ### pi.registerMarkdownTransformer(transformer)
1567
+
1568
+ Register a transformer for the Markdown in normal user text, assistant text, and thinking blocks. Transformers run in extension load order, and each transformer receives the Markdown returned by the previous transformer. After the chain finishes, Sling renders the transformed content with its built-in renderer.
1569
+
1570
+ The transformer receives the Markdown string and a context with:
1571
+
1572
+ - `messageType` — `"user"`, `"assistant"`, or `"assistant-thinking"`
1573
+ - `isStreaming` — `true` for partial assistant updates; `false` for user, finalized assistant, and restored messages
1574
+ - `availableWidth` — exact terminal columns available for the transformed Markdown content
1575
+
1576
+ Return the transformed Markdown:
1577
+
1578
+ ```typescript
1579
+ pi.registerMarkdownTransformer((markdown, { messageType, isStreaming }) => {
1580
+ if (isStreaming || messageType === "assistant-thinking") return markdown;
1581
+ return markdown.replaceAll("-->", "→");
1582
+ });
1583
+ ```
1584
+
1585
+ If a transformer throws, Sling keeps the Markdown produced so far and continues with the next transformer. The hook is display-only: the original message remains unchanged in the session and model context. It runs for new user messages, assistant streaming updates, restored session messages, and terminal width changes, so transformers should remain synchronous and inexpensive.
1586
+
1563
1587
  ### pi.registerEntryRenderer(customType, renderer)
1564
1588
 
1565
1589
  Register a custom TUI renderer for custom entries with your `customType`. Custom entries are created with `pi.appendEntry()` and do not participate in LLM context.
@@ -1684,7 +1708,9 @@ Register or override a model provider dynamically. Useful for proxies, custom en
1684
1708
 
1685
1709
  Calls made during the extension factory function are queued and applied once the runner initialises. Calls made after that — for example from a command handler following a user setup flow — take effect immediately without requiring a `/reload`.
1686
1710
 
1687
- Dynamic providers can implement `refreshModels`. Pi calls it during model refresh, publishes the returned list synchronously through the provider, and passes the canonical credential/store/network/signal context. The extension decides whether to persist the catalog through `context.store`; live servers such as llama.cpp can ignore it.
1711
+ Dynamic providers can implement `refreshModels`. Sling calls it during model refresh, publishes the returned list synchronously through the provider, and passes the canonical credential/stored-catalog/network/signal context. The extension decides whether to persist catalog metadata through generation-checked `context.publish({ persist: entry })`; live servers such as llama.cpp can return models without persisting them.
1712
+
1713
+ `context.signal` is always a concrete signal and provider callbacks must pass it to blocking I/O. Public `ModelRuntime.refresh()` and `ModelRegistry.refresh()` calls accept an optional signal and are unbounded when it is omitted; extensions and applications choose their own deadlines. Cancellation stops the caller waiting even if a provider ignores the signal, but cooperation is still required to stop the underlying work.
1688
1714
 
1689
1715
  Extensions that need native provider auth, filtering, refresh, or stream behavior can register a complete `Provider` from `@earendil-works/pi-ai`. The provider becomes the composition base and `models.json` overrides still apply above it.
1690
1716
 
@@ -1774,7 +1800,8 @@ pi.registerProvider("corporate-ai", {
1774
1800
  const code = await callbacks.onPrompt({ message: "Enter code:" });
1775
1801
  return { refresh: code, access: code, expires: Date.now() + 3600000 };
1776
1802
  },
1777
- async refreshToken(credentials) {
1803
+ async refreshToken(credentials, signal) {
1804
+ signal.throwIfAborted();
1778
1805
  // Refresh logic
1779
1806
  return credentials;
1780
1807
  },
@@ -1795,7 +1822,7 @@ The object form accepts a complete pi-ai `Provider`, including native `auth`, `g
1795
1822
  - `headers` - Custom headers to include in requests.
1796
1823
  - `authHeader` - If true, adds `Authorization: Bearer` header automatically.
1797
1824
  - `models` - Array of model definitions. If provided, replaces all existing models for this provider. Model definitions can set `baseUrl` to override the provider endpoint for that model.
1798
- - `refreshModels` - Async dynamic discovery callback. Its returned models replace extension-provided models. Use the scoped `context.store` only when results should persist.
1825
+ - `refreshModels` - Async dynamic discovery callback. Its returned models replace extension-provided models. `context.stored` contains the persisted provider snapshot; use generation-checked `context.publish({ persist: entry })` only when updated catalog data should persist. Use `persist: null` to delete that snapshot.
1799
1826
  - `oauth` - OAuth provider config for `/login` support. When provided, the provider appears in the login menu.
1800
1827
  - `streamSimple` - Custom streaming implementation for non-standard APIs.
1801
1828
 
@@ -1952,7 +1979,7 @@ pi.registerTool({
1952
1979
  });
1953
1980
  ```
1954
1981
 
1955
- **Usage accounting:** If a tool makes nested LLM calls, return their combined `Usage` as `usage`. Pi persists it on the tool result and includes it in footer, `/session`, and RPC session totals. `tool_result` handlers can inspect or replace this value.
1982
+ **Usage accounting:** If a tool makes nested LLM calls, return their combined `Usage` as `usage`. Sling persists it on the tool result and includes it in footer, `/session`, and RPC session totals. `tool_result` handlers can inspect or replace this value.
1956
1983
 
1957
1984
  **Signaling errors:** To mark a tool execution as failed (sets `isError: true` on the result and reports it to the LLM), throw an error from `execute`. Returning a value never sets the error flag regardless of what properties you include in the return object.
1958
1985
 
@@ -2023,13 +2050,13 @@ Extensions can override built-in tools (`read`, `bash`, `edit`, `write`, `grep`,
2023
2050
 
2024
2051
  ```bash
2025
2052
  # Extension's read tool replaces built-in read
2026
- sling -e ./tool-override.ts
2053
+ pi -e ./tool-override.ts
2027
2054
  ```
2028
2055
 
2029
2056
  Alternatively, use `--no-builtin-tools` to start without any built-in tools while keeping extension tools enabled:
2030
2057
  ```bash
2031
2058
  # No built-in tools, only extension tools
2032
- sling --no-builtin-tools -e ./my-extension.ts
2059
+ pi --no-builtin-tools -e ./my-extension.ts
2033
2060
  ```
2034
2061
 
2035
2062
  See [examples/extensions/tool-override.ts](../examples/extensions/tool-override.ts) for a complete example that overrides `read` with logging and access control.
@@ -2303,7 +2330,7 @@ If a slot renderer is not defined or throws:
2303
2330
 
2304
2331
  ### Dynamic Tool Loading
2305
2332
 
2306
- Extensions can register many tools while keeping only a small initial set active. A tool can then add more tools with `pi.setActiveTools()` during execution. Pi detects purely additive changes, records the newly available tool names on that tool result, and applies the updated active set before the next model request.
2333
+ Extensions can register many tools while keeping only a small initial set active. A tool can then add more tools with `pi.setActiveTools()` during execution. sling detects purely additive changes, records the newly available tool names on that tool result, and applies the updated active set before the next model request.
2307
2334
 
2308
2335
  This works with every model. Models with native deferred-loading support preserve the stable prompt prefix and load the new definitions at the tool-result position. Other models use the fallback described below.
2309
2336
 
@@ -2312,8 +2339,8 @@ The lifecycle is:
2312
2339
  1. Register every tool with `pi.registerTool()` so it appears in `pi.getAllTools()`.
2313
2340
  2. Keep loader tools, such as `search_tools`, active and leave searchable tools inactive.
2314
2341
  3. During loader execution, call `pi.setActiveTools([...currentTools, ...matchingTools])`. The change must be additive: do not remove currently active tools in the same call.
2315
- 4. Pi records which tools were added on the loader's tool result.
2316
- 5. Before the next model response, Pi exposes the added definitions using native deferred loading when supported, or the normal active tool list otherwise.
2342
+ 4. sling records which tools were added on the loader's tool result.
2343
+ 5. Before the next model response, sling exposes the added definitions using native deferred loading when supported, or the normal active tool list otherwise.
2317
2344
 
2318
2345
  You do not need to return provider-specific tool references or mark the loader as a special search tool. The active-tool change is the signal. Names passed to `pi.setActiveTools()` must already be registered; unknown names are ignored.
2319
2346
 
@@ -2324,13 +2351,13 @@ You do not need to return provider-specific tool references or mark the loader a
2324
2351
  - **Native representation:** Deferred definitions use `defer_loading`; the load point uses `tool_reference` content.
2325
2352
  - **OpenAI**
2326
2353
  - **Models:** `gpt-5.4` and newer family
2327
- - **Native representation:** Pi adds completed client `tool_search_call` and `tool_search_output` items at the load point.
2354
+ - **Native representation:** sling adds completed client `tool_search_call` and `tool_search_output` items at the load point.
2328
2355
 
2329
2356
  For a verified custom model or proxy, native handling can be enabled with `compat.supportsToolReferences: true` for `anthropic-messages`, or `compat.supportsToolSearch: true` for `openai-responses` and `openai-codex-responses`. Leave these disabled unless the endpoint and model accept the corresponding native protocol.
2330
2357
 
2331
2358
  #### Fallback behavior
2332
2359
 
2333
- For all other models and providers, dynamic activation still works: Pi sends the complete current active tool list normally on the next request. The model can call the newly activated tools, but adding their definitions may invalidate the provider's cached prompt prefix.
2360
+ For all other models and providers, dynamic activation still works: sling sends the complete current active tool list normally on the next request. The model can call the newly activated tools, but adding their definitions may invalidate the provider's cached prompt prefix.
2334
2361
 
2335
2362
  Pi also uses this safe fallback when the active set is not purely additive, such as replacing one group of tools with another. Tool removals therefore work, but they do not use deferred loading.
2336
2363
 
@@ -1,21 +1,21 @@
1
- # Sling Documentation
1
+ # sling Documentation
2
2
 
3
- Sling is a minimal terminal coding harness. It is designed to stay small at the core while being extended through TypeScript extensions, skills, prompt templates, themes, and sling packages.
3
+ sling is a minimal terminal coding harness. It is designed to stay small at the core while being extended through TypeScript extensions, skills, prompt templates, themes, and sling packages.
4
4
 
5
5
  ## Quick start
6
6
 
7
- Install Sling with npm:
7
+ Install sling with npm:
8
8
 
9
9
  ```bash
10
10
  npm install -g --ignore-scripts @psnext/slingcli
11
11
  ```
12
12
 
13
- `--ignore-scripts` disables dependency lifecycle scripts during install. Sling does not require install scripts for normal npm installs.
13
+ `--ignore-scripts` disables dependency lifecycle scripts during install. sling does not require install scripts for normal npm installs.
14
14
 
15
15
  On Linux or macOS, you can also use the installer:
16
16
 
17
17
  ```bash
18
- curl -fsSL https://pi.dev/install.sh | sh
18
+ curl -fsSL https://sapientaiproducts.com/slingshot/slingcli/install.sh | sh
19
19
  ```
20
20
 
21
21
  To uninstall sling itself, use npm for curl and npm installs:
@@ -32,14 +32,14 @@ Then run it in a project directory:
32
32
  sling
33
33
  ```
34
34
 
35
- Authenticate with `/login` for subscription providers, or set an API key such as `ANTHROPIC_API_KEY` before starting sling.
35
+ Authenticate with `/login` for subscription providers, or set an API key such as `SLINGSHOT_API_KEY` before starting sling.
36
36
 
37
37
  For the full first-run flow, see [Quickstart](quickstart.md).
38
38
 
39
39
  ## Start here
40
40
 
41
41
  - [Quickstart](quickstart.md) - install, authenticate, and run a first session.
42
- - [Using Sling](usage.md) - interactive mode, slash commands, context files, and CLI reference.
42
+ - [Using sling](usage.md) - interactive mode, slash commands, context files, and CLI reference.
43
43
  - [Providers](providers.md) - subscription and API-key setup for built-in providers.
44
44
  - [llama.cpp](llama-cpp.md) - run a local router and manage models with `/llama`.
45
45
  - [Security](security.md) - project trust, sandbox boundaries, and vulnerability reporting.
@@ -55,19 +55,20 @@ For the full first-run flow, see [Quickstart](quickstart.md).
55
55
  - [Skills](skills.md) - Agent Skills for reusable on-demand capabilities.
56
56
  - [Prompt templates](prompt-templates.md) - reusable prompts that expand from slash commands.
57
57
  - [Themes](themes.md) - built-in and custom terminal themes.
58
- - [Sling/Pi packages](packages.md) - bundle and share extensions, skills, prompts, and themes.
58
+ - [sling packages](packages.md) - bundle and share extensions, skills, prompts, and themes.
59
59
  - [Custom models](models.md) - add model entries for supported provider APIs.
60
60
  - [Custom providers](custom-provider.md) - implement custom APIs and OAuth flows.
61
61
 
62
62
  ## Programmatic usage
63
63
 
64
+ - [SDK](sdk.md) - embed sling in Node.js applications.
64
65
  - [RPC mode](rpc.md) - integrate over stdin/stdout JSONL.
65
66
  - [JSON event stream mode](json.md) - print mode with structured events.
66
67
  - [TUI components](tui.md) - build custom terminal UI for extensions.
67
68
 
68
69
  ## Reference
69
70
 
70
- - [Environment variables](environment-variables.md) - Sling process configuration and session metadata available to bash tools.
71
+ - [Environment variables](environment-variables.md) - sling process configuration and session metadata available to bash tools.
71
72
  - [Session format](session-format.md) - JSONL session file format, entry types, and SessionManager API.
72
73
 
73
74
  ## Platform setup
@@ -1,32 +1,32 @@
1
1
  # JSON Event Stream Mode
2
2
 
3
3
  ```bash
4
- sling --mode json "Your prompt"
4
+ pi --mode json "Your prompt"
5
5
  ```
6
6
 
7
7
  Outputs all session events as JSON lines to stdout. Useful for integrating sling into other tools or custom UIs.
8
8
 
9
9
  ## Event Types
10
10
 
11
- Events are defined in [`AgentSessionEvent`](https://github.com/earendil-works/pi-mono/blob/main/packages/coding-agent/src/core/agent-session.ts#L102):
11
+ Wire events use `JsonAgentSessionEvent`. It matches
12
+ [`AgentSessionEvent`](https://github.com/earendil-works/pi-mono/blob/main/packages/coding-agent/src/core/agent-session.ts)
13
+ except that streaming message updates omit cumulative snapshots:
12
14
 
13
15
  ```typescript
14
- type AgentSessionEvent =
15
- | AgentEvent
16
- | { type: "queue_update"; steering: readonly string[]; followUp: readonly string[] }
17
- | { type: "compaction_start"; reason: "manual" | "threshold" | "overflow" }
18
- | { type: "compaction_end"; reason: "manual" | "threshold" | "overflow"; result: CompactionResult | undefined; aborted: boolean; willRetry: boolean; errorMessage?: string }
19
- | { type: "auto_retry_start"; attempt: number; maxAttempts: number; delayMs: number; errorMessage: string }
20
- | { type: "auto_retry_end"; success: boolean; attempt: number; finalError?: string }
21
- | { type: "summarization_retry_scheduled"; attempt: number; maxAttempts: number; delayMs: number; errorMessage: string }
22
- | { type: "summarization_retry_attempt_start"; source: "branchSummary" }
23
- | { type: "summarization_retry_attempt_start"; source: "compaction"; reason: "manual" | "threshold" | "overflow" }
24
- | { type: "summarization_retry_finished" };
16
+ type WithoutPartial<T> = T extends { partial: unknown } ? Omit<T, "partial"> : T;
17
+
18
+ type JsonAgentSessionEvent =
19
+ | Exclude<AgentSessionEvent, { type: "message_update" }>
20
+ | {
21
+ type: "message_update";
22
+ assistantMessageEvent: WithoutPartial<AssistantMessageEvent>;
23
+ };
25
24
  ```
26
25
 
27
26
  `queue_update` emits the full pending steering and follow-up queues whenever they change. `compaction_start` and `compaction_end` cover both manual and automatic compaction.
28
27
 
29
- Base events from [`AgentEvent`](https://github.com/earendil-works/pi-mono/blob/main/packages/agent/src/types.ts#L179):
28
+ Other base events come from
29
+ [`AgentEvent`](https://github.com/earendil-works/pi-mono/blob/main/packages/agent/src/types.ts):
30
30
 
31
31
  ```typescript
32
32
  type AgentEvent =
@@ -73,14 +73,19 @@ Followed by events as they occur:
73
73
  {"type":"agent_start"}
74
74
  {"type":"turn_start"}
75
75
  {"type":"message_start","message":{"role":"assistant","content":[],...}}
76
- {"type":"message_update","message":{...},"assistantMessageEvent":{"type":"text_delta","delta":"Hello",...}}
76
+ {"type":"message_update","assistantMessageEvent":{"type":"text_delta","contentIndex":0,"delta":"Hello"}}
77
77
  {"type":"message_end","message":{...}}
78
78
  {"type":"turn_end","message":{...},"toolResults":[]}
79
79
  {"type":"agent_end","messages":[...]}
80
80
  ```
81
81
 
82
+ `message_update` records are delta-only. They omit both the cumulative `message` field and
83
+ `assistantMessageEvent.partial` to keep stream size linear. Use `contentIndex` and `delta`
84
+ to assemble live text, thinking, or tool-call arguments if needed. `message_end` contains
85
+ the final authoritative message.
86
+
82
87
  ## Example
83
88
 
84
89
  ```bash
85
- sling --mode json "List files" 2>/dev/null | jq -c 'select(.type == "message_end")'
90
+ pi --mode json "List files" 2>/dev/null | jq -c 'select(.type == "message_end")'
86
91
  ```
@@ -10,7 +10,7 @@ After editing `keybindings.json`, run `/reload` in sling to apply the changes wi
10
10
 
11
11
  ## Key Format
12
12
 
13
- `modifier+key` where modifiers are `ctrl`, `shift`, `alt` (combinable) and keys are:
13
+ `modifier+key` where modifiers are `ctrl`, `shift`, `alt`, `super` (combinable) and keys are:
14
14
 
15
15
  - **Letters:** `a-z`
16
16
  - **Digits:** `0-9`
@@ -18,7 +18,9 @@ After editing `keybindings.json`, run `/reload` in sling to apply the changes wi
18
18
  - **Function:** `f1`-`f12`
19
19
  - **Symbols:** `` ` ``, `-`, `=`, `[`, `]`, `\`, `;`, `'`, `,`, `.`, `/`, `!`, `@`, `#`, `$`, `%`, `^`, `&`, `*`, `(`, `)`, `_`, `+`, `|`, `~`, `{`, `}`, `:`, `<`, `>`, `?`
20
20
 
21
- Modifier combinations: `ctrl+shift+x`, `alt+ctrl+x`, `ctrl+shift+alt+x`, `ctrl+1`, etc.
21
+ Modifier combinations: `ctrl+shift+x`, `alt+ctrl+x`, `ctrl+shift+alt+x`, `super+k`, `ctrl+super+k`, `ctrl+1`, etc.
22
+
23
+ `super` bindings require a terminal that reports the modifier separately, typically through the Kitty keyboard protocol. They may not work in terminals without that support.
22
24
 
23
25
  ## All Actions
24
26
 
@@ -26,18 +28,22 @@ Modifier combinations: `ctrl+shift+x`, `alt+ctrl+x`, `ctrl+shift+alt+x`, `ctrl+1
26
28
 
27
29
  | Keybinding id | Default | Description |
28
30
  |--------|---------|-------------|
29
- | `tui.editor.cursorUp` | `up` | Move cursor up |
30
- | `tui.editor.cursorDown` | `down` | Move cursor down |
31
+ | `tui.editor.cursorUp` | `up` | Move cursor up, browsing older history at the top |
32
+ | `tui.editor.cursorDown` | `down` | Move cursor down, browsing newer history at the bottom |
33
+ | `tui.editor.historyPrevious` | *(none)* | Select the previous prompt history entry |
34
+ | `tui.editor.historyNext` | *(none)* | Select the next prompt history entry |
31
35
  | `tui.editor.cursorLeft` | `left`, `ctrl+b` | Move cursor left |
32
36
  | `tui.editor.cursorRight` | `right`, `ctrl+f` | Move cursor right |
33
37
  | `tui.editor.cursorWordLeft` | `alt+left`, `ctrl+left`, `alt+b` | Move cursor word left |
34
38
  | `tui.editor.cursorWordRight` | `alt+right`, `ctrl+right`, `alt+f` | Move cursor word right |
35
- | `tui.editor.cursorLineStart` | `home`, `ctrl+a` | Move to line start |
36
- | `tui.editor.cursorLineEnd` | `end`, `ctrl+e` | Move to line end |
39
+ | `tui.editor.cursorLineStart` | `home`, `ctrl+home`, `ctrl+a` | Move to line start |
40
+ | `tui.editor.cursorLineEnd` | `end`, `ctrl+end`, `ctrl+e` | Move to line end |
37
41
  | `tui.editor.jumpForward` | `ctrl+]` | Jump forward to character |
38
42
  | `tui.editor.jumpBackward` | `ctrl+alt+]` | Jump backward to character |
39
- | `tui.editor.pageUp` | `pageUp` | Scroll up by page |
40
- | `tui.editor.pageDown` | `pageDown` | Scroll down by page |
43
+ | `tui.editor.pageUp` | `pageUp`, `ctrl+pageUp` | Scroll up by page |
44
+ | `tui.editor.pageDown` | `pageDown`, `ctrl+pageDown` | Scroll down by page |
45
+
46
+ The dedicated history actions always change history entries, regardless of the cursor position in a multiline prompt. Explicit history bindings take precedence over application actions while the main editor is focused, so binding `tui.editor.historyPrevious` to `ctrl+p` overrides model cycling in that context without changing `Ctrl+P` in selectors.
41
47
 
42
48
  ### TUI Editor Deletion
43
49
 
@@ -78,16 +84,42 @@ Modifier combinations: `ctrl+shift+x`, `alt+ctrl+x`, `ctrl+shift+alt+x`, `ctrl+1
78
84
  | `tui.select.confirm` | `enter` | Confirm selection |
79
85
  | `tui.select.cancel` | `escape`, `ctrl+c` | Cancel selection |
80
86
 
87
+ ### TUI Fullscreen Viewport
88
+
89
+ These actions apply when interactive mode uses `--tui-mode fullscreen` and target the primary transcript scroll region. Two-finger trackpad and mouse-wheel input scroll the region under the pointer, falling back to the transcript over the fixed editor/status/footer dock. Clicking an OSC 8 hyperlink opens it in the default handler. Dragging with the primary mouse button selects text and copies it to the clipboard; holding at the transcript's top or bottom edge auto-scrolls into off-screen content.
90
+
91
+ Fullscreen transcript bindings take precedence over editor bindings. The default unmodified navigation keys therefore control the transcript in fullscreen mode, while their `ctrl` variants continue to control the editor. Outside fullscreen mode, both variants control the editor.
92
+
93
+ | Key | Default mode | Fullscreen mode |
94
+ |-----|--------------|-----------------|
95
+ | `home`, `end` | Editor | Transcript |
96
+ | `ctrl+home`, `ctrl+end` | Editor | Editor |
97
+ | `pageUp`, `pageDown` | Editor | Transcript |
98
+ | `ctrl+pageUp`, `ctrl+pageDown` | Editor | Editor |
99
+
100
+ This routing remains configurable through the ordinary action bindings. For example, `"tui.altScreen.pageUp": "ctrl+pageUp"` makes `pageUp` control the editor and `ctrl+pageUp` control the transcript in fullscreen mode. Bind `tui.altScreen.halfPageUp` and `tui.altScreen.halfPageDown` for smaller transcript steps while keeping the full-page bindings. Setting `"tui.altScreen.pageUp": []` disables that transcript shortcut entirely. User bindings replace the defaults for that action.
101
+
102
+ | Keybinding id | Default | Description |
103
+ |--------|---------|-------------|
104
+ | `tui.altScreen.pageUp` | `pageUp` | Scroll the transcript up by one page |
105
+ | `tui.altScreen.pageDown` | `pageDown` | Scroll the transcript down by one page |
106
+ | `tui.altScreen.halfPageUp` | *(none)* | Scroll the transcript up by half a page |
107
+ | `tui.altScreen.halfPageDown` | *(none)* | Scroll the transcript down by half a page |
108
+ | `tui.altScreen.previousPrompt` | `ctrl+shift+up` | Jump to the previous marked message |
109
+ | `tui.altScreen.nextPrompt` | `ctrl+shift+down` | Jump to the next marked message |
110
+ | `tui.altScreen.top` | `home` | Scroll to the beginning of the transcript |
111
+ | `tui.altScreen.bottom` | `end` | Scroll to the transcript end and follow new output |
112
+
81
113
  ### Application
82
114
 
83
115
  | Keybinding id | Default | Description |
84
116
  |--------|---------|-------------|
85
117
  | `app.interrupt` | `escape` | Cancel / abort |
86
- | `app.clear` | `ctrl+c` | Clear editor |
118
+ | `app.clear` | `ctrl+c` | Clear editor (first) / exit (second) |
87
119
  | `app.exit` | `ctrl+d` | Exit (when editor empty) |
88
120
  | `app.suspend` | `ctrl+z` (none on Windows) | Suspend to background |
89
121
  | `app.editor.external` | `ctrl+g` | Open in external editor (`externalEditor`, `$VISUAL`, `$EDITOR`, Notepad on Windows, or `nano` elsewhere) |
90
- | `app.clipboard.pasteImage` | `ctrl+v` (`alt+v` on Windows) | Paste image from clipboard |
122
+ | `app.clipboard.pasteImage` | `ctrl+v` (`alt+v` on Windows) | Paste image or text from clipboard |
91
123
 
92
124
  ### Sessions
93
125
 
@@ -158,8 +190,8 @@ Create `~/.sling/agent/keybindings.json`:
158
190
 
159
191
  ```json
160
192
  {
161
- "tui.editor.cursorUp": ["up", "ctrl+p"],
162
- "tui.editor.cursorDown": ["down", "ctrl+n"],
193
+ "tui.editor.historyPrevious": "ctrl+p",
194
+ "tui.editor.historyNext": "ctrl+n",
163
195
  "tui.editor.deleteWordBackward": ["ctrl+w", "alt+backspace"]
164
196
  }
165
197
  ```
@@ -172,8 +204,8 @@ On native Windows, `app.suspend` has no default binding because Windows terminal
172
204
 
173
205
  ```json
174
206
  {
175
- "tui.editor.cursorUp": ["up", "ctrl+p"],
176
- "tui.editor.cursorDown": ["down", "ctrl+n"],
207
+ "tui.editor.historyPrevious": "ctrl+p",
208
+ "tui.editor.historyNext": "ctrl+n",
177
209
  "tui.editor.cursorLeft": ["left", "ctrl+b"],
178
210
  "tui.editor.cursorRight": ["right", "ctrl+f"],
179
211
  "tui.editor.cursorWordLeft": ["alt+left", "alt+b"],
@@ -1,6 +1,6 @@
1
1
  # llama.cpp
2
2
 
3
- sling supports the [llama.cpp](https://github.com/ggml-org/llama.cpp) router server. The router discovers multiple GGUF models and loads or unloads them on demand.
3
+ Pi supports the [llama.cpp](https://github.com/ggml-org/llama.cpp) router server. The router discovers multiple GGUF models and loads or unloads them on demand.
4
4
 
5
5
  Use a current llama.cpp build with router support. Follow the [build instructions](https://github.com/ggml-org/llama.cpp/blob/master/docs/build.md) or install a [prebuilt release](https://github.com/ggml-org/llama.cpp/releases) for your platform.
6
6
 
@@ -43,7 +43,7 @@ A single-file model can sit directly in the model directory. Put multimodal and
43
43
 
44
44
  Restart the router after manually adding files. For per-model context sizes and other options, use [llama.cpp model presets](https://github.com/ggml-org/llama.cpp/blob/master/tools/server/README.md#model-presets).
45
45
 
46
- ## Configure sling
46
+ ## Configure Pi
47
47
 
48
48
  Start sling and configure the provider:
49
49
 
@@ -58,7 +58,7 @@ Environment variables can configure the same values without `/login`:
58
58
  ```bash
59
59
  export LLAMA_BASE_URL=http://127.0.0.1:8080
60
60
  export LLAMA_API_KEY=optional-secret
61
- sling
61
+ pi
62
62
  ```
63
63
 
64
64
  If the server uses an API key, start `llama-server` with the matching `--api-key` value. Keep `--host 127.0.0.1` for local-only access.
@@ -40,7 +40,7 @@ If you prefer the terminal, you can also run:
40
40
  pi-mcp-adapter init
41
41
  ```
42
42
 
43
- That scans for compatible configs and writes the missing Pi adapter files.
43
+ That scans for compatible configs and writes the missing sling adapter files.
44
44
 
45
45
  ## Using MCP Servers
46
46
 
@@ -134,7 +134,7 @@ Create or edit `~/.config/mcp/mcp.json` for servers you want available everywher
134
134
  Use Sling-owned files when you need Sling-specific overrides:
135
135
 
136
136
  - `<Sling agent dir>/mcp.json` — global override, usually `~/.sling/agent/mcp.json`
137
- - `.pi/mcp.json` — project override
137
+ - `.sling/mcp.json` — project override
138
138
 
139
139
  These files let Sling store adapter-specific settings without modifying shared MCP configs.
140
140
 
@@ -206,6 +206,7 @@ If your command is slow, expensive, rate-limited, or should keep using a previou
206
206
  | `input` | No | `["text"]` | Input types: `["text"]` or `["text", "image"]` |
207
207
  | `contextWindow` | No | `128000` | Context window size in tokens |
208
208
  | `maxTokens` | No | `16384` | Maximum output tokens |
209
+ | `samplingParams` | No | omitted | Sampling parameters merged verbatim into every request body (see below) |
209
210
  | `cost` | No | all zeros | Per-million-token rates with optional request-wide input pricing tiers |
210
211
  | `compat` | No | provider `compat` | Provider compatibility overrides. Merged with provider-level `compat` when both are set. |
211
212
 
@@ -235,6 +236,24 @@ Current behavior:
235
236
  - `/model`, `--list-models`, and the interactive footer display entries by model `id`.
236
237
  - The configured `name` is used for model matching and secondary model detail text. It does not replace the footer/status-bar model id.
237
238
 
239
+ ### Sampling Parameters
240
+
241
+ `samplingParams` is a free-form object merged verbatim into every request body for the model, after the fields sling sets itself, so its keys win. Use it to send sampling parameters sling does not model — including server-specific ones like llama.cpp's `min_p` or vLLM's `top_k`:
242
+
243
+ ```json
244
+ {
245
+ "id": "deepseek-v4-flash",
246
+ "samplingParams": {
247
+ "temperature": 1.0,
248
+ "top_p": 0.95,
249
+ "top_k": 0,
250
+ "min_p": 0.0
251
+ }
252
+ }
253
+ ```
254
+
255
+ Only OpenAI-compatible APIs apply it (`openai-completions`, `openai-responses`, `azure-openai-responses`); other APIs ignore it. Keys override sling's named request fields (for example a `temperature` key here beats the request-level temperature), so prefer it as the single source of sampling truth for a model. In `modelOverrides`, `samplingParams` merges per key with the base model's value.
256
+
238
257
  ### Thinking Level Map
239
258
 
240
259
  Use `thinkingLevelMap` on a model to describe model-specific thinking controls. Keys are sling thinking levels: `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max`. Maps may contain holes; for example, a model can expose `high` and `max` without exposing `xhigh`.
@@ -338,7 +357,7 @@ Use `modelOverrides` to customize built-in models and matching extension-registe
338
357
  }
339
358
  ```
340
359
 
341
- `modelOverrides` supports these fields per model: `name`, `reasoning`, `thinkingLevelMap`, `input`, `cost` (partial), `contextWindow`, `maxTokens`, `headers`, `compat`.
360
+ `modelOverrides` supports these fields per model: `name`, `reasoning`, `thinkingLevelMap`, `input`, `cost` (partial), `contextWindow`, `maxTokens`, `samplingParams` (merged per key), `headers`, `compat`.
342
361
 
343
362
  Direct OpenAI GPT-5.6 Sol, Terra, and Luna default to a `272000` context window so requests remain within OpenAI's short-context pricing tier. To opt into OpenAI's 1.05M context window, increase it for each model you use:
344
363
 
@@ -441,13 +460,15 @@ For providers with partial OpenAI compatibility, use the `compat` field.
441
460
  | `supportsDeveloperRole` | Use `developer` vs `system` role |
442
461
  | `supportsReasoningEffort` | Support for `reasoning_effort` parameter |
443
462
  | `supportsUsageInStreaming` | Supports `stream_options: { include_usage: true }` (default: `true`) |
463
+ | `supportsFinishReason` | Whether streamed responses include `finish_reason`. When `false`, sling infers `stop` or `toolUse` when the stream ends. Default: `true`. |
444
464
  | `maxTokensField` | Use `max_completion_tokens` or `max_tokens` |
445
465
  | `requiresToolResultName` | Include `name` on tool result messages |
446
466
  | `requiresAssistantAfterToolResult` | Insert an assistant message before a user message after tool results |
447
467
  | `requiresThinkingAsText` | Convert thinking blocks to plain text |
448
468
  | `requiresReasoningContentOnAssistantMessages` | Include empty `reasoning_content` on all replayed assistant messages when reasoning is enabled |
449
- | `thinkingFormat` | Use `reasoning_effort`, `openrouter`, `deepseek`, `together`, `zai`, `qwen`, `chat-template`, or `qwen-chat-template` thinking parameters |
450
- | `chatTemplateKwargs` | `chat_template_kwargs` values for `thinkingFormat: "chat-template"`; use `{ "$var": "thinking.enabled" }` or `{ "$var": "thinking.effort" }` for sling-controlled thinking values |
469
+ | `thinkingFormat` | Use `reasoning_effort`, `openrouter`, `deepseek`, `together`, `baseten`, `zai`, `qwen`, `chat-template`, or `qwen-chat-template` thinking parameters |
470
+ | `chatTemplateKwargs` | `chat_template_kwargs` values for `thinkingFormat: "chat-template"`; use `{ "$var": "thinking.enabled" }` or `{ "$var": "thinking.effort" }` for pi-controlled thinking values |
471
+ | `chatTemplateArgs` | `chat_template_args` values for `thinkingFormat: "baseten"`; use `{ "$var": "thinking.enabled" }` or `{ "$var": "thinking.effort" }` for pi-controlled thinking values |
451
472
  | `cacheControlFormat` | Use Anthropic-style `cache_control` markers on the system prompt, last tool definition, and last user, assistant, or tool-result text content. Currently only `anthropic` is supported. |
452
473
  | `sendSessionAffinityHeaders` | For `openai-completions`, send session-affinity headers from the session id when caching is enabled. Default: `false`. |
453
474
  | `sessionAffinityFormat` | For `openai-completions` and `openai-responses`, the session-affinity header format: `openai` sends `session_id`/`x-client-request-id` (completions also `x-session-affinity`), `openai-nosession` omits the underscore-containing `session_id` header, `openrouter` sends `x-session-id`. Does not affect the `prompt_cache_key` body param. Default: auto-detected. |
@@ -458,7 +479,7 @@ For providers with partial OpenAI compatibility, use the `compat` field.
458
479
  | `openRouterRouting` | OpenRouter provider routing preferences. This object is sent as-is in the `provider` field of the [OpenRouter API request](https://openrouter.ai/docs/guides/routing/provider-selection). |
459
480
  | `vercelGatewayRouting` | Vercel AI Gateway routing config for provider selection (`only`, `order`) |
460
481
 
461
- `openrouter` uses `reasoning: { effort }`. `together` uses `reasoning: { enabled }` and also `reasoning_effort` when `supportsReasoningEffort` is enabled. `qwen` uses top-level `enable_thinking`. Use `qwen-chat-template` for local Qwen-compatible servers that require `chat_template_kwargs.enable_thinking` and `preserve_thinking`. Use `chat-template` for vLLM/Hugging Face chat templates that need configurable `chat_template_kwargs`, such as `chatTemplateKwargs: { "thinking": { "$var": "thinking.enabled" } }` for DeepSeek V3.x templates.
482
+ `openrouter` uses `reasoning: { effort }`. `together` uses `reasoning: { enabled }` and also `reasoning_effort` when `supportsReasoningEffort` is enabled. `qwen` uses top-level `enable_thinking`. Use `qwen-chat-template` for local Qwen-compatible servers that require `chat_template_kwargs.enable_thinking` and `preserve_thinking`. Use `chat-template` for vLLM/Hugging Face chat templates that need configurable `chat_template_kwargs`, such as `chatTemplateKwargs: { "thinking": { "$var": "thinking.enabled" } }` for DeepSeek V3.x templates. Use `thinkingFormat: "baseten"` with `chatTemplateArgs` for providers that expose toggle controls through `chat_template_args` and optionally support top-level `reasoning_effort`.
462
483
 
463
484
  `cacheControlFormat: "anthropic"` is for OpenAI-compatible providers that expose Anthropic-style prompt caching through `cache_control` markers on text content and tool definitions.
464
485