@elizaos/agent 2.0.0-alpha.428 → 2.0.0-alpha.430

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.428",
3
+ "version": "2.0.0-alpha.430",
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.428",
470
+ "@elizaos/core": "^2.0.0-alpha.430",
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.428",
480
- "@elizaos/skills": "^2.0.0-alpha.428",
479
+ "@elizaos/shared": "^2.0.0-alpha.430",
480
+ "@elizaos/skills": "^2.0.0-alpha.430",
481
481
  "@hapi/boom": "^10.0.1",
482
482
  "@noble/curves": "^2.0.1",
483
483
  "@solana/web3.js": "^1.98.4",
@@ -426,14 +426,29 @@ async function handleOAuthRoutes(ctx, providerId, rest) {
426
426
  error(res, parsed.error.issues[0]?.message ?? "Invalid body", 400);
427
427
  return true;
428
428
  }
429
- // Reserve an accountId and the priority slot up front so the
430
- // post-save hook lands at a deterministic position.
429
+ // Reserve an accountId up front so the OAuth flow can wire it
430
+ // into the credential record before any token exchange completes.
431
+ // Priority is computed AT SAVE TIME, not now: pre-allocating leaks
432
+ // a stale priority if two users start parallel OAuth flows before
433
+ // either completes (both would get the same number). Computing in
434
+ // the post-save hook is monotonic regardless of concurrency since
435
+ // the on-disk credential file appears strictly before the hook
436
+ // fires.
431
437
  const accountId = nodeCrypto.randomUUID();
432
438
  const pool = await getPool();
