@ixo/oracle-runtime-workers 0.2.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 +110 -0
- package/package.json +77 -0
- package/src/attachments/classify.ts +114 -0
- package/src/attachments/content-blocks.ts +51 -0
- package/src/attachments/download.ts +235 -0
- package/src/attachments/extract.ts +241 -0
- package/src/attachments/index.ts +84 -0
- package/src/attachments/magic.ts +133 -0
- package/src/attachments/pipeline.ts +409 -0
- package/src/attachments/retention.ts +205 -0
- package/src/attachments/route.ts +41 -0
- package/src/attachments/sandbox-archive.ts +90 -0
- package/src/attachments/sanitize.ts +35 -0
- package/src/attachments/types.ts +86 -0
- package/src/attachments/view.ts +107 -0
- package/src/core/env.ts +200 -0
- package/src/core/index.ts +513 -0
- package/src/core/llm.ts +842 -0
- package/src/core/main-agent-types.ts +123 -0
- package/src/core/main-agent.ts +399 -0
- package/src/core/manifest.ts +365 -0
- package/src/core/meta-tools.ts +232 -0
- package/src/core/middlewares/byo-history-sanitizer.ts +165 -0
- package/src/core/middlewares/capability-gate.ts +76 -0
- package/src/core/middlewares/index.ts +18 -0
- package/src/core/middlewares/page-context.ts +98 -0
- package/src/core/middlewares/safety-guardrail.ts +117 -0
- package/src/core/middlewares/summarization.ts +99 -0
- package/src/core/middlewares/tool-repetition-guard.ts +125 -0
- package/src/core/middlewares/tool-validation.ts +83 -0
- package/src/core/middlewares/work-status.ts +57 -0
- package/src/core/openrouter-pricing.ts +124 -0
- package/src/core/plugin-loader.ts +192 -0
- package/src/core/plugins/index.ts +6 -0
- package/src/core/plugins/skills/index.ts +8 -0
- package/src/core/plugins/skills/skills-tools.ts +356 -0
- package/src/core/plugins/skills/skills.plugin.ts +113 -0
- package/src/core/plugins/weather/index.ts +8 -0
- package/src/core/plugins/weather/weather-client.ts +153 -0
- package/src/core/plugins/weather/weather-middleware.ts +27 -0
- package/src/core/plugins/weather/weather-sub-agent.ts +65 -0
- package/src/core/plugins/weather/weather-tools.ts +103 -0
- package/src/core/plugins/weather/weather.plugin.ts +227 -0
- package/src/core/prompt-composer.ts +469 -0
- package/src/core/registries.ts +642 -0
- package/src/core/runtime-context.ts +619 -0
- package/src/core/state.ts +108 -0
- package/src/core/sub-agent-fallback.ts +187 -0
- package/src/core/subagent-as-tool.ts +322 -0
- package/src/core/template.ts +245 -0
- package/src/core/utils.ts +246 -0
- package/src/core/wrap-plugin-tool.ts +100 -0
- package/src/do/ambient.ts +266 -0
- package/src/do/contracts.ts +562 -0
- package/src/do/debug-timers.ts +120 -0
- package/src/do/fresh-stub.ts +32 -0
- package/src/do/gateway-retry.ts +73 -0
- package/src/do/secrets-adapter.ts +32 -0
- package/src/do/size-alerts.ts +76 -0
- package/src/do/sse-stream.ts +472 -0
- package/src/do/transcript.ts +192 -0
- package/src/do/turn-body.ts +84 -0
- package/src/do/turn-metadata.ts +146 -0
- package/src/do/ucan-service.ts +339 -0
- package/src/do/user-oracle-do.ts +3035 -0
- package/src/gateway-worker.ts +82 -0
- package/src/index.ts +237 -0
- package/src/llm/byo-adapter.ts +59 -0
- package/src/llm/byo-catalog.ts +520 -0
- package/src/llm/byo-client.ts +280 -0
- package/src/llm/byo-routes.ts +255 -0
- package/src/llm/byo-service.ts +836 -0
- package/src/llm/chatgpt-oauth.ts +430 -0
- package/src/llm/provider-error.ts +283 -0
- package/src/matrix/gateway-do.ts +918 -0
- package/src/matrix/ingest.ts +209 -0
- package/src/matrix/replay-format.ts +34 -0
- package/src/matrix/reply-chain.ts +109 -0
- package/src/matrix/room-state-codec.ts +115 -0
- package/src/matrix/user-homeserver.ts +106 -0
- package/src/matrix/work-status.ts +294 -0
- package/src/memory/session-history-indexer.ts +328 -0
- package/src/owner-store/ixo-vfs-store.ts +570 -0
- package/src/owner-store/matrix-media-store.ts +136 -0
- package/src/owner-store/migrating-store.ts +185 -0
- package/src/owner-store/owner-copy-errors.ts +175 -0
- package/src/owner-store/retry.ts +45 -0
- package/src/owner-store/tus-upload.ts +333 -0
- package/src/owner-store/types.ts +0 -0
- package/src/plugin-api/define-plugin.ts +45 -0
- package/src/plugin-api/index.ts +56 -0
- package/src/plugin-api/oracle-plugin.ts +145 -0
- package/src/plugin-api/tool-helper.ts +82 -0
- package/src/plugin-api/types.ts +816 -0
- package/src/plugin-api/ucan-errors.ts +16 -0
- package/src/plugins/agui/agui-agent.ts +115 -0
- package/src/plugins/agui/agui.plugin.ts +148 -0
- package/src/plugins/agui/index.ts +2 -0
- package/src/plugins/attachments/attachments.plugin.ts +54 -0
- package/src/plugins/attachments/index.ts +6 -0
- package/src/plugins/attachments/view-attachment-tool.ts +91 -0
- package/src/plugins/base64.ts +26 -0
- package/src/plugins/composio/composio-tools.ts +365 -0
- package/src/plugins/composio/composio-ucan.ts +43 -0
- package/src/plugins/composio/composio.plugin.ts +214 -0
- package/src/plugins/composio/index.ts +15 -0
- package/src/plugins/domain-indexer/domain-indexer-agent.ts +57 -0
- package/src/plugins/domain-indexer/domain-indexer-tools.ts +253 -0
- package/src/plugins/domain-indexer/domain-indexer.plugin.ts +100 -0
- package/src/plugins/domain-indexer/index.ts +3 -0
- package/src/plugins/editor/blocknote-bridge.ts +120 -0
- package/src/plugins/editor/content-session.ts +331 -0
- package/src/plugins/editor/content-tools.ts +686 -0
- package/src/plugins/editor/document-model.ts +0 -0
- package/src/plugins/editor/editor-access-denied-tool.ts +58 -0
- package/src/plugins/editor/editor-agent.ts +34 -0
- package/src/plugins/editor/editor-config.ts +64 -0
- package/src/plugins/editor/editor-mx.ts +128 -0
- package/src/plugins/editor/editor.plugin.ts +143 -0
- package/src/plugins/editor/failures.ts +154 -0
- package/src/plugins/editor/index.ts +31 -0
- package/src/plugins/editor/jsdom-shim.ts +55 -0
- package/src/plugins/editor/prompts.ts +59 -0
- package/src/plugins/editor/prop-policy.ts +147 -0
- package/src/plugins/editor/provider.ts +386 -0
- package/src/plugins/editor/room-membership.ts +95 -0
- package/src/plugins/editor/standalone-editor-tool.ts +200 -0
- package/src/plugins/firecrawl/firecrawl-agent.ts +93 -0
- package/src/plugins/firecrawl/firecrawl-tools.ts +220 -0
- package/src/plugins/firecrawl/firecrawl.plugin.ts +105 -0
- package/src/plugins/firecrawl/index.ts +15 -0
- package/src/plugins/flows/action-metadata.ts +3505 -0
- package/src/plugins/flows/actions.ts +190 -0
- package/src/plugins/flows/edit.ts +314 -0
- package/src/plugins/flows/errors.ts +38 -0
- package/src/plugins/flows/explain.ts +43 -0
- package/src/plugins/flows/flow-doc.ts +111 -0
- package/src/plugins/flows/flows.plugin.ts +129 -0
- package/src/plugins/flows/forms.ts +330 -0
- package/src/plugins/flows/index.ts +2 -0
- package/src/plugins/flows/linkage.ts +147 -0
- package/src/plugins/flows/port-types.ts +53 -0
- package/src/plugins/flows/prompts.ts +65 -0
- package/src/plugins/flows/read.ts +392 -0
- package/src/plugins/flows/references.ts +66 -0
- package/src/plugins/flows/test-support.ts +165 -0
- package/src/plugins/flows/tools/authoring.ts +516 -0
- package/src/plugins/flows/tools/discovery.ts +119 -0
- package/src/plugins/flows/tools/forms.ts +142 -0
- package/src/plugins/flows/tools/inspect.ts +165 -0
- package/src/plugins/flows/tools/linkage.ts +117 -0
- package/src/plugins/flows/tools/settings.ts +184 -0
- package/src/plugins/flows/translator.ts +328 -0
- package/src/plugins/flows/types.ts +210 -0
- package/src/plugins/flows/ydoc-helpers.ts +543 -0
- package/src/plugins/index.ts +70 -0
- package/src/plugins/mcp-call-timeout.ts +38 -0
- package/src/plugins/mcp-tool-adapter.ts +87 -0
- package/src/plugins/memory/index.ts +17 -0
- package/src/plugins/memory/memory-tools.ts +463 -0
- package/src/plugins/memory/memory-ucan.ts +51 -0
- package/src/plugins/memory/memory.plugin.ts +122 -0
- package/src/plugins/memory/types.ts +147 -0
- package/src/plugins/portal/action-log.ts +34 -0
- package/src/plugins/portal/index.ts +2 -0
- package/src/plugins/portal/portal.plugin.ts +203 -0
- package/src/plugins/sandbox/index.ts +34 -0
- package/src/plugins/sandbox/sandbox-bridge.ts +295 -0
- package/src/plugins/sandbox/sandbox-mcp.ts +127 -0
- package/src/plugins/sandbox/sandbox-write-blob.ts +103 -0
- package/src/plugins/sandbox/sandbox.plugin.ts +599 -0
- package/src/plugins/tasks/index.ts +17 -0
- package/src/plugins/tasks/manifest.ts +81 -0
- package/src/plugins/tasks/middleware.ts +219 -0
- package/src/plugins/tasks/tasks-tools.ts +414 -0
- package/src/plugins/tasks/tasks.plugin.ts +74 -0
- package/src/plugins/ucan-failure.ts +133 -0
- package/src/plugins/user-preferences/index.ts +22 -0
- package/src/plugins/user-preferences/schema.ts +31 -0
- package/src/plugins/user-preferences/user-preferences-store.ts +172 -0
- package/src/plugins/user-preferences/user-preferences-tool.ts +131 -0
- package/src/plugins/user-preferences/user-preferences.plugin.ts +137 -0
- package/src/plugins/vfs/index.ts +40 -0
- package/src/plugins/vfs/vfs-auth.ts +56 -0
- package/src/plugins/vfs/vfs-client.ts +655 -0
- package/src/plugins/vfs/vfs-content.ts +181 -0
- package/src/plugins/vfs/vfs-errors.ts +163 -0
- package/src/plugins/vfs/vfs-sandbox-tools.ts +272 -0
- package/src/plugins/vfs/vfs-tools.ts +635 -0
- package/src/plugins/vfs/vfs.plugin.ts +201 -0
- package/src/realtime/frontend-call-registry.ts +163 -0
- package/src/realtime/realtime-endpoint.ts +619 -0
- package/src/realtime/session-socket-hub.ts +249 -0
- package/src/realtime/socket-io-codec.ts +187 -0
- package/src/secrets/jwe.ts +74 -0
- package/src/secrets/pin-cipher.ts +43 -0
- package/src/secrets/secrets-service.ts +319 -0
- package/src/shell/app.ts +564 -0
- package/src/shell/auth.ts +290 -0
- package/src/sqlite/blob-codec.ts +100 -0
- package/src/sqlite/blob-compactor.ts +146 -0
- package/src/sqlite/cache-config.ts +45 -0
- package/src/sqlite/database.ts +643 -0
- package/src/sqlite/do-vfs.ts +1485 -0
- package/src/sqlite/index.ts +61 -0
- package/src/sqlite/serialization.ts +370 -0
- package/src/sqlite/sessions-store.ts +311 -0
- package/src/sqlite/sqlite-saver.ts +919 -0
- package/src/sqlite/vacuum-policy.ts +77 -0
- package/src/sqlite/wa-sqlite-loader.ts +123 -0
- package/src/sqlite/wasm.d.ts +10 -0
- package/src/tasks/schedule.ts +174 -0
- package/src/tasks/scheduler.ts +753 -0
- package/src/tasks/spec.ts +125 -0
- package/src/tasks/store.ts +351 -0
|
@@ -0,0 +1,520 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bring-your-own-credential (BYO) provider and model catalog — the Workers
|
|
3
|
+
* port of the Node runtime's `llm/byo-catalog.ts`.
|
|
4
|
+
*
|
|
5
|
+
* When `BYO_LLM_ENABLED` is on, a user can connect their own ChatGPT
|
|
6
|
+
* subscription (OAuth) or per-provider API keys and run their chat turns on
|
|
7
|
+
* their own account instead of the platform's OpenRouter key. This file is
|
|
8
|
+
* the single source of truth for:
|
|
9
|
+
*
|
|
10
|
+
* - which providers are supported and how each authenticates,
|
|
11
|
+
* - the reserved room-secret name each credential is stored under,
|
|
12
|
+
* - the `byo:` model-id namespace and its parsing,
|
|
13
|
+
* - the curated per-provider model list shown in the picker, and
|
|
14
|
+
* - the role→model translation used so sub-agents, vision, guards etc. run
|
|
15
|
+
* on the user's provider too (embedding always stays platform-side).
|
|
16
|
+
*
|
|
17
|
+
* Everything here is pure data + pure functions; credential resolution and
|
|
18
|
+
* client construction live in `byo-service.ts` / `byo-client.ts`. Wire shapes
|
|
19
|
+
* (secret names, `byo:` ids) are identical to the Node runtime, so a user's
|
|
20
|
+
* connected credentials survive the runtime swap.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import {
|
|
24
|
+
TIER_DISPLAY,
|
|
25
|
+
type ModelListItem,
|
|
26
|
+
type ModelTier,
|
|
27
|
+
type ProviderModelRole,
|
|
28
|
+
} from '../core/llm';
|
|
29
|
+
|
|
30
|
+
export type ByoProvider =
|
|
31
|
+
| 'chatgpt'
|
|
32
|
+
| 'openai'
|
|
33
|
+
| 'anthropic'
|
|
34
|
+
| 'gemini'
|
|
35
|
+
| 'deepseek';
|
|
36
|
+
|
|
37
|
+
export const BYO_PROVIDERS: readonly ByoProvider[] = [
|
|
38
|
+
'chatgpt',
|
|
39
|
+
'openai',
|
|
40
|
+
'anthropic',
|
|
41
|
+
'gemini',
|
|
42
|
+
'deepseek',
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
export function isByoProvider(value: string): value is ByoProvider {
|
|
46
|
+
return (BYO_PROVIDERS as readonly string[]).includes(value);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** How a provider's credential is obtained on the connect surface. */
|
|
50
|
+
export type ByoAuthType = 'oauth' | 'api-key';
|
|
51
|
+
|
|
52
|
+
export interface ByoProviderInfo {
|
|
53
|
+
provider: ByoProvider;
|
|
54
|
+
/** Display name for the connect UI. */
|
|
55
|
+
label: string;
|
|
56
|
+
authType: ByoAuthType;
|
|
57
|
+
/** Picker badge for models running on this credential. */
|
|
58
|
+
badge: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export const BYO_PROVIDER_INFO: Record<ByoProvider, ByoProviderInfo> = {
|
|
62
|
+
chatgpt: {
|
|
63
|
+
provider: 'chatgpt',
|
|
64
|
+
label: 'ChatGPT (subscription)',
|
|
65
|
+
authType: 'oauth',
|
|
66
|
+
badge: 'Your subscription',
|
|
67
|
+
},
|
|
68
|
+
openai: {
|
|
69
|
+
provider: 'openai',
|
|
70
|
+
label: 'OpenAI API',
|
|
71
|
+
authType: 'api-key',
|
|
72
|
+
badge: 'Your API key',
|
|
73
|
+
},
|
|
74
|
+
anthropic: {
|
|
75
|
+
provider: 'anthropic',
|
|
76
|
+
label: 'Anthropic API',
|
|
77
|
+
authType: 'api-key',
|
|
78
|
+
badge: 'Your API key',
|
|
79
|
+
},
|
|
80
|
+
gemini: {
|
|
81
|
+
provider: 'gemini',
|
|
82
|
+
label: 'Google Gemini API',
|
|
83
|
+
authType: 'api-key',
|
|
84
|
+
badge: 'Your API key',
|
|
85
|
+
},
|
|
86
|
+
deepseek: {
|
|
87
|
+
provider: 'deepseek',
|
|
88
|
+
label: 'DeepSeek API',
|
|
89
|
+
authType: 'api-key',
|
|
90
|
+
badge: 'Your API key',
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Reserved room-secret names, one per provider. The portal writes API keys
|
|
96
|
+
* under these names via the existing agent-secrets flow (JWE-encrypted to the
|
|
97
|
+
* oracle's on-chain P-256 key); the ChatGPT OAuth blob is written server-side
|
|
98
|
+
* after the token exchange. Stored in the canonical user↔oracle room, so the
|
|
99
|
+
* credential is account-level for this oracle, not per-session.
|
|
100
|
+
*/
|
|
101
|
+
export const BYO_SECRET_NAMES: Record<ByoProvider, string> = {
|
|
102
|
+
chatgpt: 'BYO_LLM_CHATGPT_OAUTH',
|
|
103
|
+
openai: 'BYO_LLM_OPENAI_API_KEY',
|
|
104
|
+
anthropic: 'BYO_LLM_ANTHROPIC_API_KEY',
|
|
105
|
+
gemini: 'BYO_LLM_GEMINI_API_KEY',
|
|
106
|
+
deepseek: 'BYO_LLM_DEEPSEEK_API_KEY',
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const SECRET_NAME_TO_PROVIDER = new Map<string, ByoProvider>(
|
|
110
|
+
(Object.entries(BYO_SECRET_NAMES) as Array<[ByoProvider, string]>).map(
|
|
111
|
+
([provider, name]) => [name, provider],
|
|
112
|
+
),
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
/** Reverse lookup: room-secret name → provider (undefined for non-BYO names). */
|
|
116
|
+
export function providerForSecretName(name: string): ByoProvider | undefined {
|
|
117
|
+
return SECRET_NAME_TO_PROVIDER.get(name);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// ---------------------------------------------------------------------------
|
|
121
|
+
// Credentials
|
|
122
|
+
// ---------------------------------------------------------------------------
|
|
123
|
+
|
|
124
|
+
/** Parsed ChatGPT OAuth blob stored under `BYO_LLM_CHATGPT_OAUTH`. */
|
|
125
|
+
export interface ChatGptOAuthTokens {
|
|
126
|
+
accessToken: string;
|
|
127
|
+
refreshToken: string;
|
|
128
|
+
/** `chatgpt_account_id` claim — sent as a header on backend calls. */
|
|
129
|
+
accountId: string;
|
|
130
|
+
/** Epoch ms when `accessToken` expires. */
|
|
131
|
+
expiresAt: number;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export type ByoCredential =
|
|
135
|
+
| { provider: 'chatgpt'; oauth: ChatGptOAuthTokens }
|
|
136
|
+
| {
|
|
137
|
+
provider: Exclude<ByoProvider, 'chatgpt'>;
|
|
138
|
+
apiKey: string;
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Parse the stored ChatGPT OAuth JSON. Returns `null` on any malformed value
|
|
143
|
+
* so a corrupted secret degrades to "not connected" instead of throwing on
|
|
144
|
+
* the chat hot path.
|
|
145
|
+
*/
|
|
146
|
+
export function parseChatGptOAuthTokens(
|
|
147
|
+
raw: string,
|
|
148
|
+
): ChatGptOAuthTokens | null {
|
|
149
|
+
try {
|
|
150
|
+
const parsed: unknown = JSON.parse(raw);
|
|
151
|
+
if (!parsed || typeof parsed !== 'object') return null;
|
|
152
|
+
const o = parsed as Record<string, unknown>;
|
|
153
|
+
if (
|
|
154
|
+
typeof o.accessToken === 'string' &&
|
|
155
|
+
o.accessToken.length > 0 &&
|
|
156
|
+
typeof o.refreshToken === 'string' &&
|
|
157
|
+
typeof o.accountId === 'string' &&
|
|
158
|
+
typeof o.expiresAt === 'number'
|
|
159
|
+
) {
|
|
160
|
+
return {
|
|
161
|
+
accessToken: o.accessToken,
|
|
162
|
+
refreshToken: o.refreshToken,
|
|
163
|
+
accountId: o.accountId,
|
|
164
|
+
expiresAt: o.expiresAt,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
return null;
|
|
168
|
+
} catch {
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// ---------------------------------------------------------------------------
|
|
174
|
+
// Model-id namespace
|
|
175
|
+
// ---------------------------------------------------------------------------
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* BYO model ids are namespaced `byo:<provider>/<provider-native-id>` so they
|
|
179
|
+
* can never collide with OpenRouter slugs in the platform catalog, and so the
|
|
180
|
+
* allow-list gate can route them to the BYO validation path.
|
|
181
|
+
*/
|
|
182
|
+
export const BYO_MODEL_PREFIX = 'byo:';
|
|
183
|
+
|
|
184
|
+
export function isByoModelId(id: string | undefined | null): id is string {
|
|
185
|
+
return typeof id === 'string' && id.startsWith(BYO_MODEL_PREFIX);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export function toByoModelId(provider: ByoProvider, modelId: string): string {
|
|
189
|
+
return `${BYO_MODEL_PREFIX}${provider}/${modelId}`;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export interface ParsedByoModelId {
|
|
193
|
+
provider: ByoProvider;
|
|
194
|
+
/** The provider-native model id (what goes on the wire). */
|
|
195
|
+
modelId: string;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Parse and validate a `byo:` model id. Returns `null` for unknown providers
|
|
200
|
+
* or models outside the curated per-provider list — this is the BYO
|
|
201
|
+
* counterpart of `isAllowedModel`, so a client can't point a turn at an
|
|
202
|
+
* arbitrary model even on their own key.
|
|
203
|
+
*/
|
|
204
|
+
export function parseByoModelId(
|
|
205
|
+
id: string | undefined | null,
|
|
206
|
+
): ParsedByoModelId | null {
|
|
207
|
+
if (!isByoModelId(id)) return null;
|
|
208
|
+
const rest = id.slice(BYO_MODEL_PREFIX.length);
|
|
209
|
+
const slash = rest.indexOf('/');
|
|
210
|
+
if (slash <= 0) return null;
|
|
211
|
+
const provider = rest.slice(0, slash);
|
|
212
|
+
const modelId = rest.slice(slash + 1);
|
|
213
|
+
if (!isByoProvider(provider) || modelId.length === 0) return null;
|
|
214
|
+
const known = BYO_PROVIDER_MODELS[provider].some((m) => m.id === modelId);
|
|
215
|
+
return known ? { provider, modelId } : null;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// ---------------------------------------------------------------------------
|
|
219
|
+
// Curated per-provider models
|
|
220
|
+
// ---------------------------------------------------------------------------
|
|
221
|
+
|
|
222
|
+
export interface ByoModelEntry {
|
|
223
|
+
/** Provider-native model id (sent on the wire, no `byo:` prefix). */
|
|
224
|
+
id: string;
|
|
225
|
+
label: string;
|
|
226
|
+
tier: ModelTier;
|
|
227
|
+
blurb: string;
|
|
228
|
+
vision: boolean;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* The models a connected user can pick per provider. Kept deliberately small,
|
|
233
|
+
* mirroring the platform catalog's curation stance. Ids are the providers'
|
|
234
|
+
* native API ids.
|
|
235
|
+
*/
|
|
236
|
+
export const BYO_PROVIDER_MODELS: Record<
|
|
237
|
+
ByoProvider,
|
|
238
|
+
readonly ByoModelEntry[]
|
|
239
|
+
> = {
|
|
240
|
+
// Served by the ChatGPT backend on the user's subscription quota. Ids match
|
|
241
|
+
// the backend's live model catalog (the Codex client's models.json).
|
|
242
|
+
chatgpt: [
|
|
243
|
+
{
|
|
244
|
+
id: 'gpt-5.6-luna',
|
|
245
|
+
label: 'GPT-5.6 Luna',
|
|
246
|
+
tier: 'everyday',
|
|
247
|
+
blurb: 'Runs on your ChatGPT plan — fast everyday model.',
|
|
248
|
+
vision: true,
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
id: 'gpt-5.6-terra',
|
|
252
|
+
label: 'GPT-5.6 Terra',
|
|
253
|
+
tier: 'balanced',
|
|
254
|
+
blurb: 'Runs on your ChatGPT plan — the balanced flagship.',
|
|
255
|
+
vision: true,
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
id: 'gpt-5.6-sol',
|
|
259
|
+
label: 'GPT-5.6 Sol',
|
|
260
|
+
tier: 'top',
|
|
261
|
+
blurb: 'Runs on your ChatGPT plan — strongest for complex work.',
|
|
262
|
+
vision: true,
|
|
263
|
+
},
|
|
264
|
+
],
|
|
265
|
+
openai: [
|
|
266
|
+
{
|
|
267
|
+
id: 'gpt-5.6-luna',
|
|
268
|
+
label: 'GPT-5.6 Luna',
|
|
269
|
+
tier: 'everyday',
|
|
270
|
+
blurb: 'Fast and low-cost, billed to your OpenAI account.',
|
|
271
|
+
vision: true,
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
id: 'gpt-5.6-terra',
|
|
275
|
+
label: 'GPT-5.6 Terra',
|
|
276
|
+
tier: 'balanced',
|
|
277
|
+
blurb: 'Smart all-rounder, billed to your OpenAI account.',
|
|
278
|
+
vision: true,
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
id: 'gpt-5.6-sol',
|
|
282
|
+
label: 'GPT-5.6 Sol',
|
|
283
|
+
tier: 'top',
|
|
284
|
+
blurb: "OpenAI's flagship, billed to your OpenAI account.",
|
|
285
|
+
vision: true,
|
|
286
|
+
},
|
|
287
|
+
],
|
|
288
|
+
anthropic: [
|
|
289
|
+
{
|
|
290
|
+
id: 'claude-haiku-4-5',
|
|
291
|
+
label: 'Claude Haiku 4.5',
|
|
292
|
+
tier: 'everyday',
|
|
293
|
+
blurb: 'Fast and inexpensive on your Anthropic key.',
|
|
294
|
+
vision: true,
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
id: 'claude-sonnet-5',
|
|
298
|
+
label: 'Claude Sonnet 5',
|
|
299
|
+
tier: 'balanced',
|
|
300
|
+
blurb: 'Careful writing, reasoning and coding on your Anthropic key.',
|
|
301
|
+
vision: true,
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
id: 'claude-opus-5',
|
|
305
|
+
label: 'Claude Opus 5',
|
|
306
|
+
tier: 'top',
|
|
307
|
+
blurb: "Anthropic's most capable model on your Anthropic key.",
|
|
308
|
+
vision: true,
|
|
309
|
+
},
|
|
310
|
+
],
|
|
311
|
+
gemini: [
|
|
312
|
+
{
|
|
313
|
+
id: 'gemini-3.1-flash-lite',
|
|
314
|
+
label: 'Gemini 3.1 Flash Lite',
|
|
315
|
+
tier: 'everyday',
|
|
316
|
+
blurb: 'Speedy and inexpensive on your Gemini key.',
|
|
317
|
+
vision: true,
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
id: 'gemini-3.6-flash',
|
|
321
|
+
label: 'Gemini 3.6 Flash',
|
|
322
|
+
tier: 'balanced',
|
|
323
|
+
blurb: 'Capable multimodal model on your Gemini key.',
|
|
324
|
+
vision: true,
|
|
325
|
+
},
|
|
326
|
+
{
|
|
327
|
+
id: 'gemini-3.1-pro',
|
|
328
|
+
label: 'Gemini 3.1 Pro',
|
|
329
|
+
tier: 'top',
|
|
330
|
+
blurb: "Google's most capable model on your Gemini key.",
|
|
331
|
+
vision: true,
|
|
332
|
+
},
|
|
333
|
+
],
|
|
334
|
+
deepseek: [
|
|
335
|
+
{
|
|
336
|
+
id: 'deepseek-v4-flash',
|
|
337
|
+
label: 'DeepSeek V4 Flash',
|
|
338
|
+
tier: 'everyday',
|
|
339
|
+
blurb: 'Budget-friendly general chat on your DeepSeek key.',
|
|
340
|
+
vision: false,
|
|
341
|
+
},
|
|
342
|
+
{
|
|
343
|
+
id: 'deepseek-v4-pro',
|
|
344
|
+
label: 'DeepSeek V4 Pro',
|
|
345
|
+
tier: 'balanced',
|
|
346
|
+
blurb: 'Deep step-by-step reasoning on your DeepSeek key.',
|
|
347
|
+
vision: false,
|
|
348
|
+
},
|
|
349
|
+
],
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
/** Default `main` model when a user connects a provider (picker preselect). */
|
|
353
|
+
export const BYO_DEFAULT_MODEL: Record<ByoProvider, string> = {
|
|
354
|
+
chatgpt: 'gpt-5.6-terra',
|
|
355
|
+
openai: 'gpt-5.6-terra',
|
|
356
|
+
anthropic: 'claude-sonnet-5',
|
|
357
|
+
gemini: 'gemini-3.6-flash',
|
|
358
|
+
deepseek: 'deepseek-v4-flash',
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
// ---------------------------------------------------------------------------
|
|
362
|
+
// Role translation
|
|
363
|
+
// ---------------------------------------------------------------------------
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Role→model translation per provider. On a BYO turn every model role the
|
|
367
|
+
* provider can serve runs on the user's credential — sub-agents, vision,
|
|
368
|
+
* guards, routing, titles — so the user's own account absorbs the whole turn.
|
|
369
|
+
* A role absent here falls back to the platform provider (deliberately:
|
|
370
|
+
* `embedding` everywhere — none of these providers is wired for the runtime's
|
|
371
|
+
* embedding path — and `vision` on DeepSeek, whose chat models are text-only).
|
|
372
|
+
*/
|
|
373
|
+
export const BYO_ROLE_MODELS: Record<
|
|
374
|
+
ByoProvider,
|
|
375
|
+
Partial<Record<ProviderModelRole, string>>
|
|
376
|
+
> = {
|
|
377
|
+
chatgpt: {
|
|
378
|
+
skills: 'gpt-5.6-luna',
|
|
379
|
+
subagent: 'gpt-5.6-luna',
|
|
380
|
+
vision: 'gpt-5.6-luna',
|
|
381
|
+
guard: 'gpt-5.6-luna',
|
|
382
|
+
routing: 'gpt-5.6-luna',
|
|
383
|
+
'session-title': 'gpt-5.6-luna',
|
|
384
|
+
custom_low: 'gpt-5.6-luna',
|
|
385
|
+
custom_medium: 'gpt-5.6-terra',
|
|
386
|
+
},
|
|
387
|
+
openai: {
|
|
388
|
+
skills: 'gpt-5.6-luna',
|
|
389
|
+
subagent: 'gpt-5.6-luna',
|
|
390
|
+
vision: 'gpt-5.6-luna',
|
|
391
|
+
guard: 'gpt-5.6-luna',
|
|
392
|
+
routing: 'gpt-5.6-luna',
|
|
393
|
+
'session-title': 'gpt-5.6-luna',
|
|
394
|
+
custom_low: 'gpt-5.6-luna',
|
|
395
|
+
custom_medium: 'gpt-5.6-terra',
|
|
396
|
+
},
|
|
397
|
+
anthropic: {
|
|
398
|
+
skills: 'claude-haiku-4-5',
|
|
399
|
+
subagent: 'claude-haiku-4-5',
|
|
400
|
+
vision: 'claude-haiku-4-5',
|
|
401
|
+
guard: 'claude-haiku-4-5',
|
|
402
|
+
routing: 'claude-haiku-4-5',
|
|
403
|
+
'session-title': 'claude-haiku-4-5',
|
|
404
|
+
custom_low: 'claude-haiku-4-5',
|
|
405
|
+
custom_medium: 'claude-sonnet-5',
|
|
406
|
+
},
|
|
407
|
+
gemini: {
|
|
408
|
+
skills: 'gemini-3.1-flash-lite',
|
|
409
|
+
subagent: 'gemini-3.1-flash-lite',
|
|
410
|
+
vision: 'gemini-3.1-flash-lite',
|
|
411
|
+
guard: 'gemini-3.1-flash-lite',
|
|
412
|
+
routing: 'gemini-3.1-flash-lite',
|
|
413
|
+
'session-title': 'gemini-3.1-flash-lite',
|
|
414
|
+
custom_low: 'gemini-3.1-flash-lite',
|
|
415
|
+
custom_medium: 'gemini-3.6-flash',
|
|
416
|
+
},
|
|
417
|
+
deepseek: {
|
|
418
|
+
skills: 'deepseek-v4-flash',
|
|
419
|
+
subagent: 'deepseek-v4-flash',
|
|
420
|
+
guard: 'deepseek-v4-flash',
|
|
421
|
+
routing: 'deepseek-v4-flash',
|
|
422
|
+
'session-title': 'deepseek-v4-flash',
|
|
423
|
+
custom_low: 'deepseek-v4-flash',
|
|
424
|
+
custom_medium: 'deepseek-v4-pro',
|
|
425
|
+
},
|
|
426
|
+
};
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* The full role vocabulary the provider maps cover. A KNOWN role absent from
|
|
430
|
+
* a provider's map is deliberately unserved (embedding everywhere, vision on
|
|
431
|
+
* DeepSeek) and must fall back to the platform adapter — while an UNKNOWN
|
|
432
|
+
* plugin-custom role falls back to the provider's cheap `subagent` model,
|
|
433
|
+
* mirroring the platform adapter's behaviour.
|
|
434
|
+
*/
|
|
435
|
+
const KNOWN_MODEL_ROLES: ReadonlySet<string> = new Set([
|
|
436
|
+
'main',
|
|
437
|
+
'skills',
|
|
438
|
+
'subagent',
|
|
439
|
+
'vision',
|
|
440
|
+
'guard',
|
|
441
|
+
'routing',
|
|
442
|
+
'session-title',
|
|
443
|
+
'embedding',
|
|
444
|
+
'custom_low',
|
|
445
|
+
'custom_medium',
|
|
446
|
+
]);
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Resolve the provider-native model id for a role on a BYO turn.
|
|
450
|
+
* `main` uses the turn's selected model; other roles use the translation
|
|
451
|
+
* table. `null` → the role is not served by this provider and the caller
|
|
452
|
+
* must fall back to the platform adapter.
|
|
453
|
+
*/
|
|
454
|
+
export function byoModelForRole(
|
|
455
|
+
provider: ByoProvider,
|
|
456
|
+
role: ProviderModelRole | string,
|
|
457
|
+
mainModelId: string,
|
|
458
|
+
): string | null {
|
|
459
|
+
if (role === 'main') return mainModelId;
|
|
460
|
+
const map = BYO_ROLE_MODELS[provider];
|
|
461
|
+
const direct = map[role as ProviderModelRole];
|
|
462
|
+
if (direct) return direct;
|
|
463
|
+
if (KNOWN_MODEL_ROLES.has(role)) return null;
|
|
464
|
+
return map.subagent ?? null;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// ---------------------------------------------------------------------------
|
|
468
|
+
// Listing
|
|
469
|
+
// ---------------------------------------------------------------------------
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* Build picker entries for a user's connected providers, in the platform
|
|
473
|
+
* `ModelListItem` shape so the client SDK renders them without changes. Tier
|
|
474
|
+
* badge and cost label mirror the platform catalog so BYO rows read the same
|
|
475
|
+
* (the Workers catalog carries no pricing — the user pays their provider
|
|
476
|
+
* directly); the client marks BYO-ness itself from the `byo:` id namespace.
|
|
477
|
+
*/
|
|
478
|
+
export function buildByoModelListing(
|
|
479
|
+
connected: readonly ByoProvider[],
|
|
480
|
+
): ModelListItem[] {
|
|
481
|
+
const items: ModelListItem[] = [];
|
|
482
|
+
for (const provider of connected) {
|
|
483
|
+
for (const entry of BYO_PROVIDER_MODELS[provider]) {
|
|
484
|
+
items.push({
|
|
485
|
+
id: toByoModelId(provider, entry.id),
|
|
486
|
+
label: entry.label,
|
|
487
|
+
family: byoFamily(provider),
|
|
488
|
+
tier: entry.tier,
|
|
489
|
+
costLabel: TIER_DISPLAY[entry.tier].costLabel,
|
|
490
|
+
badge: TIER_DISPLAY[entry.tier].badge,
|
|
491
|
+
blurb: entry.blurb,
|
|
492
|
+
vision: entry.vision,
|
|
493
|
+
// The user pays their own provider; the platform charges nothing per
|
|
494
|
+
// token for a BYO model, so the listed price is zero.
|
|
495
|
+
pricing: {
|
|
496
|
+
inputPerMillion: 0,
|
|
497
|
+
outputPerMillion: 0,
|
|
498
|
+
currency: 'USD',
|
|
499
|
+
unit: 'per_million_tokens',
|
|
500
|
+
},
|
|
501
|
+
isDefault: false,
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
return items;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
function byoFamily(provider: ByoProvider): ModelListItem['family'] {
|
|
509
|
+
switch (provider) {
|
|
510
|
+
case 'chatgpt':
|
|
511
|
+
case 'openai':
|
|
512
|
+
return 'openai';
|
|
513
|
+
case 'anthropic':
|
|
514
|
+
return 'anthropic';
|
|
515
|
+
case 'gemini':
|
|
516
|
+
return 'google';
|
|
517
|
+
case 'deepseek':
|
|
518
|
+
return 'deepseek';
|
|
519
|
+
}
|
|
520
|
+
}
|