@tiens.nguyen/gu-cli 1.0.734 → 1.0.736
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/coding-choice-tags.mjs +40 -1
- package/coding-provider-setup.mjs +18 -0
- package/gonext_agent_chat.py +25 -5
- package/gu-repl.mjs +49 -25
- package/package.json +1 -1
package/coding-choice-tags.mjs
CHANGED
|
@@ -95,7 +95,46 @@ export function ollamaWhere(kind, url) {
|
|
|
95
95
|
return (local ? "127.0.0.1" : host) + port;
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
/**
|
|
98
|
+
/**
|
|
99
|
+
* WHICH SERVER A CODER RUNS ON — for EVERY coder, not only Ollama ones (#263). Reported: "you
|
|
100
|
+
* should show the server before the model, correctly — later I can have another model on a
|
|
101
|
+
* different server". A bare "claude-opus-5" cannot say which provider (or which of two gateways)
|
|
102
|
+
* it goes to; "api.anthropic.com/claude-opus-5" can.
|
|
103
|
+
* · Ollama keeps ollamaWhere's rules (ollama.com, 127.0.0.1, a non-default port).
|
|
104
|
+
* · Anything else: its host, this machine as 127.0.0.1, and the port when it is not the
|
|
105
|
+
* scheme's default — two local servers on two ports are two servers.
|
|
106
|
+
*/
|
|
107
|
+
export function coderWhere(kind, url) {
|
|
108
|
+
if (kind === "ollama") return ollamaWhere(kind, url);
|
|
109
|
+
let u;
|
|
110
|
+
try {
|
|
111
|
+
u = new URL(String(url ?? ""));
|
|
112
|
+
} catch {
|
|
113
|
+
return "";
|
|
114
|
+
}
|
|
115
|
+
const host = u.hostname.toLowerCase().replace(/\.$/, "").replace(/^\[|\]$/g, "");
|
|
116
|
+
if (!host) return "";
|
|
117
|
+
const local = host === "localhost" || host === "127.0.0.1" || host === "::1";
|
|
118
|
+
const port = u.port ? `:${u.port}` : ""; // URL() already drops :443 on https and :80 on http
|
|
119
|
+
return (local ? "127.0.0.1" : host) + port;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* WHERE THE CODER IN USE RUNS — the host the bar and banner put in front of its name (#263).
|
|
124
|
+
* The folder's pick (a choice id, or a bare model name), else the account default. No list (an
|
|
125
|
+
* older API) → "", never a guess from raw payload fields (key-choice-regression.test.mjs).
|
|
126
|
+
* One rule for startup and for every path that changes the coder: computing it in only one of them
|
|
127
|
+
* is what left "ollama.com/" in front of a newly added Claude model.
|
|
128
|
+
*/
|
|
129
|
+
export function whereOfActiveCoder(choices, sessionModel) {
|
|
130
|
+
const list = Array.isArray(choices) ? choices : [];
|
|
131
|
+
const pick = String(sessionModel ?? "");
|
|
132
|
+
const active =
|
|
133
|
+
(pick && list.find((c) => c.id === pick || c.model === pick)) || list.find((c) => c.isDefault) || null;
|
|
134
|
+
return active ? coderWhere(active.kind, active.url) : "";
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** The coder's name as shown: "<server>/<model>" ("api.anthropic.com/claude-opus-5"), bare when no server is known. */
|
|
99
138
|
export function coderDisplayName(name, where) {
|
|
100
139
|
const n = String(name ?? "").trim();
|
|
101
140
|
return where && n ? `${where}/${n}` : n;
|
|
@@ -233,6 +233,24 @@ export function modelListRequest(url, key = "") {
|
|
|
233
233
|
return { url: modelsUrlFor(url), headers: keyed ? { Authorization: `Bearer ${k}` } : {} };
|
|
234
234
|
}
|
|
235
235
|
|
|
236
|
+
/**
|
|
237
|
+
* Why a provider's model list could not be shown, and what to type instead (#266). Reported:
|
|
238
|
+
* "did not list its models without a key — type the id … what id?". The line now says WHY
|
|
239
|
+
* (no key saved / key refused / unreachable) and WHAT to type: the model's exact name as that
|
|
240
|
+
* provider calls it, with the current one as the example.
|
|
241
|
+
* reason: "no-key" | "refused" | "failed"
|
|
242
|
+
*/
|
|
243
|
+
export function describeListFailure({ host = "", reason = "failed", status = 0, keyName = "", example = "" } = {}) {
|
|
244
|
+
const what = `type the model's name exactly as ${host} calls it${example ? ` (e.g. ${example})` : ""}`;
|
|
245
|
+
if (reason === "no-key") {
|
|
246
|
+
return [`no API key is saved for ${host}, so it cannot list its models —`, `${what}, or add a key with ctrl+k first.`];
|
|
247
|
+
}
|
|
248
|
+
if (reason === "refused") {
|
|
249
|
+
return [`${host} refused the key${keyName ? ` "${keyName}"` : ""}${status ? ` (HTTP ${status})` : ""} when listing its models —`, `${what}, or fix the key with ctrl+k.`];
|
|
250
|
+
}
|
|
251
|
+
return [`couldn't list ${host}'s models${status ? ` (HTTP ${status})` : " (no answer)"} —`, `${what}.`];
|
|
252
|
+
}
|
|
253
|
+
|
|
236
254
|
/**
|
|
237
255
|
* The hosted providers offered by name when adding one (#261), before "another URL". Claude has
|
|
238
256
|
* no embedding models (Anthropic points to Voyage AI), so it is not offered for the embedder.
|
package/gonext_agent_chat.py
CHANGED
|
@@ -1152,6 +1152,18 @@ def _anthropic_usage(u) -> "types.SimpleNamespace":
|
|
|
1152
1152
|
cache_read_input_tokens=cr, cache_creation_input_tokens=cw)
|
|
1153
1153
|
|
|
1154
1154
|
|
|
1155
|
+
def _log_anthropic_usage(u) -> None:
|
|
1156
|
+
"""One content-free line per Claude request with Anthropic's OWN cache figures (task #264).
|
|
1157
|
+
Without it the log could not say whether caching worked — the reported question — although
|
|
1158
|
+
every response carried the answer."""
|
|
1159
|
+
g = (lambda n: int(_usage_field(u, n) or 0))
|
|
1160
|
+
read, write, fresh = g("cache_read_input_tokens"), g("cache_creation_input_tokens"), g("input_tokens")
|
|
1161
|
+
total = read + write + fresh
|
|
1162
|
+
pct = f"{100 * read // total}%" if total else "—"
|
|
1163
|
+
_log(f"claude usage: prompt {total} = cache read {read} + cache write {write} + uncached {fresh} "
|
|
1164
|
+
f"({pct} read from cache) · output {g('output_tokens')}")
|
|
1165
|
+
|
|
1166
|
+
|
|
1155
1167
|
class _AnthropicChatAdapter:
|
|
1156
1168
|
"""client.chat.completions.create() backed by Anthropic's native Messages API (task #262)."""
|
|
1157
1169
|
|
|
@@ -1176,6 +1188,7 @@ class _AnthropicChatAdapter:
|
|
|
1176
1188
|
if kw.get("stream"):
|
|
1177
1189
|
return self._stream(req)
|
|
1178
1190
|
msg = self._sdk.messages.create(**req)
|
|
1191
|
+
_log_anthropic_usage(getattr(msg, "usage", None))
|
|
1179
1192
|
import types
|
|
1180
1193
|
text = "".join(getattr(b, "text", "") or "" for b in (msg.content or []) if getattr(b, "type", "") == "text")
|
|
1181
1194
|
return types.SimpleNamespace(
|
|
@@ -1217,6 +1230,7 @@ class _AnthropicChatAdapter:
|
|
|
1217
1230
|
usage["output_tokens"] = int(out)
|
|
1218
1231
|
if stop_reason == "refusal":
|
|
1219
1232
|
_log("claude: the model declined this request (stop_reason=refusal)")
|
|
1233
|
+
_log_anthropic_usage(usage)
|
|
1220
1234
|
yield chunk(content="", finish=_ANTHROPIC_FINISH.get(stop_reason, "stop"))
|
|
1221
1235
|
yield chunk(usage=_anthropic_usage(usage))
|
|
1222
1236
|
|
|
@@ -8243,12 +8257,18 @@ def run_agent_chat(cfg):
|
|
|
8243
8257
|
# Variable text — the deploy target and the current date/time — lives in the
|
|
8244
8258
|
# variable tail next to the task instead.
|
|
8245
8259
|
tool_hint = (
|
|
8260
|
+
# ONE batching rule, the system prompt's (task #265): this line used to say "ONE code block
|
|
8261
|
+
# that calls a SINGLE tool", contradicting the scaffold's BATCH rule from nearer the task —
|
|
8262
|
+
# measured as a 102-step turn in which every file read was its own full round trip.
|
|
8246
8263
|
f"Solve the TASK step by step (up to {max_steps} steps). At EACH step write a "
|
|
8247
|
-
"brief Thought, then ONE code block
|
|
8248
|
-
"
|
|
8249
|
-
"
|
|
8250
|
-
"
|
|
8251
|
-
"
|
|
8264
|
+
"brief Thought, then ONE code block. BATCH INDEPENDENT LOOKUPS into that block — "
|
|
8265
|
+
"several reads, greps or listings whose arguments you already know, printing each "
|
|
8266
|
+
"result under a header (up to about 8). Make ONE CHANGE per block (an edit, a file "
|
|
8267
|
+
"write or a command): if a line fails, the lines after it never run. Take a separate "
|
|
8268
|
+
"step only when a call depends on a result you have not seen yet (e.g. web_search to "
|
|
8269
|
+
"find a URL, THEN fetch_url to read it). You SEE the Observation before the next step. "
|
|
8270
|
+
"When you have enough to answer, call final_answer(<your answer>) in a code block. "
|
|
8271
|
+
"Prefer FEWER steps, and never repeat a tool call that already succeeded.\n\n"
|
|
8252
8272
|
"{{TOOL_LIST}}\n" # filled in below, once from the REAL registered tool objects
|
|
8253
8273
|
+ _rag_tool_block + _ws_tool_block + _auto_test_block +
|
|
8254
8274
|
"\n"
|
package/gu-repl.mjs
CHANGED
|
@@ -120,7 +120,7 @@ import {
|
|
|
120
120
|
} from "./terminal-layout.mjs";
|
|
121
121
|
import { completedFlush } from "./completed-flush.mjs";
|
|
122
122
|
import { codingBannerLabel, isOllamaCloudUrl, SECRET_KEY_HINT } from "./coding-key-prompt.mjs";
|
|
123
|
-
import { codingChoiceTags, coderDisplayName,
|
|
123
|
+
import { codingChoiceTags, coderDisplayName, coderWhere, whereOfActiveCoder } from "./coding-choice-tags.mjs";
|
|
124
124
|
import {
|
|
125
125
|
isDoubleClick,
|
|
126
126
|
clickRun,
|
|
@@ -5242,6 +5242,7 @@ async function reconfigureOllamaServer() {
|
|
|
5242
5242
|
// Persist the CLEARING too. The stored override names a model on the OLD box; leaving it on
|
|
5243
5243
|
// disk would resurrect it at the next launch, against a server that has never heard of it.
|
|
5244
5244
|
await saveSessionCoding(resolve(process.cwd()));
|
|
5245
|
+
await syncActiveCoderWhere(); // the bar now names the account default — with ITS host (#263)
|
|
5245
5246
|
// The GLOBAL output-token total is keyed to the coding backend (task #125), and the backend
|
|
5246
5247
|
// just moved — re-fetch it for the new one, exactly as /model does after a pick, or the
|
|
5247
5248
|
// footer keeps counting against the box that is no longer in use.
|
|
@@ -5355,7 +5356,7 @@ async function chooseCodingModelFrom(ps, all, question, current) {
|
|
|
5355
5356
|
let list = coders;
|
|
5356
5357
|
let showingAll = filtered === 0;
|
|
5357
5358
|
for (;;) {
|
|
5358
|
-
const rows = [...list, "type a model
|
|
5359
|
+
const rows = [...list, "type a model name…"];
|
|
5359
5360
|
// The row that lifts the filter, offered only while it is on and only when it hides something.
|
|
5360
5361
|
if (!showingAll) rows.push(`show all ${all.length} models…`);
|
|
5361
5362
|
// Opens on the CURRENT model where there is one — a long list that opens at the top tells the
|
|
@@ -5368,7 +5369,7 @@ async function chooseCodingModelFrom(ps, all, question, current) {
|
|
|
5368
5369
|
const idx = await pickFromList(rows, at >= 0 ? at : 0);
|
|
5369
5370
|
if (idx < 0) return null;
|
|
5370
5371
|
if (idx === list.length) {
|
|
5371
|
-
const typed = (await ask(" model
|
|
5372
|
+
const typed = (await ask(" model name: ")).trim();
|
|
5372
5373
|
return typed || null;
|
|
5373
5374
|
}
|
|
5374
5375
|
if (idx === list.length + 1) {
|
|
@@ -5394,13 +5395,13 @@ async function chooseCodingModelFrom(ps, all, question, current) {
|
|
|
5394
5395
|
* unlisted one may be exactly right.
|
|
5395
5396
|
*/
|
|
5396
5397
|
async function chooseFromModelList(all, question, current = 0) {
|
|
5397
|
-
const rows = [...all, "type a model
|
|
5398
|
+
const rows = [...all, "type a model name…"];
|
|
5398
5399
|
const at = typeof current === "string" ? all.indexOf(current) : Number(current) || 0;
|
|
5399
5400
|
console.log(dim(` ${question} (${PICK_HINT})`));
|
|
5400
5401
|
const idx = await pickFromList(rows, at >= 0 ? at : 0);
|
|
5401
5402
|
if (idx < 0) return null;
|
|
5402
5403
|
if (idx === all.length) {
|
|
5403
|
-
const typed = (await ask(" model
|
|
5404
|
+
const typed = (await ask(" model name: ")).trim();
|
|
5404
5405
|
return typed || null;
|
|
5405
5406
|
}
|
|
5406
5407
|
return all[idx];
|
|
@@ -5458,15 +5459,22 @@ async function changeProviderModel(ps, rows, p) {
|
|
|
5458
5459
|
const md = await import("./model-doctor.mjs");
|
|
5459
5460
|
const { proxyFetch } = await import("./proxy-dispatcher.mjs");
|
|
5460
5461
|
|
|
5461
|
-
//
|
|
5462
|
-
//
|
|
5463
|
-
//
|
|
5464
|
-
//
|
|
5465
|
-
//
|
|
5462
|
+
// LISTED WITH THIS PROVIDER'S OWN KEY (#266). This used to probe with NO key, on purpose: the
|
|
5463
|
+
// only key the terminal could fetch was the ACCOUNT DEFAULT, which may belong to another provider.
|
|
5464
|
+
// Since #245 a key belongs to its server, so the key used is the one bound to THIS row — the same
|
|
5465
|
+
// check addServer makes — and never another provider's. Keyed providers (Claude, OpenAI, Kimi,
|
|
5466
|
+
// ollama.com) can finally list their models here instead of asking for an id.
|
|
5467
|
+
const sk = await import("./server-keys.mjs");
|
|
5468
|
+
const own = await withPendingStatus(`reading the key for ${p.host}`, async () => {
|
|
5469
|
+
const keys = await fetchServerKeys(p.id);
|
|
5470
|
+
const inUse = sk.keyInUse(keys ?? [], p.id, sessionCodingKeyId);
|
|
5471
|
+
if (!inUse || inUse.backendId !== p.id) return { key: "", name: "" };
|
|
5472
|
+
const r = await fetchCodingKey(p.id);
|
|
5473
|
+
return r.keyId && r.keyId !== inUse.id ? { key: "", name: "" } : { key: r.key, name: String(inUse.name ?? "") };
|
|
5474
|
+
});
|
|
5466
5475
|
const probe = await withPendingStatus(`asking ${p.host} what it has`, async () => {
|
|
5467
5476
|
try {
|
|
5468
|
-
|
|
5469
|
-
const lr = ps.modelListRequest(p.url);
|
|
5477
|
+
const lr = ps.modelListRequest(p.url, own.key); // the list's own dialect too (Claude's headers, #261)
|
|
5470
5478
|
const res = await proxyFetch(lr.url, { headers: lr.headers, signal: AbortSignal.timeout(10_000) });
|
|
5471
5479
|
if (!res.ok) return { status: res.status };
|
|
5472
5480
|
return { body: await res.json().catch(() => null) };
|
|
@@ -5486,7 +5494,11 @@ async function changeProviderModel(ps, rows, p) {
|
|
|
5486
5494
|
const rn = ps.retiredNote(parsed.retired, p.host);
|
|
5487
5495
|
if (rn) console.log(GUTTER + dim(rn));
|
|
5488
5496
|
} else {
|
|
5489
|
-
|
|
5497
|
+
const refused = probe.status === 401 || probe.status === 403;
|
|
5498
|
+
const reason = refused ? (own.key ? "refused" : "no-key") : "failed";
|
|
5499
|
+
for (const l of ps.describeListFailure({ host: p.host, reason, status: probe.status, keyName: own.name, example: p.model })) {
|
|
5500
|
+
console.log(GUTTER + dim(l));
|
|
5501
|
+
}
|
|
5490
5502
|
}
|
|
5491
5503
|
|
|
5492
5504
|
let model = "";
|
|
@@ -5498,7 +5510,7 @@ async function changeProviderModel(ps, rows, p) {
|
|
|
5498
5510
|
}
|
|
5499
5511
|
} else {
|
|
5500
5512
|
console.log(GUTTER + dim(`Enter keeps ${p.model || "it unset"}`));
|
|
5501
|
-
model = (await ask(" model
|
|
5513
|
+
model = (await ask(" model name: ")).trim();
|
|
5502
5514
|
}
|
|
5503
5515
|
if (!model) {
|
|
5504
5516
|
console.log(GUTTER + dim("(nothing changed)\n"));
|
|
@@ -5533,6 +5545,7 @@ async function changeProviderModel(ps, rows, p) {
|
|
|
5533
5545
|
sessionCodingLabel = "";
|
|
5534
5546
|
sessionCodingUrl = "";
|
|
5535
5547
|
await saveSessionCoding(resolve(process.cwd()));
|
|
5548
|
+
await syncActiveCoderWhere();
|
|
5536
5549
|
console.log(GUTTER + dim("this folder's pick named the old model — cleared it."));
|
|
5537
5550
|
}
|
|
5538
5551
|
console.log(accent(` ✓ ${p.host} → ${model}`) + dim(" · pick it in /model\n"));
|
|
@@ -5625,6 +5638,7 @@ async function removeCodingProvider(ps, rows, p) {
|
|
|
5625
5638
|
sessionCodingLabel = "";
|
|
5626
5639
|
sessionCodingUrl = "";
|
|
5627
5640
|
await saveSessionCoding(resolve(process.cwd()));
|
|
5641
|
+
await syncActiveCoderWhere();
|
|
5628
5642
|
console.log(GUTTER + dim("this folder was using it — cleared the pick.\n"));
|
|
5629
5643
|
}
|
|
5630
5644
|
console.log(
|
|
@@ -5834,7 +5848,7 @@ async function addServer({ role = "coding", dialect = "openai", url: presetUrl =
|
|
|
5834
5848
|
return;
|
|
5835
5849
|
}
|
|
5836
5850
|
} else {
|
|
5837
|
-
model = String((await ask(" model
|
|
5851
|
+
model = String((await ask(" model name: ")) ?? "").trim();
|
|
5838
5852
|
}
|
|
5839
5853
|
if (!model) {
|
|
5840
5854
|
console.log(GUTTER + dim("(cancelled — nothing was saved)\n"));
|
|
@@ -5979,6 +5993,9 @@ async function useCoderInThisFolder({ ps, rows, beforeChoices, url, host, locali
|
|
|
5979
5993
|
sessionCodingModel = offered.id;
|
|
5980
5994
|
sessionCodingLabel = model;
|
|
5981
5995
|
sessionCodingUrl = url;
|
|
5996
|
+
// …and the host the bar puts in front of it (#263): left alone, the PREVIOUS coder's
|
|
5997
|
+
// "ollama.com/" stayed in front of a newly added Claude model until the next /model pick.
|
|
5998
|
+
activeCoderWhere = whereOfActiveCoder(after?.codingChoices, sessionCodingModel);
|
|
5982
5999
|
// The folder follows the SERVER's key (task #245) — its default, which a first key already is.
|
|
5983
6000
|
sessionCodingKeyId = "";
|
|
5984
6001
|
sessionApiKeyTail = key ? key.slice(-4) : "";
|
|
@@ -6072,9 +6089,9 @@ async function chooseModel() {
|
|
|
6072
6089
|
// OpenAI-compatible providers are both "OpenAI-compatible", so the kind names neither.
|
|
6073
6090
|
const { tags, show: showTag } = codingChoiceTags(choices);
|
|
6074
6091
|
const labels = choices.map((c, i) => {
|
|
6075
|
-
//
|
|
6076
|
-
//
|
|
6077
|
-
const where =
|
|
6092
|
+
// Every row says WHERE it runs in front of the name ("ollama.com/kimi-k3",
|
|
6093
|
+
// "api.anthropic.com/claude-opus-5" — #263), so the kind tag is only for a row with no server.
|
|
6094
|
+
const where = coderWhere(c.kind, c.url);
|
|
6078
6095
|
const tag = showTag && !where ? ` ${dim(`· ${tags[i]}`)}` : "";
|
|
6079
6096
|
return `${coderDisplayName(c.model, where)}${tag}${c.id === def ? " (default)" : ""}`;
|
|
6080
6097
|
});
|
|
@@ -6119,10 +6136,12 @@ async function chooseModel() {
|
|
|
6119
6136
|
// Task #125: remember the chosen backend's coding URL so the GLOBAL output-token total is
|
|
6120
6137
|
// keyed to it. Default pick → "" so activeCodingUrl() falls back to defaultCodingUrl.
|
|
6121
6138
|
sessionCodingUrl = chosen.id === def ? "" : (chosen.url ?? "");
|
|
6139
|
+
// The server goes WITH the model, before any await (#263): set after the save below, the bar
|
|
6140
|
+
// could repaint in between and show the NEW model under the OLD server for a moment.
|
|
6141
|
+
activeCoderWhere = coderWhere(chosen.kind, chosen.url);
|
|
6122
6142
|
// WRITTEN NOW, not at the end of the next turn: `/model` then `/exit` is how the choice is
|
|
6123
6143
|
// made and then tested, and that path never reaches saveSession.
|
|
6124
6144
|
await saveSessionCoding(resolve(process.cwd()));
|
|
6125
|
-
activeCoderWhere = ollamaWhere(chosen.kind, chosen.url);
|
|
6126
6145
|
const where = showTag && !activeCoderWhere ? ` (${tags[idx]})` : "";
|
|
6127
6146
|
console.log(
|
|
6128
6147
|
accent(
|
|
@@ -8644,6 +8663,16 @@ let defaultCodingModel = "";
|
|
|
8644
8663
|
let sessionCodingUrl = "";
|
|
8645
8664
|
/** "ollama.com" / "127.0.0.1" / a host — where the ACTIVE coder runs, when it is an Ollama one. */
|
|
8646
8665
|
let activeCoderWhere = "";
|
|
8666
|
+
/**
|
|
8667
|
+
* Re-read where the coder in use runs (#263) — for the paths that change it without holding the
|
|
8668
|
+
* API's choice list (they clear the folder's pick back to the account default). Best-effort: an
|
|
8669
|
+
* unreachable API leaves no prefix rather than a stale one.
|
|
8670
|
+
*/
|
|
8671
|
+
async function syncActiveCoderWhere() {
|
|
8672
|
+
const payload = await fetchAgentPayload([{ role: "user", content: "ping" }]).catch(() => null);
|
|
8673
|
+
activeCoderWhere = whereOfActiveCoder(payload?.codingChoices, sessionCodingModel);
|
|
8674
|
+
if (barPinned()) paintBar();
|
|
8675
|
+
}
|
|
8647
8676
|
// The account-default backend's URL (from the startup probe's codingChoices), used when no
|
|
8648
8677
|
// /model override is active. Set once at startup alongside defaultCodingModel.
|
|
8649
8678
|
let defaultCodingUrl = "";
|
|
@@ -10527,14 +10556,9 @@ async function main() {
|
|
|
10527
10556
|
}
|
|
10528
10557
|
{
|
|
10529
10558
|
// WHERE THE CODER RUNS, for the banner and the bar: the folder's pick, else the default.
|
|
10530
|
-
const choices = Array.isArray(probe?.codingChoices) ? probe.codingChoices : [];
|
|
10531
|
-
const active =
|
|
10532
|
-
(sessionCodingModel && choices.find((c) => c.id === sessionCodingModel || c.model === sessionCodingModel)) ||
|
|
10533
|
-
choices.find((c) => c.isDefault) ||
|
|
10534
|
-
null;
|
|
10535
10559
|
// No choice list (an older API): no prefix, rather than guess from the ACCOUNT default's raw
|
|
10536
10560
|
// payload fields, which key-choice-regression.test.mjs forbids for exactly that reason.
|
|
10537
|
-
activeCoderWhere =
|
|
10561
|
+
activeCoderWhere = whereOfActiveCoder(probe?.codingChoices, sessionCodingModel);
|
|
10538
10562
|
}
|
|
10539
10563
|
// Task #133: no RAG storage question any more — the terminal ALWAYS keeps the index on
|
|
10540
10564
|
// this machine. `/rag-cloud` opts into additionally mirroring it to the user's S3 bucket.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tiens.nguyen/gu-cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.736",
|
|
4
4
|
"description": "Gu CLI — the gu terminal plus the local worker that runs agent / OCR / PDF / embedding jobs on your Mac (Ollama / MLX / OpenAI-compatible).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|