mindweave 2.4.8 → 2.5.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.
- package/README.md +4 -2
- package/dist/cli/App.js +101 -27
- package/dist/cli/App.js.map +1 -1
- package/dist/cli/commandArgs.js +66 -16
- package/dist/cli/commandArgs.js.map +1 -1
- package/dist/cli/commands.js +1 -0
- package/dist/cli/commands.js.map +1 -1
- package/dist/cli/components/ApprovalBox.js +10 -1
- package/dist/cli/components/ApprovalBox.js.map +1 -1
- package/dist/cli/components/McpMinitabs.js +1 -1
- package/dist/cli/components/Picker.js +61 -19
- package/dist/cli/components/Picker.js.map +1 -1
- package/dist/cli/components/ToolLine.js +1 -1
- package/dist/cli/components/ToolLine.js.map +1 -1
- package/dist/cli/feedback.js +223 -0
- package/dist/cli/feedback.js.map +1 -0
- package/dist/cli/framebuffer/writer.js +1 -1
- package/dist/cli/toolDisplay.js +4 -3
- package/dist/cli/toolDisplay.js.map +1 -1
- package/dist/drivers/clientId.js +2 -3
- package/dist/drivers/clientId.js.map +1 -1
- package/dist/drivers/openaiCompat/wire.js +55 -3
- package/dist/drivers/openaiCompat/wire.js.map +1 -1
- package/dist/drivers/openrouter/catalog.js +136 -0
- package/dist/drivers/openrouter/catalog.js.map +1 -0
- package/dist/drivers/openrouter/client.js +102 -0
- package/dist/drivers/openrouter/client.js.map +1 -0
- package/dist/drivers/openrouter/endpoint.js +20 -0
- package/dist/drivers/openrouter/endpoint.js.map +1 -0
- package/dist/drivers/openrouter/index.js +5 -0
- package/dist/drivers/openrouter/index.js.map +1 -0
- package/dist/drivers/openrouter/manifest.js +83 -0
- package/dist/drivers/openrouter/manifest.js.map +1 -0
- package/dist/drivers/providerError.js +51 -0
- package/dist/drivers/providerError.js.map +1 -1
- package/dist/drivers/registry.js +111 -3
- package/dist/drivers/registry.js.map +1 -1
- package/dist/dynamo/engine.js +152 -99
- package/dist/dynamo/engine.js.map +1 -1
- package/dist/dynamo/model.js +63 -4
- package/dist/dynamo/model.js.map +1 -1
- package/dist/governor/skills.js +1 -1
- package/dist/governor/write.js +61 -0
- package/dist/governor/write.js.map +1 -1
- package/dist/mcp/configWrite.js +1 -1
- package/dist/memory/compaction.js +9 -1
- package/dist/memory/compaction.js.map +1 -1
- package/dist/memory/store.js +8 -1
- package/dist/memory/store.js.map +1 -1
- package/dist/tools/backgroundShells.js +4 -3
- package/dist/tools/backgroundShells.js.map +1 -1
- package/dist/tools/deferredNative.js +13 -4
- package/dist/tools/deferredNative.js.map +1 -1
- package/dist/tools/governorTools.js +113 -9
- package/dist/tools/governorTools.js.map +1 -1
- package/dist/tools/mcpAdd.js +87 -4
- package/dist/tools/mcpAdd.js.map +1 -1
- package/dist/tools/mcpSearch.js +5 -4
- package/dist/tools/mcpSearch.js.map +1 -1
- package/dist/tools/mindweaveStatus.js +113 -0
- package/dist/tools/mindweaveStatus.js.map +1 -0
- package/dist/tools/nativeStderr.js +27 -0
- package/dist/tools/nativeStderr.js.map +1 -0
- package/dist/tools/registry.js +6 -4
- package/dist/tools/registry.js.map +1 -1
- package/dist/tools/runCommand.js +53 -18
- package/dist/tools/runCommand.js.map +1 -1
- package/dist/tools/screenshot.js +17 -4
- package/dist/tools/screenshot.js.map +1 -1
- package/dist/tools/todo.js +5 -5
- package/dist/tools/todo.js.map +1 -1
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -70,8 +70,9 @@ else is configured inside a session.
|
|
|
70
70
|
|
|
71
71
|
Short version, one line each. The depth is in the linked pages.
|
|
72
72
|
|
|
73
|
-
- **
|
|
74
|
-
Mistral, Groq, Cerebras, Qwen, Kimi, GLM, Meta, MiniMax, Tencent
|
|
73
|
+
- **15 providers, 53 models, one key** — DeepSeek, Anthropic, OpenAI, Gemini, xAI,
|
|
74
|
+
Mistral, Groq, Cerebras, Qwen, Kimi, GLM, Meta, MiniMax, Tencent, plus every
|
|
75
|
+
tool-capable model on OpenRouter. Only the driver you
|
|
75
76
|
use is loaded. Switch with `/provider` and `/model`; remembered per project.
|
|
76
77
|
[PROVIDERS.md](src/drivers/PROVIDERS.md)
|
|
77
78
|
- **Real tools** — read and edit files, multi-file edits, ripgrep search, a shell with
|
|
@@ -105,6 +106,7 @@ Short version, one line each. The depth is in the linked pages.
|
|
|
105
106
|
| `/undo` | Reverts what the last turn changed |
|
|
106
107
|
| `/update` | Installs the newest version and reopens on this conversation |
|
|
107
108
|
| `/mcp` | Manages connected MCP servers |
|
|
109
|
+
| `/feedback` | Sends the maintainer a message, no account needed |
|
|
108
110
|
| `shift-tab` | Cycles interaction modes |
|
|
109
111
|
|
|
110
112
|
Type while it works and your message queues; press up to take it back and edit it.
|
package/dist/cli/App.js
CHANGED
|
@@ -35,7 +35,8 @@ import { appendForbidden, appendForbiddenCommand } from "../governor/write.js";
|
|
|
35
35
|
import { addRoot, removeRoot } from "../tools/workspace.js";
|
|
36
36
|
import { discoverRelatedRoots } from "../tools/workspaceDiscover.js";
|
|
37
37
|
import { rootLabel, rootsOf, relativize } from "../tools/paths.js";
|
|
38
|
-
import { APPROVAL_DISMISSED, APPROVAL_TEXT } from "../tools/approval.js";
|
|
38
|
+
import { APPROVAL_DISMISSED, APPROVAL_TEXT, readFreeText } from "../tools/approval.js";
|
|
39
|
+
import { buildFeedback, previewOf, refuseReason, sendFeedback, withAddition } from "./feedback.js";
|
|
39
40
|
import { KeySetup } from "./components/KeySetup.js";
|
|
40
41
|
import { setupView } from "./keySetup.js";
|
|
41
42
|
import { KeyManager } from "./components/KeyManager.js";
|
|
@@ -46,10 +47,10 @@ import { TrustGate } from "./components/TrustGate.js";
|
|
|
46
47
|
import { rootBreadth, breadthWarning, trustPersists, isTrusted, rememberTrust } from "./trust.js";
|
|
47
48
|
import { projectDir } from "../memory/store.js";
|
|
48
49
|
import { parseUndoArg, undoNotice } from "../tools/checkpoints.js";
|
|
49
|
-
import { DEFAULT_MODEL_CONFIG, thinkLevels, thinkLabel, modelLabel, modelsOfProvider, providerOf, usableFallback, needsKeySetup, withModel, saveModelConfig, refreshModels } from "../dynamo/model.js";
|
|
50
|
+
import { DEFAULT_MODEL_CONFIG, thinkLevels, thinkLabel, modelLabel, modelsOfProvider, providerOf, usableFallback, needsKeySetup, withModel, saveModelConfig, refreshModels, DISCOVERY_TTL_MS } from "../dynamo/model.js";
|
|
50
51
|
import { allProviders, manifestForModel, modelsOf } from "../drivers/registry.js";
|
|
51
52
|
import { orderProviders, orderModels } from "./pickerOrder.js";
|
|
52
|
-
import { accessRefusal } from "../drivers/providerError.js";
|
|
53
|
+
import { accessRefusal, providerOutage } from "../drivers/providerError.js";
|
|
53
54
|
import { resolveAttachments, stripAttachments } from "./attachments.js";
|
|
54
55
|
import { collapsePastes, wrapPastedText } from "../memory/pastedText.js";
|
|
55
56
|
import { createDropHandles, expandHandles } from "./dropHandles.js";
|
|
@@ -89,7 +90,7 @@ import { perf, perfEnabled } from "./perfLog.js";
|
|
|
89
90
|
import { isGroupMember, groupSettled, planGroupReveal, planStandaloneReveal, resultQueued, STANDALONE_HOLD_MS } from "./groupReveal.js";
|
|
90
91
|
import { drain as drainQueue, popAll as popAllQueued, queueMessage, takeSteerable, visibleQueue } from "./messageQueue.js";
|
|
91
92
|
import { routeCommand, parseCommandLine, unknownCommandMessage } from "./commandRoute.js";
|
|
92
|
-
import { resolveChoice } from "./commandArgs.js";
|
|
93
|
+
import { resolveChoice, splitModelArg } from "./commandArgs.js";
|
|
93
94
|
import { carryAcrossFreshSession } from "./sessionCarry.js";
|
|
94
95
|
import { toolDisplay, isGroupable, KIND_COLOR } from "./toolDisplay.js";
|
|
95
96
|
import { workingVerb } from "./workingVerb.js";
|
|
@@ -141,6 +142,11 @@ function orderedProviderList() {
|
|
|
141
142
|
function orderedModelList(model) {
|
|
142
143
|
return orderModels(modelsOfProvider(model));
|
|
143
144
|
}
|
|
145
|
+
/** The same order, for a provider named by id rather than by one of its models. */
|
|
146
|
+
function orderedModelsOf(providerId) {
|
|
147
|
+
const provider = allProviders().find((p) => p.id === providerId);
|
|
148
|
+
return provider ? orderModels(modelsOf(provider)) : [];
|
|
149
|
+
}
|
|
144
150
|
// After you pick a session in /continue, the three ways to resume it.
|
|
145
151
|
const RESUME_MODES = [
|
|
146
152
|
{ label: "Compact & continue", description: "summarize the old chat first so it won't eat your context, then pick up where you left off" },
|
|
@@ -1427,7 +1433,7 @@ export function App({ resumeSessionId, initialScreen }) {
|
|
|
1427
1433
|
/**
|
|
1428
1434
|
* Write a server's config and connect it live — shared by the typed `/mcp add`, the
|
|
1429
1435
|
* `/mcp` box's Add and Edit, and (indirectly, through the same writer) the
|
|
1430
|
-
* `
|
|
1436
|
+
* `mcp_server` tool. One path so a config any of them accepts is one the others
|
|
1431
1437
|
* would too. Connects immediately: writing the file and telling the user to restart
|
|
1432
1438
|
* would defeat the entire point of a guided add.
|
|
1433
1439
|
*/
|
|
@@ -1476,7 +1482,7 @@ export function App({ resumeSessionId, initialScreen }) {
|
|
|
1476
1482
|
/**
|
|
1477
1483
|
* `/mcp add <name> <command|url> [args…]` and `/mcp remove <name>`.
|
|
1478
1484
|
*
|
|
1479
|
-
* Shares its parser and writer with the `
|
|
1485
|
+
* Shares its parser and writer with the `mcp_server` tool, so a config the command
|
|
1480
1486
|
* accepts is exactly one the tool would, and vice versa.
|
|
1481
1487
|
*/
|
|
1482
1488
|
async function mcpConfigCommand(arg) {
|
|
@@ -2013,7 +2019,10 @@ export function App({ resumeSessionId, initialScreen }) {
|
|
|
2013
2019
|
// the provider's own sentence quoted inside it. Anything else — a malformed
|
|
2014
2020
|
// request, a bug of ours — stays loud, which is the point of classifying
|
|
2015
2021
|
// narrowly. See drivers/providerError.ts.
|
|
2016
|
-
|
|
2022
|
+
// A provider falling over after the retries ran out gets the same calm treatment:
|
|
2023
|
+
// it is not our crash, and the useful news is "wait, or switch model".
|
|
2024
|
+
const refusal = accessRefusal(error, providerOf(s.modelConfig.model).label, otherProviderHasKey(s.modelConfig.model)) ??
|
|
2025
|
+
providerOutage(error, providerOf(s.modelConfig.model).label, modelLabel(s.modelConfig.model));
|
|
2017
2026
|
if (refusal)
|
|
2018
2027
|
enqueueReveal({ type: "notice", title: refusal.title, body: refusal.body });
|
|
2019
2028
|
else
|
|
@@ -2148,15 +2157,28 @@ export function App({ resumeSessionId, initialScreen }) {
|
|
|
2148
2157
|
}
|
|
2149
2158
|
// Apply a /model pick: switch model (clamping reasoning to a valid level), persist
|
|
2150
2159
|
// the choice for this project, and confirm.
|
|
2151
|
-
async function applyModel(index) {
|
|
2160
|
+
async function applyModel(index, providerId) {
|
|
2152
2161
|
const s = session.current;
|
|
2153
|
-
// Index into the SAME list the picker rendered —
|
|
2154
|
-
//
|
|
2155
|
-
//
|
|
2156
|
-
//
|
|
2157
|
-
const
|
|
2162
|
+
// Index into the SAME list the picker rendered — that provider's models in display
|
|
2163
|
+
// order, not every model everywhere. Indexing a differently-ordered list here would
|
|
2164
|
+
// silently select a different model than the one on screen, and would type-check
|
|
2165
|
+
// perfectly.
|
|
2166
|
+
const list = !s ? [] : providerId ? orderedModelsOf(providerId) : orderedModelList(s.modelConfig.model);
|
|
2167
|
+
const choice = list[index];
|
|
2158
2168
|
if (!s || !choice)
|
|
2159
2169
|
return;
|
|
2170
|
+
// A model on another provider is a provider switch, and gets the same key check.
|
|
2171
|
+
const target = manifestForModel(choice.id);
|
|
2172
|
+
if (target.id !== providerOf(s.modelConfig.model).id) {
|
|
2173
|
+
if (missingKeyFor(choice.id)) {
|
|
2174
|
+
pendingSwitch.current = { model: choice.id, apiKeyEnv: target.apiKeyEnv, label: target.label };
|
|
2175
|
+
note(`${target.label} has no key yet — add one and the switch will finish.`);
|
|
2176
|
+
setKeysOpen(true);
|
|
2177
|
+
return;
|
|
2178
|
+
}
|
|
2179
|
+
await switchTo(choice.id, target.label);
|
|
2180
|
+
return;
|
|
2181
|
+
}
|
|
2160
2182
|
s.modelConfig = withModel(s.modelConfig, choice.id);
|
|
2161
2183
|
await saveModelConfig(s.cwd, s.modelConfig);
|
|
2162
2184
|
note(`model → ${modelLabel(s.modelConfig.model)} · ${thinkLabel(s.modelConfig)}`);
|
|
@@ -2202,6 +2224,10 @@ export function App({ resumeSessionId, initialScreen }) {
|
|
|
2202
2224
|
s.modelConfig = withModel(s.modelConfig, model);
|
|
2203
2225
|
await saveModelConfig(s.cwd, s.modelConfig);
|
|
2204
2226
|
note(`provider → ${providerLabel} · model → ${modelLabel(s.modelConfig.model)} · ${thinkLabel(s.modelConfig)}`);
|
|
2227
|
+
// Where prompts go is worth a sentence at the moment someone moves their work there.
|
|
2228
|
+
const notice = manifestForModel(model).notice;
|
|
2229
|
+
if (notice)
|
|
2230
|
+
note(notice);
|
|
2205
2231
|
}
|
|
2206
2232
|
// Apply a /think pick for the current model: set thinking + effort, persist, confirm.
|
|
2207
2233
|
async function applyThink(index) {
|
|
@@ -2256,7 +2282,7 @@ export function App({ resumeSessionId, initialScreen }) {
|
|
|
2256
2282
|
else if (o.kind === "provider")
|
|
2257
2283
|
void applyProvider(index);
|
|
2258
2284
|
else if (o.kind === "model")
|
|
2259
|
-
void applyModel(index);
|
|
2285
|
+
void applyModel(index, o.providerId);
|
|
2260
2286
|
else if (o.kind === "think")
|
|
2261
2287
|
void applyThink(index);
|
|
2262
2288
|
else if (o.kind === "screen") {
|
|
@@ -2295,7 +2321,7 @@ export function App({ resumeSessionId, initialScreen }) {
|
|
|
2295
2321
|
}
|
|
2296
2322
|
// Hand a free-text directive to the model with an instruction wrapper, as its own
|
|
2297
2323
|
// turn — used by the manual /rules and /skills commands (the model does the
|
|
2298
|
-
// "rewrite it well and save it" work via remember_rule /
|
|
2324
|
+
// "rewrite it well and save it" work via remember_rule / the skill tool).
|
|
2299
2325
|
async function runDirective(instruction, activity) {
|
|
2300
2326
|
const s = session.current;
|
|
2301
2327
|
if (!s)
|
|
@@ -2397,6 +2423,41 @@ export function App({ resumeSessionId, initialScreen }) {
|
|
|
2397
2423
|
}
|
|
2398
2424
|
// /analytics — the anonymous usage ping. Bare opens the on/off switch, the same
|
|
2399
2425
|
// fixed box every other setting uses; an argument acts directly, same as /model.
|
|
2426
|
+
// /feedback — a message to the maintainer, from inside the app. No account, no issue
|
|
2427
|
+
// tracker, no mail client: the whole point is that it costs the sender nothing. What
|
|
2428
|
+
// leaves the machine is decided in cli/feedback.ts and shown here before it goes.
|
|
2429
|
+
if (name === "/feedback") {
|
|
2430
|
+
const typed = arg.trim();
|
|
2431
|
+
const message = typed || (await askApproval.current("What would you like to tell the maintainer?", ["Cancel"], undefined, undefined, { label: "Your message", placeholder: "what is missing, broken, or worth adding" }).then((answer) => readFreeText(answer) ?? ""));
|
|
2432
|
+
if (!message.trim())
|
|
2433
|
+
return;
|
|
2434
|
+
// The confirm LOOPS, because the moment you read your own message back is the
|
|
2435
|
+
// moment you remember the thing you left out — usually an email address to reply
|
|
2436
|
+
// to. Typing in the box's own text row adds a line and shows the message again,
|
|
2437
|
+
// rather than cancelling and retyping the whole thing.
|
|
2438
|
+
let text = message;
|
|
2439
|
+
for (;;) {
|
|
2440
|
+
const refusal = refuseReason(text);
|
|
2441
|
+
if (refusal)
|
|
2442
|
+
return say(refusal);
|
|
2443
|
+
const feedback = buildFeedback(text, appVersion());
|
|
2444
|
+
const choice = await askApproval.current(previewOf(feedback), ["Send it", "Don't send"], undefined, undefined, {
|
|
2445
|
+
label: "add a line",
|
|
2446
|
+
placeholder: "your email for a reply, or more detail",
|
|
2447
|
+
});
|
|
2448
|
+
const added = readFreeText(choice);
|
|
2449
|
+
if (added !== null) {
|
|
2450
|
+
text = withAddition(text, added);
|
|
2451
|
+
continue;
|
|
2452
|
+
}
|
|
2453
|
+
if (choice !== "Send it")
|
|
2454
|
+
return note("Not sent.");
|
|
2455
|
+
note("sending…");
|
|
2456
|
+
const result = await sendFeedback(feedback);
|
|
2457
|
+
note(result.message, result.ok ? undefined : { error: true });
|
|
2458
|
+
return;
|
|
2459
|
+
}
|
|
2460
|
+
}
|
|
2400
2461
|
if (name === "/analytics") {
|
|
2401
2462
|
const verb = arg.trim().toLowerCase();
|
|
2402
2463
|
if (verb === "on" || verb === "off") {
|
|
@@ -2620,12 +2681,12 @@ export function App({ resumeSessionId, initialScreen }) {
|
|
|
2620
2681
|
setOverlay({ kind: "sessions", items: sessions });
|
|
2621
2682
|
return;
|
|
2622
2683
|
}
|
|
2623
|
-
// Both pickers refresh discovered providers
|
|
2624
|
-
//
|
|
2684
|
+
// Both pickers refresh discovered providers whose list has gone stale, so a newly
|
|
2685
|
+
// served model appears without a restart. Awaited rather than fired off: the
|
|
2625
2686
|
// picker renders from the list, and opening on a stale one that then changes
|
|
2626
2687
|
// under the cursor is the exact "two-stage reveal" the UI work removed.
|
|
2627
2688
|
if (name === "/provider") {
|
|
2628
|
-
await refreshModels();
|
|
2689
|
+
await refreshModels({ maxAgeMs: DISCOVERY_TTL_MS });
|
|
2629
2690
|
setOverlay({ kind: "provider" });
|
|
2630
2691
|
return;
|
|
2631
2692
|
}
|
|
@@ -2641,12 +2702,21 @@ export function App({ resumeSessionId, initialScreen }) {
|
|
|
2641
2702
|
// getting the picker anyway — which is what happened before, because the argument
|
|
2642
2703
|
// was dropped without a word — reads as the app not having heard you.
|
|
2643
2704
|
if (name === "/model") {
|
|
2644
|
-
await refreshModels();
|
|
2705
|
+
await refreshModels({ maxAgeMs: DISCOVERY_TTL_MS });
|
|
2645
2706
|
if (arg) {
|
|
2646
|
-
const
|
|
2707
|
+
const current = providerOf(s.modelConfig.model).id;
|
|
2708
|
+
const here = resolveChoice(arg, orderedModelList(s.modelConfig.model), "model");
|
|
2709
|
+
const { providerId, words } = splitModelArg(arg, allProviders(), current, here.kind !== "error");
|
|
2710
|
+
// `/model openrouter` alone: that provider's picker.
|
|
2711
|
+
if (!words)
|
|
2712
|
+
return setOverlay({ kind: "model", providerId });
|
|
2713
|
+
const picked = providerId === current && words === arg.trim() ? here : resolveChoice(words, orderedModelsOf(providerId), "model");
|
|
2647
2714
|
if (picked.kind === "error")
|
|
2648
2715
|
return say(picked.message);
|
|
2649
|
-
|
|
2716
|
+
// Several fit: show exactly those, in the picker, rather than a list to retype from.
|
|
2717
|
+
if (picked.kind === "several")
|
|
2718
|
+
return setOverlay({ kind: "model", providerId, filter: words });
|
|
2719
|
+
await applyModel(picked.index, providerId);
|
|
2650
2720
|
return;
|
|
2651
2721
|
}
|
|
2652
2722
|
setOverlay({ kind: "model" });
|
|
@@ -2675,7 +2745,7 @@ export function App({ resumeSessionId, initialScreen }) {
|
|
|
2675
2745
|
if (name === "/think") {
|
|
2676
2746
|
if (arg) {
|
|
2677
2747
|
const picked = resolveChoice(arg, thinkLevels(s.modelConfig.model), "reasoning level");
|
|
2678
|
-
if (picked.kind
|
|
2748
|
+
if (picked.kind !== "match")
|
|
2679
2749
|
return say(picked.message);
|
|
2680
2750
|
await applyThink(picked.index);
|
|
2681
2751
|
return;
|
|
@@ -2828,7 +2898,7 @@ export function App({ resumeSessionId, initialScreen }) {
|
|
|
2828
2898
|
if (name === "/skills") {
|
|
2829
2899
|
if (arg) {
|
|
2830
2900
|
await runDirective(`The user wants a new reusable skill for this project: "${arg}". Design it and save it ` +
|
|
2831
|
-
`with
|
|
2901
|
+
`with the skill tool — a short invocation name, a one-line description, and the procedure ` +
|
|
2832
2902
|
`as a clear markdown checklist (use $ARGUMENTS/$1 if it should take input). Then confirm.`, "creating a skill…");
|
|
2833
2903
|
return;
|
|
2834
2904
|
}
|
|
@@ -3102,9 +3172,10 @@ export function App({ resumeSessionId, initialScreen }) {
|
|
|
3102
3172
|
}
|
|
3103
3173
|
if (overlay.kind === "model") {
|
|
3104
3174
|
const id = cur?.modelConfig.model ?? DEFAULT_MODEL_CONFIG.model;
|
|
3105
|
-
//
|
|
3106
|
-
//
|
|
3107
|
-
const models = orderedModelList(id);
|
|
3175
|
+
// One provider's models, in display order (default first, rest A→Z): the one in use,
|
|
3176
|
+
// unless `/model <provider> …` named another.
|
|
3177
|
+
const models = overlay.providerId ? orderedModelsOf(overlay.providerId) : orderedModelList(id);
|
|
3178
|
+
const pickerProvider = overlay.providerId ? allProviders().find((p) => p.id === overlay.providerId)?.label ?? "" : providerOf(id).label;
|
|
3108
3179
|
// The two facts that decide the choice and are nowhere else on the screen: how much
|
|
3109
3180
|
// it can hold, and whether it can see an image you attach. They lead the description
|
|
3110
3181
|
// because the row truncates from the RIGHT — put behind the prose they would be the
|
|
@@ -3116,9 +3187,12 @@ export function App({ resumeSessionId, initialScreen }) {
|
|
|
3116
3187
|
return {
|
|
3117
3188
|
label: m.label + (m.id === id ? " ✓" : ""),
|
|
3118
3189
|
description: m.description ? `${facts} · ${m.description}` : facts,
|
|
3190
|
+
// Searched but not shown, so a filter opened by `/model <words>` matches the same
|
|
3191
|
+
// things the words matched: the id as well as the name.
|
|
3192
|
+
keywords: m.id,
|
|
3119
3193
|
};
|
|
3120
3194
|
});
|
|
3121
|
-
return (_jsx(Picker, { title: `Choose
|
|
3195
|
+
return (_jsx(Picker, { title: `Choose ${/^[aeiou]/i.test(pickerProvider) ? "an" : "a"} ${pickerProvider} model`, items: items, width: width, maxRows: maxRows, initialIndex: Math.max(0, models.findIndex((m) => m.id === id)), initialFilter: overlay.filter, onSelect: onOverlaySelect, onCancel: onOverlayCancel }));
|
|
3122
3196
|
}
|
|
3123
3197
|
if (overlay.kind === "think") {
|
|
3124
3198
|
const model = cur?.modelConfig.model ?? DEFAULT_MODEL_CONFIG.model;
|