@shanepadgett/tau-agent 0.19.0 → 0.20.1
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.
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
# Image Generation
|
|
2
2
|
|
|
3
|
-
`image_gen` generates raster images and edits up to three local raster images with Grok Imagine. It uses `
|
|
3
|
+
`image_gen` generates raster images and edits up to three local raster images with OpenAI GPT Image or xAI Grok Imagine. It uses `gpt-image-2` for OpenAI and `grok-imagine-image-quality` for xAI.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
By default, the tool follows the parent model: GPT models prefer OpenAI and Grok models prefer xAI. If that provider has no configured authentication, it tries the other provider. The model can pass `provider: "openai"` or `provider: "xai"` to override automatic selection.
|
|
6
|
+
|
|
7
|
+
Run `/login openai-codex` for GPT Image or `/login xai` for Grok Imagine before invoking the tool. OpenAI image generation uses the existing Codex subscription login; xAI supports its configured login methods.
|
|
6
8
|
|
|
7
9
|
Run `/reload` after installing or changing the extension.
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
Results are saved under `~/.local/share/tau-agent/images/` by default. Pass an explicit path with the expected image extension when the image should be saved in the current repository or another chosen location. For edits, supply one to three local PNG, JPEG, or WebP paths. Successful images up to 12 MiB are returned inline for inspection; larger results remain available at the saved path.
|
|
10
12
|
|
|
11
|
-
xAI controls
|
|
13
|
+
The OpenAI path calls a private Codex backend, which may change without notice. xAI controls Grok Imagine availability and subscription entitlements. A successful login does not guarantee image access.
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
OPENAI_IMAGE_API_BASE_URL,
|
|
3
|
+
OPENAI_IMAGE_MODEL,
|
|
4
|
+
XAI_API_BASE_URL,
|
|
5
|
+
XAI_IMAGE_MODEL,
|
|
6
|
+
type ImageProvider,
|
|
7
|
+
} from "./constants.ts";
|
|
2
8
|
|
|
3
9
|
const MAX_ERROR_BODY_BYTES = 8192;
|
|
4
10
|
const MAX_ERROR_MESSAGE_LENGTH = 2000;
|
|
@@ -6,6 +12,11 @@ const REQUEST_TIMEOUT_MS = 60_000;
|
|
|
6
12
|
const MAX_ATTEMPTS = 3;
|
|
7
13
|
const RETRY_BASE_DELAY_MS = 500;
|
|
8
14
|
|
|
15
|
+
export interface CodexAuth {
|
|
16
|
+
token: string;
|
|
17
|
+
accountId: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
9
20
|
export interface EditImage {
|
|
10
21
|
mimeType: "image/png" | "image/jpeg" | "image/webp";
|
|
11
22
|
data: string;
|
|
@@ -28,6 +39,29 @@ function isRecord(value: unknown): value is Record<string, unknown> {
|
|
|
28
39
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
29
40
|
}
|
|
30
41
|
|
|
42
|
+
export function resolveCodexAuth(token: string): CodexAuth {
|
|
43
|
+
const invalidCredential = () =>
|
|
44
|
+
new Error("The OpenAI Codex credential does not contain a usable ChatGPT account ID. Run /login again.");
|
|
45
|
+
const segments = token.split(".");
|
|
46
|
+
if (segments.length !== 3 || !segments[1]) throw invalidCredential();
|
|
47
|
+
if (!/^[A-Za-z0-9_-]+$/.test(segments[1]) || segments[1].length % 4 === 1) throw invalidCredential();
|
|
48
|
+
|
|
49
|
+
let payload: unknown;
|
|
50
|
+
try {
|
|
51
|
+
const decoded = Buffer.from(segments[1], "base64url");
|
|
52
|
+
if (decoded.toString("base64url") !== segments[1]) throw invalidCredential();
|
|
53
|
+
payload = JSON.parse(decoded.toString("utf8"));
|
|
54
|
+
} catch {
|
|
55
|
+
throw invalidCredential();
|
|
56
|
+
}
|
|
57
|
+
if (!isRecord(payload)) throw invalidCredential();
|
|
58
|
+
const authClaim = payload["https://api.openai.com/auth"];
|
|
59
|
+
if (!isRecord(authClaim)) throw invalidCredential();
|
|
60
|
+
const accountId = authClaim.chatgpt_account_id;
|
|
61
|
+
if (typeof accountId !== "string" || !accountId.trim()) throw invalidCredential();
|
|
62
|
+
return { token, accountId: accountId.trim() };
|
|
63
|
+
}
|
|
64
|
+
|
|
31
65
|
async function boundedError(response: HttpResponse): Promise<string> {
|
|
32
66
|
if (!response.body) return "";
|
|
33
67
|
const reader = response.body.getReader();
|
|
@@ -85,25 +119,61 @@ export function detectImageMimeType(bytes: Buffer): GeneratedImage["mimeType"] |
|
|
|
85
119
|
return undefined;
|
|
86
120
|
}
|
|
87
121
|
|
|
88
|
-
function decodeImageResponse(value: unknown): GeneratedImage {
|
|
122
|
+
function decodeImageResponse(value: unknown, service: "OpenAI Codex" | "xAI", pngOnly: boolean): GeneratedImage {
|
|
89
123
|
if (!isRecord(value) || !Array.isArray(value.data) || !isRecord(value.data[0])) {
|
|
90
|
-
throw new Error(
|
|
124
|
+
throw new Error(`${service} returned an invalid image response`);
|
|
91
125
|
}
|
|
92
126
|
const encoded = value.data[0].b64_json;
|
|
93
|
-
if (typeof encoded !== "string" || !encoded.trim()) throw new Error(
|
|
127
|
+
if (typeof encoded !== "string" || !encoded.trim()) throw new Error(`${service} returned an invalid image response`);
|
|
94
128
|
const base64 = encoded.trim();
|
|
95
129
|
if (!/^[A-Za-z0-9+/]*={0,2}$/.test(base64) || base64.length % 4 === 1) {
|
|
96
|
-
throw new Error(
|
|
130
|
+
throw new Error(`${service} returned invalid base64 image data`);
|
|
97
131
|
}
|
|
98
132
|
const bytes = Buffer.from(base64, "base64");
|
|
99
133
|
if (bytes.toString("base64").replace(/=+$/, "") !== base64.replace(/=+$/, "")) {
|
|
100
|
-
throw new Error(
|
|
134
|
+
throw new Error(`${service} returned invalid base64 image data`);
|
|
101
135
|
}
|
|
102
136
|
const mimeType = detectImageMimeType(bytes);
|
|
103
|
-
if (!mimeType
|
|
137
|
+
if (!mimeType || (pngOnly && mimeType !== "image/png")) {
|
|
138
|
+
throw new Error(`${service} returned unsupported image data`);
|
|
139
|
+
}
|
|
104
140
|
return { bytes, base64: bytes.toString("base64"), mimeType };
|
|
105
141
|
}
|
|
106
142
|
|
|
143
|
+
async function requestOpenAIImage(
|
|
144
|
+
operation: "generation" | "edit",
|
|
145
|
+
body: Record<string, unknown>,
|
|
146
|
+
auth: CodexAuth,
|
|
147
|
+
signal?: AbortSignal,
|
|
148
|
+
): Promise<GeneratedImage> {
|
|
149
|
+
const route = operation === "generation" ? "generations" : "edits";
|
|
150
|
+
const response = (await fetch(`${OPENAI_IMAGE_API_BASE_URL}/${route}`, {
|
|
151
|
+
method: "POST",
|
|
152
|
+
headers: {
|
|
153
|
+
Accept: "application/json",
|
|
154
|
+
Authorization: `Bearer ${auth.token}`,
|
|
155
|
+
"chatgpt-account-id": auth.accountId,
|
|
156
|
+
"Content-Type": "application/json",
|
|
157
|
+
originator: "pi",
|
|
158
|
+
},
|
|
159
|
+
body: JSON.stringify(body),
|
|
160
|
+
signal,
|
|
161
|
+
})) as HttpResponse;
|
|
162
|
+
if (!response.ok) {
|
|
163
|
+
const message = serverErrorMessage(await boundedError(response), auth.token);
|
|
164
|
+
throw new Error(
|
|
165
|
+
`OpenAI Codex image ${operation} failed with status ${response.status}${message ? `: ${message}` : ""}`,
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
let value: unknown;
|
|
169
|
+
try {
|
|
170
|
+
value = await response.json();
|
|
171
|
+
} catch {
|
|
172
|
+
throw new Error("OpenAI Codex returned a non-JSON image response");
|
|
173
|
+
}
|
|
174
|
+
return decodeImageResponse(value, "OpenAI Codex", true);
|
|
175
|
+
}
|
|
176
|
+
|
|
107
177
|
function retryable(status: number): boolean {
|
|
108
178
|
return status === 408 || status === 409 || status === 425 || status === 429 || status >= 500;
|
|
109
179
|
}
|
|
@@ -124,7 +194,7 @@ async function delay(milliseconds: number, signal?: AbortSignal): Promise<void>
|
|
|
124
194
|
});
|
|
125
195
|
}
|
|
126
196
|
|
|
127
|
-
async function
|
|
197
|
+
async function requestXaiImage(
|
|
128
198
|
operation: "generation" | "edit",
|
|
129
199
|
body: Record<string, unknown>,
|
|
130
200
|
token: string,
|
|
@@ -160,7 +230,7 @@ async function requestImage(
|
|
|
160
230
|
} catch {
|
|
161
231
|
throw new Error("xAI returned a non-JSON image response");
|
|
162
232
|
}
|
|
163
|
-
return decodeImageResponse(value);
|
|
233
|
+
return decodeImageResponse(value, "xAI", false);
|
|
164
234
|
}
|
|
165
235
|
const message = serverErrorMessage(await boundedError(response), token);
|
|
166
236
|
if (!retryable(response.status) || attempt === MAX_ATTEMPTS) {
|
|
@@ -173,8 +243,21 @@ async function requestImage(
|
|
|
173
243
|
throw new Error(`xAI image ${operation} failed`);
|
|
174
244
|
}
|
|
175
245
|
|
|
176
|
-
export function generateImage(
|
|
177
|
-
|
|
246
|
+
export function generateImage(
|
|
247
|
+
provider: ImageProvider,
|
|
248
|
+
prompt: string,
|
|
249
|
+
token: string,
|
|
250
|
+
signal?: AbortSignal,
|
|
251
|
+
): Promise<GeneratedImage> {
|
|
252
|
+
if (provider === "openai") {
|
|
253
|
+
return requestOpenAIImage(
|
|
254
|
+
"generation",
|
|
255
|
+
{ prompt, model: OPENAI_IMAGE_MODEL, background: "auto", quality: "auto", size: "auto" },
|
|
256
|
+
resolveCodexAuth(token),
|
|
257
|
+
signal,
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
return requestXaiImage(
|
|
178
261
|
"generation",
|
|
179
262
|
{ model: XAI_IMAGE_MODEL, prompt, n: 1, resolution: "1k", response_format: "b64_json" },
|
|
180
263
|
token,
|
|
@@ -183,13 +266,29 @@ export function generateImage(prompt: string, token: string, signal?: AbortSigna
|
|
|
183
266
|
}
|
|
184
267
|
|
|
185
268
|
export function editImage(
|
|
269
|
+
provider: ImageProvider,
|
|
186
270
|
prompt: string,
|
|
187
271
|
images: readonly EditImage[],
|
|
188
272
|
token: string,
|
|
189
273
|
signal?: AbortSignal,
|
|
190
274
|
): Promise<GeneratedImage> {
|
|
275
|
+
if (provider === "openai") {
|
|
276
|
+
return requestOpenAIImage(
|
|
277
|
+
"edit",
|
|
278
|
+
{
|
|
279
|
+
images: images.map((image) => ({ image_url: `data:${image.mimeType};base64,${image.data}` })),
|
|
280
|
+
prompt,
|
|
281
|
+
model: OPENAI_IMAGE_MODEL,
|
|
282
|
+
background: "auto",
|
|
283
|
+
quality: "auto",
|
|
284
|
+
size: "auto",
|
|
285
|
+
},
|
|
286
|
+
resolveCodexAuth(token),
|
|
287
|
+
signal,
|
|
288
|
+
);
|
|
289
|
+
}
|
|
191
290
|
const references = images.map((image) => ({ url: `data:${image.mimeType};base64,${image.data}` }));
|
|
192
|
-
return
|
|
291
|
+
return requestXaiImage(
|
|
193
292
|
"edit",
|
|
194
293
|
{
|
|
195
294
|
model: XAI_IMAGE_MODEL,
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
export const OPENAI_PROVIDER = "openai-codex";
|
|
2
|
+
export const OPENAI_IMAGE_MODEL = "gpt-image-2";
|
|
3
|
+
export const OPENAI_IMAGE_API_BASE_URL = "https://chatgpt.com/backend-api/codex/images";
|
|
1
4
|
export const XAI_PROVIDER = "xai";
|
|
2
5
|
export const XAI_IMAGE_MODEL = "grok-imagine-image-quality";
|
|
3
6
|
export const XAI_API_BASE_URL = "https://api.x.ai/v1";
|
|
7
|
+
|
|
8
|
+
export type ImageProvider = "openai" | "xai";
|
|
@@ -5,7 +5,7 @@ import { homedir } from "node:os";
|
|
|
5
5
|
import { basename, dirname, extname, isAbsolute, join, resolve } from "node:path";
|
|
6
6
|
import { type Static, Type } from "typebox";
|
|
7
7
|
import { detectImageMimeType, editImage, generateImage, type EditImage, type GeneratedImage } from "./client.ts";
|
|
8
|
-
import { XAI_IMAGE_MODEL, XAI_PROVIDER } from "./constants.ts";
|
|
8
|
+
import { OPENAI_IMAGE_MODEL, OPENAI_PROVIDER, XAI_IMAGE_MODEL, XAI_PROVIDER, type ImageProvider } from "./constants.ts";
|
|
9
9
|
|
|
10
10
|
const MAX_INPUT_BYTES = 50 * 1024 * 1024;
|
|
11
11
|
const MAX_INLINE_BYTES = 12 * 1024 * 1024;
|
|
@@ -13,6 +13,12 @@ const MAX_INLINE_BYTES = 12 * 1024 * 1024;
|
|
|
13
13
|
const imageGenSchema = Type.Object(
|
|
14
14
|
{
|
|
15
15
|
prompt: Type.String({ minLength: 1 }),
|
|
16
|
+
provider: Type.Optional(
|
|
17
|
+
Type.Union([Type.Literal("openai"), Type.Literal("xai")], {
|
|
18
|
+
description:
|
|
19
|
+
"Image provider override. Omit to follow the parent model, preferring OpenAI for GPT and xAI for Grok.",
|
|
20
|
+
}),
|
|
21
|
+
),
|
|
16
22
|
path: Type.Optional(
|
|
17
23
|
Type.String({ description: "Explicit image destination path; defaults to Tau's external image store" }),
|
|
18
24
|
),
|
|
@@ -25,7 +31,8 @@ type ImageGenParams = Static<typeof imageGenSchema>;
|
|
|
25
31
|
|
|
26
32
|
interface ImageGenDetails {
|
|
27
33
|
path: string;
|
|
28
|
-
|
|
34
|
+
provider: ImageProvider;
|
|
35
|
+
model: typeof OPENAI_IMAGE_MODEL | typeof XAI_IMAGE_MODEL;
|
|
29
36
|
operation: "generate" | "edit";
|
|
30
37
|
}
|
|
31
38
|
|
|
@@ -41,7 +48,7 @@ export default function imageGenExtension(pi: ExtensionAPI): void {
|
|
|
41
48
|
name: "image_gen",
|
|
42
49
|
label: "Image Generation",
|
|
43
50
|
description:
|
|
44
|
-
"Generate a requested raster image or AI-edit existing images with
|
|
51
|
+
"Generate a requested raster image or AI-edit existing images with OpenAI GPT Image or xAI Grok Imagine. Omit provider to follow the parent model; set it to openai or xai to override. Omit referenced_image_paths to generate; pass one to three local paths to edit or compose. Omit path to use Tau's external image store; pass path only when the user explicitly requests a repository file or other destination. Returns the image for inspection.",
|
|
45
52
|
parameters: imageGenSchema,
|
|
46
53
|
async execute(_toolCallId, params: ImageGenParams, signal, onUpdate, ctx) {
|
|
47
54
|
signal?.throwIfAborted();
|
|
@@ -61,10 +68,37 @@ export default function imageGenExtension(pi: ExtensionAPI): void {
|
|
|
61
68
|
throw new Error("Image path must end in .jpg, .jpeg, .png, or .webp");
|
|
62
69
|
}
|
|
63
70
|
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
71
|
+
const parentUsesXai =
|
|
72
|
+
ctx.model?.provider.toLowerCase() === XAI_PROVIDER ||
|
|
73
|
+
ctx.model?.id.toLowerCase().includes("grok") === true;
|
|
74
|
+
const preferredProvider: ImageProvider = params.provider ?? (parentUsesXai ? "xai" : "openai");
|
|
75
|
+
const providers: readonly ImageProvider[] = params.provider
|
|
76
|
+
? [params.provider]
|
|
77
|
+
: preferredProvider === "xai"
|
|
78
|
+
? ["xai", "openai"]
|
|
79
|
+
: ["openai", "xai"];
|
|
80
|
+
let provider: ImageProvider | undefined;
|
|
81
|
+
let token: string | undefined;
|
|
82
|
+
for (const candidate of providers) {
|
|
83
|
+
const candidateToken = await ctx.modelRegistry.getApiKeyForProvider(
|
|
84
|
+
candidate === "openai" ? OPENAI_PROVIDER : XAI_PROVIDER,
|
|
85
|
+
);
|
|
86
|
+
if (candidateToken) {
|
|
87
|
+
provider = candidate;
|
|
88
|
+
token = candidateToken;
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (!provider || !token) {
|
|
93
|
+
if (params.provider === "openai") {
|
|
94
|
+
throw new Error("OpenAI Codex authentication is unavailable. Run /login openai-codex.");
|
|
95
|
+
}
|
|
96
|
+
if (params.provider === "xai") {
|
|
97
|
+
throw new Error("xAI authentication is unavailable. Run /login xai and choose a login method.");
|
|
98
|
+
}
|
|
99
|
+
throw new Error("Image generation authentication is unavailable. Run /login for OpenAI Codex or xAI.");
|
|
67
100
|
}
|
|
101
|
+
const model = provider === "openai" ? OPENAI_IMAGE_MODEL : XAI_IMAGE_MODEL;
|
|
68
102
|
const images: EditImage[] = [];
|
|
69
103
|
for (const path of params.referenced_image_paths ?? []) {
|
|
70
104
|
const rawPath = path.startsWith("@") ? path.slice(1) : path;
|
|
@@ -88,16 +122,16 @@ export default function imageGenExtension(pi: ExtensionAPI): void {
|
|
|
88
122
|
type: "text",
|
|
89
123
|
text:
|
|
90
124
|
operation === "generate"
|
|
91
|
-
? `Generating image with ${
|
|
92
|
-
: `Editing image with ${
|
|
125
|
+
? `Generating image with ${model}...`
|
|
126
|
+
: `Editing image with ${model}...`,
|
|
93
127
|
},
|
|
94
128
|
],
|
|
95
129
|
details: undefined,
|
|
96
130
|
});
|
|
97
131
|
const generated =
|
|
98
132
|
operation === "generate"
|
|
99
|
-
? await generateImage(prompt, token, signal)
|
|
100
|
-
: await editImage(prompt, images, token, signal);
|
|
133
|
+
? await generateImage(provider, prompt, token, signal)
|
|
134
|
+
: await editImage(provider, prompt, images, token, signal);
|
|
101
135
|
signal?.throwIfAborted();
|
|
102
136
|
const generatedExtension = outputExtension(generated);
|
|
103
137
|
if (requestedAbsolutePath) {
|
|
@@ -106,7 +140,9 @@ export default function imageGenExtension(pi: ExtensionAPI): void {
|
|
|
106
140
|
requestedExtension === generatedExtension ||
|
|
107
141
|
(generatedExtension === ".jpg" && requestedExtension === ".jpeg");
|
|
108
142
|
if (!matches)
|
|
109
|
-
throw new Error(
|
|
143
|
+
throw new Error(
|
|
144
|
+
`${provider === "openai" ? "OpenAI Codex" : "xAI"} returned ${generated.mimeType}; destination must end in ${generatedExtension}`,
|
|
145
|
+
);
|
|
110
146
|
}
|
|
111
147
|
const absolutePath =
|
|
112
148
|
requestedAbsolutePath ??
|
|
@@ -129,13 +165,13 @@ export default function imageGenExtension(pi: ExtensionAPI): void {
|
|
|
129
165
|
});
|
|
130
166
|
|
|
131
167
|
const verb = operation === "generate" ? "Generated" : "Edited";
|
|
132
|
-
const details: ImageGenDetails = { path: absolutePath, model
|
|
168
|
+
const details: ImageGenDetails = { path: absolutePath, provider, model, operation };
|
|
133
169
|
if (generated.bytes.length > MAX_INLINE_BYTES) {
|
|
134
170
|
return {
|
|
135
171
|
content: [
|
|
136
172
|
{
|
|
137
173
|
type: "text",
|
|
138
|
-
text: `${verb} image saved to ${absolutePath}. The
|
|
174
|
+
text: `${verb} image saved to ${absolutePath}. The image exceeds the 12 MiB attachment limit, so it was not added to model context.`,
|
|
139
175
|
},
|
|
140
176
|
],
|
|
141
177
|
details,
|
|
@@ -135,18 +135,13 @@ export default function subagentExtension(pi: ExtensionAPI): void {
|
|
|
135
135
|
const discovery = await discoverAgents(ctx.cwd, ctx.isProjectTrusted());
|
|
136
136
|
warn(discovery, ctx);
|
|
137
137
|
const lines = (await parentVisibleAgents(ctx, discovery)).map((agent) => `- ${agent.name}: ${agent.description}`);
|
|
138
|
-
const activeThreads = runtime.listThreads(ctx.cwd).map((thread) => {
|
|
139
|
-
const task = thread.initialTask.replace(/\s+/g, " ").trim();
|
|
140
|
-
return `- ${thread.id} (${thread.displayName} · ${thread.definition.name}): ${task.length <= 160 ? task : `${task.slice(0, 159)}…`}`;
|
|
141
|
-
});
|
|
142
|
-
const threadSection = activeThreads.length ? `\n\nActive reusable threads:\n${activeThreads.join("\n")}` : "";
|
|
143
138
|
const prompt = `## Subagents
|
|
144
139
|
Use \`subagent\` when an available agent matches a focused part of the task.
|
|
145
140
|
|
|
146
141
|
Available agents for this turn:
|
|
147
142
|
${lines.join("\n")}
|
|
148
143
|
|
|
149
|
-
Start a fresh thread with \`agent\` and \`task\`. Continue an existing thread with \`thread\` and \`task\`. Reuse a thread when feedback or follow-up work depends on its prior reads and reasoning. Start fresh for unrelated work or when its context is stale or oversized
|
|
144
|
+
Start a fresh thread with \`agent\` and \`task\`. Continue an existing thread with \`thread\` and \`task\`. Reuse a thread when feedback or follow-up work depends on its prior reads and reasoning. Start fresh for unrelated work or when its context is stale or oversized.
|
|
150
145
|
|
|
151
146
|
Pass \`files\` when exact relevant files are already known. Tau autoreads current line-numbered snapshots into that child turn before it starts.
|
|
152
147
|
|
|
@@ -52,7 +52,7 @@ Adds `/ideas` to log rough ideas or open the ideas browser.
|
|
|
52
52
|
|
|
53
53
|
## image-gen
|
|
54
54
|
|
|
55
|
-
Gives the agent
|
|
55
|
+
Gives the agent an OpenAI GPT Image and xAI Grok Imagine generation and editing tool. It follows the parent model by default and can override the provider per request. Run `/login openai-codex` or `/login xai` before use. Generated images are saved for inspection.
|
|
56
56
|
|
|
57
57
|
## manage-sessions
|
|
58
58
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shanepadgett/tau-agent",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.1",
|
|
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.20.1",
|
|
32
32
|
"@toon-format/toon": "2.3.0",
|
|
33
33
|
"smol-toml": "1.7.0"
|
|
34
34
|
},
|