@elizaos/agent 2.0.0-alpha.413 → 2.0.0-alpha.414

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elizaos/agent",
3
- "version": "2.0.0-alpha.413",
3
+ "version": "2.0.0-alpha.414",
4
4
  "description": "Standalone elizaOS-based agent and backend server package.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -467,7 +467,7 @@
467
467
  "@elizaos/app-steward": "^0.0.0",
468
468
  "@elizaos/app-task-coordinator": "^0.0.0",
469
469
  "@elizaos/app-training": "^0.0.1",
470
- "@elizaos/core": "^2.0.0-alpha.413",
470
+ "@elizaos/core": "^2.0.0-alpha.414",
471
471
  "@elizaos/plugin-agent-orchestrator": "^0.6.2-alpha.0",
472
472
  "@elizaos/plugin-browser-bridge": "^0.1.0",
473
473
  "@elizaos/plugin-local-embedding": "^2.0.0-alpha.12",
@@ -476,8 +476,8 @@
476
476
  "@elizaos/plugin-solana": "^2.0.0-alpha.6",
477
477
  "@elizaos/plugin-sql": "^2.0.0-alpha.19",
478
478
  "@elizaos/plugin-wechat": "^0.1.0",
479
- "@elizaos/shared": "^2.0.0-alpha.413",
480
- "@elizaos/skills": "^2.0.0-alpha.413",
479
+ "@elizaos/shared": "^2.0.0-alpha.414",
480
+ "@elizaos/skills": "^2.0.0-alpha.414",
481
481
  "@hapi/boom": "^10.0.1",
482
482
  "@noble/curves": "^2.0.1",
483
483
  "@solana/web3.js": "^1.98.4",
@@ -34,28 +34,28 @@ export function getModelOptions() {
34
34
  },
35
35
  // OpenAI
36
36
  {
37
- id: "openai/gpt-5.4-pro",
38
- name: "GPT-5.4 Pro",
37
+ id: "openai/gpt-5.5-pro",
38
+ name: "GPT-5.5 Pro",
39
39
  provider: "OpenAI",
40
- description: "Highest-precision GPT-5.4 variant.",
40
+ description: "Highest-precision GPT-5.5 variant.",
41
41
  },
42
42
  {
43
- id: "openai/gpt-5.4",
44
- name: "GPT-5.4",
43
+ id: "openai/gpt-5.5",
44
+ name: "GPT-5.5",
45
45
  provider: "OpenAI",
46
46
  description: "Flagship OpenAI model for coding and reasoning.",
47
47
  },
48
48
  {
49
- id: "openai/gpt-5.4-mini",
50
- name: "GPT-5.4 Mini",
49
+ id: "openai/gpt-5.5-mini",
50
+ name: "GPT-5.5 Mini",
51
51
  provider: "OpenAI",
52
52
  description: "High-volume OpenAI mini model.",
53
53
  },
54
54
  {
55
- id: "openai/gpt-5.4-nano",
56
- name: "GPT-5.4 Nano",
55
+ id: "openai/gpt-5.5-nano",
56
+ name: "GPT-5.5 Nano",
57
57
  provider: "OpenAI",
58
- description: "Cheapest GPT-5.4 tier for fast routing and gating.",
58
+ description: "Cheapest GPT-5.5 tier for fast routing and gating.",
59
59
  },
60
60
  // Google