433
- const priority = nextPriorityFromPool(pool, providerId);
434
- const onAccountSaved = (record) => {
435
- const linkedConfig = buildLinkedAccountConfigFromRecord(record, priority);
436
- void pool.upsert(linkedConfig);
439
+ const onAccountSaved = async (record) => {
440
+ // Exclude the just-saved record from the priority calc — its
441
+ // credential file already exists on disk so `pool.list` would
442
+ // include it at a default priority (createdAt-sorted index),
443
+ // which would push the new max one too high.
444
+ const others = pool
445
+ .list(providerId)
446
+ .filter((a) => a.id !== record.id);
447
+ const livePriority = others.length === 0
448
+ ? 0
449
+ : Math.max(...others.map((a) => a.priority)) + 1;
450
+ const linkedConfig = buildLinkedAccountConfigFromRecord(record, livePriority);
451
+ await pool.upsert(linkedConfig);
437
452
  };
438
453
  const startFlow = subscription === "anthropic-subscription"
439
454
  ? startAnthropicOAuthFlow
@@ -1 +1 @@
1
- {"version":3,"file":"AccountList.d.ts","sourceRoot":"","sources":["../../../../../../../app-core/src/components/accounts/AccountList.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAU/D,UAAU,gBAAgB;IACxB,UAAU,EAAE,uBAAuB,CAAC;CACrC;AAOD,wBAAgB,WAAW,CAAC,EAAE,UAAU,EAAE,EAAE,gBAAgB,2CA8I3D"}
1
+ {"version":3,"file":"AccountList.d.ts","sourceRoot":"","sources":["../../../../../../../app-core/src/components/accounts/AccountList.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAU/D,UAAU,gBAAgB;IACxB,UAAU,EAAE,uBAAuB,CAAC;CACrC;AAOD,wBAAgB,WAAW,CAAC,EAAE,UAAU,EAAE,EAAE,gBAAgB,2CA6J3D"}
@@ -44,16 +44,33 @@ export function AccountList({ providerId }) {
44
44
  const neighbour = sorted[neighbourIndex];
45
45
  if (!self || !neighbour || self.priority === neighbour.priority)
46
46
  return;
47
- // Swap priorities. We patch the lower-priority one to a temp
48
- // sentinel first to avoid the unique-priority assumption later
49
- // consumers might add for the current pool there's no uniqueness
50
- // constraint, so two sequential patches are enough.
47
+ const selfOriginal = self.priority;
48
+ const neighbourOriginal = neighbour.priority;
49
+ // Swap priorities via two sequential PATCHes. There's no atomic
50
+ // server-side swap, so on failure of the second call we roll the
51
+ // first one back so the user doesn't end up with two accounts at
52
+ // the same priority. Worst case a partial-failure leaves the
53
+ // original ordering with a flash; never a corrupted ordering.
51
54
  await accounts.patch(providerId, self.id, {
52
- priority: neighbour.priority,
53
- });
54
- await accounts.patch(providerId, neighbour.id, {
55
- priority: self.priority,
55
+ priority: neighbourOriginal,
56
56
  });
57
+ try {
58
+ await accounts.patch(providerId, neighbour.id, {
59
+ priority: selfOriginal,
60
+ });
61
+ }
62
+ catch (err) {
63
+ try {
64
+ await accounts.patch(providerId, self.id, {
65
+ priority: selfOriginal,
66
+ });
67
+ }
68
+ catch {
69
+ // Rollback failed — refresh will reconcile from server state.
70
+ void accounts.refresh();
71
+ }
72
+ throw err;
73
+ }
57
74
  }, [accounts, providerId, sorted]);
58
75
  if (accounts.loading && !accounts.data) {
59
76
  return (_jsxs("div", { className: "mt-3 flex items-center gap-2 text-xs text-muted", children: [_jsx(Spinner, { className: "h-3 w-3" }), t("accounts.loading", { defaultValue: "Loading accounts…" })] }));
@@ -1 +1 @@
1
- {"version":3,"file":"AddAccountDialog.d.ts","sourceRoot":"","sources":["../../../../../../../app-core/src/components/accounts/AddAccountDialog.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAeH,OAAO,KAAK,EACV,mBAAmB,EACnB,uBAAuB,EACxB,MAAM,iBAAiB,CAAC;AAYzB,UAAU,qBAAqB;IAC7B,IAAI,EAAE,OAAO,CAAC;IACd,UAAU,EAAE,uBAAuB,CAAC;IACpC,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,SAAS,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,IAAI,CAAC;CACnD;AAoDD,wBAAgB,gBAAgB,CAAC,EAC/B,IAAI,EACJ,UAAU,EACV,OAAO,EACP,SAAS,GACV,EAAE,qBAAqB,2CAkZvB"}
1
+ {"version":3,"file":"AddAccountDialog.d.ts","sourceRoot":"","sources":["../../../../../../../app-core/src/components/accounts/AddAccountDialog.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAeH,OAAO,KAAK,EACV,mBAAmB,EACnB,uBAAuB,EACxB,MAAM,iBAAiB,CAAC;AAYzB,UAAU,qBAAqB;IAC7B,IAAI,EAAE,OAAO,CAAC;IACd,UAAU,EAAE,uBAAuB,CAAC;IACpC,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,SAAS,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,IAAI,CAAC;CACnD;AAoDD,wBAAgB,gBAAgB,CAAC,EAC/B,IAAI,EACJ,UAAU,EACV,OAAO,EACP,SAAS,GACV,EAAE,qBAAqB,2CAwdvB"}
@@ -67,6 +67,14 @@ export function AddAccountDialog({ open, providerId, onClose, onCreated, }) {
67
67
  const [sessionId, setSessionId] = useState(null);
68
68
  const eventSourceRef = useRef(null);
69
69
  const sessionIdRef = useRef(null);
70
+ // Mirrors the `open` prop in a ref so async paths (`startOAuth` mid-
71
+ // await) can detect that the dialog was closed before
72
+ // `client.startAccountOAuth` resolved and immediately cancel the
73
+ // freshly-created server-side flow.
74
+ const openRef = useRef(open);
75
+ useEffect(() => {
76
+ openRef.current = open;
77
+ }, [open]);
70
78
  const closeEventSource = useCallback(() => {
71
79
  if (eventSourceRef.current) {
72
80
  eventSourceRef.current.close();
@@ -114,10 +122,28 @@ export function AddAccountDialog({ open, providerId, onClose, onCreated, }) {
114
122
  const url = `/api/accounts/${providerId}/oauth/status?sessionId=${encodeURIComponent(newSessionId)}`;
115
123
  const source = new EventSource(url);
116
124
  eventSourceRef.current = source;
125
+ // EventSource auto-reconnects on transient network blips, which
126
+ // is fine. But persistent failures (server gone, route 404) just
127
+ // toggle readyState=2 forever and the user is stuck on "Waiting
128
+ // for browser…". Surface that after a small grace period so the
129
+ // user can retry instead of staring at a spinner.
130
+ let connectedOnce = false;
131
+ let persistentErrorTimer = null;
132
+ const cancelPersistentErrorTimer = () => {
133
+ if (persistentErrorTimer) {
134
+ clearTimeout(persistentErrorTimer);
135
+ persistentErrorTimer = null;
136
+ }
137
+ };
138
+ source.onopen = () => {
139
+ connectedOnce = true;
140
+ cancelPersistentErrorTimer();
141
+ };
117
142
  source.onmessage = (event) => {
118
143
  try {
119
144
  const data = JSON.parse(event.data);
120
145
  if (data.status === "success" && data.account) {
146
+ cancelPersistentErrorTimer();
121
147
  closeEventSource();
122
148
  sessionIdRef.current = null;
123
149
  onCreated(data.account);
@@ -126,6 +152,7 @@ export function AddAccountDialog({ open, providerId, onClose, onCreated, }) {
126
152
  else if (data.status === "error" ||
127
153
  data.status === "cancelled" ||
128
154
  data.status === "timeout") {
155
+ cancelPersistentErrorTimer();
129
156
  closeEventSource();
130
157
  sessionIdRef.current = null;
131
158
  setErrorMessage(data.error ??
@@ -144,7 +171,24 @@ export function AddAccountDialog({ open, providerId, onClose, onCreated, }) {
144
171
  }
145
172
  };
146
173
  source.onerror = () => {
147
- // Auto-reconnect noise; ignore unless we hit a terminal status.
174
+ // EventSource readyState: 0=connecting, 1=open, 2=closed.
175
+ // If we're at 2 and never got an `onopen`, the route is
176
+ // unreachable. Give the browser ~5s to retry; if it can't
177
+ // recover, surface the error.
178
+ if (persistentErrorTimer)
179
+ return;
180
+ persistentErrorTimer = setTimeout(() => {
181
+ persistentErrorTimer = null;
182
+ if (!connectedOnce &&
183
+ eventSourceRef.current?.readyState === EventSource.CLOSED) {
184
+ closeEventSource();
185
+ sessionIdRef.current = null;
186
+ setErrorMessage(t("accounts.add.oauth.sseUnreachable", {
187
+ defaultValue: "Lost connection to the OAuth status stream. Try again.",
188
+ }));
189
+ setStep("error");
190
+ }
191
+ }, 5_000);
148
192
  };
149
193
  }, [closeEventSource, onClose, onCreated, providerId, t]);
150
194
  const startOAuth = useCallback(async () => {
@@ -159,6 +203,28 @@ export function AddAccountDialog({ open, providerId, onClose, onCreated, }) {
159
203
  const flow = await client.startAccountOAuth(providerId, {
160
204
  label: label.trim(),
161
205
  });
206
+ // The dialog might have been closed between user clicking "Sign
207
+ // in" and the server returning the flow handle. If so, the
208
+ // server-side OAuth listener is orphaned — cancel it explicitly
209
+ // so the loopback port releases immediately instead of timing
210
+ // out in 5 minutes.
211
+ if (!openRef.current) {
212
+ try {
213
+ await client.cancelAccountOAuth(providerId, {
214
+ sessionId: flow.sessionId,
215
+ });
216
+ }
217
+ catch {
218
+ // Best-effort.
219
+ }
220
+ try {
221
+ win?.close();
222
+ }
223
+ catch {
224
+ // Cross-origin — ignore.
225
+ }
226
+ return;
227
+ }
162
228
  navigatePreOpenedWindow(win, flow.authUrl);
163
229
  sessionIdRef.current = flow.sessionId;
164
230
  setSessionId(flow.sessionId);
@@ -1 +1 @@
1
- {"version":3,"file":"ProviderSwitcher.d.ts","sourceRoot":"","sources":["../../../../../../../app-core/src/components/settings/ProviderSwitcher.tsx"],"names":[],"mappings":"AAkCA,OAAO,EAAkC,KAAK,cAAc,EAAE,MAAM,WAAW,CAAC;AAahF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAqBhD,UAAU,UAAU;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,cAAc,EAAE,CAAC;IAC7B,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;CAC9C;AASD,UAAU,qBAAqB;IAC7B,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,OAAO,CAAC,EAAE,UAAU,EAAE,CAAC;IACvB,YAAY,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC3B,iBAAiB,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAChC,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,sBAAsB,CAAC,EAAE,CACvB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AAwJD,wBAAgB,gBAAgB,CAAC,KAAK,GAAE,qBAA0B,2CA27BjE"}
1
+ {"version":3,"file":"ProviderSwitcher.d.ts","sourceRoot":"","sources":["../../../../../../../app-core/src/components/settings/ProviderSwitcher.tsx"],"names":[],"mappings":"AAkCA,OAAO,EAAkC,KAAK,cAAc,EAAE,MAAM,WAAW,CAAC;AAahF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAqBhD,UAAU,UAAU;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,cAAc,EAAE,CAAC;IAC7B,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;CAC9C;AASD,UAAU,qBAAqB;IAC7B,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,OAAO,CAAC,EAAE,UAAU,EAAE,CAAC;IACvB,YAAY,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC3B,iBAAiB,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAChC,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,sBAAsB,CAAC,EAAE,CACvB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AAwJD,wBAAgB,gBAAgB,CAAC,KAAK,GAAE,qBAA0B,2CAg8BjE"}
@@ -568,6 +568,16 @@ export function ProviderSwitcher(props = {}) {
568
568
  (largeModelOptions.length > 0 || cloudModelSchema) ? (_jsxs("div", { className: "border-border/40 border-t px-4 py-4 sm:px-5", children: [largeModelOptions.length > 0 ? (_jsxs("div", { children: [_jsx("label", { htmlFor: "provider-switcher-primary-model", className: "mb-1.5 block text-muted text-xs font-medium uppercase tracking-wider", children: t("providerswitcher.model", { defaultValue: "Model" }) }), _jsxs(Select, { value: currentLargeModel || "", onValueChange: (v) => handleModelFieldChange("large", v), children: [_jsx(SelectTrigger, { id: "provider-switcher-primary-model", className: "h-9 w-full max-w-sm rounded-lg border border-border bg-card text-sm", children: _jsx(SelectValue, { placeholder: t("providerswitcher.chooseModel", {
569
569
  defaultValue: "Choose a model",
570
570
  }) }) }), _jsx(SelectContent, { className: "max-h-64", children: largeModelOptions.map((model) => (_jsx(SelectItem, { value: model.id, children: model.name }, model.id))) })] })] })) : null, cloudModelSchema ? (_jsx(AdvancedSettingsDisclosure, { title: "Model overrides", className: "mt-4", children: _jsx(ConfigRenderer, { schema: cloudModelSchema.schema, hints: cloudModelSchema.hints, values: modelValues.values, setKeys: modelValues.setKeys, registry: defaultRegistry, onChange: handleModelFieldChange }) })) : null, _jsxs("div", { className: "mt-2 flex items-center justify-between gap-2", children: [_jsx("p", { className: "text-muted text-xs-tight", children: t("providerswitcher.restartRequiredHint", appNameInterpolationVars(branding)) }), _jsxs("div", { className: "flex items-center gap-2", children: [modelSaving && (_jsx("span", { className: "inline-flex items-center text-muted", title: t("providerswitcher.savingRestarting"), role: "status", "aria-label": t("providerswitcher.savingRestarting"), children: _jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin" }) })), modelSaveSuccess && (_jsx("span", { className: "inline-flex items-center text-ok", title: t("providerswitcher.savedRestartingAgent"), role: "status", "aria-label": t("providerswitcher.savedRestartingAgent"), children: _jsx(CheckCircle2, { className: "h-3.5 w-3.5" }) }))] })] })] })) : null] })) : null, isSubscriptionProviderSelectionId(visibleProviderPanelId) ? (_jsxs("div", { className: "min-w-0", children: [_jsx(ProviderPanelHeader, { icon: KeyRound, title: getSubscriptionProviderLabel(SUBSCRIPTION_PROVIDER_SELECTIONS.find((provider) => provider.id === visibleProviderPanelId) ?? SUBSCRIPTION_PROVIDER_SELECTIONS[0], t), description: "Connect subscription-backed access for models and task agents.", children: cloudCallsDisabled ||
571
- resolvedSelectedId !== visibleProviderPanelId ? (_jsx(Button, { type: "button", variant: "outline", className: "h-8 rounded-lg px-2.5 text-xs", onClick: () => void handleSelectSubscription(visibleProviderPanelId), children: "Use subscription" })) : null }), _jsxs("div", { className: "px-3 py-3 sm:px-4", children: [cloudCallsDisabled ? (_jsx("div", { className: "mb-3 rounded-lg border border-warn/30 bg-warn/5 px-3 py-2 text-warn text-xs-tight", children: "Local-only active. Remote subscription routing is paused." })) : null, _jsx(SubscriptionStatus, { resolvedSelectedId: visibleProviderPanelId, subscriptionStatus: subscriptionStatus, anthropicConnected: anthropicConnected, setAnthropicConnected: setAnthropicConnected, anthropicCliDetected: anthropicCliDetected, openaiConnected: openaiConnected, setOpenaiConnected: setOpenaiConnected, handleSelectSubscription: handleSelectSubscription, loadSubscriptionStatus: loadSubscriptionStatus }), _jsx(AccountList, { providerId: SUBSCRIPTION_PROVIDER_SELECTIONS.find((p) => p.id === visibleProviderPanelId)?.storedProvider ?? "anthropic-subscription" })] })] })) : null, selectedPanelProvider ? (_jsxs("div", { className: "min-w-0", children: [_jsx(ProviderPanelHeader, { icon: KeyRound, title: apiProviderChoices.find((choice) => choice.id === visibleProviderPanelId)?.label ?? selectedPanelProvider.name, description: "Use your own provider API key and model routing.", children: cloudCallsDisabled ||
571
+ resolvedSelectedId !== visibleProviderPanelId ? (_jsx(Button, { type: "button", variant: "outline", className: "h-8 rounded-lg px-2.5 text-xs", onClick: () => void handleSelectSubscription(visibleProviderPanelId), children: "Use subscription" })) : null }), _jsxs("div", { className: "px-3 py-3 sm:px-4", children: [cloudCallsDisabled ? (_jsx("div", { className: "mb-3 rounded-lg border border-warn/30 bg-warn/5 px-3 py-2 text-warn text-xs-tight", children: "Local-only active. Remote subscription routing is paused." })) : null, _jsx(SubscriptionStatus, { resolvedSelectedId: visibleProviderPanelId, subscriptionStatus: subscriptionStatus, anthropicConnected: anthropicConnected, setAnthropicConnected: setAnthropicConnected, anthropicCliDetected: anthropicCliDetected, openaiConnected: openaiConnected, setOpenaiConnected: setOpenaiConnected, handleSelectSubscription: handleSelectSubscription, loadSubscriptionStatus: loadSubscriptionStatus }), (() => {
572
+ const selection = SUBSCRIPTION_PROVIDER_SELECTIONS.find((p) => p.id === visibleProviderPanelId);
573
+ // The outer `isSubscriptionProviderSelectionId` guard
574
+ // makes this lookup non-null in practice; the explicit
575
+ // null check is defensive against future panel ids
576
+ // that pass the guard but aren't in the selections
577
+ // table (e.g. a renamed enum left half-migrated).
578
+ if (!selection)
579
+ return null;
580
+ return _jsx(AccountList, { providerId: selection.storedProvider });
581
+ })()] })] })) : null, selectedPanelProvider ? (_jsxs("div", { className: "min-w-0", children: [_jsx(ProviderPanelHeader, { icon: KeyRound, title: apiProviderChoices.find((choice) => choice.id === visibleProviderPanelId)?.label ?? selectedPanelProvider.name, description: "Use your own provider API key and model routing.", children: cloudCallsDisabled ||
572
582
  resolvedSelectedId !== visibleProviderPanelId ? (_jsx(Button, { type: "button", variant: "outline", className: "h-8 rounded-lg px-2.5 text-xs", onClick: () => void handleSwitchProvider(visibleProviderPanelId), children: "Use provider" })) : null }), _jsxs("div", { className: "px-3 py-3 sm:px-4", children: [cloudCallsDisabled ? (_jsx("div", { className: "mb-3 rounded-lg border border-warn/30 bg-warn/5 px-3 py-2 text-warn text-xs-tight", children: "Local-only active. Remote API routing is paused." })) : null, _jsx(ApiKeyConfig, { selectedProvider: selectedPanelProvider, pluginSaving: pluginSaving, pluginSaveSuccess: pluginSaveSuccess, handlePluginConfigSave: handlePluginConfigSave, loadPlugins: loadPlugins })] })] })) : null] })] }));
573
583
  }
@@ -6,13 +6,16 @@
6
6
  "accounts.add.disabledHint": "API-key accounts for this provider are not yet supported.",
7
7
  "accounts.add.label": "Account name",
8
8
  "accounts.add.labelPlaceholder": "e.g. Personal, Work",
9
+ "accounts.add.oauth.cancelled": "Login cancelled.",
9
10
  "accounts.add.oauth.codeFailed": "Failed to submit code.",
10
11
  "accounts.add.oauth.codeHint": "Auto-redirect didn't reach us. Paste the code (or full redirect URL) from the browser.",
11
12
  "accounts.add.oauth.codePlaceholder": "Paste the code or redirect URL",
13
+ "accounts.add.oauth.error": "Login failed.",
12
14
  "accounts.add.oauth.sessionHint": "Session: {{sessionId}}",
13
15
  "accounts.add.oauth.startFailed": "Failed to start login flow.",
14
16
  "accounts.add.oauth.starting": "Starting login flow...",
15
17
  "accounts.add.oauth.submitCode": "Submit code",
18
+ "accounts.add.oauth.timeout": "Login timed out. Try again.",
16
19
  "accounts.add.oauth.waiting": "Waiting for browser... Complete the sign-in there.",
17
20
  "accounts.add.save": "Add account",
18
21
  "accounts.add.signIn": "Sign in with {{provider}}",
@@ -52,6 +55,14 @@
52
55
  "accounts.source.oauth": "OAuth",
53
56
  "accounts.strategy.choose": "Choose strategy",
54
57
  "accounts.strategy.label": "Strategy",
58
+ "accounts.strategy.leastUsed.description": "Prefer the account with the lowest current usage.",
59
+ "accounts.strategy.leastUsed.label": "Least used",
60
+ "accounts.strategy.priority.description": "Always prefer the top healthy account.",
61
+ "accounts.strategy.priority.label": "Priority",
62
+ "accounts.strategy.quotaAware.description": "Skip accounts above 85% utilization.",
63
+ "accounts.strategy.quotaAware.label": "Quota-aware",
64
+ "accounts.strategy.roundRobin.description": "Alternate across enabled accounts.",
65
+ "accounts.strategy.roundRobin.label": "Round-robin",
55
66
  "accounts.test": "Test",
56
67
  "accounts.usage.none": "No usage data yet - click Refresh to probe.",
57
68
  "accounts.usage.session": "Session",
@@ -6,13 +6,16 @@
6
6
  "accounts.add.disabledHint": "API-key accounts for this provider are not yet supported.",
7
7
  "accounts.add.label": "Account name",
8
8
  "accounts.add.labelPlaceholder": "e.g. Personal, Work",
9
+ "accounts.add.oauth.cancelled": "Login cancelled.",
9
10
  "accounts.add.oauth.codeFailed": "Failed to submit code.",
10
11
  "accounts.add.oauth.codeHint": "Auto-redirect didn't reach us. Paste the code (or full redirect URL) from the browser.",
11
12
  "accounts.add.oauth.codePlaceholder": "Paste the code or redirect URL",
13
+ "accounts.add.oauth.error": "Login failed.",
12
14
  "accounts.add.oauth.sessionHint": "Session: {{sessionId}}",
13
15
  "accounts.add.oauth.startFailed": "Failed to start login flow.",
14
16
  "accounts.add.oauth.starting": "Starting login flow...",
15
17
  "accounts.add.oauth.submitCode": "Submit code",
18
+ "accounts.add.oauth.timeout": "Login timed out. Try again.",
16
19
  "accounts.add.oauth.waiting": "Waiting for browser... Complete the sign-in there.",
17
20
  "accounts.add.save": "Add account",
18
21
  "accounts.add.signIn": "Sign in with {{provider}}",
@@ -52,6 +55,14 @@
52
55
  "accounts.source.oauth": "OAuth",
53
56
  "accounts.strategy.choose": "Choose strategy",
54
57
  "accounts.strategy.label": "Strategy",
58
+ "accounts.strategy.leastUsed.description": "Prefer the account with the lowest current usage.",
59
+ "accounts.strategy.leastUsed.label": "Least used",
60
+ "accounts.strategy.priority.description": "Always prefer the top healthy account.",
61
+ "accounts.strategy.priority.label": "Priority",
62
+ "accounts.strategy.quotaAware.description": "Skip accounts above 85% utilization.",
63
+ "accounts.strategy.quotaAware.label": "Quota-aware",
64
+ "accounts.strategy.roundRobin.description": "Alternate across enabled accounts.",
65
+ "accounts.strategy.roundRobin.label": "Round-robin",
55
66
  "accounts.test": "Test",
56
67
  "accounts.usage.none": "No usage data yet - click Refresh to probe.",
57
68
  "accounts.usage.session": "Session",
@@ -6,13 +6,16 @@
6
6
  "accounts.add.disabledHint": "API-key accounts for this provider are not yet supported.",
7
7
  "accounts.add.label": "Account name",
8
8
  "accounts.add.labelPlaceholder": "e.g. Personal, Work",
9
+ "accounts.add.oauth.cancelled": "Login cancelled.",
9
10
  "accounts.add.oauth.codeFailed": "Failed to submit code.",
10
11
  "accounts.add.oauth.codeHint": "Auto-redirect didn't reach us. Paste the code (or full redirect URL) from the browser.",
11
12
  "accounts.add.oauth.codePlaceholder": "Paste the code or redirect URL",
13
+ "accounts.add.oauth.error": "Login failed.",
12
14
  "accounts.add.oauth.sessionHint": "Session: {{sessionId}}",
13
15
  "accounts.add.oauth.startFailed": "Failed to start login flow.",
14
16
  "accounts.add.oauth.starting": "Starting login flow...",
15
17
  "accounts.add.oauth.submitCode": "Submit code",
18
+ "accounts.add.oauth.timeout": "Login timed out. Try again.",
16
19
  "accounts.add.oauth.waiting": "Waiting for browser... Complete the sign-in there.",
17
20
  "accounts.add.save": "Add account",
18
21
  "accounts.add.signIn": "Sign in with {{provider}}",
@@ -52,6 +55,14 @@
52
55
  "accounts.source.oauth": "OAuth",
53
56
  "accounts.strategy.choose": "Choose strategy",
54
57
  "accounts.strategy.label": "Strategy",
58
+ "accounts.strategy.leastUsed.description": "Prefer the account with the lowest current usage.",
59
+ "accounts.strategy.leastUsed.label": "Least used",
60
+ "accounts.strategy.priority.description": "Always prefer the top healthy account.",
61
+ "accounts.strategy.priority.label": "Priority",
62
+ "accounts.strategy.quotaAware.description": "Skip accounts above 85% utilization.",
63
+ "accounts.strategy.quotaAware.label": "Quota-aware",
64
+ "accounts.strategy.roundRobin.description": "Alternate across enabled accounts.",
65
+ "accounts.strategy.roundRobin.label": "Round-robin",
55
66
  "accounts.test": "Test",
56
67
  "accounts.usage.none": "No usage data yet - click Refresh to probe.",
57
68
  "accounts.usage.session": "Session",
@@ -6,13 +6,16 @@
6
6
  "accounts.add.disabledHint": "API-key accounts for this provider are not yet supported.",
7
7
  "accounts.add.label": "Account name",
8
8
  "accounts.add.labelPlaceholder": "e.g. Personal, Work",
9
+ "accounts.add.oauth.cancelled": "Login cancelled.",
9
10
  "accounts.add.oauth.codeFailed": "Failed to submit code.",
10
11
  "accounts.add.oauth.codeHint": "Auto-redirect didn't reach us. Paste the code (or full redirect URL) from the browser.",
11
12
  "accounts.add.oauth.codePlaceholder": "Paste the code or redirect URL",
13
+ "accounts.add.oauth.error": "Login failed.",
12
14
  "accounts.add.oauth.sessionHint": "Session: {{sessionId}}",
13
15
  "accounts.add.oauth.startFailed": "Failed to start login flow.",
14
16
  "accounts.add.oauth.starting": "Starting login flow...",
15
17
  "accounts.add.oauth.submitCode": "Submit code",
18
+ "accounts.add.oauth.timeout": "Login timed out. Try again.",
16
19
  "accounts.add.oauth.waiting": "Waiting for browser... Complete the sign-in there.",
17
20
  "accounts.add.save": "Add account",
18
21
  "accounts.add.signIn": "Sign in with {{provider}}",
@@ -52,6 +55,14 @@
52
55
  "accounts.source.oauth": "OAuth",
53
56
  "accounts.strategy.choose": "Choose strategy",
54
57
  "accounts.strategy.label": "Strategy",
58
+ "accounts.strategy.leastUsed.description": "Prefer the account with the lowest current usage.",
59
+ "accounts.strategy.leastUsed.label": "Least used",
60
+ "accounts.strategy.priority.description": "Always prefer the top healthy account.",
61
+ "accounts.strategy.priority.label": "Priority",
62
+ "accounts.strategy.quotaAware.description": "Skip accounts above 85% utilization.",
63
+ "accounts.strategy.quotaAware.label": "Quota-aware",
64
+ "accounts.strategy.roundRobin.description": "Alternate across enabled accounts.",
65
+ "accounts.strategy.roundRobin.label": "Round-robin",
55
66
  "accounts.test": "Test",
56
67
  "accounts.usage.none": "No usage data yet - click Refresh to probe.",
57
68
  "accounts.usage.session": "Session",
@@ -6,13 +6,16 @@
6
6
  "accounts.add.disabledHint": "API-key accounts for this provider are not yet supported.",
7
7
  "accounts.add.label": "Account name",
8
8
  "accounts.add.labelPlaceholder": "e.g. Personal, Work",
9
+ "accounts.add.oauth.cancelled": "Login cancelled.",
9
10
  "accounts.add.oauth.codeFailed": "Failed to submit code.",
10
11
  "accounts.add.oauth.codeHint": "Auto-redirect didn't reach us. Paste the code (or full redirect URL) from the browser.",
11
12
  "accounts.add.oauth.codePlaceholder": "Paste the code or redirect URL",
13
+ "accounts.add.oauth.error": "Login failed.",
12
14
  "accounts.add.oauth.sessionHint": "Session: {{sessionId}}",
13
15
  "accounts.add.oauth.startFailed": "Failed to start login flow.",
14
16
  "accounts.add.oauth.starting": "Starting login flow...",
15
17
  "accounts.add.oauth.submitCode": "Submit code",
18
+ "accounts.add.oauth.timeout": "Login timed out. Try again.",
16
19
  "accounts.add.oauth.waiting": "Waiting for browser... Complete the sign-in there.",
17
20
  "accounts.add.save": "Add account",
18
21
  "accounts.add.signIn": "Sign in with {{provider}}",
@@ -52,6 +55,14 @@
52
55
  "accounts.source.oauth": "OAuth",
53
56
  "accounts.strategy.choose": "Choose strategy",
54
57
  "accounts.strategy.label": "Strategy",
58
+ "accounts.strategy.leastUsed.description": "Prefer the account with the lowest current usage.",
59
+ "accounts.strategy.leastUsed.label": "Least used",
60
+ "accounts.strategy.priority.description": "Always prefer the top healthy account.",
61
+ "accounts.strategy.priority.label": "Priority",
62
+ "accounts.strategy.quotaAware.description": "Skip accounts above 85% utilization.",
63
+ "accounts.strategy.quotaAware.label": "Quota-aware",
64
+ "accounts.strategy.roundRobin.description": "Alternate across enabled accounts.",
65
+ "accounts.strategy.roundRobin.label": "Round-robin",
55
66
  "accounts.test": "Test",
56
67
  "accounts.usage.none": "No usage data yet - click Refresh to probe.",
57
68
  "accounts.usage.session": "Session",
@@ -6,13 +6,16 @@
6
6
  "accounts.add.disabledHint": "API-key accounts for this provider are not yet supported.",
7
7
  "accounts.add.label": "Account name",
8
8
  "accounts.add.labelPlaceholder": "e.g. Personal, Work",
9
+ "accounts.add.oauth.cancelled": "Login cancelled.",
9
10
  "accounts.add.oauth.codeFailed": "Failed to submit code.",
10
11
  "accounts.add.oauth.codeHint": "Auto-redirect didn't reach us. Paste the code (or full redirect URL) from the browser.",
11
12
  "accounts.add.oauth.codePlaceholder": "Paste the code or redirect URL",
13
+ "accounts.add.oauth.error": "Login failed.",
12
14
  "accounts.add.oauth.sessionHint": "Session: {{sessionId}}",
13
15
  "accounts.add.oauth.startFailed": "Failed to start login flow.",
14
16
  "accounts.add.oauth.starting": "Starting login flow...",
15
17
  "accounts.add.oauth.submitCode": "Submit code",
18
+ "accounts.add.oauth.timeout": "Login timed out. Try again.",
16
19
  "accounts.add.oauth.waiting": "Waiting for browser... Complete the sign-in there.",
17
20
  "accounts.add.save": "Add account",
18
21
  "accounts.add.signIn": "Sign in with {{provider}}",
@@ -52,6 +55,14 @@
52
55
  "accounts.source.oauth": "OAuth",
53
56
  "accounts.strategy.choose": "Choose strategy",
54
57
  "accounts.strategy.label": "Strategy",
58
+ "accounts.strategy.leastUsed.description": "Prefer the account with the lowest current usage.",
59
+ "accounts.strategy.leastUsed.label": "Least used",
60
+ "accounts.strategy.priority.description": "Always prefer the top healthy account.",
61
+ "accounts.strategy.priority.label": "Priority",
62
+ "accounts.strategy.quotaAware.description": "Skip accounts above 85% utilization.",
63
+ "accounts.strategy.quotaAware.label": "Quota-aware",
64
+ "accounts.strategy.roundRobin.description": "Alternate across enabled accounts.",
65
+ "accounts.strategy.roundRobin.label": "Round-robin",
55
66
  "accounts.test": "Test",
56
67
  "accounts.usage.none": "No usage data yet - click Refresh to probe.",
57
68
  "accounts.usage.session": "Session",
@@ -6,13 +6,16 @@
6
6
  "accounts.add.disabledHint": "API-key accounts for this provider are not yet supported.",
7
7
  "accounts.add.label": "Account name",
8
8
  "accounts.add.labelPlaceholder": "e.g. Personal, Work",
9
+ "accounts.add.oauth.cancelled": "Login cancelled.",
9
10
  "accounts.add.oauth.codeFailed": "Failed to submit code.",
10
11
  "accounts.add.oauth.codeHint": "Auto-redirect didn't reach us. Paste the code (or full redirect URL) from the browser.",
11
12
  "accounts.add.oauth.codePlaceholder": "Paste the code or redirect URL",
13
+ "accounts.add.oauth.error": "Login failed.",
12
14
  "accounts.add.oauth.sessionHint": "Session: {{sessionId}}",
13
15
  "accounts.add.oauth.startFailed": "Failed to start login flow.",
14
16
  "accounts.add.oauth.starting": "Starting login flow...",
15
17
  "accounts.add.oauth.submitCode": "Submit code",
18
+ "accounts.add.oauth.timeout": "Login timed out. Try again.",
16
19
  "accounts.add.oauth.waiting": "Waiting for browser... Complete the sign-in there.",
17
20
  "accounts.add.save": "Add account",
18
21
  "accounts.add.signIn": "Sign in with {{provider}}",
@@ -52,6 +55,14 @@
52
55
  "accounts.source.oauth": "OAuth",
53
56
  "accounts.strategy.choose": "Choose strategy",
54
57
  "accounts.strategy.label": "Strategy",
58
+ "accounts.strategy.leastUsed.description": "Prefer the account with the lowest current usage.",
59
+ "accounts.strategy.leastUsed.label": "Least used",
60
+ "accounts.strategy.priority.description": "Always prefer the top healthy account.",
61
+ "accounts.strategy.priority.label": "Priority",
62
+ "accounts.strategy.quotaAware.description": "Skip accounts above 85% utilization.",
63
+ "accounts.strategy.quotaAware.label": "Quota-aware",
64
+ "accounts.strategy.roundRobin.description": "Alternate across enabled accounts.",
65
+ "accounts.strategy.roundRobin.label": "Round-robin",
55
66
  "accounts.test": "Test",
56
67
  "accounts.usage.none": "No usage data yet - click Refresh to probe.",
57
68
  "accounts.usage.session": "Session",