zidane 1.6.17 → 1.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/README.md CHANGED
@@ -10,15 +10,18 @@ Minimal TypeScript agent loop built with [Bun](https://bun.sh). Hook into every
10
10
 
11
11
  ```bash
12
12
  bun install
13
- bun run auth # Anthropic OAuth
13
+ bun run auth # Anthropic + OpenAI Codex OAuth
14
14
  bun start --prompt "create a hello world app"
15
15
  ```
16
16
 
17
+ `auth` runs both OAuth flows by default. Pass `--openai` or `--anthropic` to authenticate only one provider; the npm script form works too, e.g. `npm run auth --openai`.
18
+
17
19
  ## Agent Setup
18
20
 
19
21
  ```ts
20
- import { createAgent, anthropic } from 'zidane'
21
- import { basic } from 'zidane'
22
+ import { createAgent } from 'zidane'
23
+ import { basic } from 'zidane/harnesses'
24
+ import { anthropic } from 'zidane/providers'
22
25
 
23
26
  const agent = createAgent({
24
27
  provider: anthropic({ apiKey: 'sk-ant-...' }),
@@ -77,7 +80,7 @@ Precedence: `run.behavior` > `agent.behavior` > `harness.behavior` > hardcoded d
77
80
  bun start \
78
81
  --prompt "your task" \ # required
79
82
  --model claude-opus-4-6 \ # model id
80
- --provider anthropic \ # anthropic | openrouter | cerebras
83
+ --provider anthropic \ # anthropic | openai | openrouter | cerebras
81
84
  --harness basic \ # tool set
82
85
  --system "be concise" \ # system prompt
83
86
  --thinking off \ # off | minimal | low | medium | high
@@ -92,10 +95,11 @@ All providers accept runtime credentials via a params object. Env vars are fallb
92
95
  ### Anthropic
93
96
 
94
97
  ```ts
95
- import { anthropic } from 'zidane'
98
+ import { anthropic } from 'zidane/providers'
96
99
 
97
100
  anthropic({ apiKey: 'sk-ant-...' })
98
101
  anthropic({ access: 'sk-ant-oat-...' }) // OAuth
102
+ anthropic({ access: 'sk-ant-oat-...', refresh: '...', expires: Date.now() + 3600_000 }) // auto-refresh
99
103
  anthropic({ apiKey: '...', defaultModel: 'claude-sonnet-4-6' })
100
104
  ```
101
105
 
@@ -104,17 +108,31 @@ Fallback: `params.apiKey` > `params.access` > `ANTHROPIC_API_KEY` env > `.creden
104
108
  ### OpenRouter
105
109
 
106
110
  ```ts
107
- import { openrouter } from 'zidane'
111
+ import { openrouter } from 'zidane/providers'
108
112
 
109
113
  openrouter({ apiKey: 'sk-or-...', defaultModel: 'google/gemini-pro' })
110
114
  ```
111
115
 
112
116
  Fallback: `params.apiKey` > `OPENROUTER_API_KEY` env
113
117
 
118
+ ### OpenAI
119
+
120
+ ```ts
121
+ import { openai } from 'zidane/providers'
122
+
123
+ openai() // OpenAI Codex OAuth
124
+ openai({ access: 'eyJ...', defaultModel: 'gpt-5.4' })
125
+ openai({ access: 'eyJ...', refresh: '...', expires: Date.now() + 3600_000, accountId: 'acct_123' })
126
+ ```
127
+
128
+ Fallback: `params.apiKey` > `params.access` > `OPENAI_CODEX_API_KEY` env > `.credentials.json`
129
+
130
+ Pass the full OAuth credential fields (`access`, `refresh`, `expires`, plus provider extras like `accountId`) to let the provider auto-refresh tokens without reading `.credentials.json`.
131
+
114
132
  ### Cerebras
115
133
 
116
134
  ```ts
117
- import { cerebras } from 'zidane'
135
+ import { cerebras } from 'zidane/providers'
118
136
 
119
137
  cerebras({ apiKey: 'csk-...', defaultModel: 'zai-glm-4.7' })
120
138
  ```
@@ -136,7 +154,7 @@ Tools are grouped into **harnesses**. The `basic` harness includes:
136
154
  Define a custom harness:
137
155
 
138
156
  ```ts
139
- import { defineHarness, basicTools } from 'zidane'
157
+ import { defineHarness, basicTools } from 'zidane/harnesses'
140
158
 
141
159
  const harness = defineHarness({
142
160
  name: 'researcher',
@@ -221,6 +239,12 @@ agent.hooks.hook('stream:thinking', (ctx) => {
221
239
  // ctx.delta, ctx.thinking (accumulated), ctx.turnId
222
240
  // Fires when the model streams reasoning traces (Anthropic, OpenRouter)
223
241
  })
242
+
243
+ agent.hooks.hook('oauth:refresh', (ctx) => {
244
+ // ctx.provider, ctx.providerId, ctx.source
245
+ // ctx.previousCredentials, ctx.credentials
246
+ // Fires when an OAuth token is refreshed from passed credentials or .credentials.json
247
+ })
224
248
  ```
225
249
 
226
250
  ### Tool execution
@@ -290,7 +314,8 @@ agent.followUp('now write tests for what you built')
290
314
  The `spawn` tool delegates tasks to child agents that run independently.
291
315
 
292
316
  ```ts
293
- import { createSpawnTool, defineHarness, basicTools } from 'zidane'
317
+ import { createSpawnTool } from 'zidane/tools'
318
+ import { defineHarness, basicTools } from 'zidane/harnesses'
294
319
 
295
320
  const harness = defineHarness({
296
321
  name: 'orchestrator',
@@ -312,7 +337,8 @@ Children inherit the parent's harness and can spawn their own children.
312
337
  Let the agent pause and request structured input from the outside world. Not included in any harness by default.
313
338
 
314
339
  ```ts
315
- import { createInteractionTool, defineHarness, basicTools } from 'zidane'
340
+ import { createInteractionTool } from 'zidane/tools'
341
+ import { defineHarness, basicTools } from 'zidane/harnesses'
316
342
 
317
343
  const askUser = createInteractionTool({
318
344
  name: 'ask_user',
@@ -158,6 +158,22 @@ interface SpawnHookContext {
158
158
  interface StreamHookContext {
159
159
  turnId: string;
160
160
  }
161
+ /** Context for OAuth refresh hooks */
162
+ interface OAuthRefreshHookContext {
163
+ provider: string;
164
+ providerId: string;
165
+ source: 'params' | 'file';
166
+ previousCredentials: Record<string, unknown> & {
167
+ access: string;
168
+ refresh: string;
169
+ expires: number;
170
+ };
171
+ credentials: Record<string, unknown> & {
172
+ access: string;
173
+ refresh: string;
174
+ expires: number;
175
+ };
176
+ }
161
177
  type SessionEndStatus = 'completed' | 'aborted' | 'error';
162
178
 
163
179
  interface AnthropicParams {
@@ -175,6 +191,19 @@ interface CerebrasParams {
175
191
  }
176
192
  declare function cerebras(params?: CerebrasParams): Provider;
177
193
 
194
+ interface OpenAIParams {
195
+ /** OpenAI Codex OAuth access token. Falls back to OPENAI_CODEX_API_KEY and .credentials.json. */
196
+ apiKey?: string;
197
+ /** Alias for apiKey, matching the OAuth credential field. */
198
+ access?: string;
199
+ refresh?: string;
200
+ expires?: number;
201
+ accountId?: string;
202
+ defaultModel?: string;
203
+ transport?: 'sse' | 'websocket' | 'auto';
204
+ }
205
+ declare function openai(params?: OpenAIParams): Provider;
206
+
178
207
  interface OpenRouterParams {
179
208
  apiKey?: string;
180
209
  defaultModel?: string;
@@ -198,6 +227,7 @@ interface ToolResult {
198
227
  interface StreamCallbacks {
199
228
  onText: (delta: string) => void;
200
229
  onThinking?: (delta: string) => void;
230
+ onOAuthRefresh?: (ctx: OAuthRefreshHookContext) => void | Promise<void>;
201
231
  }
202
232
  interface TurnResult {
203
233
  /** Full assistant turn as a SessionMessage */
@@ -553,6 +583,7 @@ interface AgentHooks {
553
583
  delta: string;
554
584
  thinking: string;
555
585
  }) => void;
586
+ 'oauth:refresh': (ctx: OAuthRefreshHookContext) => void;
556
587
  'tool:gate': (ctx: ToolHookContext & {
557
588
  block: boolean;
558
589
  reason: string;
@@ -683,4 +714,4 @@ interface Agent {
683
714
  }
684
715
  declare function createAgent({ harness: harnessOption, provider, behavior: agentBehavior, execution, mcpServers, session, skills: agentSkills, _mcpConnector }: AgentOptions): Agent;
685
716
 
686
- export { fromOpenAI as $, type Agent as A, type ToolDef as B, type CerebrasParams as C, type ToolExecutionMode as D, type ToolHookContext as E, type ToolMap as F, type ToolResult as G, type Harness as H, type ImageContent as I, type ToolSpec as J, type TurnResult as K, type TurnUsage as L, type McpConnection as M, autoDetectAndConvert as N, type OpenRouterParams as O, type Provider as P, connectMcpServers as Q, type RemoteStoreOptions as R, type Session as S, type ThinkingLevel as T, createAgent as U, createMemoryStore as V, createRemoteStore as W, createSession as X, createSqliteStore as Y, defineHarness as Z, fromAnthropic as _, type AgentBehavior as a, loadSession as a0, noTools as a1, toAnthropic as a2, toOpenAI as a3, anthropic as a4, cerebras as a5, openrouter as a6, _default as a7, basicTools as a8, resultToString as a9, type AgentHooks as b, type AgentOptions as c, type AgentRunOptions as d, type AgentStats as e, type AnthropicParams as f, type ChildRunStats as g, type CreateSessionOptions as h, type HarnessConfig as i, type McpServerConfig as j, type McpToolHookContext as k, type SessionContentBlock as l, type SessionData as m, type SessionEndStatus as n, type SessionHookContext as o, type SessionMessage as p, type SessionRun as q, type SessionStore as r, type SessionTurn as s, type SpawnHookContext as t, type SqliteStoreOptions as u, type StreamCallbacks as v, type StreamHookContext as w, type StreamOptions as x, type ToolCall as y, type ToolContext as z };
717
+ export { defineHarness as $, type Agent as A, type ToolCall as B, type CerebrasParams as C, type ToolContext as D, type ToolDef as E, type ToolExecutionMode as F, type ToolHookContext as G, type Harness as H, type ImageContent as I, type ToolMap as J, type ToolResult as K, type ToolSpec as L, type McpConnection as M, type TurnResult as N, type OAuthRefreshHookContext as O, type Provider as P, type TurnUsage as Q, type RemoteStoreOptions as R, type Session as S, type ThinkingLevel as T, autoDetectAndConvert as U, connectMcpServers as V, createAgent as W, createMemoryStore as X, createRemoteStore as Y, createSession as Z, createSqliteStore as _, type AgentBehavior as a, fromAnthropic as a0, fromOpenAI as a1, loadSession as a2, noTools as a3, toAnthropic as a4, toOpenAI as a5, anthropic as a6, cerebras as a7, openai as a8, openrouter as a9, _default as aa, basicTools as ab, resultToString as ac, type AgentHooks as b, type AgentOptions as c, type AgentRunOptions as d, type AgentStats as e, type AnthropicParams as f, type ChildRunStats as g, type CreateSessionOptions as h, type HarnessConfig as i, type McpServerConfig as j, type McpToolHookContext as k, type OpenAIParams as l, type OpenRouterParams as m, type SessionContentBlock as n, type SessionData as o, type SessionEndStatus as p, type SessionHookContext as q, type SessionMessage as r, type SessionRun as s, type SessionStore as t, type SessionTurn as u, type SpawnHookContext as v, type SqliteStoreOptions as w, type StreamCallbacks as x, type StreamHookContext as y, type StreamOptions as z };
@@ -196,6 +196,9 @@ async function executeTurn(ctx, turn) {
196
196
  onThinking(delta) {
197
197
  currentThinking += delta;
198
198
  ctx.hooks.callHook("stream:thinking", { delta, thinking: currentThinking, turnId });
199
+ },
200
+ onOAuthRefresh(refreshCtx) {
201
+ return ctx.hooks.callHook("oauth:refresh", refreshCtx);
199
202
  }
200
203
  }
201
204
  );
@@ -243,8 +246,13 @@ async function executeTurn(ctx, turn) {
243
246
  signal: ctx.signal,
244
247
  toolChoice: { type: "tool", name: "__output__" }
245
248
  },
246
- { onText: () => {
247
- } }
249
+ {
250
+ onText: () => {
251
+ },
252
+ onOAuthRefresh(refreshCtx) {
253
+ return ctx.hooks.callHook("oauth:refresh", refreshCtx);
254
+ }
255
+ }
248
256
  );
249
257
  const output = schemaResult.toolCalls.find((tc) => tc.name === "__output__")?.input;
250
258
  if (output) {
@@ -4,7 +4,7 @@ import {
4
4
  shell,
5
5
  spawn,
6
6
  writeFile
7
- } from "./chunk-OJATGJ5F.js";
7
+ } from "./chunk-O4QCBJQF.js";
8
8
 
9
9
  // src/harnesses/basic.ts
10
10
  var basicTools = { shell, readFile, writeFile, listFiles };
@@ -1,5 +1,5 @@
1
1
  import 'hookable';
2
- export { H as Harness, i as HarnessConfig, z as ToolContext, B as ToolDef, F as ToolMap, a7 as basic, a8 as basicTools, Z as defineHarness, a1 as noTools } from './agent-CtGvwTdo.js';
2
+ export { H as Harness, i as HarnessConfig, D as ToolContext, E as ToolDef, J as ToolMap, aa as basic, ab as basicTools, $ as defineHarness, a3 as noTools } from './agent-B__ISsOE.js';
3
3
  import './types-BpvTmawk.js';
4
4
  import './types-CDI8Kmve.js';
5
5
  import '@modelcontextprotocol/sdk/client/index.js';
package/dist/harnesses.js CHANGED
@@ -3,8 +3,8 @@ import {
3
3
  basic_default,
4
4
  defineHarness,
5
5
  noTools
6
- } from "./chunk-KLFSGJE4.js";
7
- import "./chunk-OJATGJ5F.js";
6
+ } from "./chunk-T6MC3WP5.js";
7
+ import "./chunk-O4QCBJQF.js";
8
8
  import "./chunk-4C6Y56CC.js";
9
9
  import "./chunk-SZA4FKW5.js";
10
10
  import "./chunk-WDBO3JCO.js";
package/dist/index.d.ts CHANGED
@@ -1,10 +1,10 @@
1
- export { A as Agent, a as AgentBehavior, b as AgentHooks, c as AgentOptions, d as AgentRunOptions, e as AgentStats, h as CreateSessionOptions, H as Harness, i as HarnessConfig, I as ImageContent, M as McpConnection, j as McpServerConfig, k as McpToolHookContext, R as RemoteStoreOptions, S as Session, l as SessionContentBlock, m as SessionData, n as SessionEndStatus, o as SessionHookContext, p as SessionMessage, q as SessionRun, r as SessionStore, s as SessionTurn, t as SpawnHookContext, u as SqliteStoreOptions, w as StreamHookContext, T as ThinkingLevel, z as ToolContext, B as ToolDef, D as ToolExecutionMode, E as ToolHookContext, F as ToolMap, L as TurnUsage, N as autoDetectAndConvert, Q as connectMcpServers, U as createAgent, V as createMemoryStore, W as createRemoteStore, X as createSession, Y as createSqliteStore, Z as defineHarness, _ as fromAnthropic, $ as fromOpenAI, a0 as loadSession, a1 as noTools, a2 as toAnthropic, a3 as toOpenAI } from './agent-CtGvwTdo.js';
1
+ export { A as Agent, a as AgentBehavior, b as AgentHooks, c as AgentOptions, d as AgentRunOptions, e as AgentStats, h as CreateSessionOptions, H as Harness, i as HarnessConfig, I as ImageContent, M as McpConnection, j as McpServerConfig, k as McpToolHookContext, O as OAuthRefreshHookContext, R as RemoteStoreOptions, S as Session, n as SessionContentBlock, o as SessionData, p as SessionEndStatus, q as SessionHookContext, r as SessionMessage, s as SessionRun, t as SessionStore, u as SessionTurn, v as SpawnHookContext, w as SqliteStoreOptions, y as StreamHookContext, T as ThinkingLevel, D as ToolContext, E as ToolDef, F as ToolExecutionMode, G as ToolHookContext, J as ToolMap, Q as TurnUsage, U as autoDetectAndConvert, V as connectMcpServers, W as createAgent, X as createMemoryStore, Y as createRemoteStore, Z as createSession, _ as createSqliteStore, $ as defineHarness, a0 as fromAnthropic, a1 as fromOpenAI, a2 as loadSession, a3 as noTools, a4 as toAnthropic, a5 as toOpenAI } from './agent-B__ISsOE.js';
2
2
  export { createDockerContext, createProcessContext } from './contexts.js';
3
3
  export { S as SandboxProvider, c as createSandboxContext } from './sandbox-CW72eLDP.js';
4
4
  export { C as ContextCapabilities, a as ContextType, E as ExecResult, b as ExecutionContext, c as ExecutionHandle, S as SpawnConfig } from './types-BpvTmawk.js';
5
5
  export { buildCatalog, defineSkill, discoverSkills, interpolateShellCommands, mergeSkillsConfig, parseSkillFile, resolveSkills, validateSkillName, writeSkillToDisk, writeSkillsToDisk } from './skills.js';
6
6
  export { S as SkillConfig, a as SkillResource, b as SkillsConfig } from './types-CDI8Kmve.js';
7
- export { C as ChildAgent, I as InteractionToolOptions, S as SpawnToolOptions, a as SpawnToolState, c as createInteractionTool, b as createSpawnTool, s as spawn } from './spawn-hNTJA3Iw.js';
7
+ export { C as ChildAgent, I as InteractionToolOptions, S as SpawnToolOptions, a as SpawnToolState, c as createInteractionTool, b as createSpawnTool, s as spawn } from './spawn-DRamirTu.js';
8
8
  import 'hookable';
9
9
  import '@modelcontextprotocol/sdk/client/index.js';
10
10
 
package/dist/index.js CHANGED
@@ -4,13 +4,13 @@ import {
4
4
  import {
5
5
  defineHarness,
6
6
  noTools
7
- } from "./chunk-KLFSGJE4.js";
7
+ } from "./chunk-T6MC3WP5.js";
8
8
  import {
9
9
  createAgent,
10
10
  createInteractionTool,
11
11
  createSpawnTool,
12
12
  spawn
13
- } from "./chunk-OJATGJ5F.js";
13
+ } from "./chunk-O4QCBJQF.js";
14
14
  import {
15
15
  buildCatalog,
16
16
  discoverSkills,
package/dist/mcp.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import 'hookable';
2
- export { M as McpConnection, j as McpServerConfig, Q as connectMcpServers, a9 as resultToString } from './agent-CtGvwTdo.js';
2
+ export { M as McpConnection, j as McpServerConfig, V as connectMcpServers, ac as resultToString } from './agent-B__ISsOE.js';
3
3
  import '@modelcontextprotocol/sdk/client/index.js';
4
4
  import './types-BpvTmawk.js';
5
5
  import './types-CDI8Kmve.js';
@@ -1,4 +1,4 @@
1
- export { f as AnthropicParams, C as CerebrasParams, O as OpenRouterParams, P as Provider, v as StreamCallbacks, x as StreamOptions, y as ToolCall, G as ToolResult, J as ToolSpec, K as TurnResult, a4 as anthropic, a5 as cerebras, a6 as openrouter } from './agent-CtGvwTdo.js';
1
+ export { f as AnthropicParams, C as CerebrasParams, l as OpenAIParams, m as OpenRouterParams, P as Provider, x as StreamCallbacks, z as StreamOptions, B as ToolCall, K as ToolResult, L as ToolSpec, N as TurnResult, a6 as anthropic, a7 as cerebras, a8 as openai, a9 as openrouter } from './agent-B__ISsOE.js';
2
2
  import 'hookable';
3
3
  import './types-BpvTmawk.js';
4
4
  import '@modelcontextprotocol/sdk/client/index.js';
package/dist/providers.js CHANGED
@@ -11,35 +11,99 @@ import {
11
11
  } from "./chunk-QCJKUQTQ.js";
12
12
 
13
13
  // src/providers/anthropic.ts
14
- import { existsSync, readFileSync } from "fs";
15
- import { resolve } from "path";
14
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
15
+ import { resolve as resolve2 } from "path";
16
16
  import Anthropic from "@anthropic-ai/sdk";
17
- var CREDENTIALS_FILE = resolve(import.meta.dir, "../../.credentials.json");
18
- function getApiKey(anthropicParams) {
17
+
18
+ // src/providers/oauth.ts
19
+ import { existsSync, readFileSync, writeFileSync } from "fs";
20
+ import { resolve } from "path";
21
+ import { getOAuthApiKey } from "@yaelg/pi-ai/oauth";
22
+ var CREDENTIALS_FILE = resolve(process.cwd(), ".credentials.json");
23
+ function readOAuthCredentials() {
24
+ if (!existsSync(CREDENTIALS_FILE))
25
+ return {};
26
+ return JSON.parse(readFileSync(CREDENTIALS_FILE, "utf-8"));
27
+ }
28
+ function writeOAuthCredentials(credentials) {
29
+ writeFileSync(CREDENTIALS_FILE, JSON.stringify(credentials, null, 2));
30
+ }
31
+ function credentialsFromParams(params, extraKeys = []) {
32
+ if (typeof params?.access !== "string" || typeof params.refresh !== "string" || typeof params.expires !== "number")
33
+ return void 0;
34
+ const extras = Object.fromEntries(
35
+ extraKeys.map((key) => [key, params[key]]).filter(([, value]) => value !== void 0)
36
+ );
37
+ return {
38
+ access: params.access,
39
+ refresh: params.refresh,
40
+ expires: params.expires,
41
+ ...extras
42
+ };
43
+ }
44
+ async function resolveOAuthApiKey(options, callbacks) {
45
+ if (typeof options.params?.apiKey === "string")
46
+ return options.params.apiKey;
47
+ const paramsCredentials = credentialsFromParams(options.params, options.extraCredentialKeys);
48
+ if (paramsCredentials) {
49
+ return await resolveCredentialSource("params", paramsCredentials);
50
+ }
51
+ if (typeof options.params?.access === "string")
52
+ return options.params.access;
53
+ if (options.envKey && process.env[options.envKey])
54
+ return process.env[options.envKey];
55
+ const readCredentials = options.readCredentials ?? readOAuthCredentials;
56
+ const writeCredentials = options.writeCredentials ?? writeOAuthCredentials;
57
+ const allCredentials = readCredentials();
58
+ const storedCredentials = allCredentials[options.providerId];
59
+ if (!storedCredentials)
60
+ throw new Error(options.missingError);
61
+ return await resolveCredentialSource("file", storedCredentials, allCredentials, writeCredentials);
62
+ async function resolveCredentialSource(source, current, allCredentials2, persistCredentials) {
63
+ try {
64
+ const refreshOAuthApiKey = options.getOAuthApiKey ?? getOAuthApiKey;
65
+ const result = await refreshOAuthApiKey(options.providerId, { [options.providerId]: current });
66
+ if (!result)
67
+ throw new Error(options.missingError);
68
+ if (result.newCredentials !== current) {
69
+ if (source === "file" && allCredentials2 && persistCredentials) {
70
+ allCredentials2[options.providerId] = result.newCredentials;
71
+ persistCredentials(allCredentials2);
72
+ }
73
+ await callbacks?.onOAuthRefresh?.({
74
+ provider: options.provider,
75
+ providerId: options.providerId,
76
+ source,
77
+ previousCredentials: { ...current },
78
+ credentials: { ...result.newCredentials }
79
+ });
80
+ }
81
+ return result.apiKey;
82
+ } catch (err) {
83
+ const reason = err instanceof Error ? err.message : String(err);
84
+ throw new Error(options.refreshError(reason));
85
+ }
86
+ }
87
+ }
88
+
89
+ // src/providers/anthropic.ts
90
+ var CREDENTIALS_FILE2 = resolve2(process.cwd(), ".credentials.json");
91
+ function getConfiguredApiKey(anthropicParams) {
19
92
  if (anthropicParams?.apiKey)
20
93
  return anthropicParams.apiKey;
21
94
  if (anthropicParams?.access)
22
95
  return anthropicParams.access;
23
96
  if (process.env.ANTHROPIC_API_KEY)
24
97
  return process.env.ANTHROPIC_API_KEY;
25
- if (existsSync(CREDENTIALS_FILE)) {
26
- const creds = JSON.parse(readFileSync(CREDENTIALS_FILE, "utf-8"));
98
+ if (existsSync2(CREDENTIALS_FILE2)) {
99
+ const creds = JSON.parse(readFileSync2(CREDENTIALS_FILE2, "utf-8"));
27
100
  if (creds.anthropic?.access)
28
101
  return creds.anthropic.access;
29
102
  }
30
103
  throw new Error("No API key found. Run `bun run auth` first.");
31
104
  }
32
- var THINKING_BUDGETS = {
33
- minimal: 1024,
34
- low: 4096,
35
- medium: 10240,
36
- high: 32768
37
- };
38
- function anthropic(anthropicParams) {
39
- const apiKey = getApiKey(anthropicParams);
40
- const isOAuth = apiKey.includes("sk-ant-oat");
41
- const defaultModel = anthropicParams?.defaultModel || "claude-opus-4-6";
42
- const client = new Anthropic(
105
+ function createClient(apiKey, isOAuth) {
106
+ return new Anthropic(
43
107
  isOAuth ? {
44
108
  apiKey: null,
45
109
  authToken: apiKey,
@@ -52,6 +116,22 @@ function anthropic(anthropicParams) {
52
116
  }
53
117
  } : { apiKey }
54
118
  );
119
+ }
120
+ var THINKING_BUDGETS = {
121
+ minimal: 1024,
122
+ low: 4096,
123
+ medium: 10240,
124
+ high: 32768
125
+ };
126
+ function anthropic(anthropicParams) {
127
+ const configuredApiKey = getConfiguredApiKey(anthropicParams);
128
+ const isOAuth = configuredApiKey.includes("sk-ant-oat");
129
+ const defaultModel = anthropicParams?.defaultModel || "claude-opus-4-6";
130
+ let runtimeCredentials = typeof anthropicParams?.access === "string" && typeof anthropicParams.refresh === "string" && typeof anthropicParams.expires === "number" ? {
131
+ access: anthropicParams.access,
132
+ refresh: anthropicParams.refresh,
133
+ expires: anthropicParams.expires
134
+ } : void 0;
55
135
  return {
56
136
  name: "anthropic",
57
137
  meta: { defaultModel, isOAuth },
@@ -92,6 +172,30 @@ function anthropic(anthropicParams) {
92
172
  };
93
173
  },
94
174
  async stream(options, callbacks) {
175
+ const apiKey = await resolveOAuthApiKey(
176
+ {
177
+ provider: "anthropic",
178
+ providerId: "anthropic",
179
+ params: runtimeCredentials ? { ...anthropicParams, ...runtimeCredentials } : anthropicParams,
180
+ envKey: "ANTHROPIC_API_KEY",
181
+ missingError: "No API key found. Run `bun run auth` first.",
182
+ refreshError: (reason) => `Anthropic OAuth token refresh failed. Run \`bun run auth --anthropic\` again. ${reason}`
183
+ },
184
+ {
185
+ ...callbacks,
186
+ async onOAuthRefresh(ctx) {
187
+ if (ctx.source === "params") {
188
+ runtimeCredentials = {
189
+ access: ctx.credentials.access,
190
+ refresh: ctx.credentials.refresh,
191
+ expires: ctx.credentials.expires
192
+ };
193
+ }
194
+ await callbacks.onOAuthRefresh?.(ctx);
195
+ }
196
+ }
197
+ );
198
+ const client = createClient(apiKey, apiKey.includes("sk-ant-oat"));
95
199
  const system = isOAuth ? `You are Claude Code, Anthropic's official CLI for Claude.` : options.system;
96
200
  const messages = isOAuth && options.system ? [
97
201
  { role: "user", content: [{ type: "text", text: options.system }] },
@@ -157,7 +261,7 @@ function anthropic(anthropicParams) {
157
261
 
158
262
  // src/providers/cerebras.ts
159
263
  var BASE_URL = "https://api.cerebras.ai/v1";
160
- function getApiKey2(params) {
264
+ function getApiKey(params) {
161
265
  if (params?.apiKey)
162
266
  return params.apiKey;
163
267
  if (process.env.CEREBRAS_API_KEY)
@@ -165,7 +269,7 @@ function getApiKey2(params) {
165
269
  throw new Error("No Cerebras API key found. Set CEREBRAS_API_KEY in your environment.");
166
270
  }
167
271
  function cerebras(params) {
168
- const apiKey = getApiKey2(params);
272
+ const apiKey = getApiKey(params);
169
273
  const defaultModel = params?.defaultModel || "zai-glm-4.7";
170
274
  return {
171
275
  name: "cerebras",
@@ -219,9 +323,235 @@ function cerebras(params) {
219
323
  };
220
324
  }
221
325
 
326
+ // src/providers/openai.ts
327
+ import { getModel } from "@yaelg/pi-ai";
328
+ import { streamOpenAICodexResponses } from "@yaelg/pi-ai/openai-codex-responses";
329
+ var PROVIDER_ID = "openai-codex";
330
+ var DEFAULT_MODEL = "gpt-5.4";
331
+ function resolveModel(modelId) {
332
+ const model = getModel(PROVIDER_ID, modelId);
333
+ if (model)
334
+ return model;
335
+ const fallback = getModel(PROVIDER_ID, DEFAULT_MODEL);
336
+ if (!fallback)
337
+ throw new Error(`OpenAI Codex model registry is missing the default model: ${DEFAULT_MODEL}`);
338
+ return { ...fallback, id: modelId, name: modelId };
339
+ }
340
+ function emptyUsage() {
341
+ return {
342
+ input: 0,
343
+ output: 0,
344
+ cacheRead: 0,
345
+ cacheWrite: 0,
346
+ totalTokens: 0,
347
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 }
348
+ };
349
+ }
350
+ function formatTools2(tools) {
351
+ return tools.map((t) => ({
352
+ name: t.name,
353
+ description: t.description,
354
+ parameters: t.inputSchema
355
+ }));
356
+ }
357
+ function toPiMessages(messages, modelId) {
358
+ const out = [];
359
+ for (const msg of messages) {
360
+ const toolResults = msg.content.filter((b) => b.type === "tool_result");
361
+ if (toolResults.length > 0) {
362
+ for (const result of toolResults) {
363
+ out.push({
364
+ role: "toolResult",
365
+ toolCallId: result.callId,
366
+ toolName: "",
367
+ content: [{ type: "text", text: result.output }],
368
+ isError: result.isError ?? false,
369
+ timestamp: Date.now()
370
+ });
371
+ }
372
+ continue;
373
+ }
374
+ const textBlocks = msg.content.filter((b) => b.type === "text");
375
+ const imageBlocks = msg.content.filter((b) => b.type === "image");
376
+ if (msg.role === "user") {
377
+ if (imageBlocks.length === 0 && textBlocks.length === 1) {
378
+ out.push({ role: "user", content: textBlocks[0].text, timestamp: Date.now() });
379
+ continue;
380
+ }
381
+ out.push({
382
+ role: "user",
383
+ content: [
384
+ ...imageBlocks.map((img) => ({ type: "image", data: img.data, mimeType: img.mediaType })),
385
+ ...textBlocks.map((block) => ({ type: "text", text: block.text }))
386
+ ],
387
+ timestamp: Date.now()
388
+ });
389
+ continue;
390
+ }
391
+ const content = [];
392
+ for (const block of msg.content) {
393
+ if (block.type === "text") {
394
+ content.push({ type: "text", text: block.text });
395
+ } else if (block.type === "thinking") {
396
+ content.push({ type: "thinking", thinking: block.text, thinkingSignature: block.signature });
397
+ } else if (block.type === "tool_call") {
398
+ content.push({ type: "toolCall", id: block.id, name: block.name, arguments: block.input });
399
+ }
400
+ }
401
+ out.push({
402
+ role: "assistant",
403
+ content,
404
+ api: "openai-codex-responses",
405
+ provider: PROVIDER_ID,
406
+ model: modelId,
407
+ usage: emptyUsage(),
408
+ stopReason: "stop",
409
+ timestamp: Date.now()
410
+ });
411
+ }
412
+ return out;
413
+ }
414
+ function fromPiAssistantMessage(message) {
415
+ const content = [];
416
+ for (const block of message.content) {
417
+ if (block.type === "text") {
418
+ content.push({ type: "text", text: block.text });
419
+ } else if (block.type === "thinking") {
420
+ content.push({ type: "thinking", text: block.thinking, signature: block.thinkingSignature });
421
+ } else if (block.type === "toolCall") {
422
+ content.push({ type: "tool_call", id: block.id, name: block.name, input: block.arguments });
423
+ }
424
+ }
425
+ return { role: "assistant", content };
426
+ }
427
+ function extractToolCalls(message) {
428
+ return message.content.filter((block) => block.type === "toolCall").map((block) => ({
429
+ id: block.id,
430
+ name: block.name,
431
+ input: block.arguments
432
+ }));
433
+ }
434
+ function extractText(message) {
435
+ return message.content.filter((block) => block.type === "text").map((block) => block.text).join("");
436
+ }
437
+ function toTurnUsage(usage) {
438
+ return {
439
+ input: usage.input,
440
+ output: usage.output,
441
+ cacheRead: usage.cacheRead || void 0,
442
+ cacheCreation: usage.cacheWrite || void 0,
443
+ cost: usage.cost.total || void 0
444
+ };
445
+ }
446
+ function applyPayloadOverrides(payload, options) {
447
+ const body = payload;
448
+ if (options.toolChoice) {
449
+ if (options.toolChoice.type === "tool" && options.toolChoice.name)
450
+ body.tool_choice = { type: "function", name: options.toolChoice.name };
451
+ else if (options.toolChoice.type === "required")
452
+ body.tool_choice = "required";
453
+ else
454
+ body.tool_choice = "auto";
455
+ }
456
+ return body;
457
+ }
458
+ function openai(params) {
459
+ const defaultModel = params?.defaultModel || DEFAULT_MODEL;
460
+ let runtimeCredentials = typeof params?.access === "string" && typeof params.refresh === "string" && typeof params.expires === "number" ? {
461
+ access: params.access,
462
+ refresh: params.refresh,
463
+ expires: params.expires,
464
+ ...params.accountId ? { accountId: params.accountId } : {}
465
+ } : void 0;
466
+ return {
467
+ name: "openai",
468
+ meta: { defaultModel, isOAuth: true },
469
+ formatTools: formatTools2,
470
+ userMessage,
471
+ assistantMessage,
472
+ toolResultsMessage,
473
+ async stream(options, callbacks) {
474
+ const modelId = options.model || defaultModel;
475
+ const model = resolveModel(modelId);
476
+ const apiKey = await resolveOAuthApiKey(
477
+ {
478
+ provider: "openai",
479
+ providerId: PROVIDER_ID,
480
+ params: runtimeCredentials ? { ...params, ...runtimeCredentials } : params,
481
+ envKey: "OPENAI_CODEX_API_KEY",
482
+ extraCredentialKeys: ["accountId"],
483
+ missingError: "No OpenAI Codex OAuth token found. Run `bun run auth --openai` first.",
484
+ refreshError: (reason) => `OpenAI Codex OAuth token refresh failed. Run \`bun run auth --openai\` again. ${reason}`
485
+ },
486
+ {
487
+ ...callbacks,
488
+ async onOAuthRefresh(ctx) {
489
+ if (ctx.source === "params") {
490
+ runtimeCredentials = {
491
+ access: ctx.credentials.access,
492
+ refresh: ctx.credentials.refresh,
493
+ expires: ctx.credentials.expires,
494
+ ...typeof ctx.credentials.accountId === "string" ? { accountId: ctx.credentials.accountId } : {}
495
+ };
496
+ }
497
+ await callbacks.onOAuthRefresh?.(ctx);
498
+ }
499
+ }
500
+ );
501
+ const context = {
502
+ systemPrompt: options.system,
503
+ messages: toPiMessages(options.messages, modelId),
504
+ tools: options.tools
505
+ };
506
+ const stream = streamOpenAICodexResponses(model, context, {
507
+ apiKey,
508
+ maxTokens: options.maxTokens,
509
+ signal: options.signal,
510
+ transport: params?.transport,
511
+ reasoningEffort: options.thinking && options.thinking !== "off" ? options.thinking : void 0,
512
+ reasoningSummary: options.thinking && options.thinking !== "off" ? "auto" : void 0,
513
+ onPayload: (payload) => applyPayloadOverrides(payload, options)
514
+ });
515
+ let finalMessage;
516
+ let text = "";
517
+ let thinking = "";
518
+ for await (const event of stream) {
519
+ if (event.type === "text_delta") {
520
+ text += event.delta;
521
+ callbacks.onText(event.delta);
522
+ } else if (event.type === "thinking_delta") {
523
+ thinking += event.delta;
524
+ callbacks.onThinking?.(event.delta);
525
+ } else if (event.type === "thinking_end") {
526
+ const delta = event.content.startsWith(thinking) ? event.content.slice(thinking.length) : thinking ? "" : event.content;
527
+ if (delta) {
528
+ thinking += delta;
529
+ callbacks.onThinking?.(delta);
530
+ }
531
+ } else if (event.type === "done") {
532
+ finalMessage = event.message;
533
+ } else if (event.type === "error") {
534
+ throw new Error(event.error.errorMessage || "OpenAI Codex API error");
535
+ }
536
+ }
537
+ finalMessage ??= await stream.result();
538
+ text ||= extractText(finalMessage);
539
+ const toolCalls = extractToolCalls(finalMessage);
540
+ const assistantTurn = fromPiAssistantMessage(finalMessage);
541
+ return {
542
+ assistantMessage: assistantTurn,
543
+ text,
544
+ toolCalls,
545
+ done: toolCalls.length === 0,
546
+ usage: toTurnUsage(finalMessage.usage)
547
+ };
548
+ }
549
+ };
550
+ }
551
+
222
552
  // src/providers/openrouter.ts
223
553
  var BASE_URL2 = "https://openrouter.ai/api/v1";
224
- function getApiKey3(params) {
554
+ function getApiKey2(params) {
225
555
  if (params?.apiKey)
226
556
  return params.apiKey;
227
557
  if (process.env.OPENROUTER_API_KEY)
@@ -229,7 +559,7 @@ function getApiKey3(params) {
229
559
  throw new Error("No OpenRouter API key found. Set OPENROUTER_API_KEY in your environment.");
230
560
  }
231
561
  function openrouter(params) {
232
- const apiKey = getApiKey3(params);
562
+ const apiKey = getApiKey2(params);
233
563
  const defaultModel = params?.defaultModel || "anthropic/claude-sonnet-4-6";
234
564
  return {
235
565
  name: "openrouter",
@@ -287,5 +617,6 @@ function openrouter(params) {
287
617
  export {
288
618
  anthropic,
289
619
  cerebras,
620
+ openai,
290
621
  openrouter
291
622
  };
package/dist/session.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { h as CreateSessionOptions, R as RemoteStoreOptions, S as Session, l as SessionContentBlock, m as SessionData, p as SessionMessage, q as SessionRun, r as SessionStore, s as SessionTurn, u as SqliteStoreOptions, N as autoDetectAndConvert, V as createMemoryStore, W as createRemoteStore, X as createSession, Y as createSqliteStore, _ as fromAnthropic, $ as fromOpenAI, a0 as loadSession, a2 as toAnthropic, a3 as toOpenAI } from './agent-CtGvwTdo.js';
1
+ export { h as CreateSessionOptions, R as RemoteStoreOptions, S as Session, n as SessionContentBlock, o as SessionData, r as SessionMessage, s as SessionRun, t as SessionStore, u as SessionTurn, w as SqliteStoreOptions, U as autoDetectAndConvert, X as createMemoryStore, Y as createRemoteStore, Z as createSession, _ as createSqliteStore, a0 as fromAnthropic, a1 as fromOpenAI, a2 as loadSession, a4 as toAnthropic, a5 as toOpenAI } from './agent-B__ISsOE.js';
2
2
  import 'hookable';
3
3
  import './types-BpvTmawk.js';
4
4
  import '@modelcontextprotocol/sdk/client/index.js';
@@ -1,4 +1,4 @@
1
- import { z as ToolContext, B as ToolDef, i as HarnessConfig, e as AgentStats } from './agent-CtGvwTdo.js';
1
+ import { D as ToolContext, E as ToolDef, i as HarnessConfig, e as AgentStats } from './agent-B__ISsOE.js';
2
2
 
3
3
  /**
4
4
  * Interaction tool — lets the agent request structured input from the outside world.
package/dist/tools.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- export { C as ChildAgent, I as InteractionToolOptions, S as SpawnToolOptions, a as SpawnToolState, c as createInteractionTool, b as createSpawnTool, s as spawn } from './spawn-hNTJA3Iw.js';
2
- import { B as ToolDef } from './agent-CtGvwTdo.js';
1
+ export { C as ChildAgent, I as InteractionToolOptions, S as SpawnToolOptions, a as SpawnToolState, c as createInteractionTool, b as createSpawnTool, s as spawn } from './spawn-DRamirTu.js';
2
+ import { E as ToolDef } from './agent-B__ISsOE.js';
3
3
  export { V as ValidationResult, v as validateToolArgs } from './validation-DOY_k7lW.js';
4
4
  import 'hookable';
5
5
  import './types-BpvTmawk.js';
package/dist/tools.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  spawn,
8
8
  validateToolArgs,
9
9
  writeFile
10
- } from "./chunk-OJATGJ5F.js";
10
+ } from "./chunk-O4QCBJQF.js";
11
11
  import "./chunk-4C6Y56CC.js";
12
12
  import "./chunk-SZA4FKW5.js";
13
13
  import "./chunk-WDBO3JCO.js";
package/dist/types.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- export { A as Agent, a as AgentBehavior, b as AgentHooks, c as AgentOptions, d as AgentRunOptions, e as AgentStats, f as AnthropicParams, C as CerebrasParams, g as ChildRunStats, h as CreateSessionOptions, H as Harness, i as HarnessConfig, I as ImageContent, M as McpConnection, j as McpServerConfig, k as McpToolHookContext, O as OpenRouterParams, P as Provider, R as RemoteStoreOptions, S as Session, l as SessionContentBlock, m as SessionData, n as SessionEndStatus, o as SessionHookContext, p as SessionMessage, q as SessionRun, r as SessionStore, s as SessionTurn, t as SpawnHookContext, u as SqliteStoreOptions, v as StreamCallbacks, w as StreamHookContext, x as StreamOptions, T as ThinkingLevel, y as ToolCall, z as ToolContext, B as ToolDef, D as ToolExecutionMode, E as ToolHookContext, F as ToolMap, G as ToolResult, J as ToolSpec, K as TurnResult, L as TurnUsage } from './agent-CtGvwTdo.js';
1
+ export { A as Agent, a as AgentBehavior, b as AgentHooks, c as AgentOptions, d as AgentRunOptions, e as AgentStats, f as AnthropicParams, C as CerebrasParams, g as ChildRunStats, h as CreateSessionOptions, H as Harness, i as HarnessConfig, I as ImageContent, M as McpConnection, j as McpServerConfig, k as McpToolHookContext, O as OAuthRefreshHookContext, l as OpenAIParams, m as OpenRouterParams, P as Provider, R as RemoteStoreOptions, S as Session, n as SessionContentBlock, o as SessionData, p as SessionEndStatus, q as SessionHookContext, r as SessionMessage, s as SessionRun, t as SessionStore, u as SessionTurn, v as SpawnHookContext, w as SqliteStoreOptions, x as StreamCallbacks, y as StreamHookContext, z as StreamOptions, T as ThinkingLevel, B as ToolCall, D as ToolContext, E as ToolDef, F as ToolExecutionMode, G as ToolHookContext, J as ToolMap, K as ToolResult, L as ToolSpec, N as TurnResult, Q as TurnUsage } from './agent-B__ISsOE.js';
2
2
  export { C as ContextCapabilities, a as ContextType, E as ExecResult, b as ExecutionContext, c as ExecutionHandle, S as SpawnConfig } from './types-BpvTmawk.js';
3
3
  export { S as SandboxProvider } from './sandbox-CW72eLDP.js';
4
4
  export { S as SkillConfig, a as SkillResource, b as SkillsConfig } from './types-CDI8Kmve.js';
5
- export { C as ChildAgent, I as InteractionToolOptions, S as SpawnToolOptions, a as SpawnToolState } from './spawn-hNTJA3Iw.js';
5
+ export { C as ChildAgent, I as InteractionToolOptions, S as SpawnToolOptions, a as SpawnToolState } from './spawn-DRamirTu.js';
6
6
  export { V as ValidationResult } from './validation-DOY_k7lW.js';
7
7
  import 'hookable';
8
8
  import '@modelcontextprotocol/sdk/client/index.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zidane",
3
- "version": "1.6.17",
3
+ "version": "1.7.0",
4
4
  "description": "an agent that goes straight to the goal",
5
5
  "type": "module",
6
6
  "private": false,
@@ -67,9 +67,9 @@
67
67
  "typecheck": "tsc --noEmit"
68
68
  },
69
69
  "dependencies": {
70
- "@anthropic-ai/sdk": "^0.80.0",
71
- "@modelcontextprotocol/sdk": "^1.27.1",
72
- "@yaelg/pi-ai": "^0.58.4",
70
+ "@anthropic-ai/sdk": "^0.88.0",
71
+ "@modelcontextprotocol/sdk": "^1.29.0",
72
+ "@yaelg/pi-ai": "^0.66.1",
73
73
  "chalk": "^5.6.2",
74
74
  "hookable": "^6.1.0",
75
75
  "md4x": "^0.0.25"
@@ -83,13 +83,13 @@
83
83
  }
84
84
  },
85
85
  "devDependencies": {
86
- "@antfu/eslint-config": "^7.7.3",
87
- "@types/bun": "^1.3.11",
86
+ "@antfu/eslint-config": "^8.1.1",
87
+ "@types/bun": "^1.3.12",
88
88
  "@types/dockerode": "^4.0.1",
89
89
  "bumpp": "^11.0.1",
90
- "eslint": "^10.0.3",
90
+ "eslint": "^10.2.0",
91
91
  "jiti": "^2.6.1",
92
92
  "tsup": "^8.5.1",
93
- "typescript": "~5.9.3"
93
+ "typescript": "~6.0.2"
94
94
  }
95
95
  }