@shanepadgett/tau-agent 0.33.1 → 0.35.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/docs/context.md +29 -20
- package/docs/extending-tau-agent.md +63 -24
- package/extensions/appshot/index.ts +112 -111
- package/extensions/aside/README.md +7 -0
- package/extensions/aside/index.ts +168 -0
- package/extensions/aside/panel.ts +83 -0
- package/extensions/bash-approval/README.md +20 -0
- package/extensions/bash-approval/index.ts +235 -0
- package/extensions/bash-approval/settings.ts +24 -0
- package/extensions/branch/index.ts +38 -21
- package/extensions/cache-diagnostics/index.ts +70 -58
- package/extensions/checkpoint/README.md +9 -0
- package/extensions/checkpoint/checkpoint-budget.ts +66 -0
- package/extensions/checkpoint/checkpoint.ts +279 -0
- package/extensions/checkpoint/index.ts +102 -0
- package/extensions/checkpoint/messages.ts +170 -0
- package/extensions/checkpoint/prompt.ts +24 -0
- package/extensions/checkpoint/settings.ts +27 -0
- package/extensions/commit/commit-effort.ts +3 -3
- package/extensions/commit/commit-plan.ts +2 -2
- package/extensions/context/README.md +12 -8
- package/extensions/context/definitions.ts +178 -55
- package/extensions/context/index.ts +192 -160
- package/extensions/context/panel.ts +57 -37
- package/extensions/context/sync.ts +139 -89
- package/extensions/context/validation.ts +37 -25
- package/extensions/effort/README.md +1 -1
- package/extensions/effort/index.ts +3 -3
- package/extensions/effort/state.ts +3 -18
- package/extensions/explore/guidance.ts +6 -47
- package/extensions/explore/index.ts +38 -53
- package/extensions/explore/{ast/read → read}/hook.ts +12 -7
- package/extensions/explore/{ast/tools → tools}/ast-search.ts +15 -19
- package/extensions/explore/{ast/tools → tools}/context.ts +16 -23
- package/extensions/explore/{ast/tools → tools}/deps.ts +11 -15
- package/extensions/explore/{ast/tools → tools}/discover.ts +15 -17
- package/extensions/explore/{ast/tools → tools}/impact.ts +20 -23
- package/extensions/explore/{ast/tools → tools}/outline.ts +21 -24
- package/extensions/explore/{ast/tools → tools}/relationships.ts +23 -32
- package/extensions/explore/{ast/tools → tools}/render.ts +1 -1
- package/extensions/explore/{ast/tools → tools}/reverse-deps.ts +11 -15
- package/extensions/explore/{ast/tools → tools}/show.ts +16 -19
- package/extensions/explore/tools/target-params.ts +9 -0
- package/extensions/footer/index.ts +75 -56
- package/extensions/handoff/index.ts +8 -30
- package/extensions/image-gen/index.ts +46 -40
- package/extensions/patch/executor.ts +190 -135
- package/extensions/patch/matcher.ts +82 -48
- package/extensions/patch/parser.ts +149 -105
- package/extensions/patch/render.ts +32 -39
- package/extensions/patch/summary.ts +30 -26
- package/extensions/ready/README.md +7 -0
- package/extensions/ready/index.ts +58 -0
- package/extensions/ready/model.ts +128 -0
- package/extensions/ready/packs.ts +272 -0
- package/extensions/ready/render.ts +445 -0
- package/extensions/ready/scan.ts +760 -0
- package/extensions/reference/index.ts +2 -0
- package/extensions/reference/panel.ts +94 -31
- package/extensions/review/README.md +14 -6
- package/extensions/review/index.ts +58 -99
- package/extensions/review/model.ts +13 -33
- package/extensions/review/session.ts +9 -23
- package/extensions/runtime-context/context.ts +30 -26
- package/extensions/script-runner/README.md +2 -2
- package/extensions/script-runner/index.ts +122 -82
- package/extensions/silent-command-runner/index.ts +38 -23
- package/extensions/soul/README.md +2 -2
- package/extensions/soul/index.ts +4 -4
- package/extensions/soul/prompt.ts +6 -10
- package/extensions/soul/settings.ts +6 -3
- package/extensions/subagent/agents/context-sync.md +185 -33
- package/extensions/subagent/agents/scout.md +2 -2
- package/extensions/subagent/agents/web-research.md +2 -2
- package/extensions/subagent/agents.ts +137 -84
- package/extensions/subagent/cmux-dashboard.ts +39 -30
- package/extensions/subagent/index.ts +79 -59
- package/extensions/subagent/resume.ts +1 -1
- package/extensions/subagent/run.ts +308 -193
- package/extensions/subagent/runtime.ts +781 -343
- package/extensions/tau/index.ts +47 -38
- package/extensions/tau-help/help.md +20 -8
- package/extensions/tool-loader/README.md +4 -2
- package/extensions/tool-loader/index.ts +77 -52
- package/extensions/web/index.ts +6 -3
- package/extensions/web/webfetch.ts +114 -86
- package/package.json +3 -3
- package/schemas/tau.schema.json +38 -35
- package/shared/bounded-text-result.ts +1 -0
- package/shared/checkpoint-visibility.ts +9 -0
- package/shared/events.ts +28 -10
- package/shared/isolated-session.ts +72 -30
- package/shared/model-effort.ts +23 -22
- package/shared/model-fallback/index.ts +66 -41
- package/shared/settings/load.ts +45 -38
- package/shared/temporary-output-store.ts +16 -10
- package/{extensions/explore → src}/ast/adapter.ts +11 -2
- package/{extensions/explore → src}/ast/engine.ts +54 -38
- package/{extensions/explore → src}/ast/format/ast-search.ts +15 -6
- package/{extensions/explore → src}/ast/format/context.ts +3 -6
- package/src/ast/format/deps.ts +94 -0
- package/{extensions/explore → src}/ast/format/discover.ts +2 -2
- package/{extensions/explore → src}/ast/format/impact.ts +9 -11
- package/{extensions/explore → src}/ast/format/outline.ts +2 -2
- package/{extensions/explore → src}/ast/format/relationships.ts +11 -7
- package/{extensions/explore → src}/ast/format/show.ts +1 -1
- package/src/ast/format/targeted-result.ts +20 -0
- package/src/ast/grammars/c_sharp.wasm +0 -0
- package/{extensions/explore → src}/ast/grammars/manifest.json +9 -12
- package/{extensions/explore → src}/ast/grammars/manifest.ts +0 -4
- package/{extensions/explore → src}/ast/graph/file-graph.ts +210 -98
- package/{extensions/explore → src}/ast/graph/relationships.ts +285 -72
- package/{extensions/explore → src}/ast/ir.ts +6 -0
- package/src/ast/languages/csharp-file-deps.ts +90 -0
- package/{extensions/explore → src}/ast/languages/csharp.ts +9 -40
- package/src/ast/languages/dotted-index.ts +131 -0
- package/{extensions/explore → src}/ast/languages/file-dep-util.ts +22 -13
- package/src/ast/languages/fixtures/sample.cs +53 -0
- package/src/ast/languages/fixtures/sample.cs.expected.json +156 -0
- package/src/ast/languages/fixtures/sample.go +37 -0
- package/src/ast/languages/fixtures/sample.go.expected.json +96 -0
- package/src/ast/languages/fixtures/sample.java +40 -0
- package/src/ast/languages/fixtures/sample.java.expected.json +126 -0
- package/src/ast/languages/fixtures/sample.kt +31 -0
- package/src/ast/languages/fixtures/sample.kt.expected.json +134 -0
- package/src/ast/languages/fixtures/sample.md +23 -0
- package/src/ast/languages/fixtures/sample.md.expected.json +44 -0
- package/src/ast/languages/fixtures/sample.rs +54 -0
- package/src/ast/languages/fixtures/sample.rs.expected.json +178 -0
- package/src/ast/languages/fixtures/sample.swift +45 -0
- package/src/ast/languages/fixtures/sample.swift.expected.json +147 -0
- package/src/ast/languages/fixtures/sample.ts +45 -0
- package/src/ast/languages/fixtures/sample.ts.expected.json +141 -0
- package/src/ast/languages/fixtures/sample.tsx +36 -0
- package/src/ast/languages/fixtures/sample.tsx.expected.json +59 -0
- package/{extensions/explore → src}/ast/languages/go-file-deps.ts +6 -0
- package/{extensions/explore → src}/ast/languages/go.ts +21 -3
- package/{extensions/explore → src}/ast/languages/java.ts +5 -38
- package/src/ast/languages/jvm-file-deps.ts +135 -0
- package/{extensions/explore → src}/ast/languages/kotlin.ts +5 -39
- package/{extensions/explore → src}/ast/languages/rust-file-deps.ts +3 -3
- package/{extensions/explore → src}/ast/languages/rust.ts +21 -13
- package/{extensions/explore → src}/ast/languages/swift-file-deps.ts +22 -15
- package/{extensions/explore → src}/ast/languages/swift.ts +9 -39
- package/{extensions/explore → src}/ast/languages/tree.ts +47 -0
- package/{extensions/explore → src}/ast/languages/typescript-file-deps.ts +108 -0
- package/{extensions/explore → src}/ast/languages/typescript-package-surface.ts +4 -3
- package/{extensions/explore → src}/ast/languages/typescript.ts +3 -5
- package/{extensions/explore → src}/ast/markdown.ts +72 -47
- package/{extensions/explore → src}/ast/queries/ast-search.ts +164 -146
- package/{extensions/explore → src}/ast/queries/composite-target.ts +1 -1
- package/{extensions/explore → src}/ast/queries/context.ts +116 -80
- package/{extensions/explore → src}/ast/queries/discover.ts +154 -105
- package/src/ast/queries/impact.ts +291 -0
- package/{extensions/explore → src}/ast/queries/outline.ts +4 -2
- package/{extensions/explore → src}/ast/queries/show.ts +46 -42
- package/{extensions/explore → src}/ast/query.ts +1 -1
- package/{extensions/explore → src}/ast/registry.ts +1 -2
- package/{extensions/explore → src}/ast/scan.ts +1 -1
- package/src/ast/session.ts +50 -0
- package/{extensions/explore → src}/ast/slice.ts +8 -0
- package/{extensions/explore → src/ast}/traverse.ts +32 -20
- package/src/file-injection/index.ts +547 -0
- package/src/image-generation/client.ts +50 -31
- package/src/image-generation/index.ts +177 -134
- package/src/index.ts +2 -0
- package/src/tool-loading/index.ts +58 -0
- package/extensions/context/evidence.ts +0 -517
- package/extensions/context/projection.ts +0 -141
- package/extensions/context/state.ts +0 -30
- package/extensions/context/write-scope.ts +0 -109
- package/extensions/explore/ast/format/deps.ts +0 -86
- package/extensions/explore/ast/grammars/odin.wasm +0 -0
- package/extensions/explore/ast/languages/csharp-file-deps.ts +0 -189
- package/extensions/explore/ast/languages/fixtures/sample.cs +0 -17
- package/extensions/explore/ast/languages/fixtures/sample.java +0 -21
- package/extensions/explore/ast/languages/fixtures/sample.kt +0 -15
- package/extensions/explore/ast/languages/fixtures/sample.odin +0 -28
- package/extensions/explore/ast/languages/fixtures/sample.rs +0 -31
- package/extensions/explore/ast/languages/fixtures/sample.swift +0 -25
- package/extensions/explore/ast/languages/jvm-file-deps.ts +0 -217
- package/extensions/explore/ast/languages/odin-file-deps.ts +0 -175
- package/extensions/explore/ast/languages/odin.ts +0 -407
- package/extensions/explore/ast/queries/impact.ts +0 -250
- package/extensions/explore/outline-injection.ts +0 -151
- package/extensions/explore/read/autoread.ts +0 -118
- package/extensions/review/panel.ts +0 -128
- package/extensions/working-memory/README.md +0 -17
- package/extensions/working-memory/checkpoint.ts +0 -242
- package/extensions/working-memory/index.ts +0 -341
- package/extensions/working-memory/memory.ts +0 -235
- package/extensions/working-memory/render.ts +0 -185
- package/extensions/working-memory/settings.ts +0 -38
- package/extensions/working-memory/state.ts +0 -158
- package/shared/autoread.ts +0 -234
- package/shared/context-messages.ts +0 -19
- package/shared/outline-injection.ts +0 -56
- /package/{extensions/explore → src}/ast/format/composite.ts +0 -0
- /package/{extensions/explore → src}/ast/grammars/kotlin.wasm +0 -0
- /package/{extensions/explore → src}/ast/grammars/swift.wasm +0 -0
- /package/{extensions/explore → src}/ast/identity.ts +0 -0
- /package/{extensions/explore → src}/ast/package-surface.ts +0 -0
- /package/{extensions/explore/ast/read/policy.ts → src/ast/read-policy.ts} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shanepadgett/tau-agent",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.35.0",
|
|
4
4
|
"description": "Tau is a custom agentic harness built with pi extensions",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.ts",
|
|
@@ -35,10 +35,10 @@
|
|
|
35
35
|
],
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@ast-grep/wasm": "0.45.0",
|
|
38
|
-
"@shanepadgett/tau-tui": "0.
|
|
38
|
+
"@shanepadgett/tau-tui": "0.35.0",
|
|
39
39
|
"@vscode/tree-sitter-wasm": "0.3.1",
|
|
40
40
|
"image-size": "2.0.2",
|
|
41
|
-
"smol-toml": "1.7.
|
|
41
|
+
"smol-toml": "1.7.1",
|
|
42
42
|
"web-tree-sitter": "0.26.11"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
package/schemas/tau.schema.json
CHANGED
|
@@ -12,6 +12,42 @@
|
|
|
12
12
|
"extensions": {
|
|
13
13
|
"type": "object",
|
|
14
14
|
"properties": {
|
|
15
|
+
"bashApproval": {
|
|
16
|
+
"type": "object",
|
|
17
|
+
"properties": {
|
|
18
|
+
"enabled": {
|
|
19
|
+
"type": "boolean",
|
|
20
|
+
"default": true,
|
|
21
|
+
"description": "Enable bash command review and approval."
|
|
22
|
+
},
|
|
23
|
+
"autoApprove": {
|
|
24
|
+
"type": "boolean",
|
|
25
|
+
"default": true,
|
|
26
|
+
"description": "Run reviewer-approved commands without human confirmation."
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"additionalProperties": false
|
|
30
|
+
},
|
|
31
|
+
"checkpoint": {
|
|
32
|
+
"type": "object",
|
|
33
|
+
"required": [
|
|
34
|
+
"checkpointTokenLimit"
|
|
35
|
+
],
|
|
36
|
+
"properties": {
|
|
37
|
+
"showToolRows": {
|
|
38
|
+
"type": "boolean",
|
|
39
|
+
"default": false,
|
|
40
|
+
"description": "Show checkpoint and injected-file rows in the TUI for debugging."
|
|
41
|
+
},
|
|
42
|
+
"checkpointTokenLimit": {
|
|
43
|
+
"type": "integer",
|
|
44
|
+
"minimum": 1,
|
|
45
|
+
"default": 150000,
|
|
46
|
+
"description": "Context-token ceiling before a checkpoint is required."
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"additionalProperties": false
|
|
50
|
+
},
|
|
15
51
|
"context": {
|
|
16
52
|
"type": "object",
|
|
17
53
|
"properties": {
|
|
@@ -240,10 +276,10 @@
|
|
|
240
276
|
"default": true,
|
|
241
277
|
"description": "Add the lazy-senior-dev build ethos to Tau's system prompt."
|
|
242
278
|
},
|
|
243
|
-
"
|
|
279
|
+
"simplified": {
|
|
244
280
|
"type": "boolean",
|
|
245
281
|
"default": true,
|
|
246
|
-
"description": "Add
|
|
282
|
+
"description": "Add Simplified Technical English and small-chunk explanations to Tau's system prompt."
|
|
247
283
|
}
|
|
248
284
|
},
|
|
249
285
|
"additionalProperties": false
|
|
@@ -262,39 +298,6 @@
|
|
|
262
298
|
}
|
|
263
299
|
},
|
|
264
300
|
"additionalProperties": false
|
|
265
|
-
},
|
|
266
|
-
"workingMemory": {
|
|
267
|
-
"type": "object",
|
|
268
|
-
"properties": {
|
|
269
|
-
"enabled": {
|
|
270
|
-
"type": "boolean",
|
|
271
|
-
"default": true,
|
|
272
|
-
"description": "Enable working-memory checkpoints."
|
|
273
|
-
},
|
|
274
|
-
"nudgeEveryTokens": {
|
|
275
|
-
"type": "integer",
|
|
276
|
-
"default": 40000,
|
|
277
|
-
"minimum": 1,
|
|
278
|
-
"description": "Active-context token interval between advisory working-memory reminders."
|
|
279
|
-
},
|
|
280
|
-
"nudgeInstructions": {
|
|
281
|
-
"type": "array",
|
|
282
|
-
"items": {
|
|
283
|
-
"type": "string",
|
|
284
|
-
"minLength": 1,
|
|
285
|
-
"maxLength": 2000
|
|
286
|
-
},
|
|
287
|
-
"default": [
|
|
288
|
-
"Reassess working memory. Continue coherent exploration when its evidence remains useful; otherwise prune known dead ends, obsolete outputs, and other context with no expected value.",
|
|
289
|
-
"Context is materially larger. Prune stale or bulky irrelevant evidence when safe, but keep active working evidence that would otherwise need to be reread.",
|
|
290
|
-
"Strongly reassess before more broad work. Remove accumulated waste and carry useful information at cheapest sufficient fidelity without scrubbing the active working set."
|
|
291
|
-
],
|
|
292
|
-
"minItems": 1,
|
|
293
|
-
"maxItems": 5,
|
|
294
|
-
"description": "Ordered advisory working-memory instructions. Later reminders repeat final instruction."
|
|
295
|
-
}
|
|
296
|
-
},
|
|
297
|
-
"additionalProperties": false
|
|
298
301
|
}
|
|
299
302
|
},
|
|
300
303
|
"additionalProperties": true,
|
|
@@ -63,6 +63,7 @@ export class BoundedTextResultBuilder {
|
|
|
63
63
|
this.strategy = strategy;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
// fallow-ignore-next-line unused-class-member -- head/tail strategy API; callers use completeBlocks today
|
|
66
67
|
async append(text: string): Promise<void> {
|
|
67
68
|
if (this.strategy === "completeBlocks") throw new Error("Complete-block results require appendBlock");
|
|
68
69
|
this.requireOpen();
|
package/shared/events.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import type { ModelEffort } from "./model-effort.ts";
|
|
3
|
-
import type { OutlineInjectionRequest, OutlineInjectionResponse } from "./outline-injection.js";
|
|
4
3
|
import type { ToolRowVisualState } from "./tool-row-state.js";
|
|
4
|
+
import type { FileInjectionRequest, PreparedFileInjection } from "../src/file-injection/index.ts";
|
|
5
5
|
|
|
6
6
|
export type TauAgentEvents = {
|
|
7
7
|
"tau:agent.blocked": {
|
|
@@ -31,16 +31,14 @@ export type TauAgentEvents = {
|
|
|
31
31
|
snapshotRanges?: Array<{ startLine: number; endLine: number }>;
|
|
32
32
|
}>;
|
|
33
33
|
};
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
cwd: string;
|
|
38
|
-
batchId: string;
|
|
39
|
-
files: Array<{ path: string }>;
|
|
34
|
+
/** @internal Runtime-scoped discovery for deferred tool groups. */
|
|
35
|
+
"tau:deferred-tool-group.request": {
|
|
36
|
+
accept(group: { id: string; description: string; toolNames: readonly string[] }): void;
|
|
40
37
|
};
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
38
|
+
/** @internal Runtime-scoped request for Explore-owned file preparation. */
|
|
39
|
+
"tau:file-injection.prepare": {
|
|
40
|
+
request: FileInjectionRequest;
|
|
41
|
+
accept(preparation: Promise<PreparedFileInjection[]>): void;
|
|
44
42
|
};
|
|
45
43
|
"tau:footer-item": {
|
|
46
44
|
id: string;
|
|
@@ -105,6 +103,25 @@ export function onTauEvent<Name extends keyof TauAgentEvents>(
|
|
|
105
103
|
owner: string,
|
|
106
104
|
name: Name,
|
|
107
105
|
handler: TauEventHandler<Name>,
|
|
106
|
+
): () => void {
|
|
107
|
+
return subscribeToTauEvent(pi, owner, name, handler, false);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function onTauEventImmediately<Name extends keyof TauAgentEvents>(
|
|
111
|
+
pi: TauEventAPI,
|
|
112
|
+
owner: string,
|
|
113
|
+
name: Name,
|
|
114
|
+
handler: TauEventHandler<Name>,
|
|
115
|
+
): () => void {
|
|
116
|
+
return subscribeToTauEvent(pi, owner, name, handler, true);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function subscribeToTauEvent<Name extends keyof TauAgentEvents>(
|
|
120
|
+
pi: TauEventAPI,
|
|
121
|
+
owner: string,
|
|
122
|
+
name: Name,
|
|
123
|
+
handler: TauEventHandler<Name>,
|
|
124
|
+
attachImmediately: boolean,
|
|
108
125
|
): () => void {
|
|
109
126
|
if (owner.length === 0) throw new Error("Tau event owner is required.");
|
|
110
127
|
|
|
@@ -135,6 +152,7 @@ export function onTauEvent<Name extends keyof TauAgentEvents>(
|
|
|
135
152
|
}
|
|
136
153
|
|
|
137
154
|
subscriptions.set(name, subscription);
|
|
155
|
+
if (attachImmediately) attach();
|
|
138
156
|
pi.on("session_start", attach);
|
|
139
157
|
pi.on("session_shutdown", detach);
|
|
140
158
|
return subscription.stop;
|
|
@@ -32,6 +32,61 @@ export interface IsolatedSessionResource {
|
|
|
32
32
|
dispose(): Promise<void>;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
type ModelAuth = Awaited<ReturnType<ExtensionContext["modelRegistry"]["getApiKeyAndHeaders"]>>;
|
|
36
|
+
|
|
37
|
+
async function tryPreferredModel(
|
|
38
|
+
ctx: ExtensionContext,
|
|
39
|
+
preferredModel: string,
|
|
40
|
+
onWarning?: (warning: string) => void,
|
|
41
|
+
): Promise<{ model: SelectedModel; auth: ModelAuth } | undefined> {
|
|
42
|
+
const separator = preferredModel.indexOf("/");
|
|
43
|
+
const configured = ctx.modelRegistry.find(preferredModel.slice(0, separator), preferredModel.slice(separator + 1));
|
|
44
|
+
if (!configured) {
|
|
45
|
+
onWarning?.(`model ${preferredModel} is unavailable; using parent model`);
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
const auth = await ctx.modelRegistry.getApiKeyAndHeaders(configured);
|
|
49
|
+
if (!auth.ok) {
|
|
50
|
+
onWarning?.(`model ${preferredModel} is unavailable: ${auth.error}; using parent model`);
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
return { model: configured, auth };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function thinkingSupported(
|
|
57
|
+
model: SelectedModel | undefined,
|
|
58
|
+
preferred: IsolatedSessionThinkingLevel,
|
|
59
|
+
): preferred is IsolatedSessionThinkingLevel {
|
|
60
|
+
const mapped = model?.thinkingLevelMap?.[preferred];
|
|
61
|
+
if (!model?.reasoning || mapped === null) return false;
|
|
62
|
+
if ((preferred === "xhigh" || preferred === "max") && mapped === undefined) return false;
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function resolveThinkingLevel(
|
|
67
|
+
model: SelectedModel | undefined,
|
|
68
|
+
parentThinkingLevel: IsolatedSessionThinkingLevel,
|
|
69
|
+
preferredThinkingLevel: IsolatedSessionThinkingLevel | undefined,
|
|
70
|
+
allowPreferred: boolean,
|
|
71
|
+
onWarning?: (warning: string) => void,
|
|
72
|
+
): IsolatedSessionThinkingLevel {
|
|
73
|
+
if (!preferredThinkingLevel || !allowPreferred) return parentThinkingLevel;
|
|
74
|
+
if (thinkingSupported(model, preferredThinkingLevel)) return preferredThinkingLevel;
|
|
75
|
+
onWarning?.(`thinking ${preferredThinkingLevel} is unavailable for the selected model; using parent thinking`);
|
|
76
|
+
return parentThinkingLevel;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function runtimeApiKeyFor(
|
|
80
|
+
ctx: ExtensionContext,
|
|
81
|
+
model: SelectedModel,
|
|
82
|
+
provider: SelectedProvider,
|
|
83
|
+
auth: ModelAuth,
|
|
84
|
+
): string | undefined {
|
|
85
|
+
if (!auth.ok) return undefined;
|
|
86
|
+
if (!(auth.apiKey && provider.auth.apiKey && !ctx.modelRegistry.isUsingOAuth(model))) return undefined;
|
|
87
|
+
return auth.apiKey;
|
|
88
|
+
}
|
|
89
|
+
|
|
35
90
|
export async function resolveIsolatedSessionModel(options: {
|
|
36
91
|
label: string;
|
|
37
92
|
preferredModel: string | undefined;
|
|
@@ -44,37 +99,25 @@ export async function resolveIsolatedSessionModel(options: {
|
|
|
44
99
|
}): Promise<Pick<IsolatedSessionInputs, "model" | "provider" | "runtimeApiKey" | "thinkingLevel">> {
|
|
45
100
|
const { ctx, signal, onWarning } = options;
|
|
46
101
|
let model = ctx.model;
|
|
47
|
-
let
|
|
102
|
+
let selectedAuth: ModelAuth | undefined;
|
|
48
103
|
let preferredModelSelected = false;
|
|
49
|
-
|
|
104
|
+
|
|
50
105
|
if (options.preferredModel) {
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
if (!configured) onWarning?.(`model ${options.preferredModel} is unavailable; using parent model`);
|
|
57
|
-
else {
|
|
58
|
-
const auth = await ctx.modelRegistry.getApiKeyAndHeaders(configured);
|
|
59
|
-
if (!auth.ok) onWarning?.(`model ${options.preferredModel} is unavailable: ${auth.error}; using parent model`);
|
|
60
|
-
else {
|
|
61
|
-
model = configured;
|
|
62
|
-
selectedAuth = auth;
|
|
63
|
-
preferredModelSelected = true;
|
|
64
|
-
}
|
|
106
|
+
const preferred = await tryPreferredModel(ctx, options.preferredModel, onWarning);
|
|
107
|
+
if (preferred) {
|
|
108
|
+
model = preferred.model;
|
|
109
|
+
selectedAuth = preferred.auth;
|
|
110
|
+
preferredModelSelected = true;
|
|
65
111
|
}
|
|
66
112
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
onWarning?.(`thinking ${preferred} is unavailable for the selected model; using parent thinking`);
|
|
76
|
-
else thinkingLevel = preferred;
|
|
77
|
-
}
|
|
113
|
+
|
|
114
|
+
const thinkingLevel = resolveThinkingLevel(
|
|
115
|
+
model,
|
|
116
|
+
options.parentThinkingLevel,
|
|
117
|
+
options.preferredThinkingLevel,
|
|
118
|
+
preferredModelSelected || options.usePreferredThinkingAfterModelFallback,
|
|
119
|
+
onWarning,
|
|
120
|
+
);
|
|
78
121
|
if (!model) throw new Error(`${options.label} startup failed: parent has no model`);
|
|
79
122
|
const auth = selectedAuth ?? (await ctx.modelRegistry.getApiKeyAndHeaders(model));
|
|
80
123
|
if (!auth.ok) throw new Error(`${options.label} startup failed: ${auth.error}`);
|
|
@@ -84,8 +127,7 @@ export async function resolveIsolatedSessionModel(options: {
|
|
|
84
127
|
return {
|
|
85
128
|
model,
|
|
86
129
|
provider,
|
|
87
|
-
runtimeApiKey:
|
|
88
|
-
auth.apiKey && provider.auth.apiKey && !ctx.modelRegistry.isUsingOAuth(model) ? auth.apiKey : undefined,
|
|
130
|
+
runtimeApiKey: runtimeApiKeyFor(ctx, model, provider, auth),
|
|
89
131
|
thinkingLevel,
|
|
90
132
|
};
|
|
91
133
|
}
|
|
@@ -100,7 +142,7 @@ export async function createIsolatedSessionResource(
|
|
|
100
142
|
const modelRuntime = await ModelRuntime.create();
|
|
101
143
|
modelRuntime.registerNativeProvider(inputs.provider);
|
|
102
144
|
if (inputs.runtimeApiKey !== undefined)
|
|
103
|
-
await modelRuntime.setRuntimeApiKey(inputs.model.provider, inputs.runtimeApiKey, {
|
|
145
|
+
await modelRuntime.setRuntimeApiKey(inputs.model.provider, inputs.runtimeApiKey, { signal });
|
|
104
146
|
if (signal.aborted) throw new Error(`${inputs.label} startup aborted`);
|
|
105
147
|
const resourceLoader = new DefaultResourceLoader({
|
|
106
148
|
cwd: inputs.cwd,
|
package/shared/model-effort.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
|
3
3
|
import { resolveCandidates } from "./model-fallback/index.ts";
|
|
4
4
|
import type { ModelCandidate } from "./model-fallback/types.ts";
|
|
5
5
|
|
|
6
|
-
export type ModelEffort = "
|
|
6
|
+
export type ModelEffort = "quick" | "standard" | "deep";
|
|
7
7
|
|
|
8
8
|
interface ModelPreference {
|
|
9
9
|
model: string;
|
|
@@ -24,43 +24,38 @@ export interface EffortProviderCandidates {
|
|
|
24
24
|
}>;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
export interface EffortCandidateOptions {
|
|
28
|
+
includeParentModel: boolean;
|
|
29
|
+
preferredProvider?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
27
32
|
const MODEL_PREFERENCES: Record<ModelEffort, readonly ProviderPreference[]> = {
|
|
28
|
-
|
|
33
|
+
quick: [
|
|
29
34
|
{
|
|
30
35
|
provider: "openai-codex",
|
|
31
|
-
models: [
|
|
32
|
-
{ model: "gpt-5.6-luna", reasoning: "high" },
|
|
33
|
-
{ model: "gpt-5.5", reasoning: "low" },
|
|
34
|
-
],
|
|
36
|
+
models: [{ model: "gpt-5.6-luna", reasoning: "medium" }],
|
|
35
37
|
},
|
|
36
38
|
{ provider: "xai", models: [{ model: "grok-4.5", reasoning: "medium" }] },
|
|
37
39
|
{ provider: "anthropic", models: [{ model: "claude-haiku-4-5", reasoning: "high" }] },
|
|
38
40
|
],
|
|
39
|
-
|
|
41
|
+
standard: [
|
|
40
42
|
{
|
|
41
43
|
provider: "openai-codex",
|
|
42
|
-
models: [
|
|
43
|
-
{ model: "gpt-5.6-terra", reasoning: "high" },
|
|
44
|
-
{ model: "gpt-5.5", reasoning: "medium" },
|
|
45
|
-
],
|
|
44
|
+
models: [{ model: "gpt-5.6-luna", reasoning: "max" }],
|
|
46
45
|
},
|
|
47
46
|
{ provider: "xai", models: [{ model: "grok-4.5", reasoning: "high" }] },
|
|
48
47
|
{ provider: "anthropic", models: [{ model: "claude-sonnet-5", reasoning: "high" }] },
|
|
49
48
|
],
|
|
50
|
-
|
|
49
|
+
deep: [
|
|
51
50
|
{
|
|
52
51
|
provider: "openai-codex",
|
|
53
|
-
models: [
|
|
54
|
-
{ model: "gpt-5.6-sol", reasoning: "high" },
|
|
55
|
-
{ model: "gpt-5.5", reasoning: "high" },
|
|
56
|
-
],
|
|
52
|
+
models: [{ model: "gpt-5.6-sol", reasoning: "high" }],
|
|
57
53
|
},
|
|
58
|
-
{ provider: "xai", models: [{ model: "grok-4.5", reasoning: "high" }] },
|
|
59
54
|
{
|
|
60
55
|
provider: "anthropic",
|
|
61
56
|
models: [
|
|
62
57
|
{ model: "claude-opus-5", reasoning: "high" },
|
|
63
|
-
{ model: "claude-
|
|
58
|
+
{ model: "claude-fable-5", reasoning: "low" },
|
|
64
59
|
],
|
|
65
60
|
},
|
|
66
61
|
],
|
|
@@ -72,7 +67,7 @@ export function effortForSelection(
|
|
|
72
67
|
reasoning: string | undefined,
|
|
73
68
|
): ModelEffort | undefined {
|
|
74
69
|
if (!provider || !model || !reasoning) return undefined;
|
|
75
|
-
for (const effort of ["
|
|
70
|
+
for (const effort of ["deep", "standard", "quick"] as const) {
|
|
76
71
|
const preference = MODEL_PREFERENCES[effort].find((item) => item.provider === provider);
|
|
77
72
|
if (preference?.models.some((item) => item.model === model && item.reasoning === reasoning)) return effort;
|
|
78
73
|
}
|
|
@@ -104,13 +99,19 @@ export function resolveEffortProviders(
|
|
|
104
99
|
export function resolveEffortCandidates(
|
|
105
100
|
ctx: Pick<ExtensionContext, "modelRegistry" | "model" | "cwd" | "isProjectTrusted">,
|
|
106
101
|
effort: ModelEffort,
|
|
107
|
-
|
|
102
|
+
options: EffortCandidateOptions,
|
|
108
103
|
): Promise<ModelCandidate[]> {
|
|
104
|
+
const preferences = options.preferredProvider
|
|
105
|
+
? [
|
|
106
|
+
...MODEL_PREFERENCES[effort].filter(({ provider }) => provider === options.preferredProvider),
|
|
107
|
+
...MODEL_PREFERENCES[effort].filter(({ provider }) => provider !== options.preferredProvider),
|
|
108
|
+
]
|
|
109
|
+
: MODEL_PREFERENCES[effort];
|
|
109
110
|
return resolveCandidates(
|
|
110
111
|
ctx,
|
|
111
|
-
|
|
112
|
+
preferences.flatMap((preference) =>
|
|
112
113
|
preference.models.map((model) => ({ provider: preference.provider, ...model })),
|
|
113
114
|
),
|
|
114
|
-
includeParentModel,
|
|
115
|
+
options.includeParentModel,
|
|
115
116
|
);
|
|
116
117
|
}
|
|
@@ -98,6 +98,20 @@ export async function generateToolValidated<T>(
|
|
|
98
98
|
);
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
+
async function noteCandidateFailure(
|
|
102
|
+
ctx: GenerationContext,
|
|
103
|
+
options: ModelFallbackOptions | undefined,
|
|
104
|
+
label: string,
|
|
105
|
+
message: string,
|
|
106
|
+
hasMore: boolean,
|
|
107
|
+
): Promise<void> {
|
|
108
|
+
if (!hasMore) return;
|
|
109
|
+
await options?.onStatus?.(`Model failed (${label}); trying next model`);
|
|
110
|
+
if (options?.notifyOnFallback ?? false) {
|
|
111
|
+
ctx.ui.notify(`Model failed (${label}): ${message}\nTrying next model.`, "info");
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
101
115
|
async function withModelFallback<T>(
|
|
102
116
|
ctx: GenerationContext,
|
|
103
117
|
candidates: readonly ModelCandidate[],
|
|
@@ -106,13 +120,11 @@ async function withModelFallback<T>(
|
|
|
106
120
|
): Promise<T> {
|
|
107
121
|
const failures: string[] = [];
|
|
108
122
|
const statusKey = options?.statusKey;
|
|
109
|
-
const notifyOnFallback = options?.notifyOnFallback ?? false;
|
|
110
123
|
|
|
111
124
|
for (const [index, candidate] of candidates.entries()) {
|
|
112
125
|
const label = `${candidate.model.provider}/${candidate.model.id}`;
|
|
113
126
|
if (statusKey) ctx.ui.setStatus(statusKey, `generating (${label})`);
|
|
114
127
|
await options?.onStatus?.(`Generating with ${label}`);
|
|
115
|
-
|
|
116
128
|
try {
|
|
117
129
|
return await request(candidate);
|
|
118
130
|
} catch (error) {
|
|
@@ -120,16 +132,42 @@ async function withModelFallback<T>(
|
|
|
120
132
|
if (shouldCooldownProvider(error)) await markProviderUnavailable(candidate.model.provider);
|
|
121
133
|
const message = errorText(error);
|
|
122
134
|
failures.push(`- ${label}: ${message}`);
|
|
123
|
-
|
|
124
|
-
if (index < candidates.length - 1 && notifyOnFallback) {
|
|
125
|
-
ctx.ui.notify(`Model failed (${label}): ${message}\nTrying next model.`, "info");
|
|
126
|
-
}
|
|
135
|
+
await noteCandidateFailure(ctx, options, label, message, index < candidates.length - 1);
|
|
127
136
|
}
|
|
128
137
|
}
|
|
129
138
|
|
|
130
139
|
throw new Error(["Model generation failed for all candidates:", ...failures].join("\n"));
|
|
131
140
|
}
|
|
132
141
|
|
|
142
|
+
function throwUnlessRetryableStopError(response: AssistantMessage, attempt: number, maxAttempts: number): void {
|
|
143
|
+
if (response.stopReason !== "error") return;
|
|
144
|
+
const error = new Error(response.errorMessage || "model returned an error");
|
|
145
|
+
if (attempt < maxAttempts && !shouldCooldownProvider(error)) return;
|
|
146
|
+
throw error;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function pushCorrection(
|
|
150
|
+
messages: Message[],
|
|
151
|
+
error: Error,
|
|
152
|
+
output: string,
|
|
153
|
+
correctionPrompt: (error: Error, output: string) => string,
|
|
154
|
+
): void {
|
|
155
|
+
messages.push({
|
|
156
|
+
role: "user",
|
|
157
|
+
content: [{ type: "text", text: correctionPrompt(error, truncAt(output, 4_000)) }],
|
|
158
|
+
timestamp: Date.now(),
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function shouldRetryValidation(
|
|
163
|
+
error: unknown,
|
|
164
|
+
attempt: number,
|
|
165
|
+
maxAttempts: number,
|
|
166
|
+
correctionPrompt: ((error: Error, output: string) => string) | undefined,
|
|
167
|
+
): error is Error {
|
|
168
|
+
return error instanceof Error && attempt < maxAttempts && correctionPrompt !== undefined;
|
|
169
|
+
}
|
|
170
|
+
|
|
133
171
|
async function requestValidated<T>(
|
|
134
172
|
ctx: GenerationContext,
|
|
135
173
|
candidate: ModelCandidate,
|
|
@@ -137,37 +175,36 @@ async function requestValidated<T>(
|
|
|
137
175
|
validate: (text: string) => T,
|
|
138
176
|
correctionPrompt?: (error: Error, text: string) => string,
|
|
139
177
|
): Promise<T> {
|
|
140
|
-
const
|
|
141
|
-
const messages: Message[] = [userMessage];
|
|
178
|
+
const messages: Message[] = [{ role: "user", content: [{ type: "text", text: prompt }], timestamp: Date.now() }];
|
|
142
179
|
const sessionId = randomUUID();
|
|
143
180
|
|
|
144
181
|
for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
|
|
145
182
|
const response = await completeCandidate(ctx, candidate, messages, sessionId);
|
|
146
183
|
const text = responseText(response);
|
|
147
|
-
if (response.stopReason === "error") {
|
|
148
|
-
const error = new Error(response.errorMessage || "model returned an error");
|
|
149
|
-
if (attempt < MAX_ATTEMPTS && !shouldCooldownProvider(error)) continue;
|
|
150
|
-
throw error;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
184
|
try {
|
|
185
|
+
throwUnlessRetryableStopError(response, attempt, MAX_ATTEMPTS);
|
|
186
|
+
if (response.stopReason === "error") continue;
|
|
154
187
|
return validate(text);
|
|
155
188
|
} catch (error) {
|
|
156
|
-
if (!(error
|
|
157
|
-
|
|
158
|
-
|
|
189
|
+
if (!shouldRetryValidation(error, attempt, MAX_ATTEMPTS, correctionPrompt) || !correctionPrompt) {
|
|
190
|
+
throw error;
|
|
191
|
+
}
|
|
159
192
|
if (text) messages.push({ ...response, content: [{ type: "text", text }] });
|
|
160
|
-
messages
|
|
161
|
-
role: "user",
|
|
162
|
-
content: [{ type: "text", text: correctionPrompt(error, truncAt(text, 4_000)) }],
|
|
163
|
-
timestamp: Date.now(),
|
|
164
|
-
});
|
|
193
|
+
pushCorrection(messages, error, text, correctionPrompt);
|
|
165
194
|
}
|
|
166
195
|
}
|
|
167
196
|
|
|
168
197
|
throw new Error("Model generation failed.");
|
|
169
198
|
}
|
|
170
199
|
|
|
200
|
+
function validateSingleToolCall(tool: Tool, toolCalls: readonly { name: string; arguments: unknown }[]): unknown {
|
|
201
|
+
if (toolCalls.length !== 1) throw new Error(`Model must call ${tool.name} exactly once.`);
|
|
202
|
+
const toolCall = toolCalls[0];
|
|
203
|
+
if (!toolCall) throw new Error(`Model must call ${tool.name}.`);
|
|
204
|
+
if (toolCall.name !== tool.name) throw new Error(`Model called ${toolCall.name}; expected ${tool.name}.`);
|
|
205
|
+
return toolCall.arguments;
|
|
206
|
+
}
|
|
207
|
+
|
|
171
208
|
async function requestToolValidated<T>(
|
|
172
209
|
ctx: GenerationContext,
|
|
173
210
|
candidate: ModelCandidate,
|
|
@@ -185,27 +222,15 @@ async function requestToolValidated<T>(
|
|
|
185
222
|
const text = responseText(response);
|
|
186
223
|
const toolCalls = response.content.flatMap((part) => (part.type === "toolCall" ? [part] : []));
|
|
187
224
|
const output = text || formatToolCalls(toolCalls);
|
|
188
|
-
if (response.stopReason === "error") {
|
|
189
|
-
const error = new Error(response.errorMessage || "model returned an error");
|
|
190
|
-
if (attempt < maxAttempts && !shouldCooldownProvider(error)) continue;
|
|
191
|
-
throw error;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
225
|
try {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
if (toolCall.name !== tool.name) throw new Error(`Model called ${toolCall.name}; expected ${tool.name}.`);
|
|
199
|
-
return validate(toolCall.arguments);
|
|
226
|
+
throwUnlessRetryableStopError(response, attempt, maxAttempts);
|
|
227
|
+
if (response.stopReason === "error") continue;
|
|
228
|
+
return validate(validateSingleToolCall(tool, toolCalls));
|
|
200
229
|
} catch (error) {
|
|
201
|
-
if (!(error
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
messages
|
|
205
|
-
role: "user",
|
|
206
|
-
content: [{ type: "text", text: correctionPrompt(error, truncAt(output, 4_000)) }],
|
|
207
|
-
timestamp: Date.now(),
|
|
208
|
-
});
|
|
230
|
+
if (!shouldRetryValidation(error, attempt, maxAttempts, correctionPrompt) || !correctionPrompt) {
|
|
231
|
+
throw error;
|
|
232
|
+
}
|
|
233
|
+
pushCorrection(messages, error, output, correctionPrompt);
|
|
209
234
|
}
|
|
210
235
|
}
|
|
211
236
|
|