@code-yeongyu/senpi 2026.10.10-2 → 2026.10.10-3
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.
- package/CHANGELOG.md +20 -0
- package/dist/bundle/chunks/{anthropic-messages-CVXI6CLT.js → anthropic-messages-OE3QBX4P.js} +1 -1
- package/dist/bundle/chunks/{app-server-command-WZG7WYMD.js → app-server-command-DVWM37GF.js} +1 -1
- package/dist/bundle/chunks/{chunk-Q2ZTPMWD.js → chunk-4TK7VCZZ.js} +1 -1
- package/dist/bundle/chunks/{chunk-SB22X6ZR.js → chunk-H4UIUFU5.js} +1 -1
- package/dist/bundle/chunks/chunk-I5HLTDT4.js +2 -0
- package/dist/bundle/chunks/{chunk-W6B7SEAK.js → chunk-MT2C4I5R.js} +17 -16
- package/dist/bundle/chunks/{chunk-LSE7KE2C.js → chunk-ODYQK5XJ.js} +1 -1
- package/dist/bundle/chunks/{chunk-ULYOMGXX.js → chunk-PDSG6VER.js} +1 -1
- package/dist/bundle/chunks/{chunk-3YXB7Z7O.js → chunk-Z7MMKKIY.js} +1 -1
- package/dist/bundle/chunks/{cli-main-T4T5UNRJ.js → cli-main-B4S4DGBX.js} +1 -1
- package/dist/bundle/chunks/{help-fast-path-44VUGB7H.js → help-fast-path-EHRZEBRO.js} +1 -1
- package/dist/bundle/chunks/{host-command-X64D7PT3.js → host-command-VATE7HRG.js} +1 -1
- package/dist/bundle/chunks/{interactive-mode-ULFSI46Z.js → interactive-mode-QOFA64JL.js} +1 -1
- package/dist/bundle/chunks/{multi-session-host-VM7WJBC5.js → multi-session-host-E5RDPWQN.js} +1 -1
- package/dist/bundle/chunks/{openai-completions-6HU7GIGD.js → openai-completions-Q2LEWXEC.js} +1 -1
- package/dist/bundle/chunks/{openai-responses-S4QYZXF3.js → openai-responses-DHGWSTZM.js} +1 -1
- package/dist/bundle/chunks/{package-manager-cli-OAFUQY7E.js → package-manager-cli-YGTWXRG5.js} +1 -1
- package/dist/bundle/chunks/{rotation-stream-ZJQ6I2RZ.js → rotation-stream-C5BM7TMM.js} +1 -1
- package/dist/bundle/chunks/{rpc-mode-XVRJJG3K.js → rpc-mode-HDAGQ4X3.js} +1 -1
- package/dist/bundle/chunks/{session-control-endpoint-FEGOYWHN.js → session-control-endpoint-NABTZHCZ.js} +1 -1
- package/dist/bundle/chunks/{session-picker-R5N2EKUS.js → session-picker-O7OESJWU.js} +1 -1
- package/dist/bundle/chunks/session-worker.js +17 -16
- package/dist/bundle/cli.js +1 -1
- package/dist/bundle/index.js +1 -1
- package/dist/bundle/rpc-entry.js +1 -1
- package/dist/bundle/runtime-manifest.json +1 -1
- package/dist/core/agent-session.d.ts +2 -0
- package/dist/core/agent-session.js +18 -4
- package/dist/core/extensions/builtin/goal/lifecycle-helpers.js +1 -1
- package/dist/core/extensions/builtin/goal/monitor-continuation.js +3 -1
- package/dist/core/extensions/builtin/goal/prompt.d.ts +6 -1
- package/dist/core/extensions/builtin/goal/prompt.js +32 -1
- package/dist/core/extensions/builtin/websearch/websearch/config.js +2 -0
- package/dist/core/extensions/builtin/websearch/websearch/provider-endpoints.js +1 -0
- package/dist/core/extensions/builtin/websearch/websearch/providers/keenable.d.ts +3 -0
- package/dist/core/extensions/builtin/websearch/websearch/providers/keenable.js +55 -0
- package/dist/core/extensions/builtin/websearch/websearch/providers.js +2 -0
- package/dist/core/extensions/builtin/websearch/websearch/types.d.ts +1 -1
- package/dist/core/model-config-schema.d.ts +5 -0
- package/dist/core/model-config-schema.js +1 -0
- package/dist/core/provider-composer.js +5 -1
- package/dist/core/provider-timeout-retry.d.ts +7 -1
- package/dist/core/provider-timeout-retry.js +23 -12
- package/dist/core/reload-veto.d.ts +1 -1
- package/dist/core/reload-veto.js +7 -1
- package/docs/models.md +34 -0
- package/docs/web-search.md +3 -1
- package/package.json +6 -6
- package/dist/bundle/chunks/chunk-YSPHACIN.js +0 -2
package/docs/models.md
CHANGED
|
@@ -142,6 +142,10 @@ Set `api` at provider level (default for all models) or model level (override pe
|
|
|
142
142
|
| `authHeader` | Set `true` to add `Authorization: Bearer <apiKey>` automatically |
|
|
143
143
|
| `models` | Array of model configurations |
|
|
144
144
|
| `modelOverrides` | Per-model overrides for built-in or extension-registered models on this provider |
|
|
145
|
+
| `whitelist` | Model ids to keep; every other model of the provider is removed (see [Hiding Providers and Models](#hiding-providers-and-models)) |
|
|
146
|
+
| `blacklist` | Model ids to remove from the provider |
|
|
147
|
+
| `hideFreeModels` | Set `true` to remove every model of the provider whose input and output cost are both `0` |
|
|
148
|
+
| `disabled` | Set `true` to remove the whole provider |
|
|
145
149
|
|
|
146
150
|
For providers with `models`, non-built-in provider configs need `baseUrl` and an `api` value at either provider or model level. `apiKey` is not required to load the file: models become available when auth is configured through `/login`/`auth.json`, CLI `--api-key`, or provider `apiKey`. If no auth is configured, the models load but stay unavailable in `/model` and `--list-models`.
|
|
147
151
|
|
|
@@ -510,6 +514,36 @@ Merge semantics:
|
|
|
510
514
|
- If a custom model `id` matches a built-in model `id`, the custom model replaces that built-in model.
|
|
511
515
|
- If a custom model `id` is new, it is added alongside built-in models.
|
|
512
516
|
|
|
517
|
+
## Hiding Providers and Models
|
|
518
|
+
|
|
519
|
+
Hidden models and providers disappear from the catalog everywhere it is read: `--list-models`, `/model`, Ctrl+P cycling, startup selection, and `enabledModels` / `favoriteModels` resolution.
|
|
520
|
+
|
|
521
|
+
```json
|
|
522
|
+
{
|
|
523
|
+
"disabledProviders": ["openrouter"],
|
|
524
|
+
"providers": {
|
|
525
|
+
"anthropic": { "whitelist": ["claude-sonnet-4-5"] },
|
|
526
|
+
"openai": { "blacklist": ["gpt-5.4"] },
|
|
527
|
+
"opencode": { "hideFreeModels": true }
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
| Key | Where | Effect |
|
|
533
|
+
|-----|-------|--------|
|
|
534
|
+
| `disabledProviders` | Top level, array of provider ids | Removes each listed provider and all of its models. `providers.<id>.disabled: true` does the same for one provider. |
|
|
535
|
+
| `whitelist` | Provider block, array of model ids | Keeps only the listed ids. |
|
|
536
|
+
| `blacklist` | Provider block, array of model ids | Removes the listed ids. |
|
|
537
|
+
| `hideFreeModels` | Provider block, boolean | Removes every model of that provider whose `cost.input` and `cost.output` are both `0`. |
|
|
538
|
+
|
|
539
|
+
Behavior notes:
|
|
540
|
+
- `whitelist` and `blacklist` match whole model ids exactly and case-sensitively; there are no globs. When both are set, a model must be in `whitelist` and not in `blacklist`.
|
|
541
|
+
- All three provider filters run after custom `models` are merged in, and before `modelOverrides`. They also apply to the provider's custom `models` entries.
|
|
542
|
+
- `hideFreeModels` is per provider. Other providers are unaffected, so a local provider whose models cost `0` (Ollama, LM Studio, vLLM) stays listed unless that provider sets the flag itself.
|
|
543
|
+
- `hideFreeModels` also hides a custom `models` entry that omits `cost`, because an omitted `cost` is `0`. Give such a model a nonzero `cost`, or leave the flag off for that provider.
|
|
544
|
+
- `hideFreeModels` reads the catalog `cost` (or the custom model's own `cost`). A `cost` set in `modelOverrides` is not considered.
|
|
545
|
+
- A provider block that sets only `whitelist`, `blacklist`, or `hideFreeModels` is valid; no `baseUrl` or `models` is needed.
|
|
546
|
+
|
|
513
547
|
## Per-model Overrides
|
|
514
548
|
|
|
515
549
|
Use `modelOverrides` to customize built-in models and matching extension-registered models without replacing the provider's full model list.
|
package/docs/web-search.md
CHANGED
|
@@ -75,7 +75,9 @@ A config file replaces the free default completely: senpi uses only the provider
|
|
|
75
75
|
| `nativeModel` | The model native search runs on; see [Choosing the model native search runs on](#choosing-the-model-native-search-runs-on). |
|
|
76
76
|
| `providers[]` | The providers to use. Each entry takes `provider`, and optionally `id`, `apiKey`, `baseUrl`, `maxResults`, `timeoutMs`, `priority`, `weight`, `allowedDomains` or `blockedDomains`. |
|
|
77
77
|
|
|
78
|
-
Free engines you can list without a key: `duckduckgo-html`, `exa-mcp`, `startpage`, `mojeek`, `ecosia`, `google-html`, and `
|
|
78
|
+
Free engines you can list without a key: `duckduckgo-html`, `exa-mcp`, `startpage`, `mojeek`, `ecosia`, `google-html`, `searxng`, and `keenable`. Providers that need `apiKey`: `exa`, `tavily`, `brave`, `serper`, `serpdive`, `kagi`, `perplexity`, `z-ai`, `xai`, `kimi`, `deepseek`, `anthropic`, `openai` (plus `searchEngineId` for `google-cse`). `chatgpt-subscription` and `google` use your senpi login when `apiKey` is omitted; see [Listing login-based routes](#listing-login-based-routes).
|
|
79
|
+
|
|
80
|
+
`keenable` ([Keenable](https://keenable.ai)) takes an `apiKey` but also works without one: a keyed entry posts to `https://api.keenable.ai/v1/search` with `X-API-Key`, while a keyless entry uses the shared public tier at `/v1/search/public` and identifies itself with an `X-Keenable-Title` app header instead of a credential. Because the public tier is a per-IP pool, `keenable` is never in the no-config default chain — list it explicitly in `websearch.json`. A single `allowedDomains` entry maps to Keenable's native `site` filter; multiple or blocked domains stay `site:`/`-site:` terms in the query.
|
|
79
81
|
|
|
80
82
|
## Limit or turn off the free engines
|
|
81
83
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@code-yeongyu/senpi",
|
|
3
|
-
"version": "2026.10.10-
|
|
3
|
+
"version": "2026.10.10-3",
|
|
4
4
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"piConfig": {
|
|
@@ -57,9 +57,9 @@
|
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
59
|
"@earendil-works/chord": "0.99.1",
|
|
60
|
-
"@earendil-works/pi-agent-core": "npm:@code-yeongyu/senpi-agent-core@2026.10.10-
|
|
61
|
-
"@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.10.10-
|
|
62
|
-
"@earendil-works/pi-tui": "npm:@code-yeongyu/senpi-tui@2026.10.10-
|
|
60
|
+
"@earendil-works/pi-agent-core": "npm:@code-yeongyu/senpi-agent-core@2026.10.10-3",
|
|
61
|
+
"@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.10.10-3",
|
|
62
|
+
"@earendil-works/pi-tui": "npm:@code-yeongyu/senpi-tui@2026.10.10-3",
|
|
63
63
|
"@silvia-odwyer/photon-node": "0.3.4",
|
|
64
64
|
"chalk": "6.0.0",
|
|
65
65
|
"cross-spawn": "7.0.6",
|
|
@@ -78,8 +78,8 @@
|
|
|
78
78
|
"yaml": "2.9.1",
|
|
79
79
|
"@anthropic-ai/claude-agent-sdk": "0.3.289",
|
|
80
80
|
"@anthropic-ai/sdk": "0.127.0",
|
|
81
|
-
"@code-yeongyu/senpi-codemode": "2026.10.10-
|
|
82
|
-
"@earendil-works/pi-pty": "npm:@code-yeongyu/senpi-pty@2026.10.10-
|
|
81
|
+
"@code-yeongyu/senpi-codemode": "2026.10.10-3",
|
|
82
|
+
"@earendil-works/pi-pty": "npm:@code-yeongyu/senpi-pty@2026.10.10-3",
|
|
83
83
|
"@modelcontextprotocol/sdk": "1.30.0",
|
|
84
84
|
"@mozilla/readability": "0.6.0",
|
|
85
85
|
"linkedom": "0.18.13",
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import { createRequire as __piCreateRequire } from "node:module"; const require = __piCreateRequire(import.meta.url); { const __piWorkerThreads = require("node:worker_threads"); if (typeof __piWorkerThreads.markAsUncloneable !== "function") { __piWorkerThreads.markAsUncloneable = () => {}; } }
|
|
2
|
-
var forcedToolChoiceRefusals=new Set;function isRecord(value){return typeof value=="object"&&value!==null}function extractHttpStatus(error){if(!isRecord(error))return;let status=error.status;if(typeof status=="number")return status;let response=error.response;if(isRecord(response)&&typeof response.status=="number")return response.status}function errorMessage(error){return error instanceof Error?error.message:typeof error=="string"?error:String(error)}function isForcedToolChoiceUnsupportedError(error,sentForcedToolChoice){if(!sentForcedToolChoice||extractHttpStatus(error)!==400)return!1;let message=errorMessage(error);return/tool[_\s-]?choices?\b.*?(not\s+(?:currently\s+)?compatible|incompatible|not\s+(?:currently\s+)?supported|unsupported)/is.test(message)||/forces?\s+tool\s+use.*?(not\s+(?:currently\s+)?compatible|incompatible|not\s+(?:currently\s+)?supported|unsupported)/is.test(message)||/does\s+not\s+support\s+forced\s+tool[_\s-]?choices?/is.test(message)||/\bonly\s+[`"]*auto(?:matic)?[`"]*\s.{0,40}?tool[_\s-]?choice/is.test(message)||/thinking\s+may\s+not\s+be\s+enabled\s+when\s+tool[_\s-]?choice\s+forces\s+tool\s+use/is.test(message)||/tool[_\s-]?choice\s+cannot\s+force\s+tool\s+use/is.test(message)}function forcedToolName(toolChoice){if(!isRecord(toolChoice))return;let named=isRecord(toolChoice.function)?toolChoice.function.name:toolChoice.name;return typeof named=="string"&&named.length>0?named:void 0}function toolNames(tools){return Array.isArray(tools)?tools.map(tool=>{if(!isRecord(tool))return;let named=isRecord(tool.function)?tool.function.name:tool.name;return typeof named=="string"?named:void 0}):[]}function escapeRegExp(text){return text.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function refusalNamesForcedTool(error,params){if(extractHttpStatus(error)!==400)return!1;let name=forcedToolName(params.tool_choice);if(name===void 0)return!1;let message=errorMessage(error),index=toolNames(params.tools).indexOf(name);return index>=0&&new RegExp(`\\btools(?:\\.${index}|\\[${index}\\])(?!\\d)`).test(message)?!0:new RegExp(`['"\`]${escapeRegExp(name)}['"\`]`).test(message)}function omitToolChoiceParam(params){let nextParams={...params};return delete nextParams.tool_choice,nextParams}function refusalKey(target){return JSON.stringify([target.api,target.provider,target.baseUrl,target.id])}function hasRefusedForcedToolChoice(target){return forcedToolChoiceRefusals.has(refusalKey(target))}async function sendWithForcedToolChoiceFallback(request){let forced=request.isForced(request.params.tool_choice);if(forced&&(!request.acceptsForcedToolChoice||hasRefusedForcedToolChoice(request.target))){let params=omitToolChoiceParam(request.params);return{params,result:await request.send(params)}}try{return{params:request.params,result:await request.send(request.params)}}catch(error){if(!isForcedToolChoiceUnsupportedError(error,forced)&&!(forced&&refusalNamesForcedTool(error,request.params)))throw error;let params=omitToolChoiceParam(request.params),result=await request.send(params);return/thinking/i.test(errorMessage(error))||forcedToolChoiceRefusals.add(refusalKey(request.target)),{params,result}}}export{hasRefusedForcedToolChoice,sendWithForcedToolChoiceFallback};
|