@andreprado/agentkit 0.2.0 → 0.3.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.
|
@@ -49,12 +49,12 @@ To use the Codex subscription login instead of an OpenAI API key:
|
|
|
49
49
|
npm run agentkit -- provider login openai-codex
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
-
Run this in an interactive terminal,
|
|
52
|
+
Run this in an interactive terminal, choose browser login (default) or device code login for headless machines, then sign in with ChatGPT. If the browser callback cannot reach the terminal, paste the redirect URL into the prompt. This uses Pi's Codex OAuth flow and creates an AgentKit session; it does not read or change the Codex app/CLI credential cache.
|
|
53
53
|
|
|
54
54
|
Configure the capsule:
|
|
55
55
|
|
|
56
56
|
```ts
|
|
57
|
-
provider: { name: "openai-codex", model: "gpt-5.
|
|
57
|
+
provider: { name: "openai-codex", model: "gpt-5.5" },
|
|
58
58
|
secrets: [], // Keep any secrets required by your tools or other services.
|
|
59
59
|
```
|
|
60
60
|
|
|
@@ -102,7 +102,7 @@ Anthropic:
|
|
|
102
102
|
```ts
|
|
103
103
|
provider: {
|
|
104
104
|
name: "anthropic",
|
|
105
|
-
model: "claude-
|
|
105
|
+
model: "claude-haiku-4-5",
|
|
106
106
|
},
|
|
107
107
|
secrets: ["ANTHROPIC_API_KEY"],
|
|
108
108
|
```
|
|
@@ -193,6 +193,8 @@ npm run agentkit -- inspect
|
|
|
193
193
|
|
|
194
194
|
For OpenAI, Anthropic, OpenCode Zen, or OpenCode Go, use a model id known to the installed Pi SDK for that provider. For OpenRouter, prefer a known Pi alias when possible; otherwise a raw OpenRouter model id is passed through and any remaining model error comes from OpenRouter.
|
|
195
195
|
|
|
196
|
+
After upgrading to AgentKit 0.3.0 (Pi 0.99), `openai-codex` no longer offers `gpt-5.4` or `gpt-5.4-mini`, and `anthropic` no longer offers `claude-3-5-haiku-latest`. Switch to a listed model such as `gpt-5.5` or `claude-haiku-4-5`; the error message lists examples.
|
|
197
|
+
|
|
196
198
|
Provider returns auth failure:
|
|
197
199
|
|
|
198
200
|
Check the key value, provider account status, and model access.
|
package/docs/llms-full.txt
CHANGED
|
@@ -217,7 +217,7 @@ secrets: [],
|
|
|
217
217
|
|
|
218
218
|
Before claiming real conversation behavior has been tested, ask the owner which provider to use: OpenRouter, OpenAI, Anthropic, OpenCode Zen, OpenCode Go, or another supported provider. Do not choose for them. After the owner chooses, update `agentkit.config.ts`, `.env.schema`, local secrets, then rerun chat/UI checks.
|
|
219
219
|
|
|
220
|
-
ChatGPT/Codex subscription login (local only): run `agentkit provider login openai-codex` in an interactive terminal, sign in through the printed URL, and set `provider: { name: "openai-codex", model: "gpt-5.
|
|
220
|
+
ChatGPT/Codex subscription login (local only): run `agentkit provider login openai-codex` in an interactive terminal, sign in through the printed URL, and set `provider: { name: "openai-codex", model: "gpt-5.5" }`. No `OPENAI_API_KEY` is required for this provider. Keep secrets needed by tools or other services. The AgentKit session is stored outside capsules in `~/.agentkit/providers/openai-codex.json`, renewed through Pi, and never copied from or into the Codex app cache. `agentkit provider status openai-codex` reports local status without tokens; `agentkit provider logout openai-codex` removes the local session without revoking it at OpenAI. See `docs/guides/use-provider.md`.
|
|
221
221
|
|
|
222
222
|
OpenAI example:
|
|
223
223
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@andreprado/agentkit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"agentkit": "bin/agentkit.mjs"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@earendil-works/pi-ai": "^0.
|
|
32
|
+
"@earendil-works/pi-ai": "^0.99.2",
|
|
33
33
|
"@libsql/client": "^0.15.15",
|
|
34
34
|
"tsx": "^4.23.12",
|
|
35
35
|
"typebox": "^1.1.38"
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { createInterface } from "node:readline/promises";
|
|
1
|
+
import { createInterface, type Interface } from "node:readline/promises";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import type { AuthEvent, AuthPrompt } from "@earendil-works/pi-ai";
|
|
4
4
|
|
|
5
|
-
import { clearCodexLogin, codexLoginStatus, saveCodexLogin } from "../../providers/codex-auth";
|
|
5
|
+
import { clearCodexLogin, codexLoginStatus, codexOAuth, saveCodexLogin } from "../../providers/codex-auth";
|
|
6
6
|
import { AgentKitError } from "../../runtime/errors";
|
|
7
7
|
import type { ParsedArgs } from "../args";
|
|
8
8
|
|
|
@@ -28,20 +28,38 @@ export async function handleProviderCommand(args: ParsedArgs): Promise<void> {
|
|
|
28
28
|
const abort = new AbortController();
|
|
29
29
|
readline.on("SIGINT", () => abort.abort());
|
|
30
30
|
try {
|
|
31
|
-
const credentials = await
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
},
|
|
36
|
-
onPrompt: ({ message }) => readline.question(`${message} `, { signal: abort.signal }),
|
|
37
|
-
onManualCodeInput: () => readline.question("After signing in, paste the redirect URL here if the browser callback cannot reach this terminal: ", { signal: abort.signal }),
|
|
31
|
+
const credentials = await codexOAuth().login({
|
|
32
|
+
signal: abort.signal,
|
|
33
|
+
notify: printAuthEvent,
|
|
34
|
+
prompt: (prompt) => askAuthPrompt(readline, prompt, abort.signal),
|
|
38
35
|
}).catch(() => {
|
|
39
36
|
throw new AgentKitError("provider_auth_failed", "Codex login failed or was cancelled. Run agentkit provider login openai-codex again.");
|
|
40
37
|
});
|
|
41
38
|
await saveCodexLogin(credentials);
|
|
42
|
-
console.log('Signed in. Set provider: { name: "openai-codex", model: "gpt-5.
|
|
39
|
+
console.log('Signed in. Set provider: { name: "openai-codex", model: "gpt-5.5" } in agentkit.config.ts. No OPENAI_API_KEY is required.');
|
|
43
40
|
} finally {
|
|
44
41
|
abort.abort();
|
|
45
42
|
readline.close();
|
|
46
43
|
}
|
|
47
44
|
}
|
|
45
|
+
|
|
46
|
+
function printAuthEvent(event: AuthEvent): void {
|
|
47
|
+
if (event.type === "auth_url") {
|
|
48
|
+
console.log(`Open this URL to sign in with ChatGPT:\n${event.url}`);
|
|
49
|
+
if (event.instructions) console.log(event.instructions);
|
|
50
|
+
} else if (event.type === "device_code") {
|
|
51
|
+
console.log(`Open ${event.verificationUri} and enter code ${event.userCode}`);
|
|
52
|
+
} else {
|
|
53
|
+
console.log(event.message);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function askAuthPrompt(readline: Interface, prompt: AuthPrompt, loginSignal: AbortSignal): Promise<string> {
|
|
58
|
+
const signal = prompt.signal ? AbortSignal.any([loginSignal, prompt.signal]) : loginSignal;
|
|
59
|
+
if (prompt.type !== "select") return readline.question(`${prompt.message} `, { signal });
|
|
60
|
+
prompt.options.forEach((option, index) => console.log(` ${index + 1}. ${option.label}`));
|
|
61
|
+
const answer = (await readline.question(`${prompt.message} [1] `, { signal })).trim() || "1";
|
|
62
|
+
const option = prompt.options[Number(answer) - 1];
|
|
63
|
+
if (!option) throw new Error("Unknown login option.");
|
|
64
|
+
return option.id;
|
|
65
|
+
}
|
|
@@ -4,10 +4,24 @@ import { homedir } from "node:os";
|
|
|
4
4
|
import { dirname, join } from "node:path";
|
|
5
5
|
import { setTimeout as delay } from "node:timers/promises";
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import type { OAuthAuth } from "@earendil-works/pi-ai";
|
|
8
|
+
import type { OAuthCredentials } from "@earendil-works/pi-ai/oauth";
|
|
9
|
+
import { openaiCodexProvider } from "@earendil-works/pi-ai/providers/openai-codex";
|
|
8
10
|
|
|
9
11
|
import { AgentKitError } from "../runtime/errors";
|
|
10
12
|
|
|
13
|
+
const REFRESH_TIMEOUT_MS = 15_000;
|
|
14
|
+
|
|
15
|
+
export function codexOAuth(): OAuthAuth {
|
|
16
|
+
const oauth = openaiCodexProvider().auth.oauth;
|
|
17
|
+
if (!oauth) throw new AgentKitError("provider_not_supported", "The installed Pi SDK no longer supports OpenAI Codex sign-in.");
|
|
18
|
+
return oauth;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function refreshCodexToken(refresh: string): Promise<OAuthCredentials> {
|
|
22
|
+
return codexOAuth().refresh({ type: "oauth", access: "", refresh, expires: 0 }, AbortSignal.timeout(REFRESH_TIMEOUT_MS));
|
|
23
|
+
}
|
|
24
|
+
|
|
11
25
|
export function codexAuthPath(): string {
|
|
12
26
|
return join(homedir(), ".agentkit", "providers", "openai-codex.json");
|
|
13
27
|
}
|
|
@@ -90,7 +104,7 @@ export async function codexLoginStatus(path = codexAuthPath()) {
|
|
|
90
104
|
|
|
91
105
|
export async function getCodexApiKey(
|
|
92
106
|
path = codexAuthPath(),
|
|
93
|
-
refresh =
|
|
107
|
+
refresh = refreshCodexToken,
|
|
94
108
|
): Promise<string> {
|
|
95
109
|
return withAuthLock(path, async () => {
|
|
96
110
|
const credentials = await readCredentials(path);
|
package/src/providers/pi.ts
CHANGED
|
@@ -1,28 +1,51 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
getModels,
|
|
5
|
-
stream,
|
|
2
|
+
createModels,
|
|
3
|
+
envApiKeyAuth,
|
|
6
4
|
type AssistantMessage,
|
|
7
5
|
type AssistantMessageEvent,
|
|
6
|
+
type Context,
|
|
8
7
|
type Message,
|
|
9
8
|
type Model,
|
|
10
|
-
type
|
|
9
|
+
type StreamOptions,
|
|
11
10
|
type Tool as PiTool,
|
|
12
11
|
type ToolCall,
|
|
13
12
|
type Usage,
|
|
14
13
|
} from "@earendil-works/pi-ai";
|
|
14
|
+
import { anthropicProvider } from "@earendil-works/pi-ai/providers/anthropic";
|
|
15
|
+
import { openaiProvider } from "@earendil-works/pi-ai/providers/openai";
|
|
16
|
+
import { openaiCodexProvider } from "@earendil-works/pi-ai/providers/openai-codex";
|
|
17
|
+
import { opencodeProvider } from "@earendil-works/pi-ai/providers/opencode";
|
|
18
|
+
import { opencodeGoProvider } from "@earendil-works/pi-ai/providers/opencode-go";
|
|
19
|
+
import { openrouterProvider } from "@earendil-works/pi-ai/providers/openrouter";
|
|
15
20
|
|
|
16
21
|
import type { AgentProviderName, AgentTool } from "../index";
|
|
17
22
|
import { AgentKitError } from "../runtime/errors";
|
|
18
23
|
import type { ProviderAdapter, ProviderRunInput, ProviderRunResult } from "./types";
|
|
19
24
|
|
|
20
25
|
const PI_PROVIDER_NAMES = ["openai", "openai-codex", "anthropic", "openrouter", "opencode", "opencode-go"] as const satisfies AgentProviderName[];
|
|
26
|
+
type Provider = (typeof PI_PROVIDER_NAMES)[number];
|
|
27
|
+
const PROVIDER_SECRET_NAMES: Record<Exclude<Provider, "openai-codex">, string[]> = {
|
|
28
|
+
openai: ["OPENAI_API_KEY"],
|
|
29
|
+
anthropic: ["ANTHROPIC_OAUTH_TOKEN", "ANTHROPIC_API_KEY"],
|
|
30
|
+
openrouter: ["OPENROUTER_API_KEY"],
|
|
31
|
+
opencode: ["OPENCODE_API_KEY"],
|
|
32
|
+
"opencode-go": ["OPENCODE_API_KEY"],
|
|
33
|
+
};
|
|
21
34
|
const MAX_TOOL_TURNS = 8;
|
|
22
35
|
const OPENROUTER_BASE_URL = "https://openrouter.ai/api/v1";
|
|
23
36
|
const UNKNOWN_OPENROUTER_CONTEXT_WINDOW = 128000;
|
|
24
37
|
const UNKNOWN_OPENROUTER_MAX_TOKENS = 8192;
|
|
25
38
|
|
|
39
|
+
// AgentKit resolves credentials itself (.env secrets, the local Codex login) and passes
|
|
40
|
+
// them per request, so Pi never reads ambient env vars or its own credential store.
|
|
41
|
+
export const piModels = createModels();
|
|
42
|
+
for (const provider of [openaiProvider(), anthropicProvider(), openrouterProvider(), opencodeProvider(), opencodeGoProvider()]) {
|
|
43
|
+
piModels.setProvider(provider);
|
|
44
|
+
}
|
|
45
|
+
const codexProvider = openaiCodexProvider();
|
|
46
|
+
// Pi's Codex provider only accepts its own OAuth store; accept the explicit access token instead.
|
|
47
|
+
piModels.setProvider({ ...codexProvider, auth: { ...codexProvider.auth, apiKey: envApiKeyAuth("OpenAI Codex access token", []) } });
|
|
48
|
+
|
|
26
49
|
export const piProviderAdapters: ProviderAdapter[] = PI_PROVIDER_NAMES.map((name) => ({
|
|
27
50
|
name,
|
|
28
51
|
run(input) {
|
|
@@ -129,11 +152,11 @@ export function providerToolResultText(result: {
|
|
|
129
152
|
|
|
130
153
|
async function runPiTurn(
|
|
131
154
|
model: Model<any>,
|
|
132
|
-
context:
|
|
133
|
-
options:
|
|
155
|
+
context: Context,
|
|
156
|
+
options: StreamOptions,
|
|
134
157
|
rawEvents: AssistantMessageEvent[],
|
|
135
158
|
): Promise<AssistantMessage> {
|
|
136
|
-
const eventStream = stream(model, context, options);
|
|
159
|
+
const eventStream = piModels.stream(model, context, options);
|
|
137
160
|
let assistant: AssistantMessage | undefined;
|
|
138
161
|
let failed: AssistantMessage | undefined;
|
|
139
162
|
|
|
@@ -180,7 +203,7 @@ function requireProviderApiKey(provider: Provider, env: Record<string, string |
|
|
|
180
203
|
throw new AgentKitError("provider_auth_required", "Run agentkit provider login openai-codex, then use local chat, dev, or eval.");
|
|
181
204
|
}
|
|
182
205
|
|
|
183
|
-
const envNames =
|
|
206
|
+
const envNames = PROVIDER_SECRET_NAMES[provider];
|
|
184
207
|
const setName = envNames.find((name) => env[name]);
|
|
185
208
|
|
|
186
209
|
if (!setName) {
|
|
@@ -193,14 +216,6 @@ function requireProviderApiKey(provider: Provider, env: Record<string, string |
|
|
|
193
216
|
return env[setName] as string;
|
|
194
217
|
}
|
|
195
218
|
|
|
196
|
-
function defaultProviderSecretNames(provider: Provider): string[] {
|
|
197
|
-
if (provider === "opencode" || provider === "opencode-go") {
|
|
198
|
-
return ["OPENCODE_API_KEY"];
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
return [`${provider.toUpperCase().replace(/[^A-Z0-9]+/g, "_")}_API_KEY`];
|
|
202
|
-
}
|
|
203
|
-
|
|
204
219
|
function formatSecretList(names: string[]): string {
|
|
205
220
|
if (names.length === 1) {
|
|
206
221
|
return names[0];
|
|
@@ -210,7 +225,7 @@ function formatSecretList(names: string[]): string {
|
|
|
210
225
|
}
|
|
211
226
|
|
|
212
227
|
export function resolveModel(provider: Provider, modelId: string): Model<any> {
|
|
213
|
-
const model = getModel(provider
|
|
228
|
+
const model = piModels.getModel(provider, modelId);
|
|
214
229
|
|
|
215
230
|
if (model) {
|
|
216
231
|
return model;
|
|
@@ -250,7 +265,7 @@ function createOpenRouterPassThroughModel(modelId: string): Model<"openai-comple
|
|
|
250
265
|
}
|
|
251
266
|
|
|
252
267
|
function knownModelHint(provider: Provider): string {
|
|
253
|
-
const examples = getModels(provider
|
|
268
|
+
const examples = piModels.getModels(provider)
|
|
254
269
|
.slice(0, 5)
|
|
255
270
|
.map((model) => model.id);
|
|
256
271
|
|