@shanepadgett/tau-agent 0.5.1 → 0.7.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/subagents.md +1 -2
- package/extensions/auto-name/index.ts +1 -1
- package/extensions/commit/commit-plan.ts +12 -5
- package/extensions/context/README.md +1 -1
- package/extensions/context/definitions.ts +2 -142
- package/extensions/context/index.ts +67 -387
- package/extensions/context/panel.ts +43 -108
- package/extensions/context/sync.ts +693 -0
- package/extensions/explore/README.md +2 -2
- package/extensions/explore/autoread.ts +20 -3
- package/extensions/explore/index.ts +23 -2
- package/extensions/explore/read-cache.ts +209 -0
- package/extensions/explore/read-snapshots.ts +44 -0
- package/extensions/explore/read-stats-panel.ts +154 -0
- package/extensions/explore/read-stats.ts +120 -0
- package/extensions/explore/read.ts +198 -42
- package/extensions/image-gen/README.md +4 -4
- package/extensions/image-gen/client.ts +126 -107
- package/extensions/image-gen/index.ts +44 -35
- package/extensions/subagent/README.md +0 -1
- package/extensions/subagent/agents.ts +0 -4
- package/extensions/tau-help/help.md +7 -3
- package/extensions/xai/README.md +7 -0
- package/extensions/xai/auth.ts +40 -0
- package/extensions/xai/constants.ts +11 -0
- package/extensions/xai/index.ts +38 -0
- package/extensions/xai/oauth.ts +342 -0
- package/extensions/xai/payload.ts +68 -0
- package/package.json +2 -2
- package/shared/git.ts +2 -2
- package/shared/model-fallback/index.ts +17 -12
- package/extensions/subagent/agents/context-maintenance.md +0 -23
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
2
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
function contentText(value: unknown): string {
|
|
6
|
+
if (typeof value === "string") return value;
|
|
7
|
+
if (!Array.isArray(value)) return "";
|
|
8
|
+
return value
|
|
9
|
+
.map((part) => {
|
|
10
|
+
if (!isRecord(part)) return "";
|
|
11
|
+
return typeof part.text === "string" ? part.text : "";
|
|
12
|
+
})
|
|
13
|
+
.filter(Boolean)
|
|
14
|
+
.join("\n");
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function normalizeToolOutput(item: Record<string, unknown>): unknown[] {
|
|
18
|
+
if (item.type !== "function_call_output" || !Array.isArray(item.output)) return [item];
|
|
19
|
+
const images = item.output.filter((part) => isRecord(part) && part.type === "input_image");
|
|
20
|
+
if (images.length === 0) return [item];
|
|
21
|
+
const text = contentText(item.output) || "(tool returned image output)";
|
|
22
|
+
return [
|
|
23
|
+
{ ...item, output: text },
|
|
24
|
+
{
|
|
25
|
+
role: "user",
|
|
26
|
+
content: [
|
|
27
|
+
{ type: "input_text", text: "The previous tool result included image output. Use the attached image." },
|
|
28
|
+
...images,
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function rewriteXaiPayload(value: unknown): unknown {
|
|
35
|
+
if (!isRecord(value)) return value;
|
|
36
|
+
const body = { ...value };
|
|
37
|
+
delete body.prompt_cache_retention;
|
|
38
|
+
if (isRecord(body.reasoning)) {
|
|
39
|
+
const effort = body.reasoning.effort;
|
|
40
|
+
body.reasoning =
|
|
41
|
+
typeof effort === "string" && effort !== "none"
|
|
42
|
+
? { effort: effort === "minimal" ? "low" : effort }
|
|
43
|
+
: undefined;
|
|
44
|
+
}
|
|
45
|
+
if (Array.isArray(body.input)) {
|
|
46
|
+
const instructions: string[] = [];
|
|
47
|
+
const input: unknown[] = [];
|
|
48
|
+
for (const raw of body.input) {
|
|
49
|
+
if (!isRecord(raw)) {
|
|
50
|
+
input.push(raw);
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if ((raw.role === "developer" || raw.role === "system") && input.length === 0) {
|
|
54
|
+
const text = contentText(raw.content).trim();
|
|
55
|
+
if (text) instructions.push(text);
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
input.push(...normalizeToolOutput(raw));
|
|
59
|
+
}
|
|
60
|
+
body.input = input;
|
|
61
|
+
if (instructions.length > 0) {
|
|
62
|
+
body.instructions = [typeof body.instructions === "string" ? body.instructions : "", ...instructions]
|
|
63
|
+
.filter(Boolean)
|
|
64
|
+
.join("\n\n");
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return body;
|
|
68
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shanepadgett/tau-agent",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Tau is a custom agentic harness built with pi extensions",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"README.md"
|
|
29
29
|
],
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@shanepadgett/tau-tui": "0.
|
|
31
|
+
"@shanepadgett/tau-tui": "0.7.0",
|
|
32
32
|
"@toon-format/toon": "2.3.0",
|
|
33
33
|
"smol-toml": "1.7.0"
|
|
34
34
|
},
|
package/shared/git.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ExtensionAPI,
|
|
1
|
+
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
|
|
3
3
|
const DEFAULT_GIT_TIMEOUT_MS = 10_000;
|
|
4
4
|
|
|
@@ -7,7 +7,7 @@ export interface GitRunner {
|
|
|
7
7
|
run(args: string[], options?: { cwd?: string; optional?: boolean; timeout?: number }): Promise<string>;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
export function createGitRunner(pi: ExtensionAPI, ctx:
|
|
10
|
+
export function createGitRunner(pi: ExtensionAPI, ctx: ExtensionContext): GitRunner {
|
|
11
11
|
return {
|
|
12
12
|
cwd: ctx.cwd,
|
|
13
13
|
async run(args, options = {}) {
|
|
@@ -10,21 +10,23 @@ const MAX_ATTEMPTS = 5;
|
|
|
10
10
|
const MAX_TOOL_ATTEMPTS = 2;
|
|
11
11
|
const SEVEN_DAYS_MS = 604_800_000;
|
|
12
12
|
|
|
13
|
-
const PREFERRED_MODELS: ReadonlyArray<{ provider: string; model: string; reasoning: ThinkingLevel }> = [
|
|
14
|
-
{ provider: "openrouter", model: "cohere/north-mini-code:free", reasoning: "high" },
|
|
15
|
-
{ provider: "github-copilot", model: "gemini-3.5-flash", reasoning: "high" },
|
|
16
|
-
{ provider: "openai-codex", model: "gpt-5.4-mini", reasoning: "high" },
|
|
17
|
-
{ provider: "anthropic", model: "claude-haiku-4-5", reasoning: "high" },
|
|
18
|
-
];
|
|
19
|
-
|
|
20
13
|
interface GenerationContext {
|
|
21
14
|
ui: ExtensionContext["ui"];
|
|
22
15
|
signal: AbortSignal | undefined;
|
|
16
|
+
sessionManager?: { getSessionId(): string };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface ModelFallbackOptions {
|
|
20
|
+
statusKey?: string;
|
|
21
|
+
notifyOnFallback?: boolean;
|
|
22
|
+
maxAttempts?: number;
|
|
23
|
+
onStatus?: (status: string) => void | Promise<void>;
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
export async function resolveCandidates(
|
|
26
27
|
ctx: Pick<ExtensionContext, "modelRegistry" | "model" | "cwd" | "isProjectTrusted">,
|
|
27
|
-
preferredModels: ReadonlyArray<{ provider: string; model: string; reasoning: ThinkingLevel }
|
|
28
|
+
preferredModels: ReadonlyArray<{ provider: string; model: string; reasoning: ThinkingLevel }>,
|
|
29
|
+
includeParentModel: boolean,
|
|
28
30
|
): Promise<ModelCandidate[]> {
|
|
29
31
|
const settings = await loadTauExtensionSettings(ctx, modelFallbackSettings);
|
|
30
32
|
const candidates: ModelCandidate[] = [];
|
|
@@ -47,7 +49,7 @@ export async function resolveCandidates(
|
|
|
47
49
|
const model = ctx.modelRegistry.find(preferred.provider, preferred.model);
|
|
48
50
|
if (model) await add(model, preferred.reasoning);
|
|
49
51
|
}
|
|
50
|
-
if (ctx.model) await add(ctx.model, undefined);
|
|
52
|
+
if (includeParentModel && ctx.model) await add(ctx.model, undefined);
|
|
51
53
|
|
|
52
54
|
if (candidates.length === 0) throw new Error("No authenticated model available for generation.");
|
|
53
55
|
return candidates;
|
|
@@ -59,7 +61,7 @@ export async function generateValidated<T>(
|
|
|
59
61
|
prompt: string,
|
|
60
62
|
validate: (text: string) => T,
|
|
61
63
|
correctionPrompt?: (error: Error, text: string) => string,
|
|
62
|
-
options?:
|
|
64
|
+
options?: ModelFallbackOptions,
|
|
63
65
|
): Promise<T> {
|
|
64
66
|
return withModelFallback(ctx, candidates, options, (candidate) =>
|
|
65
67
|
requestValidated(ctx, candidate, prompt, validate, correctionPrompt),
|
|
@@ -73,7 +75,7 @@ export async function generateToolValidated<T>(
|
|
|
73
75
|
tool: Tool,
|
|
74
76
|
validate: (input: unknown) => T,
|
|
75
77
|
correctionPrompt?: (error: Error, output: string) => string,
|
|
76
|
-
options?:
|
|
78
|
+
options?: ModelFallbackOptions,
|
|
77
79
|
): Promise<T> {
|
|
78
80
|
return withModelFallback(ctx, candidates, options, (candidate) =>
|
|
79
81
|
requestToolValidated(
|
|
@@ -91,7 +93,7 @@ export async function generateToolValidated<T>(
|
|
|
91
93
|
async function withModelFallback<T>(
|
|
92
94
|
ctx: GenerationContext,
|
|
93
95
|
candidates: readonly ModelCandidate[],
|
|
94
|
-
options:
|
|
96
|
+
options: ModelFallbackOptions | undefined,
|
|
95
97
|
request: (candidate: ModelCandidate) => Promise<T>,
|
|
96
98
|
): Promise<T> {
|
|
97
99
|
const failures: string[] = [];
|
|
@@ -101,6 +103,7 @@ async function withModelFallback<T>(
|
|
|
101
103
|
for (const [index, candidate] of candidates.entries()) {
|
|
102
104
|
const label = `${candidate.model.provider}/${candidate.model.id}`;
|
|
103
105
|
if (statusKey) ctx.ui.setStatus(statusKey, `generating (${label})`);
|
|
106
|
+
await options?.onStatus?.(`Generating with ${label}`);
|
|
104
107
|
|
|
105
108
|
try {
|
|
106
109
|
return await request(candidate);
|
|
@@ -109,6 +112,7 @@ async function withModelFallback<T>(
|
|
|
109
112
|
if (shouldCooldownProvider(error)) await markProviderUnavailable(candidate.model.provider);
|
|
110
113
|
const message = errorText(error);
|
|
111
114
|
failures.push(`- ${label}: ${message}`);
|
|
115
|
+
if (index < candidates.length - 1) await options?.onStatus?.(`Model failed (${label}); trying next model`);
|
|
112
116
|
if (index < candidates.length - 1 && notifyOnFallback) {
|
|
113
117
|
ctx.ui.notify(`Model failed (${label}): ${message}\nTrying next model.`, "info");
|
|
114
118
|
}
|
|
@@ -209,6 +213,7 @@ function completeCandidate(
|
|
|
209
213
|
headers: candidate.headers,
|
|
210
214
|
signal: ctx.signal,
|
|
211
215
|
reasoning: candidate.reasoning,
|
|
216
|
+
sessionId: ctx.sessionManager?.getSessionId(),
|
|
212
217
|
});
|
|
213
218
|
}
|
|
214
219
|
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: context-maintenance
|
|
3
|
-
description: Reconcile repository context definitions after meaningful scope changes, with user approval before every update
|
|
4
|
-
tools:
|
|
5
|
-
- read
|
|
6
|
-
- grep
|
|
7
|
-
- find
|
|
8
|
-
- ls
|
|
9
|
-
- context_list
|
|
10
|
-
- context_get
|
|
11
|
-
- context_check
|
|
12
|
-
- context_audit
|
|
13
|
-
- context_changes
|
|
14
|
-
- context_review
|
|
15
|
-
model: openai-codex/gpt-5.6-luna
|
|
16
|
-
thinking: high
|
|
17
|
-
---
|
|
18
|
-
|
|
19
|
-
Maintain the repository context catalog. Research only the changed or requested scopes. Existing entries should stay small, reusable, and useful for future work. Prefer updating an existing entry over creating a near-duplicate. Do not create one entry per file.
|
|
20
|
-
|
|
21
|
-
Use context_changes when the task does not provide exact changed paths. Use context_list, context_get, context_check, and context_audit to understand the current catalog. Call context_review with concrete proposed operations. That tool owns user approval and applies only selected operations.
|
|
22
|
-
|
|
23
|
-
If context_review returns feedback, revise the affected proposal or batch and call context_review again. If it returns rejected, stop. Never claim an operation was applied unless context_review reports it applied. Finish with only a short applied/rejected summary.
|