@oh-my-pi/pi-coding-agent 17.0.4 → 17.0.5
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 +86 -43
- package/dist/cli.js +3540 -3521
- package/dist/types/advisor/advise-tool.d.ts +3 -0
- package/dist/types/advisor/runtime.d.ts +7 -1
- package/dist/types/async/job-manager.d.ts +2 -0
- package/dist/types/config/model-registry.d.ts +7 -0
- package/dist/types/config/model-resolver.d.ts +8 -0
- package/dist/types/config/models-config-schema.d.ts +10 -0
- package/dist/types/config/models-config.d.ts +6 -0
- package/dist/types/dap/client.d.ts +10 -0
- package/dist/types/dap/types.d.ts +6 -5
- package/dist/types/extensibility/extensions/runner.d.ts +4 -2
- package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +16 -0
- package/dist/types/launch/spawn-options.d.ts +10 -0
- package/dist/types/launch/spawn-options.test.d.ts +1 -0
- package/dist/types/modes/components/status-line/component.d.ts +2 -3
- package/dist/types/modes/components/status-line/types.d.ts +3 -1
- package/dist/types/modes/components/tool-execution.d.ts +2 -0
- package/dist/types/modes/components/transcript-container.d.ts +4 -3
- package/dist/types/modes/components/tree-selector.d.ts +6 -2
- package/dist/types/modes/interactive-mode.d.ts +2 -0
- package/dist/types/modes/print-mode.d.ts +4 -0
- package/dist/types/modes/rpc/rpc-input.d.ts +5 -0
- package/dist/types/modes/rpc/rpc-mode.d.ts +1 -1
- package/dist/types/modes/types.d.ts +2 -0
- package/dist/types/sdk.d.ts +2 -0
- package/dist/types/session/agent-session.d.ts +19 -1
- package/dist/types/session/messages.d.ts +6 -0
- package/dist/types/tools/browser/aria/aria-snapshot.d.ts +17 -7
- package/dist/types/tools/browser/cmux/socket-client.d.ts +2 -0
- package/dist/types/tools/browser/tab-protocol.d.ts +2 -0
- package/dist/types/tools/browser/tab-worker.d.ts +2 -0
- package/dist/types/tools/gh.d.ts +5 -3
- package/dist/types/tools/hub/index.d.ts +1 -1
- package/dist/types/tools/hub/jobs.d.ts +1 -0
- package/dist/types/tools/hub/types.d.ts +2 -0
- package/dist/types/tools/tool-timeouts.d.ts +1 -1
- package/dist/types/utils/git.d.ts +33 -0
- package/dist/types/web/search/providers/codex.d.ts +1 -1
- package/package.json +12 -12
- package/src/advisor/__tests__/advisor.test.ts +150 -0
- package/src/advisor/advise-tool.ts +4 -0
- package/src/advisor/runtime.ts +38 -14
- package/src/async/job-manager.ts +3 -0
- package/src/cli/bench-cli.ts +8 -2
- package/src/cli/dry-balance-cli.ts +1 -0
- package/src/config/model-registry.ts +89 -8
- package/src/config/model-resolver.ts +78 -14
- package/src/config/models-config-schema.ts +1 -0
- package/src/config/settings.ts +3 -1
- package/src/dap/client.ts +168 -1
- package/src/dap/config.ts +51 -1
- package/src/dap/session.ts +575 -234
- package/src/dap/types.ts +6 -5
- package/src/discovery/agents.ts +2 -2
- package/src/extensibility/extensions/runner.ts +6 -4
- package/src/extensibility/legacy-pi-coding-agent-shim.ts +26 -0
- package/src/launch/broker.ts +34 -31
- package/src/launch/client.ts +7 -1
- package/src/launch/spawn-options.test.ts +31 -0
- package/src/launch/spawn-options.ts +17 -0
- package/src/lsp/types.ts +5 -1
- package/src/main.ts +17 -4
- package/src/mcp/transports/stdio.test.ts +9 -1
- package/src/modes/components/ask-dialog.ts +137 -73
- package/src/modes/components/status-line/component.ts +2 -2
- package/src/modes/components/status-line/segments.ts +20 -3
- package/src/modes/components/status-line/types.ts +3 -1
- package/src/modes/components/tool-execution.ts +5 -0
- package/src/modes/components/transcript-container.ts +23 -122
- package/src/modes/components/tree-selector.ts +10 -4
- package/src/modes/controllers/event-controller.ts +1 -2
- package/src/modes/controllers/input-controller.ts +1 -1
- package/src/modes/controllers/selector-controller.ts +29 -8
- package/src/modes/interactive-mode.ts +40 -8
- package/src/modes/noninteractive-dispose.test.ts +12 -1
- package/src/modes/print-mode.ts +21 -9
- package/src/modes/rpc/rpc-input.ts +38 -0
- package/src/modes/rpc/rpc-mode.ts +7 -2
- package/src/modes/setup-wizard/scenes/sign-in.ts +3 -1
- package/src/modes/types.ts +2 -0
- package/src/modes/utils/ui-helpers.ts +3 -2
- package/src/prompts/tools/browser.md +3 -2
- package/src/prompts/tools/debug.md +2 -7
- package/src/prompts/tools/github.md +6 -1
- package/src/prompts/tools/hub.md +4 -2
- package/src/prompts/tools/task-async-contract.md +1 -0
- package/src/prompts/tools/task.md +9 -2
- package/src/sdk.ts +38 -6
- package/src/session/agent-session.ts +395 -162
- package/src/session/messages.test.ts +91 -0
- package/src/session/messages.ts +248 -110
- package/src/slash-commands/builtin-registry.ts +1 -0
- package/src/task/executor.ts +59 -33
- package/src/task/index.ts +46 -9
- package/src/task/worktree.ts +10 -0
- package/src/tools/browser/aria/aria-snapshot.ts +36 -8
- package/src/tools/browser/cmux/cmux-tab.ts +2 -1
- package/src/tools/browser/cmux/socket-client.ts +139 -3
- package/src/tools/browser/run-cancellation.ts +4 -0
- package/src/tools/browser/tab-protocol.ts +2 -0
- package/src/tools/browser/tab-supervisor.ts +21 -11
- package/src/tools/browser/tab-worker.ts +199 -33
- package/src/tools/debug.ts +3 -0
- package/src/tools/gh.ts +40 -2
- package/src/tools/hub/index.ts +4 -1
- package/src/tools/hub/jobs.ts +42 -1
- package/src/tools/hub/types.ts +2 -0
- package/src/tools/tool-timeouts.ts +1 -1
- package/src/utils/git.ts +237 -0
- package/src/web/search/index.ts +9 -5
- package/src/web/search/providers/codex.ts +195 -99
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claims Bun's singleton stdin reader immediately and exposes a separately readable stream.
|
|
3
|
+
* RPC startup uses this before extension discovery so in-process modules cannot steal protocol input.
|
|
4
|
+
*/
|
|
5
|
+
export function claimRpcInput(): ReadableStream<Uint8Array> {
|
|
6
|
+
const reader = Bun.stdin.stream().getReader();
|
|
7
|
+
let released = false;
|
|
8
|
+
const release = () => {
|
|
9
|
+
if (released) return;
|
|
10
|
+
released = true;
|
|
11
|
+
try {
|
|
12
|
+
reader.releaseLock();
|
|
13
|
+
} catch {}
|
|
14
|
+
};
|
|
15
|
+
return new ReadableStream({
|
|
16
|
+
async pull(controller) {
|
|
17
|
+
try {
|
|
18
|
+
const result = await reader.read();
|
|
19
|
+
if (result.done) {
|
|
20
|
+
release();
|
|
21
|
+
controller.close();
|
|
22
|
+
} else {
|
|
23
|
+
controller.enqueue(result.value);
|
|
24
|
+
}
|
|
25
|
+
} catch (error) {
|
|
26
|
+
release();
|
|
27
|
+
controller.error(error);
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
async cancel() {
|
|
31
|
+
try {
|
|
32
|
+
await reader.cancel();
|
|
33
|
+
} finally {
|
|
34
|
+
release();
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
}
|
|
@@ -34,6 +34,7 @@ import type { EventBus } from "../../utils/event-bus";
|
|
|
34
34
|
import { initializeExtensions } from "../runtime-init";
|
|
35
35
|
import { isRpcHostToolResult, isRpcHostToolUpdate, RpcHostToolBridge } from "./host-tools";
|
|
36
36
|
import { isRpcHostUriResult, RpcHostUriBridge } from "./host-uris";
|
|
37
|
+
import { claimRpcInput } from "./rpc-input";
|
|
37
38
|
import { RpcSubagentRegistry, readRpcSubagentTranscript } from "./rpc-subagents";
|
|
38
39
|
import type {
|
|
39
40
|
RpcCommand,
|
|
@@ -607,6 +608,7 @@ export async function runRpcMode(
|
|
|
607
608
|
session: AgentSession,
|
|
608
609
|
setToolUIContext?: (uiContext: ExtensionUIContext, hasUI: boolean) => void,
|
|
609
610
|
eventBus?: EventBus,
|
|
611
|
+
input: ReadableStream<Uint8Array> = claimRpcInput(),
|
|
610
612
|
): Promise<never> {
|
|
611
613
|
// Signal to RPC clients that the server is ready to accept commands
|
|
612
614
|
// Suppress terminal notifications: they write \x07 (BEL) or OSC sequences directly to
|
|
@@ -1327,7 +1329,10 @@ export async function runRpcMode(
|
|
|
1327
1329
|
return (await uiCtx.input(prompt.message, prompt.placeholder, { timeout: 600_000 })) ?? "";
|
|
1328
1330
|
},
|
|
1329
1331
|
});
|
|
1330
|
-
|
|
1332
|
+
// Provider-scoped online refresh so the just-persisted credential
|
|
1333
|
+
// re-runs discovery instead of reusing a fresh authoritative cache
|
|
1334
|
+
// row (#5780).
|
|
1335
|
+
await session.modelRegistry.refreshProvider(command.providerId, "online");
|
|
1331
1336
|
return success(id, "login", { providerId: command.providerId });
|
|
1332
1337
|
} catch (err: unknown) {
|
|
1333
1338
|
return error(id, "login", err instanceof Error ? err.message : String(err));
|
|
@@ -1381,7 +1386,7 @@ export async function runRpcMode(
|
|
|
1381
1386
|
// line is reported as an error frame and the loop keeps running instead of
|
|
1382
1387
|
// throwing out of the generator and killing the whole process (issue #5194).
|
|
1383
1388
|
const decoder = new TextDecoder();
|
|
1384
|
-
for await (const line of readLines(Bun.stdin.stream())) {
|
|
1389
|
+
for await (const line of readLines(input ?? Bun.stdin.stream())) {
|
|
1385
1390
|
const text = decoder.decode(line).trim();
|
|
1386
1391
|
if (!text) continue;
|
|
1387
1392
|
let parsed: unknown;
|
|
@@ -224,7 +224,9 @@ export class SignInTab implements SetupTab {
|
|
|
224
224
|
onManualCodeInput: () =>
|
|
225
225
|
this.#showPrompt({ message: "Paste the authorization code (or full redirect URL):" }),
|
|
226
226
|
});
|
|
227
|
-
|
|
227
|
+
// Provider-scoped online refresh so the just-persisted credential re-runs
|
|
228
|
+
// discovery instead of reusing a fresh authoritative cache row (#5780).
|
|
229
|
+
await this.host.ctx.session.modelRegistry.refreshProvider(providerId, "online");
|
|
228
230
|
if (this.#disposed) return;
|
|
229
231
|
this.#statusLines = [
|
|
230
232
|
theme.fg("success", `${theme.status.success} Signed in to ${providerId}`),
|
package/src/modes/types.ts
CHANGED
|
@@ -159,6 +159,7 @@ export interface InteractiveModeContext {
|
|
|
159
159
|
goalModeEnabled: boolean;
|
|
160
160
|
goalModePaused: boolean;
|
|
161
161
|
loopModeEnabled: boolean;
|
|
162
|
+
loopModePaused: boolean;
|
|
162
163
|
loopPrompt?: string;
|
|
163
164
|
loopLimit?: LoopLimitRuntime;
|
|
164
165
|
planModePlanFilePath?: string;
|
|
@@ -413,6 +414,7 @@ export interface InteractiveModeContext {
|
|
|
413
414
|
handleGoalModeCommand(rest?: string): Promise<void>;
|
|
414
415
|
handleGuidedGoalCommand(rest?: string): Promise<void>;
|
|
415
416
|
handleLoopCommand(args?: string): Promise<string | undefined>;
|
|
417
|
+
setLoopPrompt(prompt: string): void;
|
|
416
418
|
disableLoopMode(): void;
|
|
417
419
|
pauseLoop(): void;
|
|
418
420
|
handlePlanApproval(details: PlanApprovalDetails): Promise<void>;
|
|
@@ -519,10 +519,10 @@ export class UiHelpers {
|
|
|
519
519
|
const images: ImageContent[] = message.content.filter(
|
|
520
520
|
(content): content is ImageContent => content.type === "image",
|
|
521
521
|
);
|
|
522
|
-
if (images.length > 0 && assistantComponent
|
|
522
|
+
if (images.length > 0 && assistantComponent) {
|
|
523
523
|
assistantComponent.setToolResultImages(message.toolCallId, images);
|
|
524
524
|
const hasText = message.content.some(c => c.type === "text");
|
|
525
|
-
if (!hasText) {
|
|
525
|
+
if (!hasText && settings.get("terminal.showImages")) {
|
|
526
526
|
readToolCallArgs.delete(message.toolCallId);
|
|
527
527
|
readToolCallAssistantComponents.delete(message.toolCallId);
|
|
528
528
|
continue;
|
|
@@ -746,6 +746,7 @@ export class UiHelpers {
|
|
|
746
746
|
const hintText = theme.fg("dim", ` ${theme.tree.hook} ${dequeueKey} to edit`);
|
|
747
747
|
this.ctx.pendingMessagesContainer.addChild(new TruncatedText(hintText, 1, 0));
|
|
748
748
|
}
|
|
749
|
+
this.ctx.ui.requestComponentRender(this.ctx.pendingMessagesContainer);
|
|
749
750
|
}
|
|
750
751
|
|
|
751
752
|
queueCompactionMessage(text: string, mode: "steer" | "followUp", images?: ImageContent[]): void {
|
|
@@ -6,7 +6,7 @@ Drives real Chromium tab; full puppeteer access via JS.
|
|
|
6
6
|
- `run` scope: `page`, `browser`, `tab`, `display`, `assert`, `wait` available. `wait(fn)` polls until truthy — use instead of polling inside `tab.evaluate`.
|
|
7
7
|
|
|
8
8
|
- `tab` helpers (drop to raw puppeteer `page` for anything uncovered):
|
|
9
|
-
Element handles: `tab.ref("e5")` / `tab.id(n)`.
|
|
9
|
+
Element handles: `tab.ref("e5")` / `tab.id(n)` return a handle you call methods on directly — `(await tab.id(n)).click()`. Handles are NOT selectors: `tab.click`/`type`/`fill`/`waitFor*` take STRING selectors only. Snapshot refs work in any selector slot: `tab.click("e5")` ≡ `tab.click("aria-ref=e5")`.
|
|
10
10
|
Simple: `tab.goto`, `tab.click`, `tab.type`, `tab.fill`, `tab.press`, `tab.scroll`, `tab.scrollIntoView`, `tab.drag`, `tab.uploadFile`, `tab.select`, `tab.screenshot`, `tab.extract`, `tab.evaluate`.
|
|
11
11
|
Waits: `tab.waitFor`, `tab.waitForSelector`, `tab.waitForUrl`, `tab.waitForResponse`, `tab.waitForNavigation`.
|
|
12
12
|
Snapshots: `tab.observe()` → accessibility tree; `tab.ariaSnapshot()` → ARIA YAML with `[ref=eN]`.
|
|
@@ -14,8 +14,9 @@ Drives real Chromium tab; full puppeteer access via JS.
|
|
|
14
14
|
Gotchas:
|
|
15
15
|
- `tab.fill` NEVER works for `<select>` — use `tab.select`.
|
|
16
16
|
- `tab.waitForNavigation` must start BEFORE the trigger click.
|
|
17
|
-
- Navigation
|
|
17
|
+
- Navigation and re-renders (virtualized lists, SPA updates) invalidate ids/refs — re-observe or re-snapshot, then act in the same cell.
|
|
18
18
|
- Stalled actions fail fast with named error, never whole-cell timeout.
|
|
19
|
+
- Raw request interception is run-scoped: run end removes `request` handlers, disables interception, releases held requests.
|
|
19
20
|
|
|
20
21
|
- `app.path` → NEVER tamper with a real desktop app (no stealth patches).
|
|
21
22
|
- Selectors: CSS + puppeteer `aria/…`, `text/…`, `xpath/…`, `pierce/…`. Playwright-only pseudos (`:has-text()`, `:visible`) are REJECTED.
|
|
@@ -1,8 +1,3 @@
|
|
|
1
1
|
Debugger access. Prefer over bash for program state, breakpoints, stepping, or thread inspection.
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
Adapters:
|
|
6
|
-
- Python: `debugpy` (`pip install debugpy`)
|
|
7
|
-
- Go: Delve (`go install github.com/go-delve/delve/cmd/dlv@latest`)
|
|
8
|
-
- Ruby: `rdbg` (`gem install debug`)
|
|
2
|
+
Only one active session at a time. `program` is a target path, not a shell command.
|
|
3
|
+
Directories need a directory-capable adapter (e.g. `dlv`).
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
Op-based `gh` wrapper: repos, PRs, search, checkout, push, Actions watch. Read an issue/PR via `issue://<N>`/`pr://<N>`. PR diffs: `pr://<N>/diff` (file listing), `pr://<N>/diff/<i>` (file slice, 1-indexed), `pr://<N>/diff/all` (full diff).
|
|
1
|
+
Op-based `gh` wrapper: repos, repository files, PRs, search, checkout, push, Actions watch. Read an issue/PR via `issue://<N>`/`pr://<N>`. PR diffs: `pr://<N>/diff` (file listing), `pr://<N>/diff/<i>` (file slice, 1-indexed), `pr://<N>/diff/all` (full diff).
|
|
2
2
|
|
|
3
3
|
<instruction>
|
|
4
4
|
Pick op via `op`. Beyond the field descriptions, per op:
|
|
5
5
|
- `repo_view` — omit `repo` to view the current checkout.
|
|
6
|
+
- `file_read` — reads `path` from `repo`; omit `repo` for the current checkout and `branch` for its default branch.
|
|
6
7
|
- `pr_create` — `head` defaults to the current branch.
|
|
7
8
|
- `pr_checkout` — checks PR(s) out into dedicated git worktrees, not your working tree; pass an array of `pr` to batch multiple in one call.
|
|
8
9
|
- `pr_push` — requires the branch to have been checked out first via `op: pr_checkout`.
|
|
@@ -15,3 +16,7 @@ Pick op via `op`. Beyond the field descriptions, per op:
|
|
|
15
16
|
<output>
|
|
16
17
|
Concise summary per op. `run_watch` failures save full logs to a session artifact.
|
|
17
18
|
</output>
|
|
19
|
+
|
|
20
|
+
<critical>
|
|
21
|
+
GitHub-hosted repository file? MUST use `file_read`; NEVER `curl`/`wget`.
|
|
22
|
+
</critical>
|
package/src/prompts/tools/hub.md
CHANGED
|
@@ -3,7 +3,7 @@ Use `op: "list"` to discover peers. Address peers by exact roster ID — NEVER i
|
|
|
3
3
|
|
|
4
4
|
# Messaging & Jobs
|
|
5
5
|
|
|
6
|
-
Background jobs deliver
|
|
6
|
+
Background jobs auto-deliver when they finish. You NEVER need to poll; if `jobs`/`wait` observes a settled job first, that snapshot is the delivery and suppresses duplicate `async-result`.
|
|
7
7
|
|
|
8
8
|
- **`send`** (with `to`): fire-and-forget, NEVER blocks. Delivery receipts (`delivered`/`failed`) immediate; `failed` → peer gone, don't retry.
|
|
9
9
|
Sending wakes `idle`/`parked` peers. Answering: lead with answer, NEVER quote, set `replyTo`.
|
|
@@ -12,7 +12,9 @@ Background jobs deliver their results automatically the moment they finish. You
|
|
|
12
12
|
- Bare `wait` watches every running job AND incoming messages. NEVER pass an array of every running ID; `ids` narrows to specific jobs, `from` to one peer (or use `await: true` on send).
|
|
13
13
|
- **`inbox`**: drain queued messages without blocking.
|
|
14
14
|
- **`cancel`**: kill background jobs by `ids` when they have hung, stalled, or are no longer needed. Returns immediately.
|
|
15
|
-
- **`jobs`**: status snapshot of every job without waiting. Also names running subagents with no job entry — coordinate with those via `send`.
|
|
15
|
+
- **`jobs`**: status snapshot of every job without waiting. A settled row consumes auto-delivery. Also names running subagents with no job entry — coordinate with those via `send`.
|
|
16
|
+
- Job rows are process-local and expire roughly five minutes after settlement. Afterward, use the agent ID with `send`, `agent://<id>`, or `history://<id>`.
|
|
17
|
+
- `completed` means successful yield/job exit, not artifact acceptance. Verify claimed changes.
|
|
16
18
|
- NEVER use shell tools, grep, or read other sessions' files to figure out what a peer is doing. Message them directly.
|
|
17
19
|
- NEVER use hub messaging for something a tool can answer (e.g., grepping codebase, running a build).
|
|
18
20
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
No polling is needed. Inspecting a settled job with `hub jobs` or `hub wait` makes that snapshot its delivery, so no duplicate `async-result` follows. Job IDs live in process memory for roughly five minutes after settlement; afterward, use the agent ID with `hub send`, `agent://<id>`, or `history://<id>`. `completed` means the subagent yielded successfully, not that claimed artifacts were verified.
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
{{#if asyncEnabled}}{{#if batchEnabled}}Delegate work to background subagents by passing multiple items in a single `tasks[]` batch.
|
|
2
|
-
Execution does not block — you receive IDs immediately
|
|
3
|
-
Execution does not block — you receive an ID immediately
|
|
2
|
+
Execution does not block — you receive IDs immediately.{{else}}Delegate work to ONE background subagent per call.
|
|
3
|
+
Execution does not block — you receive an ID immediately.{{/if}}{{#if hasBlockingAgents}}
|
|
4
4
|
Agents marked BLOCKING run inline — results return in this call; non-blocking items in the same batch still spawn as background jobs.{{/if}}{{else}}{{#if batchEnabled}}Run subagents synchronously by passing items in a `tasks[]` batch. Execution blocks until all work finishes.{{else}}Run ONE subagent synchronously. Execution blocks until work finishes.{{/if}}{{/if}}
|
|
5
|
+
{{#if asyncEnabled}}
|
|
6
|
+
|
|
7
|
+
# Async Job Contract
|
|
8
|
+
- Results auto-deliver. A settled `hub jobs`/`hub wait` snapshot is the delivery; no duplicate `async-result` follows.
|
|
9
|
+
- Job IDs are process-local and expire roughly five minutes after settlement. Afterward, use the agent ID with `hub send`, `agent://<id>`, or `history://<id>`.
|
|
10
|
+
- `completed` means successful yield/job exit, not artifact acceptance. Verify claimed changes.
|
|
11
|
+
{{/if}}
|
|
5
12
|
|
|
6
13
|
# Task Design
|
|
7
14
|
- **Agent typing:** Pick each item's `agent` type. Read-only research MUST use `agent: "scout"` (faster model). Use default worker only when no specialist fits.
|
package/src/sdk.ts
CHANGED
|
@@ -50,6 +50,7 @@ import {
|
|
|
50
50
|
parseModelString,
|
|
51
51
|
pickDefaultAvailableModel,
|
|
52
52
|
resolveAllowedModels,
|
|
53
|
+
resolveCliModel,
|
|
53
54
|
resolveConfiguredModelPatterns,
|
|
54
55
|
resolveModelRoleValue,
|
|
55
56
|
} from "./config/model-resolver";
|
|
@@ -389,6 +390,8 @@ export interface CreateAgentSessionOptions {
|
|
|
389
390
|
modelPatternAuthFallback?: string;
|
|
390
391
|
/** Role name used to install retry fallbacks after deferred subagent patterns resolve. */
|
|
391
392
|
modelPatternFallbackRole?: string;
|
|
393
|
+
/** Validated default retry chain to install when a deferred singleton pattern resolves. */
|
|
394
|
+
modelPatternDefaultFallbackChain?: string[];
|
|
392
395
|
/** Thinking selector. Default: from settings, else unset */
|
|
393
396
|
thinkingLevel?: ConfiguredThinkingLevel;
|
|
394
397
|
/** Models available for cycling (Ctrl+P in interactive mode) */
|
|
@@ -2066,13 +2069,35 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2066
2069
|
}
|
|
2067
2070
|
}
|
|
2068
2071
|
// Resolve deferred --model/subagent patterns now that extension models are
|
|
2069
|
-
// registered.
|
|
2070
|
-
//
|
|
2071
|
-
// path (resolveModelOverride → resolveModelRoleValue) accepts.
|
|
2072
|
+
// registered. Use the same CLI resolver as the immediate path so bare role
|
|
2073
|
+
// names, exact model names, and provider selectors keep one precedence rule.
|
|
2072
2074
|
if (!model && deferredModelPatterns.length > 0) {
|
|
2073
|
-
const expandedModelPatterns = resolveConfiguredModelPatterns(deferredModelPatterns, settings);
|
|
2074
2075
|
const availableModels = modelRegistry.getAll();
|
|
2075
2076
|
const matchPreferences = getModelMatchPreferences(settings);
|
|
2077
|
+
const expandedModelPatterns = deferredModelPatterns.flatMap(pattern =>
|
|
2078
|
+
pattern.split(",").flatMap(selector => {
|
|
2079
|
+
const trimmedSelector = selector.trim();
|
|
2080
|
+
if (!trimmedSelector) return [];
|
|
2081
|
+
const resolved = resolveCliModel({
|
|
2082
|
+
cliModel: trimmedSelector,
|
|
2083
|
+
modelRegistry,
|
|
2084
|
+
settings,
|
|
2085
|
+
preferences: matchPreferences,
|
|
2086
|
+
});
|
|
2087
|
+
if (resolved.configuredPatterns && resolved.configuredPatterns.length > 0) {
|
|
2088
|
+
return resolved.configuredPatterns;
|
|
2089
|
+
}
|
|
2090
|
+
if (resolved.model) {
|
|
2091
|
+
return [
|
|
2092
|
+
formatModelSelectorValue(
|
|
2093
|
+
resolved.selector ?? formatModelStringWithRouting(resolved.model),
|
|
2094
|
+
resolved.thinkingLevel,
|
|
2095
|
+
),
|
|
2096
|
+
];
|
|
2097
|
+
}
|
|
2098
|
+
return resolveConfiguredModelPatterns([trimmedSelector], settings);
|
|
2099
|
+
}),
|
|
2100
|
+
);
|
|
2076
2101
|
for (let patternIndex = 0; patternIndex < expandedModelPatterns.length; patternIndex += 1) {
|
|
2077
2102
|
const pattern = expandedModelPatterns[patternIndex];
|
|
2078
2103
|
const primary = parseModelPattern(pattern, availableModels, matchPreferences);
|
|
@@ -2118,6 +2143,13 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2118
2143
|
seenSelectors.add(fallbackSelector);
|
|
2119
2144
|
fallbackSelectors.push(fallbackSelector);
|
|
2120
2145
|
}
|
|
2146
|
+
if (fallbackSelectors.length === 0) {
|
|
2147
|
+
for (const selector of options.modelPatternDefaultFallbackChain ?? []) {
|
|
2148
|
+
if (typeof selector !== "string" || seenSelectors.has(selector)) continue;
|
|
2149
|
+
seenSelectors.add(selector);
|
|
2150
|
+
fallbackSelectors.push(selector);
|
|
2151
|
+
}
|
|
2152
|
+
}
|
|
2121
2153
|
if (fallbackSelectors.length > 0) {
|
|
2122
2154
|
const modelRoles: Record<string, string> = {};
|
|
2123
2155
|
const existingRoles = settings.getModelRoles();
|
|
@@ -2698,8 +2730,8 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2698
2730
|
if (snapcompactInline) transformed = await snapcompactInline.transform(transformed, transformModel);
|
|
2699
2731
|
return clampProviderContextImages(transformed, transformModel);
|
|
2700
2732
|
};
|
|
2701
|
-
const onPayload = async (payload: unknown,
|
|
2702
|
-
return await extensionRunner.emitBeforeProviderRequest(payload);
|
|
2733
|
+
const onPayload = async (payload: unknown, model?: Model) => {
|
|
2734
|
+
return await extensionRunner.emitBeforeProviderRequest(payload, model);
|
|
2703
2735
|
};
|
|
2704
2736
|
const onResponse: SimpleStreamOptions["onResponse"] = async (response, model) => {
|
|
2705
2737
|
await extensionRunner.emitAfterProviderResponse(response, model);
|