61
61
  {
@@ -281,9 +281,9 @@ const PROVIDER_DEFAULT_MODELS = {
281
281
  },
282
282
  openai: {
283
283
  smallKey: "OPENAI_SMALL_MODEL",
284
- smallVal: "gpt-5.4-mini",
284
+ smallVal: "gpt-5.5-mini",
285
285
  largeKey: "OPENAI_LARGE_MODEL",
286
- largeVal: "gpt-5.4",
286
+ largeVal: "gpt-5.5",
287
287
  },
288
288
  google: {
289
289
  smallKey: "GOOGLE_SMALL_MODEL",
@@ -1,6 +1,8 @@
1
+ export * from "./account-storage.js";
1
2
  export * from "./anthropic.js";
2
3
  export * from "./claude-code-stealth.js";
3
4
  export * from "./credentials.js";
5
+ export * from "./oauth-flow.js";
4
6
  export * from "./openai-codex.js";
5
7
  export * from "./types.js";
6
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/auth/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,0BAA0B,CAAC;AACzC,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/auth/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC;AACrC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,0BAA0B,CAAC;AACzC,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,YAAY,CAAC"}
@@ -1,5 +1,7 @@
1
+ export * from "./account-storage.js";
1
2
  export * from "./anthropic.js";
2
3
  export * from "./claude-code-stealth.js";
3
4
  export * from "./credentials.js";
5
+ export * from "./oauth-flow.js";
4
6
  export * from "./openai-codex.js";
5
7
  export * from "./types.js";
@@ -0,0 +1,106 @@
1
+ /**
2
+ * OAuth flow orchestration registry.
3
+ *
4
+ * Wraps the provider-specific programmatic OAuth helpers
5
+ * (`startAnthropicOAuthFlowRaw` from
6
+ * `vendor/pi-oauth/anthropic-login.ts` and the loopback-listener flow
7
+ * in `openai-codex.ts`) with:
8
+ *
9
+ * - a 5-minute timeout per flow,
10
+ * - automatic `saveAccount(...)` after token exchange,
11
+ * - an in-memory registry keyed by `sessionId` so the HTTP API can
12
+ * stream progress over SSE and the CLI can `await` completion,
13
+ * - garbage collection 10 minutes after a flow reaches a terminal
14
+ * state.
15
+ *
16
+ * Both the CLI and the new accounts-routes HTTP endpoints drive flows
17
+ * through this module — there is no direct caller of the vendor-level
18
+ * OAuth helpers anymore.
19
+ */
20
+ import { type AccountCredentialRecord } from "./account-storage.js";
21
+ import type { SubscriptionProvider } from "./types.js";
22
+ /** Server-tracked status of an in-flight OAuth flow. */
23
+ export type FlowStatus = "pending" | "success" | "error" | "cancelled" | "timeout";
24
+ export interface FlowState {
25
+ sessionId: string;
26
+ providerId: SubscriptionProvider;
27
+ status: FlowStatus;
28
+ /** Set on `pending` (so the UI can re-open the browser) and on `success`. */
29
+ authUrl?: string;
30
+ /** Anthropic only — the user must paste `code#state`; Codex uses the loopback callback. */
31
+ needsCodeSubmission: boolean;
32
+ account?: AccountCredentialRecord;
33
+ error?: string;
34
+ startedAt: number;
35
+ endedAt?: number;
36
+ }
37
+ export interface OAuthFlowHandle {
38
+ sessionId: string;
39
+ authUrl: string;
40
+ /** Codex flows resolve via the loopback listener; Anthropic requires `submitCode`. */
41
+ needsCodeSubmission: boolean;
42
+ /** Resolves with the saved AccountCredentialRecord; rejects on cancel/timeout/error. */
43
+ completion: Promise<{
44
+ account: AccountCredentialRecord;
45
+ }>;
46
+ /** Anthropic only — submit `code#state` from the redirect page. No-op for Codex. */
47
+ submitCode: (code: string) => void;
48
+ cancel: (reason?: string) => void;
49
+ }
50
+ interface StartOptions {
51
+ label: string;
52
+ accountId?: string;
53
+ /**
54
+ * Called after the account is saved on disk. Used by the HTTP
55
+ * route layer to also write a `LinkedAccountConfig` row into
56
+ * `milady.json`. Failures here propagate as flow `error`.
57
+ */
58
+ onAccountSaved?: (account: AccountCredentialRecord) => void | Promise<void>;
59
+ }
60
+ /**
61
+ * Start a programmatic Anthropic OAuth flow. Anthropic's redirect URI
62
+ * is hardcoded to `console.anthropic.com/oauth/code/callback` — the
63
+ * UI must surface the auth URL, prompt the user to copy the
64
+ * `code#state` blob, and call `submitCode()` with it. Once the code is
65
+ * submitted, the token exchange runs and the account is persisted.
66
+ */
67
+ export declare function startAnthropicOAuthFlow(opts: StartOptions): Promise<OAuthFlowHandle>;
68
+ /**
69
+ * Start a programmatic Codex OAuth flow. Codex has a loopback listener
70
+ * on :1455, so the user just signs in in the browser and the listener
71
+ * picks up the redirect — `submitCode()` is a no-op. The accountId
72
+ * baked into the JWT is preserved on `LinkedAccountConfig.organizationId`
73
+ * (used by the Codex usage probe via the `ChatGPT-Account-Id` header).
74
+ */
75
+ export declare function startCodexOAuthFlow(opts: StartOptions): Promise<OAuthFlowHandle>;
76
+ export declare function getFlowState(sessionId: string): FlowState | null;
77
+ export declare function getFlowHandle(sessionId: string): OAuthFlowHandle | null;
78
+ /**
79
+ * Subscribe to status updates for a flow. Replays the current state
80
+ * synchronously so SSE consumers always receive an immediate frame.
81
+ * Returns an unsubscribe function.
82
+ */
83
+ export declare function subscribeFlow(sessionId: string, listener: (state: FlowState) => void): () => void;
84
+ export declare function cancelFlow(sessionId: string, reason?: string): boolean;
85
+ export declare function submitFlowCode(sessionId: string, code: string): boolean;
86
+ /**
87
+ * Remove every flow from the registry. Tests use this to reset
88
+ * between cases without resetting the whole module.
89
+ */
90
+ export declare function _resetFlowRegistry(): void;
91
+ /**
92
+ * Test-only helper to seed a synthetic flow without going through the
93
+ * vendor layer. Used by `accounts-routes.test.ts` to assert the SSE
94
+ * surface streams `success` / `error` payloads correctly.
95
+ */
96
+ export declare function _registerSyntheticFlow(args: {
97
+ sessionId?: string;
98
+ providerId: SubscriptionProvider;
99
+ authUrl: string;
100
+ needsCodeSubmission?: boolean;
101
+ }): {
102
+ sessionId: string;
103
+ complete: (state: FlowState) => void;
104
+ };
105
+ export {};
106
+ //# sourceMappingURL=oauth-flow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth-flow.d.ts","sourceRoot":"","sources":["../../../../../src/auth/oauth-flow.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH,OAAO,EACL,KAAK,uBAAuB,EAE7B,MAAM,sBAAsB,CAAC;AAG9B,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAMvD,wDAAwD;AACxD,MAAM,MAAM,UAAU,GAClB,SAAS,GACT,SAAS,GACT,OAAO,GACP,WAAW,GACX,SAAS,CAAC;AAEd,MAAM,WAAW,SAAS;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,oBAAoB,CAAC;IACjC,MAAM,EAAE,UAAU,CAAC;IACnB,6EAA6E;IAC7E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2FAA2F;IAC3F,mBAAmB,EAAE,OAAO,CAAC;IAC7B,OAAO,CAAC,EAAE,uBAAuB,CAAC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,sFAAsF;IACtF,mBAAmB,EAAE,OAAO,CAAC;IAC7B,wFAAwF;IACxF,UAAU,EAAE,OAAO,CAAC;QAAE,OAAO,EAAE,uBAAuB,CAAA;KAAE,CAAC,CAAC;IAC1D,oFAAoF;IACpF,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,MAAM,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC;AAuED,UAAU,YAAY;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,uBAAuB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7E;AAID;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC,eAAe,CAAC,CAqB1B;AAID;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC,eAAe,CAAC,CAmB1B;AA2ID,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAGhE;AAED,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAGvE;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,GACnC,MAAM,IAAI,CASZ;AAED,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAMtE;AAED,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAOvE;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CAKzC;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,oBAAoB,CAAC;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B,GAAG;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAA;CAAE,CA+B9D"}
@@ -0,0 +1,349 @@
1
+ /**
2
+ * OAuth flow orchestration registry.
3
+ *
4
+ * Wraps the provider-specific programmatic OAuth helpers
5
+ * (`startAnthropicOAuthFlowRaw` from
6
+ * `vendor/pi-oauth/anthropic-login.ts` and the loopback-listener flow
7
+ * in `openai-codex.ts`) with:
8
+ *
9
+ * - a 5-minute timeout per flow,
10
+ * - automatic `saveAccount(...)` after token exchange,
11
+ * - an in-memory registry keyed by `sessionId` so the HTTP API can
12
+ * stream progress over SSE and the CLI can `await` completion,
13
+ * - garbage collection 10 minutes after a flow reaches a terminal
14
+ * state.
15
+ *
16
+ * Both the CLI and the new accounts-routes HTTP endpoints drive flows
17
+ * through this module — there is no direct caller of the vendor-level
18
+ * OAuth helpers anymore.
19
+ */
20
+ import crypto from "node:crypto";
21
+ import { logger } from "@elizaos/core";
22
+ import { saveAccount, } from "./account-storage.js";
23
+ import { startCodexLogin } from "./openai-codex.js";
24
+ import { startAnthropicOAuthFlowRaw, } from "./vendor/pi-oauth/anthropic-login.js";
25
+ const FLOW_TIMEOUT_MS = 5 * 60 * 1000;
26
+ const FLOW_GC_MS = 10 * 60 * 1000;
27
+ const flows = new Map();
28
+ function newSessionId() {
29
+ return crypto.randomUUID();
30
+ }
31
+ function emit(entry) {
32
+ for (const listener of entry.listeners) {
33
+ listener(entry.state);
34
+ }
35
+ }
36
+ function scheduleGc(sessionId) {
37
+ const entry = flows.get(sessionId);
38
+ if (!entry)
39
+ return;
40
+ if (entry.gcTimer)
41
+ clearTimeout(entry.gcTimer);
42
+ entry.gcTimer = setTimeout(() => {
43
+ flows.delete(sessionId);
44
+ }, FLOW_GC_MS);
45
+ }
46
+ function resolveAccountId(opts) {
47
+ return opts.accountId?.trim() || crypto.randomUUID();
48
+ }
49
+ /**
50
+ * Build an `AccountCredentialRecord` and persist it via `saveAccount`.
51
+ * Returns the canonical record (with `createdAt`/`updatedAt` filled).
52
+ */
53
+ function persistAccount(args) {
54
+ const now = Date.now();
55
+ const record = {
56
+ id: args.accountId,
57
+ providerId: args.providerId,
58
+ label: args.label,
59
+ source: "oauth",
60
+ credentials: {
61
+ access: args.access,
62
+ refresh: args.refresh,
63
+ expires: args.expires,
64
+ },
65
+ createdAt: now,
66
+ updatedAt: now,
67
+ ...(args.organizationId ? { organizationId: args.organizationId } : {}),
68
+ ...(args.email ? { email: args.email } : {}),
69
+ };
70
+ saveAccount(record);
71
+ return record;
72
+ }
73
+ // Anthropic.
74
+ /**
75
+ * Start a programmatic Anthropic OAuth flow. Anthropic's redirect URI
76
+ * is hardcoded to `console.anthropic.com/oauth/code/callback` — the
77
+ * UI must surface the auth URL, prompt the user to copy the
78
+ * `code#state` blob, and call `submitCode()` with it. Once the code is
79
+ * submitted, the token exchange runs and the account is persisted.
80
+ */
81
+ export function startAnthropicOAuthFlow(opts) {
82
+ return startGenericFlow({
83
+ providerId: "anthropic-subscription",
84
+ opts,
85
+ needsCodeSubmission: true,
86
+ begin: async () => {
87
+ const raw = await startAnthropicOAuthFlowRaw();
88
+ const completion = (async () => {
89
+ const creds = await raw.completion;
90
+ return { creds };
91
+ })();
92
+ return {
93
+ authUrl: raw.authUrl,
94
+ completion,
95
+ submitCode: raw.submitCode,
96
+ cancel: raw.cancel,
97
+ };
98
+ },
99
+ });
100
+ }
101
+ // Codex (OpenAI ChatGPT subscription).
102
+ /**
103
+ * Start a programmatic Codex OAuth flow. Codex has a loopback listener
104
+ * on :1455, so the user just signs in in the browser and the listener
105
+ * picks up the redirect — `submitCode()` is a no-op. The accountId
106
+ * baked into the JWT is preserved on `LinkedAccountConfig.organizationId`
107
+ * (used by the Codex usage probe via the `ChatGPT-Account-Id` header).
108
+ */
109
+ export function startCodexOAuthFlow(opts) {
110
+ return startGenericFlow({
111
+ providerId: "openai-codex",
112
+ opts,
113
+ needsCodeSubmission: false,
114
+ begin: async () => {
115
+ const flow = await startCodexLogin();
116
+ const completion = (async () => {
117
+ const creds = await flow.credentials;
118
+ return { creds, codexFlow: flow };
119
+ })();
120
+ return {
121
+ authUrl: flow.authUrl,
122
+ completion,
123
+ submitCode: (code) => flow.submitCode(code),
124
+ cancel: () => flow.close(),
125
+ };
126
+ },
127
+ });
128
+ }
129
+ async function startGenericFlow(args) {
130
+ const { providerId, opts, needsCodeSubmission, begin } = args;
131
+ const sessionId = newSessionId();
132
+ const accountId = resolveAccountId(opts);
133
+ const vendor = await begin();
134
+ const startedAt = Date.now();
135
+ const initialState = {
136
+ sessionId,
137
+ providerId,
138
+ status: "pending",
139
+ authUrl: vendor.authUrl,
140
+ needsCodeSubmission,
141
+ startedAt,
142
+ };
143
+ let resolveCompletion;
144
+ let rejectCompletion;
145
+ const completion = new Promise((resolve, reject) => {
146
+ resolveCompletion = resolve;
147
+ rejectCompletion = reject;
148
+ });
149
+ const entry = {
150
+ state: initialState,
151
+ handle: {}, // filled below
152
+ listeners: new Set(),
153
+ };
154
+ flows.set(sessionId, entry);
155
+ const setTerminal = (next) => {
156
+ if (entry.state.status !== "pending")
157
+ return;
158
+ entry.state = {
159
+ ...entry.state,
160
+ ...next,
161
+ endedAt: Date.now(),
162
+ };
163
+ emit(entry);
164
+ scheduleGc(sessionId);
165
+ };
166
+ const timer = setTimeout(() => {
167
+ const err = new Error("OAuth flow timed out after 5 minutes");
168
+ try {
169
+ vendor.cancel("timeout");
170
+ }
171
+ catch (cancelErr) {
172
+ logger.debug(`[oauth-flow] cancel during timeout failed: ${String(cancelErr)}`);
173
+ }
174
+ setTerminal({ status: "timeout", error: err.message });
175
+ rejectCompletion(err);
176
+ }, FLOW_TIMEOUT_MS);
177
+ // Drive the vendor completion through to account-save and listener emit.
178
+ void (async () => {
179
+ try {
180
+ const { creds } = await vendor.completion;
181
+ clearTimeout(timer);
182
+ let organizationId;
183
+ // Codex bakes the account_id into the JWT — pull it back out for
184
+ // the usage probe header.
185
+ if (providerId === "openai-codex") {
186
+ const codexAccountId = extractCodexAccountId(creds.access);
187
+ if (codexAccountId)
188
+ organizationId = codexAccountId;
189
+ }
190
+ const record = persistAccount({
191
+ providerId,
192
+ accountId,
193
+ label: opts.label,
194
+ access: creds.access,
195
+ refresh: creds.refresh,
196
+ expires: creds.expires,
197
+ ...(organizationId ? { organizationId } : {}),
198
+ });
199
+ if (opts.onAccountSaved) {
200
+ await opts.onAccountSaved(record);
201
+ }
202
+ setTerminal({ status: "success", account: record });
203
+ resolveCompletion({ account: record });
204
+ }
205
+ catch (err) {
206
+ clearTimeout(timer);
207
+ const message = err instanceof Error ? err.message : String(err);
208
+ setTerminal({ status: "error", error: message });
209
+ rejectCompletion(err instanceof Error ? err : new Error(message));
210
+ }
211
+ })();
212
+ const handle = {
213
+ sessionId,
214
+ authUrl: vendor.authUrl,
215
+ needsCodeSubmission,
216
+ completion,
217
+ submitCode: (code) => {
218
+ vendor.submitCode(code);
219
+ },
220
+ cancel: (reason = "Cancelled") => {
221
+ if (entry.state.status !== "pending")
222
+ return;
223
+ try {
224
+ vendor.cancel(reason);
225
+ }
226
+ catch (err) {
227
+ logger.debug(`[oauth-flow] vendor cancel failed: ${String(err)}`);
228
+ }
229
+ clearTimeout(timer);
230
+ const error = new Error(reason);
231
+ setTerminal({ status: "cancelled", error: reason });
232
+ rejectCompletion(error);
233
+ },
234
+ };
235
+ entry.handle = handle;
236
+ emit(entry);
237
+ return handle;
238
+ }
239
+ // Registry surface used by the SSE / cancel routes.
240
+ export function getFlowState(sessionId) {
241
+ const entry = flows.get(sessionId);
242
+ return entry ? entry.state : null;
243
+ }
244
+ export function getFlowHandle(sessionId) {
245
+ const entry = flows.get(sessionId);
246
+ return entry ? entry.handle : null;
247
+ }
248
+ /**
249
+ * Subscribe to status updates for a flow. Replays the current state
250
+ * synchronously so SSE consumers always receive an immediate frame.
251
+ * Returns an unsubscribe function.
252
+ */
253
+ export function subscribeFlow(sessionId, listener) {
254
+ const entry = flows.get(sessionId);
255
+ if (!entry)
256
+ return () => { };
257
+ entry.listeners.add(listener);
258
+ // Replay current state.
259
+ listener(entry.state);
260
+ return () => {
261
+ entry.listeners.delete(listener);
262
+ };
263
+ }
264
+ export function cancelFlow(sessionId, reason) {
265
+ const entry = flows.get(sessionId);
266
+ if (!entry)
267
+ return false;
268
+ if (entry.state.status !== "pending")
269
+ return false;
270
+ entry.handle.cancel(reason);
271
+ return true;
272
+ }
273
+ export function submitFlowCode(sessionId, code) {
274
+ const entry = flows.get(sessionId);
275
+ if (!entry)
276
+ return false;
277
+ if (entry.state.status !== "pending")
278
+ return false;
279
+ if (!entry.state.needsCodeSubmission)
280
+ return false;
281
+ entry.handle.submitCode(code);
282
+ return true;
283
+ }
284
+ /**
285
+ * Remove every flow from the registry. Tests use this to reset
286
+ * between cases without resetting the whole module.
287
+ */
288
+ export function _resetFlowRegistry() {
289
+ for (const entry of flows.values()) {
290
+ if (entry.gcTimer)
291
+ clearTimeout(entry.gcTimer);
292
+ }
293
+ flows.clear();
294
+ }
295
+ /**
296
+ * Test-only helper to seed a synthetic flow without going through the
297
+ * vendor layer. Used by `accounts-routes.test.ts` to assert the SSE
298
+ * surface streams `success` / `error` payloads correctly.
299
+ */
300
+ export function _registerSyntheticFlow(args) {
301
+ const sessionId = args.sessionId ?? newSessionId();
302
+ const state = {
303
+ sessionId,
304
+ providerId: args.providerId,
305
+ status: "pending",
306
+ authUrl: args.authUrl,
307
+ needsCodeSubmission: Boolean(args.needsCodeSubmission),
308
+ startedAt: Date.now(),
309
+ };
310
+ const entry = {
311
+ state,
312
+ handle: {
313
+ sessionId,
314
+ authUrl: args.authUrl,
315
+ needsCodeSubmission: Boolean(args.needsCodeSubmission),
316
+ completion: new Promise(() => undefined),
317
+ submitCode: () => undefined,
318
+ cancel: () => undefined,
319
+ },
320
+ listeners: new Set(),
321
+ };
322
+ flows.set(sessionId, entry);
323
+ const complete = (next) => {
324
+ entry.state = { ...next, endedAt: next.endedAt ?? Date.now() };
325
+ emit(entry);
326
+ scheduleGc(sessionId);
327
+ };
328
+ return { sessionId, complete };
329
+ }
330
+ /** OpenAI Codex JWT carries `chatgpt_account_id` under a vendor claim. */
331
+ function extractCodexAccountId(accessToken) {
332
+ try {
333
+ const parts = accessToken.split(".");
334
+ if (parts.length !== 3)
335
+ return null;
336
+ const payload = parts[1];
337
+ const decoded = JSON.parse(Buffer.from(payload, "base64").toString("utf-8"));
338
+ const claim = decoded["https://api.openai.com/auth"];
339
+ if (!claim || typeof claim !== "object")
340
+ return null;
341
+ const accountId = claim.chatgpt_account_id;
342
+ return typeof accountId === "string" && accountId.length > 0
343
+ ? accountId
344
+ : null;
345
+ }
346
+ catch {
347
+ return null;
348
+ }
349
+ }
@@ -1,15 +1,47 @@
1
1
  /**
2
2
  * Anthropic OAuth (Claude Pro/Max) — authorization code + PKCE.
3
3
  * Inlined OAuth flow (MIT) — vendored to avoid a runtime dependency.
4
+ *
5
+ * Anthropic's OAuth uses a fixed redirect URI on `console.anthropic.com`
6
+ * that displays the auth code on completion — there's no loopback
7
+ * listener. The flow surfaces an `authUrl` plus a `submitCode()` hook
8
+ * the UI / CLI calls once the user has copied the code.
4
9
  */
5
10
  export interface AnthropicOAuthCredentials {
6
11
  refresh: string;
7
12
  access: string;
8
13
  expires: number;
9
14
  }
15
+ /**
16
+ * Programmatic Anthropic OAuth flow.
17
+ *
18
+ * `authUrl` is ready immediately. The caller MUST eventually call
19
+ * either `submitCode(code)` (after the user has pasted the
20
+ * `code#state` blob from the redirect page) or `cancel()`. The
21
+ * `completion` promise rejects if the flow is cancelled.
22
+ */
23
+ export interface AnthropicOAuthFlowHandle {
24
+ authUrl: string;
25
+ /** Pass `code#state` from the redirect page. */
26
+ submitCode: (code: string) => void;
27
+ /** Resolves with credentials once `submitCode` lands and the token exchange succeeds. */
28
+ completion: Promise<AnthropicOAuthCredentials>;
29
+ cancel: (reason?: string) => void;
30
+ }
31
+ /**
32
+ * Start an Anthropic OAuth flow. Returns immediately with the auth
33
+ * URL and a `submitCode` hook. The token exchange happens inside
34
+ * `completion` once the caller submits the code.
35
+ */
36
+ export declare function startAnthropicOAuthFlowRaw(): Promise<AnthropicOAuthFlowHandle>;
10
37
  /**
11
38
  * @param onAuthUrl - Receives the browser authorization URL
12
39
  * @param onPromptCode - Resolves with pasted code (format: code#state)
40
+ *
41
+ * Thin compatibility wrapper around `startAnthropicOAuthFlowRaw` for
42
+ * the CLI entrypoint and any older callers. New code should use
43
+ * `startAnthropicOAuthFlowRaw` (or the higher-level
44
+ * `startAnthropicOAuthFlow` in `auth/oauth-flow.ts`) directly.
13
45
  */
14
46
  export declare function loginAnthropic(onAuthUrl: (url: string) => void, onPromptCode: () => Promise<string>): Promise<AnthropicOAuthCredentials>;
15
47
  export declare function refreshAnthropicToken(refreshToken: string): Promise<AnthropicOAuthCredentials>;
@@ -1 +1 @@
1
- {"version":3,"file":"anthropic-login.d.ts","sourceRoot":"","sources":["../../../../../../../src/auth/vendor/pi-oauth/anthropic-login.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAWH,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAClC,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,EAChC,YAAY,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,GAClC,OAAO,CAAC,yBAAyB,CAAC,CA6CpC;AAED,wBAAsB,qBAAqB,CACzC,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,yBAAyB,CAAC,CAwBpC"}
1
+ {"version":3,"file":"anthropic-login.d.ts","sourceRoot":"","sources":["../../../../../../../src/auth/vendor/pi-oauth/anthropic-login.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAWH,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,gDAAgD;IAChD,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,yFAAyF;IACzF,UAAU,EAAE,OAAO,CAAC,yBAAyB,CAAC,CAAC;IAC/C,MAAM,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC;AAED;;;;GAIG;AACH,wBAAsB,0BAA0B,IAAI,OAAO,CAAC,wBAAwB,CAAC,CA6DpF;AAED;;;;;;;;GAQG;AACH,wBAAsB,cAAc,CAClC,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,EAChC,YAAY,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,GAClC,OAAO,CAAC,yBAAyB,CAAC,CAMpC;AAED,wBAAsB,qBAAqB,CACzC,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,yBAAyB,CAAC,CAwBpC"}
@@ -1,6 +1,11 @@
1
1
  /**
2
2
  * Anthropic OAuth (Claude Pro/Max) — authorization code + PKCE.
3
3
  * Inlined OAuth flow (MIT) — vendored to avoid a runtime dependency.
4
+ *
5
+ * Anthropic's OAuth uses a fixed redirect URI on `console.anthropic.com`
6
+ * that displays the auth code on completion — there's no loopback
7
+ * listener. The flow surfaces an `authUrl` plus a `submitCode()` hook
8
+ * the UI / CLI calls once the user has copied the code.
4
9
  */
5
10
  import { generatePKCE } from "./pkce.js";
6
11
  const decode = (s) => atob(s);
@@ -10,10 +15,11 @@ const TOKEN_URL = "https://console.anthropic.com/v1/oauth/token";
10
15
  const REDIRECT_URI = "https://console.anthropic.com/oauth/code/callback";
11
16
  const SCOPES = "org:create_api_key user:profile user:inference";
12
17
  /**
13
- * @param onAuthUrl - Receives the browser authorization URL
14
- * @param onPromptCode - Resolves with pasted code (format: code#state)
18
+ * Start an Anthropic OAuth flow. Returns immediately with the auth
19
+ * URL and a `submitCode` hook. The token exchange happens inside
20
+ * `completion` once the caller submits the code.
15
21
  */
16
- export async function loginAnthropic(onAuthUrl, onPromptCode) {
22
+ export async function startAnthropicOAuthFlowRaw() {
17
23
  const { verifier, challenge } = await generatePKCE();
18
24
  const authParams = new URLSearchParams({
19
25
  code: "true",
@@ -26,35 +32,64 @@ export async function loginAnthropic(onAuthUrl, onPromptCode) {
26
32
  state: verifier,
27
33
  });
28
34
  const authUrl = `${AUTHORIZE_URL}?${authParams.toString()}`;
29
- onAuthUrl(authUrl);
30
- const authCode = await onPromptCode();
31
- const splits = authCode.split("#");
32
- const code = splits[0];
33
- const state = splits[1];
34
- const tokenResponse = await fetch(TOKEN_URL, {
35
- method: "POST",
36
- headers: { "Content-Type": "application/json" },
37
- body: JSON.stringify({
38
- grant_type: "authorization_code",
39
- client_id: CLIENT_ID,
40
- code,
41
- state,
42
- redirect_uri: REDIRECT_URI,
43
- code_verifier: verifier,
44
- }),
35
+ let resolveCode = null;
36
+ let rejectCode = null;
37
+ const codePromise = new Promise((resolve, reject) => {
38
+ resolveCode = resolve;
39
+ rejectCode = reject;
45
40
  });
46
- if (!tokenResponse.ok) {
47
- const errText = await tokenResponse.text();
48
- throw new Error(`Token exchange failed: ${errText}`);
49
- }
50
- const tokenData = (await tokenResponse.json());
51
- const expiresAt = Date.now() + tokenData.expires_in * 1000 - 5 * 60 * 1000;
41
+ const completion = (async () => {
42
+ const authCode = await codePromise;
43
+ const splits = authCode.split("#");
44
+ const code = splits[0];
45
+ const state = splits[1];
46
+ const tokenResponse = await fetch(TOKEN_URL, {
47
+ method: "POST",
48
+ headers: { "Content-Type": "application/json" },
49
+ body: JSON.stringify({
50
+ grant_type: "authorization_code",
51
+ client_id: CLIENT_ID,
52
+ code,
53
+ state,
54
+ redirect_uri: REDIRECT_URI,
55
+ code_verifier: verifier,
56
+ }),
57
+ });
58
+ if (!tokenResponse.ok) {
59
+ const errText = await tokenResponse.text();
60
+ throw new Error(`Token exchange failed: ${errText}`);
61
+ }
62
+ const tokenData = (await tokenResponse.json());
63
+ const expiresAt = Date.now() + tokenData.expires_in * 1000 - 5 * 60 * 1000;
64
+ return {
65
+ refresh: tokenData.refresh_token,
66
+ access: tokenData.access_token,
67
+ expires: expiresAt,
68
+ };
69
+ })();
52
70
  return {
53
- refresh: tokenData.refresh_token,
54
- access: tokenData.access_token,
55
- expires: expiresAt,
71
+ authUrl,
72
+ submitCode: (code) => resolveCode?.(code),
73
+ completion,
74
+ cancel: (reason = "Cancelled") => rejectCode?.(new Error(reason)),
56
75
  };
57
76
  }
77
+ /**
78
+ * @param onAuthUrl - Receives the browser authorization URL
79
+ * @param onPromptCode - Resolves with pasted code (format: code#state)
80
+ *
81
+ * Thin compatibility wrapper around `startAnthropicOAuthFlowRaw` for
82
+ * the CLI entrypoint and any older callers. New code should use
83
+ * `startAnthropicOAuthFlowRaw` (or the higher-level
84
+ * `startAnthropicOAuthFlow` in `auth/oauth-flow.ts`) directly.
85
+ */
86
+ export async function loginAnthropic(onAuthUrl, onPromptCode) {
87
+ const handle = await startAnthropicOAuthFlowRaw();
88
+ onAuthUrl(handle.authUrl);
89
+ const code = await onPromptCode();
90
+ handle.submitCode(code);
91
+ return handle.completion;
92
+ }
58
93
  export async function refreshAnthropicToken(refreshToken) {
59
94
  const response = await fetch(TOKEN_URL, {
60
95
  method: "POST",
@@ -413,7 +413,7 @@ const FIELD_HELP = {
413
413
  "diagnostics.cacheTrace.includePrompt": "Include prompt text in trace output (default: true).",
414
414
  "diagnostics.cacheTrace.includeSystem": "Include system prompt in trace output (default: true).",
415
415
  "tools.exec.applyPatch.enabled": "Experimental. Enables apply_patch for OpenAI models when allowed by tool policy.",
416
- "tools.exec.applyPatch.allowModels": 'Optional allowlist of model ids (e.g. "gpt-5.4" or "openai/gpt-5.4").',
416
+ "tools.exec.applyPatch.allowModels": 'Optional allowlist of model ids (e.g. "gpt-5.5" or "openai/gpt-5.5").',
417
417
  "tools.exec.notifyOnExit": "When true (default), backgrounded exec sessions enqueue a system event and request a heartbeat on exit.",
418
418
  "tools.exec.pathPrepend": "Directories to prepend to PATH for exec runs (gateway/sandbox).",
419
419
  "tools.exec.safeBins": "Allow stdin-only safe binaries to run without explicit allowlist entries.",
@@ -123,7 +123,7 @@ export type MessagesConfig = {
123
123
  * - special value: `"auto"` derives `[{agents.list[].identity.name}]` for the routed agent (when set)
124
124
  *
125
125
  * Supported template variables (case-insensitive):
126
- * - `{model}` - short model name (e.g., `claude-opus-4-7`, `gpt-5.4`)
126
+ * - `{model}` - short model name (e.g., `claude-opus-4-7`, `gpt-5.5`)
127
127
  * - `{modelFull}` - full model identifier (e.g., `anthropic/claude-opus-4.7`)
128
128
  * - `{provider}` - provider name (e.g., `anthropic`, `openai`)
129
129
  * - `{thinkingLevel}` or `{think}` - current thinking level (`high`, `low`, `off`)
@@ -141,7 +141,7 @@ export type ExecToolConfig = {
141
141
  enabled?: boolean;
142
142
  /**
143
143
  * Optional allowlist of model ids that can use apply_patch.
144
- * Accepts either raw ids (e.g. "gpt-5.4") or full ids (e.g. "openai/gpt-5.4").
144
+ * Accepts either raw ids (e.g. "gpt-5.5") or full ids (e.g. "openai/gpt-5.5").
145
145
  */
146
146
  allowModels?: string[];
147
147
  };
@@ -464,8 +464,8 @@ function isLikelyOpenAiTextModel(value) {
464
464
  * Normalize known-bad provider compatibility shims before plugin resolution.
465
465
  *
466
466
  * A common failure mode is routing the OpenAI plugin through Groq's
467
- * OpenAI-compatible base URL while leaving OpenAI defaults (`gpt-5.4`,
468
- * `gpt-5.4-mini`) in place. Structured XML/object generation then fails during
467
+ * OpenAI-compatible base URL while leaving OpenAI defaults (`gpt-5.5`,
468
+ * `gpt-5.5-mini`) in place. Structured XML/object generation then fails during
469
469
  * message handling because Groq does not serve those model IDs.
470
470
  *
471
471
  * When we can confidently detect that state, rewrite the effective runtime
@@ -1036,7 +1036,7 @@ export function applyCloudConfigToEnv(config) {
1036
1036
  const llmText = resolveServiceRoutingInConfig(config)?.llmText;
1037
1037
  const models = config.models;
1038
1038
  if (effectivelyEnabled) {
1039
- const nano = llmText?.nanoModel || models?.nano || "openai/gpt-5.4-nano";
1039
+ const nano = llmText?.nanoModel || models?.nano || "openai/gpt-5.5-nano";
1040
1040
  const small = llmText?.smallModel || models?.small || "minimax/minimax-m2.7";
1041
1041
  const medium = llmText?.mediumModel || models?.medium || small;
1042
1042
  const large = llmText?.largeModel || models?.large || "anthropic/claude-opus-4-7";
@@ -104,10 +104,10 @@ export function isNonChatModelLabel(model) {
104
104
  export function estimateTokenCost(promptTokens, completionTokens, model) {
105
105
  const normalizedModel = (model ?? "").toLowerCase();
106
106
  const pricingByMillion = {
107
- "gpt-5.4-pro": [30.0, 180.0],
108
- "gpt-5.4-mini": [0.75, 4.5],
109
- "gpt-5.4-nano": [0.2, 1.25],
110
- "gpt-5.4": [2.5, 15.0],
107
+ "gpt-5.5-pro": [30.0, 180.0],
108
+ "gpt-5.5-mini": [0.75, 4.5],
109
+ "gpt-5.5-nano": [0.2, 1.25],
110
+ "gpt-5.5": [2.5, 15.0],
111
111
  "gpt-4.1": [2.0, 8.0],
112
112
  "gpt-4o": [2.5, 10.0],
113
113
  "gpt-4": [30.0, 60.0],
@@ -168,7 +168,7 @@ export function ProviderSwitcher(props = {}) {
168
168
  const providerId = getOnboardingProviderOption(llmText?.backend)?.id;
169
169
  const elizaCloudEnabledCfg = llmText?.transport === "cloud-proxy" && providerId === "elizacloud";
170
170
  const defaults = {
171
- nano: "openai/gpt-5.4-nano",
171
+ nano: "openai/gpt-5.5-nano",
172
172
  small: "minimax/minimax-m2.7",
173
173
  medium: "anthropic/claude-sonnet-4.6",
174
174
  large: "moonshotai/kimi-k2.5",
@@ -121,7 +121,7 @@ function createInitialState(cloudOnly) {
121
121
  apiKey: "",
122
122
  voiceProvider: "",
123
123
  voiceApiKey: "",
124
- nanoModel: "openai/gpt-5.4-nano",
124
+ nanoModel: "openai/gpt-5.5-nano",
125
125
  smallModel: "minimax/minimax-m2.7",
126
126
  mediumModel: "anthropic/claude-sonnet-4.6",
127
127
  largeModel: "anthropic/claude-opus-4-7",