@oh-my-pi/pi-coding-agent 16.2.12 → 16.3.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/CHANGELOG.md +115 -7
- package/dist/cli.js +5996 -5945
- package/dist/types/advisor/config.d.ts +4 -2
- package/dist/types/collab/host.d.ts +16 -0
- package/dist/types/collab/protocol.d.ts +9 -3
- package/dist/types/commit/model-selection.d.ts +5 -0
- package/dist/types/config/model-resolver.d.ts +12 -10
- package/dist/types/config/models-config-schema.d.ts +12 -0
- package/dist/types/config/models-config.d.ts +9 -0
- package/dist/types/config/settings-schema.d.ts +28 -5
- package/dist/types/edit/modes/patch.d.ts +11 -0
- package/dist/types/eval/agent-bridge.d.ts +5 -1
- package/dist/types/exec/bash-executor.d.ts +1 -0
- package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +86 -2
- package/dist/types/extensibility/skills.d.ts +2 -1
- package/dist/types/mnemopi/state.d.ts +12 -0
- package/dist/types/modes/components/agent-hub.d.ts +9 -5
- package/dist/types/modes/components/status-line/component.test.d.ts +1 -0
- package/dist/types/modes/components/usage-row.d.ts +1 -1
- package/dist/types/modes/controllers/command-controller.d.ts +0 -1
- package/dist/types/modes/controllers/extension-ui-controller.d.ts +6 -0
- package/dist/types/modes/controllers/streaming-reveal.d.ts +17 -1
- package/dist/types/modes/controllers/tool-args-reveal.d.ts +30 -3
- package/dist/types/modes/interactive-mode.d.ts +12 -7
- package/dist/types/modes/rpc/rpc-client.d.ts +5 -0
- package/dist/types/modes/rpc/rpc-mode.d.ts +64 -1
- package/dist/types/modes/session-teardown.d.ts +63 -0
- package/dist/types/modes/session-teardown.test.d.ts +1 -0
- package/dist/types/modes/theme/theme.d.ts +9 -4
- package/dist/types/modes/types.d.ts +2 -3
- package/dist/types/modes/utils/copy-targets.d.ts +2 -0
- package/dist/types/plan-mode/approved-plan-prompt.test.d.ts +1 -0
- package/dist/types/sdk.d.ts +9 -0
- package/dist/types/session/agent-session.d.ts +46 -13
- package/dist/types/session/exit-diagnostics.d.ts +48 -0
- package/dist/types/session/session-loader.d.ts +5 -0
- package/dist/types/task/executor.d.ts +12 -5
- package/dist/types/task/parallel.d.ts +1 -0
- package/dist/types/task/render.test.d.ts +1 -0
- package/dist/types/thinking.d.ts +2 -0
- package/dist/types/tools/checkpoint.d.ts +8 -0
- package/dist/types/tools/eval-render.d.ts +1 -0
- package/dist/types/tools/fetch.d.ts +11 -1
- package/dist/types/tools/index.d.ts +3 -1
- package/dist/types/tools/irc.d.ts +1 -0
- package/dist/types/tools/output-meta.d.ts +7 -0
- package/dist/types/tools/output-schema-validator.d.ts +7 -4
- package/dist/types/tools/path-utils.d.ts +16 -0
- package/dist/types/tools/render-utils.d.ts +4 -1
- package/dist/types/utils/git.d.ts +47 -2
- package/dist/types/web/search/provider.d.ts +7 -0
- package/dist/types/web/search/types.d.ts +1 -1
- package/package.json +12 -12
- package/src/advisor/config.ts +4 -2
- package/src/cli/bench-cli.ts +7 -2
- package/src/collab/guest.ts +94 -5
- package/src/collab/host.ts +80 -3
- package/src/collab/protocol.ts +9 -2
- package/src/commit/model-selection.ts +8 -2
- package/src/config/keybindings.ts +3 -1
- package/src/config/model-discovery.ts +66 -2
- package/src/config/model-registry.ts +7 -3
- package/src/config/model-resolver.ts +51 -23
- package/src/config/models-config-schema.ts +13 -0
- package/src/config/settings-schema.ts +44 -14
- package/src/edit/hashline/diff.ts +13 -2
- package/src/edit/index.ts +58 -8
- package/src/edit/modes/patch.ts +53 -18
- package/src/edit/streaming.ts +7 -6
- package/src/eval/__tests__/agent-bridge.test.ts +57 -1
- package/src/eval/agent-bridge.ts +15 -5
- package/src/exec/bash-executor.ts +7 -12
- package/src/extensibility/legacy-pi-coding-agent-shim.ts +534 -16
- package/src/extensibility/skills.ts +29 -6
- package/src/goals/guided-setup.ts +4 -3
- package/src/internal-urls/docs-index.generated.txt +1 -1
- package/src/lsp/client.ts +11 -14
- package/src/main.ts +38 -10
- package/src/mnemopi/state.ts +43 -7
- package/src/modes/acp/acp-agent.ts +1 -1
- package/src/modes/components/agent-hub.ts +10 -2
- package/src/modes/components/agent-transcript-viewer.ts +39 -7
- package/src/modes/components/assistant-message.ts +16 -10
- package/src/modes/components/chat-transcript-builder.ts +11 -1
- package/src/modes/components/custom-editor.test.ts +20 -0
- package/src/modes/components/hook-selector.ts +44 -25
- package/src/modes/components/model-selector.ts +1 -1
- package/src/modes/components/status-line/component.test.ts +44 -0
- package/src/modes/components/status-line/component.ts +9 -1
- package/src/modes/components/status-line/segments.ts +3 -3
- package/src/modes/components/usage-row.ts +16 -2
- package/src/modes/controllers/command-controller.ts +6 -21
- package/src/modes/controllers/event-controller.ts +11 -9
- package/src/modes/controllers/extension-ui-controller.ts +84 -3
- package/src/modes/controllers/input-controller.ts +16 -13
- package/src/modes/controllers/selector-controller.ts +3 -8
- package/src/modes/controllers/streaming-reveal.ts +75 -53
- package/src/modes/controllers/tool-args-reveal.ts +340 -10
- package/src/modes/interactive-mode.ts +122 -79
- package/src/modes/rpc/rpc-client.ts +21 -0
- package/src/modes/rpc/rpc-mode.ts +197 -46
- package/src/modes/session-teardown.test.ts +219 -0
- package/src/modes/session-teardown.ts +82 -0
- package/src/modes/setup-wizard/scenes/theme.ts +2 -2
- package/src/modes/skill-command.ts +7 -20
- package/src/modes/theme/theme.ts +29 -15
- package/src/modes/types.ts +2 -3
- package/src/modes/utils/copy-targets.ts +12 -0
- package/src/modes/utils/ui-helpers.ts +19 -2
- package/src/plan-mode/approved-plan-prompt.test.ts +36 -0
- package/src/prompts/advisor/system.md +1 -1
- package/src/prompts/agents/tester.md +6 -2
- package/src/prompts/skills/autoload.md +8 -0
- package/src/prompts/skills/user-invocation.md +11 -0
- package/src/prompts/steering/parent-irc.md +5 -0
- package/src/prompts/system/irc-incoming.md +2 -0
- package/src/prompts/system/mid-run-todo-nudge.md +3 -0
- package/src/prompts/system/plan-mode-approved.md +7 -10
- package/src/prompts/system/plan-mode-compact-instructions.md +2 -1
- package/src/prompts/system/plan-mode-reference.md +3 -4
- package/src/prompts/system/rewind-report.md +6 -0
- package/src/prompts/system/subagent-system-prompt.md +3 -0
- package/src/prompts/tools/irc.md +2 -1
- package/src/prompts/tools/job.md +2 -2
- package/src/prompts/tools/rewind.md +3 -2
- package/src/prompts/tools/ssh.md +1 -0
- package/src/prompts/tools/task.md +4 -0
- package/src/sdk.ts +18 -4
- package/src/session/agent-session.ts +660 -114
- package/src/session/exit-diagnostics.ts +202 -0
- package/src/session/session-context.ts +25 -13
- package/src/session/session-loader.ts +58 -25
- package/src/session/session-manager.ts +0 -1
- package/src/session/session-persistence.ts +30 -4
- package/src/session/settings-stream-fn.ts +14 -0
- package/src/slash-commands/builtin-registry.ts +35 -3
- package/src/system-prompt.ts +15 -1
- package/src/task/executor.ts +31 -8
- package/src/task/index.ts +31 -9
- package/src/task/isolation-runner.ts +18 -2
- package/src/task/parallel.ts +7 -2
- package/src/task/render.test.ts +121 -0
- package/src/task/render.ts +48 -2
- package/src/task/worktree.ts +12 -13
- package/src/task/yield-assembly.ts +8 -5
- package/src/thinking.ts +5 -0
- package/src/tools/ask.ts +188 -9
- package/src/tools/ast-edit.ts +7 -0
- package/src/tools/ast-grep.ts +11 -0
- package/src/tools/bash.ts +6 -30
- package/src/tools/checkpoint.ts +15 -1
- package/src/tools/eval-render.ts +15 -0
- package/src/tools/fetch.ts +82 -18
- package/src/tools/gh.ts +1 -1
- package/src/tools/grep.ts +45 -27
- package/src/tools/index.ts +3 -1
- package/src/tools/irc.ts +24 -9
- package/src/tools/output-meta.ts +50 -0
- package/src/tools/output-schema-validator.ts +152 -15
- package/src/tools/path-utils.ts +55 -10
- package/src/tools/read.ts +1 -1
- package/src/tools/render-utils.ts +5 -3
- package/src/tools/ssh.ts +8 -1
- package/src/tools/yield.ts +41 -1
- package/src/utils/commit-message-generator.ts +2 -2
- package/src/utils/git.ts +271 -29
- package/src/utils/thinking-display.ts +13 -0
- package/src/web/search/index.ts +9 -28
- package/src/web/search/provider.ts +26 -1
- package/src/web/search/providers/duckduckgo.ts +1 -1
- package/src/web/search/types.ts +5 -1
package/src/collab/host.ts
CHANGED
|
@@ -13,7 +13,14 @@ import { timingSafeEqual } from "node:crypto";
|
|
|
13
13
|
import * as fs from "node:fs/promises";
|
|
14
14
|
import type { ImageContent, TextContent } from "@oh-my-pi/pi-ai";
|
|
15
15
|
import { logger } from "@oh-my-pi/pi-utils";
|
|
16
|
-
import type {
|
|
16
|
+
import type {
|
|
17
|
+
BusChannel,
|
|
18
|
+
CollabUiRequest,
|
|
19
|
+
CollabUiRequestDraft,
|
|
20
|
+
CollabUiResponseValue,
|
|
21
|
+
AgentEvent as WireAgentEvent,
|
|
22
|
+
SessionEntry as WireSessionEntry,
|
|
23
|
+
} from "@oh-my-pi/pi-wire";
|
|
17
24
|
import type { InteractiveModeContext } from "../modes/types";
|
|
18
25
|
import { AgentLifecycleManager } from "../registry/agent-lifecycle";
|
|
19
26
|
import { type AgentRef, AgentRegistry } from "../registry/agent-registry";
|
|
@@ -94,7 +101,8 @@ function isWireSessionEntry(entry: StoredSessionEntry): entry is StoredSessionEn
|
|
|
94
101
|
}
|
|
95
102
|
const CONNECT_TIMEOUT_MS = 15_000;
|
|
96
103
|
/** Max bytes served per fetch-transcript reply (guest re-requests from `newSize`). */
|
|
97
|
-
const TRANSCRIPT_READ_CAP = 4 * 1024 * 1024;
|
|
104
|
+
export const TRANSCRIPT_READ_CAP = 4 * 1024 * 1024;
|
|
105
|
+
const TRANSCRIPT_ENTRY_TOO_LARGE_ERROR = `transcript entry exceeds transcript fetch cap (${TRANSCRIPT_READ_CAP} bytes)`;
|
|
98
106
|
/**
|
|
99
107
|
* Soft byte cap per `snapshot-chunk` frame. The first MB of a snapshot takes
|
|
100
108
|
* ~3s through the default relay, so a 512 KB chunk lands well under the
|
|
@@ -102,6 +110,13 @@ const TRANSCRIPT_READ_CAP = 4 * 1024 * 1024;
|
|
|
102
110
|
* ship in a chunk of their own.
|
|
103
111
|
*/
|
|
104
112
|
const SNAPSHOT_CHUNK_BYTES = 512 * 1024;
|
|
113
|
+
/**
|
|
114
|
+
* Outcome of {@link CollabHost.requestGuestUi}. `answered` carries the guest's
|
|
115
|
+
* response (an `undefined` value is a genuine guest cancel); `unavailable`
|
|
116
|
+
* means the collab channel went away (teardown, relay drop) or the request was
|
|
117
|
+
* aborted before any guest answered — callers MUST NOT treat it as a cancel.
|
|
118
|
+
*/
|
|
119
|
+
export type CollabGuestUiResult = { kind: "answered"; value: CollabUiResponseValue } | { kind: "unavailable" };
|
|
105
120
|
|
|
106
121
|
export class CollabHost {
|
|
107
122
|
#ctx: InteractiveModeContext;
|
|
@@ -114,6 +129,8 @@ export class CollabHost {
|
|
|
114
129
|
#sessionId = "";
|
|
115
130
|
#unsubscribe?: () => void;
|
|
116
131
|
#peers = new Map<number, { name: string; canWrite: boolean }>();
|
|
132
|
+
#uiReqSeq = 0;
|
|
133
|
+
#pendingUi = new Map<number, { request: CollabUiRequest; settle(result: CollabGuestUiResult): void }>();
|
|
117
134
|
#lastStateJson = "";
|
|
118
135
|
#stateDebounce: Timer | null = null;
|
|
119
136
|
#streamingInterval: Timer | null = null;
|
|
@@ -153,6 +170,43 @@ export class CollabHost {
|
|
|
153
170
|
return list;
|
|
154
171
|
}
|
|
155
172
|
|
|
173
|
+
requestGuestUi(request: CollabUiRequestDraft, signal?: AbortSignal): Promise<CollabGuestUiResult> | null {
|
|
174
|
+
if (!this.#socket || !this.#hasWritablePeers()) return null;
|
|
175
|
+
const reqId = ++this.#uiReqSeq;
|
|
176
|
+
const fullRequest: CollabUiRequest = { ...request, reqId };
|
|
177
|
+
const { promise, resolve } = Promise.withResolvers<CollabGuestUiResult>();
|
|
178
|
+
let settled = false;
|
|
179
|
+
const settle = (result: CollabGuestUiResult): void => {
|
|
180
|
+
if (settled) return;
|
|
181
|
+
settled = true;
|
|
182
|
+
signal?.removeEventListener("abort", onAbort);
|
|
183
|
+
this.#pendingUi.delete(reqId);
|
|
184
|
+
this.#sendWritablePeers({ t: "ui-request-end", reqId });
|
|
185
|
+
resolve(result);
|
|
186
|
+
};
|
|
187
|
+
const onAbort = (): void => settle({ kind: "unavailable" });
|
|
188
|
+
if (signal?.aborted) return Promise.resolve({ kind: "unavailable" });
|
|
189
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
190
|
+
this.#pendingUi.set(reqId, { request: fullRequest, settle });
|
|
191
|
+
this.#sendWritablePeers({ t: "ui-request", request: fullRequest });
|
|
192
|
+
return promise;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
#hasWritablePeers(): boolean {
|
|
196
|
+
for (const peer of this.#peers.values()) {
|
|
197
|
+
if (peer.canWrite) return true;
|
|
198
|
+
}
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
#sendWritablePeers(frame: CollabFrame): void {
|
|
203
|
+
const socket = this.#socket;
|
|
204
|
+
if (!socket) return;
|
|
205
|
+
for (const [peerId, peer] of this.#peers) {
|
|
206
|
+
if (peer.canWrite) socket.send(frame, peerId);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
156
210
|
async start(relayUrl: string, webUrl = ""): Promise<void> {
|
|
157
211
|
const rawKey = generateRoomKey();
|
|
158
212
|
const writeToken = generateWriteToken();
|
|
@@ -255,6 +309,8 @@ export class CollabHost {
|
|
|
255
309
|
this.#agentsDebounce = null;
|
|
256
310
|
clearInterval(this.#streamingInterval ?? undefined);
|
|
257
311
|
this.#streamingInterval = null;
|
|
312
|
+
for (const pending of this.#pendingUi.values()) pending.settle({ kind: "unavailable" });
|
|
313
|
+
this.#pendingUi.clear();
|
|
258
314
|
this.#peers.clear();
|
|
259
315
|
this.#socket?.close();
|
|
260
316
|
this.#socket = null;
|
|
@@ -287,6 +343,9 @@ export class CollabHost {
|
|
|
287
343
|
case "agent-cmd":
|
|
288
344
|
this.#handleAgentCmd(frame.cmd, frame.agentId, frame.text, fromPeer);
|
|
289
345
|
break;
|
|
346
|
+
case "ui-response":
|
|
347
|
+
this.#handleUiResponse(frame.reqId, frame.value, fromPeer);
|
|
348
|
+
break;
|
|
290
349
|
case "fetch-transcript":
|
|
291
350
|
void this.#handleFetchTranscript(frame.reqId, frame.agentId, frame.fromByte, fromPeer);
|
|
292
351
|
break;
|
|
@@ -348,6 +407,11 @@ export class CollabHost {
|
|
|
348
407
|
fromPeer,
|
|
349
408
|
);
|
|
350
409
|
this.#sendSnapshotChunks(entries, fromPeer);
|
|
410
|
+
if (canWrite) {
|
|
411
|
+
for (const pending of this.#pendingUi.values()) {
|
|
412
|
+
socket.send({ t: "ui-request", request: pending.request }, fromPeer);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
351
415
|
this.#ctx.session.emitNotice(
|
|
352
416
|
"info",
|
|
353
417
|
`${cleanName} joined the collab session${canWrite ? "" : " (read-only)"}`,
|
|
@@ -392,6 +456,15 @@ export class CollabHost {
|
|
|
392
456
|
}
|
|
393
457
|
}
|
|
394
458
|
|
|
459
|
+
#handleUiResponse(reqId: number, value: CollabUiResponseValue, fromPeer: number): void {
|
|
460
|
+
const peer = this.#peers.get(fromPeer);
|
|
461
|
+
if (!peer?.canWrite) {
|
|
462
|
+
this.#rejectReadOnly("responding to ask", fromPeer);
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
this.#pendingUi.get(reqId)?.settle({ kind: "answered", value });
|
|
466
|
+
}
|
|
467
|
+
|
|
395
468
|
#handlePrompt(text: string, images: ImageContent[] | undefined, fromPeer: number): void {
|
|
396
469
|
const peer = this.#peers.get(fromPeer);
|
|
397
470
|
if (!peer?.canWrite) {
|
|
@@ -584,7 +657,11 @@ export class CollabHost {
|
|
|
584
657
|
if (!reachedEof) {
|
|
585
658
|
// Trim to the last complete JSONL line so no line or UTF-8 char is split.
|
|
586
659
|
const lastNewline = slice.lastIndexOf(0x0a);
|
|
587
|
-
|
|
660
|
+
if (lastNewline < 0) {
|
|
661
|
+
reply("", fromByte, TRANSCRIPT_ENTRY_TOO_LARGE_ERROR);
|
|
662
|
+
return;
|
|
663
|
+
}
|
|
664
|
+
slice = slice.subarray(0, lastNewline + 1);
|
|
588
665
|
}
|
|
589
666
|
reply(slice.toString("utf-8"), reachedEof ? stat.size : fromByte + slice.byteLength);
|
|
590
667
|
} catch (err) {
|
package/src/collab/protocol.ts
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import type { ImageContent, Model } from "@oh-my-pi/pi-ai";
|
|
11
11
|
import type {
|
|
12
12
|
BusChannel,
|
|
13
|
+
CollabUiRequest,
|
|
13
14
|
GuestFrame,
|
|
14
15
|
ParsedCollabLink,
|
|
15
16
|
Participant,
|
|
@@ -29,6 +30,10 @@ import type { SessionEntry, SessionHeader } from "../session/session-entries";
|
|
|
29
30
|
|
|
30
31
|
export type {
|
|
31
32
|
CollabPromptDetails,
|
|
33
|
+
CollabUiRequest,
|
|
34
|
+
CollabUiRequestDraft,
|
|
35
|
+
CollabUiResponseValue,
|
|
36
|
+
CollabUiSelectItem,
|
|
32
37
|
ParsedCollabLink,
|
|
33
38
|
RelayControlMessage,
|
|
34
39
|
RelayControlToGuest,
|
|
@@ -57,7 +62,7 @@ export type CollabSessionState = SessionState & {
|
|
|
57
62
|
* that serialize into those shapes.
|
|
58
63
|
*/
|
|
59
64
|
export type CollabFrame =
|
|
60
|
-
// guest -> host (hello/abort/agent-cmd/fetch-transcript are taken verbatim from the wire grammar)
|
|
65
|
+
// guest -> host (hello/abort/agent-cmd/fetch-transcript/ui-response are taken verbatim from the wire grammar)
|
|
61
66
|
| Exclude<GuestFrame, { t: "prompt" }>
|
|
62
67
|
| { t: "prompt"; text: string; images?: ImageContent[] }
|
|
63
68
|
// host -> guest
|
|
@@ -93,7 +98,9 @@ export type CollabFrame =
|
|
|
93
98
|
| { t: "bus"; channel: BusChannel; data: unknown }
|
|
94
99
|
/** Full agent-registry snapshot (debounced on registry change). */
|
|
95
100
|
| { t: "agents"; agents: AgentSnapshot[] }
|
|
96
|
-
|
|
101
|
+
| { t: "ui-request"; request: CollabUiRequest }
|
|
102
|
+
| { t: "ui-request-end"; reqId: number }
|
|
103
|
+
/** Targeted reply to fetch-transcript; `error` marks a terminal read failure that guests must surface without hot retrying. */
|
|
97
104
|
| { t: "transcript"; reqId: number; text: string; newSize: number; error?: string }
|
|
98
105
|
| { t: "bye"; reason: string }
|
|
99
106
|
| { t: "error"; message: string };
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
import { MODEL_ROLE_IDS } from "../config/model-roles";
|
|
12
12
|
import type { Settings } from "../config/settings";
|
|
13
13
|
import MODEL_PRIO from "../priority.json" with { type: "json" };
|
|
14
|
+
import { concreteThinkingLevel } from "../thinking";
|
|
14
15
|
|
|
15
16
|
export interface ResolvedCommitModel {
|
|
16
17
|
model: Model<Api>;
|
|
@@ -20,6 +21,11 @@ export interface ResolvedCommitModel {
|
|
|
20
21
|
* central force-refresh + account-rotation policy.
|
|
21
22
|
*/
|
|
22
23
|
apiKey: ApiKey;
|
|
24
|
+
/**
|
|
25
|
+
* Commit-time inference is stateless: session-level auto classification
|
|
26
|
+
* isn't available, so an explicit `:auto` selector collapses to "no
|
|
27
|
+
* override" and the model's own default level fills in.
|
|
28
|
+
*/
|
|
23
29
|
thinkingLevel?: ThinkingLevel;
|
|
24
30
|
}
|
|
25
31
|
|
|
@@ -49,7 +55,7 @@ export async function resolvePrimaryModel(
|
|
|
49
55
|
return {
|
|
50
56
|
model,
|
|
51
57
|
apiKey: modelRegistry.resolver(model),
|
|
52
|
-
thinkingLevel: resolved?.thinkingLevel,
|
|
58
|
+
thinkingLevel: concreteThinkingLevel(resolved?.thinkingLevel),
|
|
53
59
|
};
|
|
54
60
|
}
|
|
55
61
|
|
|
@@ -67,7 +73,7 @@ export async function resolveSmolModel(
|
|
|
67
73
|
return {
|
|
68
74
|
model: resolvedSmol.model,
|
|
69
75
|
apiKey: modelRegistry.resolver(resolvedSmol.model),
|
|
70
|
-
thinkingLevel: resolvedSmol.thinkingLevel,
|
|
76
|
+
thinkingLevel: concreteThinkingLevel(resolvedSmol.thinkingLevel),
|
|
71
77
|
};
|
|
72
78
|
}
|
|
73
79
|
}
|
|
@@ -65,7 +65,9 @@ declare module "@oh-my-pi/pi-tui" {
|
|
|
65
65
|
* Resolve default image-paste shortcuts for the current terminal platform.
|
|
66
66
|
*/
|
|
67
67
|
export function getDefaultPasteImageKeys(platform: NodeJS.Platform = process.platform): KeyId[] {
|
|
68
|
-
|
|
68
|
+
if (platform === "win32") return ["ctrl+v", "alt+v"];
|
|
69
|
+
if (platform === "darwin") return ["ctrl+v", "super+v"];
|
|
70
|
+
return ["ctrl+v"];
|
|
69
71
|
}
|
|
70
72
|
|
|
71
73
|
/**
|
|
@@ -158,6 +158,15 @@ type LlamaCppDiscoveredModelRuntimeMetadata = {
|
|
|
158
158
|
type LlamaCppModelListEntry = {
|
|
159
159
|
id: string;
|
|
160
160
|
runtimeContextWindow?: number;
|
|
161
|
+
/**
|
|
162
|
+
* `--ctx-size` extracted from the entry's `status.args` (rendered CLI arg
|
|
163
|
+
* vector) or `status.preset` INI. Populated for llama-server router-mode
|
|
164
|
+
* presets so unloaded models surface the user's configured window instead
|
|
165
|
+
* of falling through to the 128K default — the router-level `/props`
|
|
166
|
+
* reports a dummy `n_ctx: 0` and `meta.n_ctx` is only merged in after a
|
|
167
|
+
* child instance loads (issue #4190).
|
|
168
|
+
*/
|
|
169
|
+
configuredContextWindow?: number;
|
|
161
170
|
trainingContextWindow?: number;
|
|
162
171
|
};
|
|
163
172
|
|
|
@@ -274,10 +283,60 @@ function parseLlamaCppModelList(payload: unknown): LlamaCppModelListEntry[] {
|
|
|
274
283
|
if (!isRecord(item) || typeof item.id !== "string" || !item.id) {
|
|
275
284
|
return [];
|
|
276
285
|
}
|
|
277
|
-
return [
|
|
286
|
+
return [
|
|
287
|
+
{
|
|
288
|
+
id: item.id,
|
|
289
|
+
...extractLlamaCppModelContextWindows(item),
|
|
290
|
+
configuredContextWindow: extractLlamaCppConfiguredContextWindow(item),
|
|
291
|
+
},
|
|
292
|
+
];
|
|
278
293
|
});
|
|
279
294
|
}
|
|
280
295
|
|
|
296
|
+
// llama-server's `to_args()` renders the long form `--ctx-size` (never `-c`),
|
|
297
|
+
// but tolerate the short form and the embedded `--flag=value` shape so a
|
|
298
|
+
// hand-rolled forwarder cannot silently downgrade the discovered window.
|
|
299
|
+
const LLAMA_CPP_CTX_SIZE_FLAGS = new Set(["--ctx-size", "-c"]);
|
|
300
|
+
|
|
301
|
+
function extractLlamaCppCtxSizeFromArgs(value: unknown): number | undefined {
|
|
302
|
+
if (!Array.isArray(value)) {
|
|
303
|
+
return undefined;
|
|
304
|
+
}
|
|
305
|
+
for (let i = 0; i < value.length; i++) {
|
|
306
|
+
const raw = value[i];
|
|
307
|
+
if (typeof raw !== "string") continue;
|
|
308
|
+
const eq = raw.indexOf("=");
|
|
309
|
+
const flag = eq >= 0 ? raw.slice(0, eq) : raw;
|
|
310
|
+
if (!LLAMA_CPP_CTX_SIZE_FLAGS.has(flag)) continue;
|
|
311
|
+
const rawValue = eq >= 0 ? raw.slice(eq + 1) : value[i + 1];
|
|
312
|
+
const parsed = toPositiveNumberOrUndefined(rawValue);
|
|
313
|
+
if (parsed !== undefined) return parsed;
|
|
314
|
+
}
|
|
315
|
+
return undefined;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// `common_preset::to_ini()` emits one option per line as `<long-arg-without-dashes> = <value>`,
|
|
319
|
+
// so `ctx-size = 8192` is the exact wire form (issue #4190).
|
|
320
|
+
function extractLlamaCppCtxSizeFromIni(value: unknown): number | undefined {
|
|
321
|
+
if (typeof value !== "string") {
|
|
322
|
+
return undefined;
|
|
323
|
+
}
|
|
324
|
+
const match = value.match(/(?:^|\n)\s*ctx-size\s*=\s*(-?\d+)\s*(?:$|\n)/);
|
|
325
|
+
return match ? toPositiveNumberOrUndefined(match[1]) : undefined;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function extractLlamaCppConfiguredContextWindow(item: Record<string, unknown>): number | undefined {
|
|
329
|
+
const status = item.status;
|
|
330
|
+
if (!isRecord(status)) {
|
|
331
|
+
return undefined;
|
|
332
|
+
}
|
|
333
|
+
const fromArgs = extractLlamaCppCtxSizeFromArgs(status.args);
|
|
334
|
+
if (fromArgs !== undefined) {
|
|
335
|
+
return fromArgs;
|
|
336
|
+
}
|
|
337
|
+
return extractLlamaCppCtxSizeFromIni(status.preset);
|
|
338
|
+
}
|
|
339
|
+
|
|
281
340
|
function extractLlamaCppInputCapabilities(payload: Record<string, unknown>): ("text" | "image")[] | undefined {
|
|
282
341
|
const modalities = payload.modalities;
|
|
283
342
|
if (!isRecord(modalities)) {
|
|
@@ -473,6 +532,7 @@ export async function discoverLlamaCppModels(
|
|
|
473
532
|
if (!id) continue;
|
|
474
533
|
const contextWindow =
|
|
475
534
|
item.runtimeContextWindow ??
|
|
535
|
+
item.configuredContextWindow ??
|
|
476
536
|
serverMetadata?.contextWindow ??
|
|
477
537
|
item.trainingContextWindow ??
|
|
478
538
|
DISCOVERY_DEFAULT_CONTEXT_WINDOW;
|
|
@@ -529,7 +589,11 @@ export async function discoverLlamaCppModelRuntimeMetadata(
|
|
|
529
589
|
if (!entry) {
|
|
530
590
|
return undefined;
|
|
531
591
|
}
|
|
532
|
-
const contextWindow =
|
|
592
|
+
const contextWindow =
|
|
593
|
+
entry.runtimeContextWindow ??
|
|
594
|
+
entry.configuredContextWindow ??
|
|
595
|
+
serverMetadata?.contextWindow ??
|
|
596
|
+
entry.trainingContextWindow;
|
|
533
597
|
if (contextWindow === undefined) {
|
|
534
598
|
return undefined;
|
|
535
599
|
}
|
|
@@ -52,7 +52,7 @@ import type { ApiKeyResolver, FetchImpl } from "@oh-my-pi/pi-ai";
|
|
|
52
52
|
import { registerOAuthProvider, unregisterOAuthProviders } from "@oh-my-pi/pi-ai/oauth";
|
|
53
53
|
import type { OAuthCredentials, OAuthLoginCallbacks } from "@oh-my-pi/pi-ai/oauth/types";
|
|
54
54
|
import { getBundledModelReferenceIndex, resolveModelReference } from "@oh-my-pi/pi-catalog/identity";
|
|
55
|
-
import { isBunTestRuntime, isRecord, logger } from "@oh-my-pi/pi-utils";
|
|
55
|
+
import { isBunTestRuntime, isRecord, logger, wrapFetchForExtraCa } from "@oh-my-pi/pi-utils";
|
|
56
56
|
import { parseModelString, resolveProviderModelReference } from "../config/model-resolver";
|
|
57
57
|
import type { AuthStorage, OAuthCredential } from "../session/auth-storage";
|
|
58
58
|
import { type ApiKeyResolverModel, type ApiKeyResolverOptions, createApiKeyResolver } from "./api-key-resolver";
|
|
@@ -646,6 +646,7 @@ function normalizeSuppressedSelector(
|
|
|
646
646
|
if (!trimmed) return trimmed;
|
|
647
647
|
const parsed = parseModelString(trimmed, {
|
|
648
648
|
allowMaxAlias: true,
|
|
649
|
+
allowAutoAlias: true,
|
|
649
650
|
isLiteralModelId: (provider, id) => hasLiveModel?.(provider, id) === true,
|
|
650
651
|
});
|
|
651
652
|
if (!parsed) return trimmed;
|
|
@@ -762,7 +763,7 @@ export class ModelRegistry {
|
|
|
762
763
|
options?.fetch ??
|
|
763
764
|
(isBunTestRuntime()
|
|
764
765
|
? () => Promise.reject(new Error("network disabled in model-registry runtime test"))
|
|
765
|
-
: fetch);
|
|
766
|
+
: wrapFetchForExtraCa(fetch));
|
|
766
767
|
this.#modelsConfigFile = ModelsConfigFile.relocate(modelsPath);
|
|
767
768
|
this.#cacheDbPath = modelsPath ? path.join(path.dirname(modelsPath), "models.db") : undefined;
|
|
768
769
|
// Set up fallback resolver for custom provider API keys
|
|
@@ -1368,7 +1369,10 @@ export class ModelRegistry {
|
|
|
1368
1369
|
return `${providerConfig.provider}:openai-models-list-context-v2`;
|
|
1369
1370
|
}
|
|
1370
1371
|
if (providerConfig.discovery.type === "litellm") {
|
|
1371
|
-
|
|
1372
|
+
// rich-v2 invalidates rows cached before reseller usage-suffix stripping
|
|
1373
|
+
// (stale display names like `MiniMax-M3 (3x usage)`); keep in lockstep
|
|
1374
|
+
// with the catalog package's `litellm:rich-vN` namespace.
|
|
1375
|
+
return `${providerConfig.provider}:litellm-rich-v2`;
|
|
1372
1376
|
}
|
|
1373
1377
|
return providerConfig.provider;
|
|
1374
1378
|
}
|
|
@@ -29,7 +29,13 @@ import { fuzzyMatch } from "@oh-my-pi/pi-tui";
|
|
|
29
29
|
import { logger } from "@oh-my-pi/pi-utils";
|
|
30
30
|
import chalk from "chalk";
|
|
31
31
|
import MODEL_PRIO from "../priority.json" with { type: "json" };
|
|
32
|
-
import {
|
|
32
|
+
import {
|
|
33
|
+
AUTO_THINKING,
|
|
34
|
+
type ConfiguredThinkingLevel,
|
|
35
|
+
concreteThinkingLevel,
|
|
36
|
+
parseThinkingLevel,
|
|
37
|
+
resolveThinkingLevelForModel,
|
|
38
|
+
} from "../thinking";
|
|
33
39
|
import { isAuthenticated, kNoAuth, type ModelRegistry } from "./model-registry";
|
|
34
40
|
import { MODEL_ROLE_IDS, type ModelRole } from "./model-roles";
|
|
35
41
|
import type { Settings } from "./settings";
|
|
@@ -74,24 +80,32 @@ export interface ScopedModel {
|
|
|
74
80
|
|
|
75
81
|
interface ThinkingSuffixOptions {
|
|
76
82
|
allowMaxAlias?: boolean;
|
|
83
|
+
allowAutoAlias?: boolean;
|
|
77
84
|
}
|
|
78
85
|
|
|
79
86
|
interface ModelStringParseOptions extends ThinkingSuffixOptions {
|
|
80
87
|
isLiteralModelId?: (provider: string, id: string) => boolean;
|
|
81
88
|
}
|
|
82
|
-
|
|
89
|
+
// Alias-suffix recognition for the model-pattern parser: `:max` maps to xhigh
|
|
90
|
+
// and `:auto` maps to the auto sentinel. Both are gated behind the alias flags
|
|
91
|
+
// (and the literal-id / exact-match guards on the callers) so a real model id
|
|
92
|
+
// ending in `:max` / `:auto` isn't silently reinterpreted as a thinking suffix.
|
|
93
|
+
const MAX_THINKING_SUFFIX_OPTIONS: ThinkingSuffixOptions = { allowMaxAlias: true, allowAutoAlias: true };
|
|
83
94
|
|
|
84
|
-
function parseThinkingSuffix(value: string, options?: ThinkingSuffixOptions):
|
|
95
|
+
function parseThinkingSuffix(value: string, options?: ThinkingSuffixOptions): ConfiguredThinkingLevel | undefined {
|
|
85
96
|
const level = parseThinkingLevel(value);
|
|
86
97
|
if (level !== undefined) return level;
|
|
87
|
-
|
|
98
|
+
if (options?.allowMaxAlias === true && value === "max") return ThinkingLevel.XHigh;
|
|
99
|
+
if (options?.allowAutoAlias === true && value === AUTO_THINKING) return AUTO_THINKING;
|
|
100
|
+
return undefined;
|
|
88
101
|
}
|
|
89
102
|
|
|
90
103
|
/**
|
|
91
104
|
* Split a trailing `:<level>` thinking selector off a model pattern.
|
|
92
105
|
*
|
|
93
|
-
* `level` is set
|
|
94
|
-
*
|
|
106
|
+
* `level` is set when the suffix parses as a concrete thinking level (or, when
|
|
107
|
+
* the caller opts in via `allowMaxAlias`/`allowAutoAlias`, the `:max` / `:auto`
|
|
108
|
+
* aliases); `base` then has the suffix stripped. Otherwise `base` is the input.
|
|
95
109
|
* `minColonIndex` requires the colon to appear strictly after that index —
|
|
96
110
|
* role-alias callers pass `PREFIX_MODEL_ROLE.length` so the base is at least
|
|
97
111
|
* as long as the `pi/` prefix.
|
|
@@ -100,7 +114,7 @@ function splitThinkingSuffix(
|
|
|
100
114
|
pattern: string,
|
|
101
115
|
minColonIndex = -1,
|
|
102
116
|
options?: ThinkingSuffixOptions,
|
|
103
|
-
): { base: string; level?:
|
|
117
|
+
): { base: string; level?: ConfiguredThinkingLevel } {
|
|
104
118
|
const colonIdx = pattern.lastIndexOf(":");
|
|
105
119
|
if (colonIdx <= minColonIndex) return { base: pattern };
|
|
106
120
|
const level = parseThinkingSuffix(pattern.slice(colonIdx + 1), options);
|
|
@@ -119,12 +133,17 @@ function resolveGlobScopePattern(
|
|
|
119
133
|
pattern: string,
|
|
120
134
|
availableModels: readonly Model<Api>[],
|
|
121
135
|
): { models: Model<Api>[]; thinkingLevel?: ThinkingLevel; explicitThinkingLevel: boolean } {
|
|
136
|
+
// Glob scopes describe which models are enabled, not per-role thinking.
|
|
137
|
+
// Coerce the `auto` sentinel to a concrete-only view so scope callers stay
|
|
138
|
+
// typed on `ThinkingLevel` and `enabledModels: [\"openai/*:auto\"]` doesn't
|
|
139
|
+
// pin a stray per-model level.
|
|
122
140
|
const strictSuffix = splitThinkingSuffix(pattern);
|
|
123
141
|
if (strictSuffix.level !== undefined) {
|
|
142
|
+
const thinkingLevel = concreteThinkingLevel(strictSuffix.level);
|
|
124
143
|
return {
|
|
125
144
|
models: matchingGlobModels(strictSuffix.base, availableModels),
|
|
126
|
-
thinkingLevel
|
|
127
|
-
explicitThinkingLevel:
|
|
145
|
+
thinkingLevel,
|
|
146
|
+
explicitThinkingLevel: thinkingLevel !== undefined,
|
|
128
147
|
};
|
|
129
148
|
}
|
|
130
149
|
|
|
@@ -134,10 +153,11 @@ function resolveGlobScopePattern(
|
|
|
134
153
|
if (literalMatches.length > 0) {
|
|
135
154
|
return { models: literalMatches, thinkingLevel: undefined, explicitThinkingLevel: false };
|
|
136
155
|
}
|
|
156
|
+
const thinkingLevel = concreteThinkingLevel(maxSuffix.level);
|
|
137
157
|
return {
|
|
138
158
|
models: matchingGlobModels(maxSuffix.base, availableModels),
|
|
139
|
-
thinkingLevel
|
|
140
|
-
explicitThinkingLevel:
|
|
159
|
+
thinkingLevel,
|
|
160
|
+
explicitThinkingLevel: thinkingLevel !== undefined,
|
|
141
161
|
};
|
|
142
162
|
}
|
|
143
163
|
|
|
@@ -155,7 +175,7 @@ function resolveGlobScopePattern(
|
|
|
155
175
|
export function parseModelString(
|
|
156
176
|
modelStr: string,
|
|
157
177
|
options?: ModelStringParseOptions,
|
|
158
|
-
): { provider: string; id: string; thinkingLevel?:
|
|
178
|
+
): { provider: string; id: string; thinkingLevel?: ConfiguredThinkingLevel } | undefined {
|
|
159
179
|
const slashIdx = modelStr.indexOf("/");
|
|
160
180
|
if (slashIdx <= 0) return undefined;
|
|
161
181
|
const id = modelStr.slice(slashIdx + 1);
|
|
@@ -208,7 +228,7 @@ export function formatModelStringWithRouting(model: Model<Api>): string {
|
|
|
208
228
|
return upstream ? `${selector}@${upstream}` : selector;
|
|
209
229
|
}
|
|
210
230
|
|
|
211
|
-
export function formatModelSelectorValue(selector: string, thinkingLevel:
|
|
231
|
+
export function formatModelSelectorValue(selector: string, thinkingLevel: ConfiguredThinkingLevel | undefined): string {
|
|
212
232
|
return thinkingLevel && thinkingLevel !== ThinkingLevel.Inherit ? `${selector}:${thinkingLevel}` : selector;
|
|
213
233
|
}
|
|
214
234
|
|
|
@@ -711,7 +731,7 @@ function matchModel(
|
|
|
711
731
|
export interface ParsedModelResult {
|
|
712
732
|
model: Model<Api> | undefined;
|
|
713
733
|
/** Thinking level if explicitly specified in pattern, undefined otherwise */
|
|
714
|
-
thinkingLevel?:
|
|
734
|
+
thinkingLevel?: ConfiguredThinkingLevel;
|
|
715
735
|
/** Upstream provider slug from an `@upstream` routing selector, if present. */
|
|
716
736
|
upstream?: string;
|
|
717
737
|
warning: string | undefined;
|
|
@@ -989,7 +1009,7 @@ export function resolveAgentModelPatterns(options: AgentModelPatternResolutionOp
|
|
|
989
1009
|
*/
|
|
990
1010
|
export interface ResolvedModelRoleValue {
|
|
991
1011
|
model: Model<Api> | undefined;
|
|
992
|
-
thinkingLevel?:
|
|
1012
|
+
thinkingLevel?: ConfiguredThinkingLevel;
|
|
993
1013
|
explicitThinkingLevel: boolean;
|
|
994
1014
|
warning: string | undefined;
|
|
995
1015
|
}
|
|
@@ -1021,7 +1041,9 @@ export function resolveModelRoleValue(
|
|
|
1021
1041
|
return {
|
|
1022
1042
|
model: resolved.model,
|
|
1023
1043
|
thinkingLevel: resolved.explicitThinkingLevel
|
|
1024
|
-
?
|
|
1044
|
+
? resolved.thinkingLevel === AUTO_THINKING
|
|
1045
|
+
? AUTO_THINKING
|
|
1046
|
+
: (resolveThinkingLevelForModel(resolved.model, resolved.thinkingLevel) ?? resolved.thinkingLevel)
|
|
1025
1047
|
: resolved.thinkingLevel,
|
|
1026
1048
|
explicitThinkingLevel: resolved.explicitThinkingLevel,
|
|
1027
1049
|
warning: resolved.warning,
|
|
@@ -1048,7 +1070,7 @@ export function extractExplicitThinkingSelector(
|
|
|
1048
1070
|
value: string | undefined,
|
|
1049
1071
|
settings?: Settings,
|
|
1050
1072
|
options?: ExplicitThinkingSelectorOptions,
|
|
1051
|
-
):
|
|
1073
|
+
): ConfiguredThinkingLevel | undefined {
|
|
1052
1074
|
if (!value) return undefined;
|
|
1053
1075
|
const normalized = value.trim();
|
|
1054
1076
|
if (!normalized || normalized === DEFAULT_MODEL_ROLE) return undefined;
|
|
@@ -1127,7 +1149,7 @@ export function resolveModelOverride(
|
|
|
1127
1149
|
modelPatterns: string[],
|
|
1128
1150
|
modelRegistry: ModelLookupRegistry,
|
|
1129
1151
|
settings?: Settings,
|
|
1130
|
-
): { model?: Model<Api>; thinkingLevel?:
|
|
1152
|
+
): { model?: Model<Api>; thinkingLevel?: ConfiguredThinkingLevel; explicitThinkingLevel: boolean } {
|
|
1131
1153
|
if (modelPatterns.length === 0) return { explicitThinkingLevel: false };
|
|
1132
1154
|
const availableModels = modelRegistry.getAvailable();
|
|
1133
1155
|
const matchPreferences = getModelMatchPreferences(settings);
|
|
@@ -1171,7 +1193,7 @@ export async function resolveModelOverrideWithAuthFallback(
|
|
|
1171
1193
|
settings?: Settings,
|
|
1172
1194
|
): Promise<{
|
|
1173
1195
|
model?: Model<Api>;
|
|
1174
|
-
thinkingLevel?:
|
|
1196
|
+
thinkingLevel?: ConfiguredThinkingLevel;
|
|
1175
1197
|
explicitThinkingLevel: boolean;
|
|
1176
1198
|
authFallbackUsed: boolean;
|
|
1177
1199
|
}> {
|
|
@@ -1207,7 +1229,7 @@ export function resolveRoleSelection(
|
|
|
1207
1229
|
roles: readonly string[],
|
|
1208
1230
|
settings: Settings,
|
|
1209
1231
|
availableModels: Model<Api>[],
|
|
1210
|
-
): { model: Model<Api>; thinkingLevel?:
|
|
1232
|
+
): { model: Model<Api>; thinkingLevel?: ConfiguredThinkingLevel } | undefined {
|
|
1211
1233
|
const matchPreferences = getModelMatchPreferences(settings);
|
|
1212
1234
|
for (const role of roles) {
|
|
1213
1235
|
const resolved = resolveModelRoleValue(settings.getModelRole(role), availableModels, {
|
|
@@ -1232,7 +1254,7 @@ export function resolveRoleSelection(
|
|
|
1232
1254
|
export function resolveAdvisorRoleSelection(
|
|
1233
1255
|
settings: Settings,
|
|
1234
1256
|
availableModels: Model<Api>[],
|
|
1235
|
-
): { model: Model<Api>; thinkingLevel?:
|
|
1257
|
+
): { model: Model<Api>; thinkingLevel?: ConfiguredThinkingLevel } | undefined {
|
|
1236
1258
|
const resolved = resolveModelRoleValue(`${PREFIX_MODEL_ROLE}advisor`, availableModels, {
|
|
1237
1259
|
settings,
|
|
1238
1260
|
matchPreferences: getModelMatchPreferences(settings),
|
|
@@ -1307,7 +1329,13 @@ export async function resolveModelScope(
|
|
|
1307
1329
|
continue;
|
|
1308
1330
|
}
|
|
1309
1331
|
|
|
1310
|
-
|
|
1332
|
+
// Scoped models (Ctrl+P cycling) carry concrete per-model overrides;
|
|
1333
|
+
// `auto` lives on the session, so drop the sentinel here.
|
|
1334
|
+
if (thinkingLevel === AUTO_THINKING) {
|
|
1335
|
+
addScopedModel(model, undefined, false);
|
|
1336
|
+
} else {
|
|
1337
|
+
addScopedModel(model, thinkingLevel, explicitThinkingLevel);
|
|
1338
|
+
}
|
|
1311
1339
|
}
|
|
1312
1340
|
|
|
1313
1341
|
return scopedModels;
|
|
@@ -1393,7 +1421,7 @@ export function filterAvailableModelsByEnabledPatterns(
|
|
|
1393
1421
|
export interface ResolveCliModelResult {
|
|
1394
1422
|
model: Model<Api> | undefined;
|
|
1395
1423
|
selector?: string;
|
|
1396
|
-
thinkingLevel?:
|
|
1424
|
+
thinkingLevel?: ConfiguredThinkingLevel;
|
|
1397
1425
|
warning: string | undefined;
|
|
1398
1426
|
error: string | undefined;
|
|
1399
1427
|
}
|
|
@@ -127,6 +127,9 @@ const RemoteCompactionSchema = type({
|
|
|
127
127
|
"api?": ApiSchema,
|
|
128
128
|
"endpoint?": "string",
|
|
129
129
|
"model?": "string",
|
|
130
|
+
"v2StreamingEnabled?": "boolean",
|
|
131
|
+
"v2Endpoint?": "string",
|
|
132
|
+
"streamingEndpoint?": "string",
|
|
130
133
|
}).narrow((value, ctx) => {
|
|
131
134
|
if (value.endpoint !== undefined && typeof value.endpoint === "string" && value.endpoint.length === 0) {
|
|
132
135
|
return ctx.mustBe("remoteCompaction.endpoint a non-empty string");
|
|
@@ -134,6 +137,16 @@ const RemoteCompactionSchema = type({
|
|
|
134
137
|
if (value.model !== undefined && typeof value.model === "string" && value.model.length === 0) {
|
|
135
138
|
return ctx.mustBe("remoteCompaction.model a non-empty string");
|
|
136
139
|
}
|
|
140
|
+
if (value.v2Endpoint !== undefined && typeof value.v2Endpoint === "string" && value.v2Endpoint.length === 0) {
|
|
141
|
+
return ctx.mustBe("remoteCompaction.v2Endpoint a non-empty string");
|
|
142
|
+
}
|
|
143
|
+
if (
|
|
144
|
+
value.streamingEndpoint !== undefined &&
|
|
145
|
+
typeof value.streamingEndpoint === "string" &&
|
|
146
|
+
value.streamingEndpoint.length === 0
|
|
147
|
+
) {
|
|
148
|
+
return ctx.mustBe("remoteCompaction.streamingEndpoint a non-empty string");
|
|
149
|
+
}
|
|
137
150
|
return true;
|
|
138
151
|
});
|
|
139
152
|
|