@ixo/oracle-runtime-workers 0.12.0 → 0.15.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 +32 -10
- package/package.json +14 -9
- package/src/artifacts/config.ts +158 -0
- package/src/artifacts/crypto.ts +94 -0
- package/src/artifacts/routes.ts +73 -0
- package/src/artifacts/store.ts +238 -0
- package/src/artifacts/sweep.ts +200 -0
- package/src/artifacts/tool.ts +103 -0
- package/src/artifacts/viewer-page.ts +614 -0
- package/src/attachments/download.ts +168 -45
- package/src/attachments/index.ts +9 -0
- package/src/attachments/magic.ts +33 -0
- package/src/attachments/pipeline.ts +105 -49
- package/src/attachments/sandbox-archive.ts +9 -8
- package/src/attachments/view-cache.ts +142 -0
- package/src/attachments/view.ts +53 -12
- package/src/channels/auth.ts +267 -0
- package/src/channels/contract.ts +137 -0
- package/src/channels/turns.ts +270 -0
- package/src/core/budgeted-llm.ts +223 -0
- package/src/core/capability-router.ts +273 -0
- package/src/core/context-budget.ts +97 -10
- package/src/core/env.ts +111 -0
- package/src/core/execution-profile.ts +17 -0
- package/src/core/index.ts +267 -9
- package/src/core/llm.ts +30 -9
- package/src/core/main-agent-types.ts +41 -1
- package/src/core/main-agent.ts +367 -65
- package/src/core/manifest.ts +34 -0
- package/src/core/meta-tools.ts +128 -18
- package/src/core/middlewares/capability-gate.ts +130 -12
- package/src/core/middlewares/context-guard.ts +55 -24
- package/src/core/middlewares/index.ts +20 -2
- package/src/core/middlewares/page-context.ts +46 -2
- package/src/core/middlewares/return-direct-first.ts +47 -0
- package/src/core/middlewares/summarization.ts +152 -10
- package/src/core/middlewares/tool-execution.ts +323 -0
- package/src/core/middlewares/tool-repetition-guard.ts +202 -35
- package/src/core/middlewares/turn-boundary.ts +162 -0
- package/src/core/openrouter-pricing.ts +155 -22
- package/src/core/prompt-composer.ts +112 -7
- package/src/core/registries.ts +253 -48
- package/src/core/request-admission.ts +159 -0
- package/src/core/runtime-context.ts +107 -8
- package/src/core/state.ts +1 -1
- package/src/core/sub-agent-fallback.ts +21 -2
- package/src/core/subagent-as-tool.ts +78 -43
- package/src/core/tool-access.ts +189 -0
- package/src/core/tool-scheduler.ts +125 -0
- package/src/core/turn-budget.ts +205 -0
- package/src/core/turn-time-note.ts +43 -0
- package/src/core/user-kv.ts +446 -0
- package/src/core/wrap-plugin-tool.ts +20 -0
- package/src/delivery/plan.ts +278 -0
- package/src/delivery/profile.ts +161 -0
- package/src/delivery/prompt.ts +27 -0
- package/src/delivery/schema.ts +61 -0
- package/src/delivery/shaper.ts +389 -0
- package/src/delivery/types.ts +84 -0
- package/src/do/ambient.ts +51 -15
- package/src/do/contracts.ts +198 -6
- package/src/do/idle-eviction.ts +38 -0
- package/src/do/matrix-turn-ledger.ts +24 -5
- package/src/do/result-store-test-do.ts +137 -3
- package/src/do/result-store.ts +239 -58
- package/src/do/room-mirror.ts +29 -0
- package/src/do/run-buffer.ts +64 -9
- package/src/do/run-coordinator.ts +451 -76
- package/src/do/run-request.ts +2 -0
- package/src/do/run-store-test-do.ts +301 -1
- package/src/do/run-store.ts +385 -43
- package/src/do/secrets-adapter.ts +22 -3
- package/src/do/sse-stream.ts +60 -9
- package/src/do/transcript.ts +48 -5
- package/src/do/turn-body.ts +105 -5
- package/src/do/ucan-service.ts +204 -34
- package/src/do/user-oracle-do.ts +1883 -264
- package/src/feedback/config.ts +127 -0
- package/src/feedback/contract.ts +148 -0
- package/src/feedback/linear-sink.ts +285 -0
- package/src/feedback/privacy.ts +150 -0
- package/src/feedback/reservation.ts +298 -0
- package/src/feedback/submit.ts +247 -0
- package/src/index.ts +138 -6
- package/src/llm/byo-adapter.ts +27 -6
- package/src/llm/byo-catalog.ts +13 -0
- package/src/llm/byo-model-fallback.ts +248 -0
- package/src/llm/byo-service.ts +45 -8
- package/src/llm/provider-error.ts +139 -2
- package/src/matrix/gateway-do.ts +523 -111
- package/src/matrix/group-chat.ts +66 -23
- package/src/matrix/inbox-store.ts +16 -1
- package/src/matrix/ingest.ts +79 -14
- package/src/matrix/media-deadline.ts +130 -0
- package/src/matrix/replay-format.ts +5 -3
- package/src/matrix/reply-parts.ts +92 -0
- package/src/matrix/room-alias.ts +96 -0
- package/src/matrix/user-homeserver.ts +166 -7
- package/src/owner-store/ixo-vfs-store.ts +170 -43
- package/src/owner-store/migrating-store.ts +31 -15
- package/src/owner-store/types.ts +18 -3
- package/src/plugin-api/index.ts +43 -2
- package/src/plugin-api/oracle-plugin.ts +35 -0
- package/src/plugin-api/request-admission.ts +41 -0
- package/src/plugin-api/tool-helper.ts +25 -1
- package/src/plugin-api/tool-plane.ts +92 -0
- package/src/plugin-api/types.ts +194 -6
- package/src/plugins/agui/agui.plugin.ts +31 -16
- package/src/plugins/composio/composio-tools.ts +57 -6
- package/src/plugins/composio/composio-ucan.ts +3 -2
- package/src/plugins/composio/composio.plugin.ts +2 -0
- package/src/plugins/delegated-capabilities.ts +25 -0
- package/src/plugins/editor/content-session.ts +173 -83
- package/src/plugins/editor/content-tools.ts +76 -17
- package/src/plugins/editor/editor-mx.ts +28 -9
- package/src/plugins/editor/editor.plugin.ts +3 -1
- package/src/plugins/editor/failures.ts +28 -7
- package/src/plugins/editor/prompts.ts +2 -1
- package/src/plugins/editor/provider.ts +553 -40
- package/src/plugins/editor/standalone-editor-tool.ts +61 -18
- package/src/plugins/firecrawl/firecrawl-tools.ts +13 -5
- package/src/plugins/flows/action-metadata.ts +111 -11
- package/src/plugins/flows/actions.ts +18 -0
- package/src/plugins/flows/decision-context.ts +31 -0
- package/src/plugins/flows/edit.ts +223 -52
- package/src/plugins/flows/errors.ts +22 -1
- package/src/plugins/flows/flow-doc.ts +92 -9
- package/src/plugins/flows/flows.plugin.ts +11 -5
- package/src/plugins/flows/forms.ts +34 -0
- package/src/plugins/flows/index.ts +2 -0
- package/src/plugins/flows/input-policy.ts +140 -0
- package/src/plugins/flows/prompts.ts +14 -11
- package/src/plugins/flows/read.ts +49 -9
- package/src/plugins/flows/ref-syntax.ts +129 -0
- package/src/plugins/flows/test-homeserver.ts +245 -0
- package/src/plugins/flows/tools/authoring.ts +216 -112
- package/src/plugins/flows/tools/discovery.ts +4 -1
- package/src/plugins/flows/tools/forms.ts +21 -2
- package/src/plugins/flows/tools/settings.ts +34 -13
- package/src/plugins/flows/translator.ts +13 -10
- package/src/plugins/flows/types.ts +167 -42
- package/src/plugins/index.ts +11 -1
- package/src/plugins/ixo-transaction/chain-ids.ts +43 -0
- package/src/plugins/ixo-transaction/index.ts +2 -0
- package/src/plugins/ixo-transaction/ixo-transaction.plugin.ts +119 -0
- package/src/plugins/ixo-transaction/receipts.ts +99 -0
- package/src/plugins/ixo-transaction/tools.ts +329 -0
- package/src/plugins/ixo-transaction/wallet-signing.ts +202 -0
- package/src/plugins/mcp-tool-adapter.ts +23 -2
- package/src/plugins/memory/memory-tools.ts +7 -2
- package/src/plugins/memory/memory-ucan.ts +3 -2
- package/src/plugins/memory/memory.plugin.ts +2 -0
- package/src/plugins/pod-creator/blueprint-store.ts +163 -0
- package/src/plugins/pod-creator/blueprint-types.ts +61 -0
- package/src/plugins/pod-creator/bounded-map.ts +87 -0
- package/src/plugins/pod-creator/capsule-content-client.ts +285 -0
- package/src/plugins/pod-creator/chain-gateway.ts +110 -0
- package/src/plugins/pod-creator/config.ts +85 -0
- package/src/plugins/pod-creator/create-session-store.ts +180 -0
- package/src/plugins/pod-creator/create-tools.ts +538 -0
- package/src/plugins/pod-creator/design-pod-roles.ts +111 -0
- package/src/plugins/pod-creator/index.ts +65 -0
- package/src/plugins/pod-creator/orchestration-tools.ts +172 -0
- package/src/plugins/pod-creator/pod-creator.plugin.ts +190 -0
- package/src/plugins/pod-creator/stage.ts +201 -0
- package/src/plugins/pod-creator/sub-agents.ts +189 -0
- package/src/plugins/portal/action-log.ts +21 -1
- package/src/plugins/portal/declared-tools.ts +90 -0
- package/src/plugins/portal/portal.plugin.ts +42 -16
- package/src/plugins/sandbox/sandbox-bridge.ts +79 -10
- package/src/plugins/sandbox/sandbox-mcp.ts +3 -2
- package/src/plugins/sandbox/sandbox-write-blob.ts +11 -1
- package/src/plugins/sandbox/sandbox.plugin.ts +8 -16
- package/src/plugins/tasks/manifest.ts +1 -1
- package/src/plugins/tasks/middleware.ts +20 -7
- package/src/plugins/tasks/tasks-tools.ts +30 -3
- package/src/plugins/tasks/tasks.plugin.ts +64 -11
- package/src/plugins/user-preferences/schema.ts +12 -1
- package/src/plugins/user-preferences/user-preferences-store.ts +38 -13
- package/src/plugins/user-preferences/user-preferences-tool.ts +3 -2
- package/src/plugins/vfs/vfs-client.ts +250 -71
- package/src/plugins/vfs/vfs-content.ts +30 -11
- package/src/plugins/vfs/vfs-errors.ts +22 -0
- package/src/plugins/vfs/vfs-sandbox-tools.ts +12 -5
- package/src/plugins/vfs/vfs-tools.ts +60 -20
- package/src/realtime/frontend-call-registry.ts +228 -81
- package/src/realtime/realtime-endpoint.ts +215 -44
- package/src/realtime/session-socket-hub.ts +47 -2
- package/src/secrets/secrets-service.ts +9 -3
- package/src/shell/app.ts +529 -82
- package/src/shell/auth.ts +131 -37
- package/src/shell/turn-body-cap.ts +11 -13
- package/src/sqlite/blob-compactor.ts +61 -28
- package/src/sqlite/database.ts +156 -90
- package/src/sqlite/do-vfs.ts +416 -129
- package/src/sqlite/page-tier.ts +133 -40
- package/src/sqlite/serialization.ts +40 -0
- package/src/sqlite/sessions-store.ts +10 -2
- package/src/sqlite/sqlite-saver.ts +139 -19
- package/src/sqlite/user-kv-store.ts +317 -0
- package/src/tasks/schedule.ts +201 -12
- package/src/tasks/scheduler.ts +586 -98
- package/src/tasks/spec.ts +67 -3
- package/src/tasks/store.ts +292 -27
- package/src/tasks/topic-deliverables.ts +70 -0
package/src/index.ts
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
import {
|
|
17
17
|
createRuntimeCore,
|
|
18
18
|
listModels,
|
|
19
|
+
type DecisionAdapter,
|
|
19
20
|
type RuntimeCoreOptions,
|
|
20
21
|
type RuntimeCore,
|
|
21
22
|
} from './core';
|
|
@@ -27,8 +28,17 @@ import {
|
|
|
27
28
|
type OracleWorkerHooks,
|
|
28
29
|
} from './do/user-oracle-do';
|
|
29
30
|
import { MatrixGatewayDO } from './matrix/gateway-do';
|
|
30
|
-
import {
|
|
31
|
+
import {
|
|
32
|
+
createShell,
|
|
33
|
+
gateway,
|
|
34
|
+
type ListModelsOptions,
|
|
35
|
+
type PluginRoute,
|
|
36
|
+
} from './shell/app';
|
|
31
37
|
import type { RouteExclusion } from './shell/auth';
|
|
38
|
+
import { artifactSweepIntervalMs } from './artifacts/config';
|
|
39
|
+
import { sweepExpiredArtifacts } from './artifacts/sweep';
|
|
40
|
+
import { feedbackConfigFromEnv } from './feedback/config';
|
|
41
|
+
import { LinearFeedbackSink } from './feedback/linear-sink';
|
|
32
42
|
|
|
33
43
|
export * from './plugin-api';
|
|
34
44
|
export * from './plugins';
|
|
@@ -46,7 +56,7 @@ export {
|
|
|
46
56
|
createUserOracleDO,
|
|
47
57
|
type OracleWorkerHooks,
|
|
48
58
|
} from './do/user-oracle-do';
|
|
49
|
-
export { createShell } from './shell/app';
|
|
59
|
+
export { createShell, type ListModelsOptions } from './shell/app';
|
|
50
60
|
export { authenticate } from './shell/auth';
|
|
51
61
|
export {
|
|
52
62
|
createRuntimeCore,
|
|
@@ -62,6 +72,64 @@ export {
|
|
|
62
72
|
type RuntimeCore,
|
|
63
73
|
type RuntimeCoreOptions,
|
|
64
74
|
} from './core';
|
|
75
|
+
// --- bounded semantic decisions --------------------------------------------
|
|
76
|
+
// `defineDecision` and the plugin-facing types come with `./plugin-api` above.
|
|
77
|
+
export {
|
|
78
|
+
AmbiguousDecisionProviderError,
|
|
79
|
+
CloudflareJevDecisionAdapter,
|
|
80
|
+
DECISION_PROVIDERS,
|
|
81
|
+
DecisionNotApplicableError,
|
|
82
|
+
DecisionProviderNotFoundError,
|
|
83
|
+
DecisionProviderRegistry,
|
|
84
|
+
DecisionProviderRouter,
|
|
85
|
+
DecisionProviderUnavailableError,
|
|
86
|
+
DecisionRegistry,
|
|
87
|
+
DecisionRuntime,
|
|
88
|
+
HOST_DECISION_PROVIDER_ID,
|
|
89
|
+
JevDecisionError,
|
|
90
|
+
OpenRouterJevDecisionAdapter,
|
|
91
|
+
StaleDecisionSubjectError,
|
|
92
|
+
UNAVAILABLE_DECISION_EVALUATOR,
|
|
93
|
+
WorkersAiJevDecisionAdapter,
|
|
94
|
+
assertFinalDecisionSubjectUnchanged,
|
|
95
|
+
canonicalizeFinalDecisionSubject,
|
|
96
|
+
createDecisionAuthorityReceipt,
|
|
97
|
+
createDecisionExecutionReceipt,
|
|
98
|
+
decisionProviderEnvShape,
|
|
99
|
+
digestFinalDecisionSubject,
|
|
100
|
+
measureDecisionQuestionIsolation,
|
|
101
|
+
resolveDecisionAdapter,
|
|
102
|
+
type CloudflareJevAdapterOptions,
|
|
103
|
+
type DecisionApplicability,
|
|
104
|
+
type DecisionAuthorityReceipt,
|
|
105
|
+
type DecisionCalibrationProvenance,
|
|
106
|
+
type DecisionEvaluator,
|
|
107
|
+
type DecisionExecution,
|
|
108
|
+
type DecisionExecutionReceipt,
|
|
109
|
+
type DecisionJudgmentMethod,
|
|
110
|
+
type DecisionJudgmentProvenance,
|
|
111
|
+
type DecisionLookup,
|
|
112
|
+
type DecisionProviderConfigIssue,
|
|
113
|
+
type DecisionProviderName,
|
|
114
|
+
type DecisionProviderPolicy,
|
|
115
|
+
type DecisionProviderProvenance,
|
|
116
|
+
type DecisionProviderRegistration,
|
|
117
|
+
type DecisionProviderResolution,
|
|
118
|
+
type DecisionProviderSelection,
|
|
119
|
+
type DecisionQuestionIsolationObservation,
|
|
120
|
+
type DecisionQuestionIsolationReport,
|
|
121
|
+
type DecisionRuntimeLogger,
|
|
122
|
+
type FinalDecisionSubject,
|
|
123
|
+
type FinalDecisionSubjectBinding,
|
|
124
|
+
type FinalDecisionSubjectValue,
|
|
125
|
+
type JevProviderName,
|
|
126
|
+
type OpenRouterJevAdapterOptions,
|
|
127
|
+
type RegisteredDecision,
|
|
128
|
+
type ResolveDecisionAdapterOptions,
|
|
129
|
+
type ResolveDecisionAdapterResult,
|
|
130
|
+
type WorkersAiBinding,
|
|
131
|
+
type WorkersAiJevAdapterOptions,
|
|
132
|
+
} from './core';
|
|
65
133
|
export {
|
|
66
134
|
NEBIUS_BASE_URL,
|
|
67
135
|
NEBIUS_MODEL_MAP,
|
|
@@ -74,6 +142,12 @@ export {
|
|
|
74
142
|
type ResolveLangsmithTracingArgs,
|
|
75
143
|
} from './core/llm';
|
|
76
144
|
|
|
145
|
+
// --- task adapters -----------------------------------------------------------
|
|
146
|
+
export {
|
|
147
|
+
agentWakeFromTaskRecord,
|
|
148
|
+
portableWorkFromTaskRecord,
|
|
149
|
+
} from './tasks/spec';
|
|
150
|
+
|
|
77
151
|
// --- per-room JWE secrets ---------------------------------------------------
|
|
78
152
|
export { decryptJwe, encryptJwe, parseJwk, type JWK } from './secrets/jwe';
|
|
79
153
|
export { decryptWithPin } from './secrets/pin-cipher';
|
|
@@ -107,9 +181,16 @@ export {
|
|
|
107
181
|
type WorkersByoServiceOptions,
|
|
108
182
|
} from './llm/byo-service';
|
|
109
183
|
export { handleByoRequest } from './llm/byo-routes';
|
|
184
|
+
export {
|
|
185
|
+
ByoModelFallbackChatModel,
|
|
186
|
+
ByoModelFallbackState,
|
|
187
|
+
type ByoModelFallbackFields,
|
|
188
|
+
} from './llm/byo-model-fallback';
|
|
110
189
|
export {
|
|
111
190
|
BYO_FALLBACK_KIND,
|
|
112
191
|
buildByoFallbackNotice,
|
|
192
|
+
isModelUnavailableError,
|
|
193
|
+
type ByoFallbackNoticeOptions,
|
|
113
194
|
type ByoFallbackNoticePayload,
|
|
114
195
|
type ByoFallbackReason,
|
|
115
196
|
} from './llm/provider-error';
|
|
@@ -145,15 +226,39 @@ export interface CreateOracleWorkerOptions {
|
|
|
145
226
|
* the merged manifest is validated like an authored one.
|
|
146
227
|
*/
|
|
147
228
|
manifestOverrides?: RuntimeCoreOptions['manifestOverrides'];
|
|
229
|
+
/**
|
|
230
|
+
* Single host-supplied Decision adapter, registered as provider `host`.
|
|
231
|
+
* Wins over the `DECISION_PROVIDER` env configuration; without any
|
|
232
|
+
* provider, every `ctx.decisions` call rejects as unavailable. Mutually
|
|
233
|
+
* exclusive with `decisionProviders`.
|
|
234
|
+
*/
|
|
235
|
+
decisionAdapter?: DecisionAdapter;
|
|
236
|
+
/**
|
|
237
|
+
* Host-configured Decision providers, registered beside the env-selected
|
|
238
|
+
* one (id = the `DECISION_PROVIDER` value). Adapters are built once per
|
|
239
|
+
* oracle, outside any `env`; a provider that needs a Worker binding comes
|
|
240
|
+
* from `DECISION_PROVIDER`.
|
|
241
|
+
*/
|
|
242
|
+
decisionProviders?: RuntimeCoreOptions['decisionProviders'];
|
|
243
|
+
/**
|
|
244
|
+
* Default and exact per-Decision routes over the registered providers. The
|
|
245
|
+
* env-selected provider is the default unless this names another.
|
|
246
|
+
*/
|
|
247
|
+
decisionProviderPolicy?: RuntimeCoreOptions['decisionProviderPolicy'];
|
|
148
248
|
/** Extra host routes mounted on the shell. */
|
|
149
249
|
routes?: PluginRoute[];
|
|
150
250
|
/** Host routes exempt from UCAN auth. */
|
|
151
251
|
authExcludedRoutes?: RouteExclusion[];
|
|
152
252
|
/**
|
|
153
253
|
* Overrides for the model catalog exposed on `GET /models`. Default: the
|
|
154
|
-
* curated catalog with live OpenRouter prices (`listModels`).
|
|
254
|
+
* curated catalog with live OpenRouter prices (`listModels`). The second
|
|
255
|
+
* argument carries the request's `waitUntil` (for background work such as
|
|
256
|
+
* a price refresh); an override may ignore it.
|
|
155
257
|
*/
|
|
156
|
-
listModels?: (
|
|
258
|
+
listModels?: (
|
|
259
|
+
env: OracleWorkerEnv,
|
|
260
|
+
options?: ListModelsOptions,
|
|
261
|
+
) => unknown | Promise<unknown>;
|
|
157
262
|
/**
|
|
158
263
|
* Host hooks for the per-turn agent build — the Node runtime's
|
|
159
264
|
* `opts.hooks.getRoomTitle` / `safetyModel`, resolved per turn against the
|
|
@@ -195,6 +300,9 @@ export function createOracleWorker(
|
|
|
195
300
|
features: opts.features,
|
|
196
301
|
manifestOverrides: opts.manifestOverrides,
|
|
197
302
|
env,
|
|
303
|
+
decisionAdapter: opts.decisionAdapter,
|
|
304
|
+
decisionProviders: opts.decisionProviders,
|
|
305
|
+
decisionProviderPolicy: opts.decisionProviderPolicy,
|
|
198
306
|
logger: console,
|
|
199
307
|
});
|
|
200
308
|
cores.set(env, core);
|
|
@@ -207,17 +315,23 @@ export function createOracleWorker(
|
|
|
207
315
|
let app = apps.get(env);
|
|
208
316
|
if (!app) {
|
|
209
317
|
const core = coreFor(env);
|
|
318
|
+
// Validated with the core: half-configured feedback fails the boot.
|
|
319
|
+
const feedback = feedbackConfigFromEnv(env);
|
|
210
320
|
app = createShell({
|
|
211
321
|
routes: [...core.pluginRoutes, ...(opts.routes ?? [])],
|
|
212
322
|
authExcludedRoutes: [
|
|
213
323
|
...core.authExcludedRoutes,
|
|
214
324
|
...(opts.authExcludedRoutes ?? []),
|
|
215
325
|
],
|
|
216
|
-
listModels: opts.listModels ?? ((e) => listModels(e)),
|
|
326
|
+
listModels: opts.listModels ?? ((e, options) => listModels(e, options)),
|
|
217
327
|
banner: {
|
|
218
328
|
name: opts.config.name,
|
|
219
329
|
description: opts.config.description,
|
|
220
330
|
},
|
|
331
|
+
feedback: feedback && {
|
|
332
|
+
config: feedback,
|
|
333
|
+
sink: new LinearFeedbackSink(feedback.linear),
|
|
334
|
+
},
|
|
221
335
|
});
|
|
222
336
|
apps.set(env, app);
|
|
223
337
|
}
|
|
@@ -246,7 +360,25 @@ export function createOracleWorker(
|
|
|
246
360
|
scheduled: async (_event, env) => {
|
|
247
361
|
// Cron safety net: if the gateway's keep-alive loop ever died, this
|
|
248
362
|
// restarts it. Declare a `triggers.crons` entry (e.g. every 5 minutes).
|
|
249
|
-
|
|
363
|
+
// A failed keep-alive must not stop the sweep, and is rethrown after it
|
|
364
|
+
// so the cron invocation still reports the failure.
|
|
365
|
+
let keepAlive: { error: unknown } | undefined;
|
|
366
|
+
try {
|
|
367
|
+
await gateway(env).ensureStarted();
|
|
368
|
+
} catch (error) {
|
|
369
|
+
keepAlive = { error };
|
|
370
|
+
}
|
|
371
|
+
// The sweep throttles itself to one R2 read per tick between sweeps and
|
|
372
|
+
// contains its own errors (it logs them and never throws).
|
|
373
|
+
if (env.ARTIFACT_BUCKET)
|
|
374
|
+
await sweepExpiredArtifacts({
|
|
375
|
+
bucket: env.ARTIFACT_BUCKET,
|
|
376
|
+
intervalMs: artifactSweepIntervalMs(env, (message) =>
|
|
377
|
+
console.warn(message),
|
|
378
|
+
),
|
|
379
|
+
log: console,
|
|
380
|
+
});
|
|
381
|
+
if (keepAlive) throw keepAlive.error;
|
|
250
382
|
},
|
|
251
383
|
};
|
|
252
384
|
}
|
package/src/llm/byo-adapter.ts
CHANGED
|
@@ -11,13 +11,23 @@
|
|
|
11
11
|
*
|
|
12
12
|
* The credential is captured in this closure only; it never appears on the
|
|
13
13
|
* request context, graph state, or trace metadata.
|
|
14
|
+
*
|
|
15
|
+
* Every BYO model it hands out is wrapped in `ByoModelFallbackChatModel`:
|
|
16
|
+
* a model the provider refuses (`isModelUnavailableError`) is answered by
|
|
17
|
+
* the platform model for the rest of the turn, behind a `byo_fallback`
|
|
18
|
+
* notice. The refusal state lives in this adapter, so it is per turn.
|
|
14
19
|
*/
|
|
15
20
|
|
|
16
21
|
import type { BaseChatModel } from '@langchain/core/language_models/chat_models';
|
|
17
22
|
import type { ChatOpenAIFields, Logger, ModelRole } from '../plugin-api/types';
|
|
18
23
|
import type { LlmAdapter } from '../core/runtime-context';
|
|
24
|
+
import { NOOP_LOGGER } from '../core/utils';
|
|
19
25
|
import { byoModelForRole, type ByoCredential } from './byo-catalog';
|
|
20
26
|
import { createByoChatModel, type ChatGptBackendConfig } from './byo-client';
|
|
27
|
+
import {
|
|
28
|
+
ByoModelFallbackChatModel,
|
|
29
|
+
ByoModelFallbackState,
|
|
30
|
+
} from './byo-model-fallback';
|
|
21
31
|
|
|
22
32
|
export interface ByoTurnResolution {
|
|
23
33
|
credential: ByoCredential;
|
|
@@ -32,6 +42,7 @@ export function createByoLlmAdapter(
|
|
|
32
42
|
turn: ByoTurnResolution,
|
|
33
43
|
logger?: Logger,
|
|
34
44
|
): LlmAdapter {
|
|
45
|
+
const fallbackState = new ByoModelFallbackState();
|
|
35
46
|
return {
|
|
36
47
|
get(role: ModelRole, params?: ChatOpenAIFields): BaseChatModel {
|
|
37
48
|
const modelId = byoModelForRole(
|
|
@@ -46,13 +57,23 @@ export function createByoLlmAdapter(
|
|
|
46
57
|
// Strip a caller-supplied `model` (for `main` it carries the `byo:` id,
|
|
47
58
|
// which is not a wire id) — the translated id wins.
|
|
48
59
|
const { model: _model, ...rest } = params ?? {};
|
|
49
|
-
|
|
50
|
-
|
|
60
|
+
// Wrapped even after a refusal this turn: the wrapper then goes straight
|
|
61
|
+
// to the platform model and attributes that model's failures to the
|
|
62
|
+
// platform, not to the user's account.
|
|
63
|
+
return new ByoModelFallbackChatModel({
|
|
64
|
+
provider: turn.credential.provider,
|
|
51
65
|
modelId,
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
66
|
+
byo: createByoChatModel({
|
|
67
|
+
credential: turn.credential,
|
|
68
|
+
modelId,
|
|
69
|
+
role,
|
|
70
|
+
chatGptBackend: turn.chatGptBackend,
|
|
71
|
+
params: rest,
|
|
72
|
+
logger,
|
|
73
|
+
}),
|
|
74
|
+
platform: () => platform.get(role, rest),
|
|
75
|
+
state: fallbackState,
|
|
76
|
+
logger: logger ?? NOOP_LOGGER,
|
|
56
77
|
});
|
|
57
78
|
},
|
|
58
79
|
};
|
package/src/llm/byo-catalog.ts
CHANGED
|
@@ -98,6 +98,19 @@ export const BYO_PROVIDER_INFO: Record<ByoProvider, ByoProviderInfo> = {
|
|
|
98
98
|
* after the token exchange. Stored in the canonical user↔oracle room, so the
|
|
99
99
|
* credential is account-level for this oracle, not per-session.
|
|
100
100
|
*/
|
|
101
|
+
/**
|
|
102
|
+
* The room-secret namespace reserved for the runtime's own LLM credentials:
|
|
103
|
+
* every `BYO_SECRET_NAMES` entry, and any provider added later. Plugins never
|
|
104
|
+
* see a secret under it (`createSecretsAdapter`) — above all the sandbox,
|
|
105
|
+
* which forwards what it sees to code the model writes.
|
|
106
|
+
*/
|
|
107
|
+
export const BYO_SECRET_PREFIX = 'BYO_LLM_';
|
|
108
|
+
|
|
109
|
+
/** A room secret only the runtime itself may read (a BYO LLM credential). */
|
|
110
|
+
export function isRuntimeOnlySecret(name: string): boolean {
|
|
111
|
+
return name.startsWith(BYO_SECRET_PREFIX);
|
|
112
|
+
}
|
|
113
|
+
|
|
101
114
|
export const BYO_SECRET_NAMES: Record<ByoProvider, string> = {
|
|
102
115
|
chatgpt: 'BYO_LLM_CHATGPT_OAUTH',
|
|
103
116
|
openai: 'BYO_LLM_OPENAI_API_KEY',
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Call-time fallback for a BYO model the user's provider refuses.
|
|
3
|
+
*
|
|
4
|
+
* The pre-turn checks (`WorkersByoService.resolveForTurn`) cannot know
|
|
5
|
+
* whether the provider serves the selected model: the ChatGPT backend answers
|
|
6
|
+
* an immediate `400` with an empty body for a model id the subscription does
|
|
7
|
+
* not offer, and only the first real request finds out. This wrapper stands
|
|
8
|
+
* in for the BYO chat model: when its call fails that way before the
|
|
9
|
+
* provider produced any output, it announces the fallback once (an
|
|
10
|
+
* `on_custom_event` named `byo_fallback`, which the SSE producer writes as
|
|
11
|
+
* the same `error`-channel notice the pre-turn fallbacks send) and answers
|
|
12
|
+
* the very same call on the platform model. The refusal is remembered for
|
|
13
|
+
* the rest of the turn (`ByoModelFallbackState`, one per adapter, i.e. per
|
|
14
|
+
* turn): every later call to that model id goes to the platform model
|
|
15
|
+
* directly. Other BYO model ids of the turn (the helper roles' models) stay
|
|
16
|
+
* on the user's account. Nothing is persisted across turns. A failure of the
|
|
17
|
+
* answering platform model is marked (`markPlatformFallbackFailure`) so the
|
|
18
|
+
* SSE classification blames — and redacts — the platform, not the user's
|
|
19
|
+
* account.
|
|
20
|
+
*
|
|
21
|
+
* Delegation runs at the `_streamResponseChunks` / `_generate` level, inside
|
|
22
|
+
* this wrapper's own run, so callbacks, metering and the stream events see
|
|
23
|
+
* one model call whichever model answered it.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import type { CallbackManagerForLLMRun } from '@langchain/core/callbacks/manager';
|
|
27
|
+
import type {
|
|
28
|
+
BaseLanguageModelInput,
|
|
29
|
+
ToolDefinition,
|
|
30
|
+
} from '@langchain/core/language_models/base';
|
|
31
|
+
import {
|
|
32
|
+
BaseChatModel,
|
|
33
|
+
type BaseChatModelCallOptions,
|
|
34
|
+
type BindToolsInput,
|
|
35
|
+
} from '@langchain/core/language_models/chat_models';
|
|
36
|
+
import type { ModelProfile } from '@langchain/core/language_models/profile';
|
|
37
|
+
import type { AIMessageChunk, BaseMessage } from '@langchain/core/messages';
|
|
38
|
+
import type { ChatGenerationChunk, ChatResult } from '@langchain/core/outputs';
|
|
39
|
+
import type { Runnable } from '@langchain/core/runnables';
|
|
40
|
+
import { convertToOpenAITool } from '@langchain/core/utils/function_calling';
|
|
41
|
+
import type { Logger } from '../plugin-api/types';
|
|
42
|
+
import type { ByoProvider } from './byo-catalog';
|
|
43
|
+
import {
|
|
44
|
+
BYO_FALLBACK_KIND,
|
|
45
|
+
buildByoFallbackNotice,
|
|
46
|
+
classifyLlmError,
|
|
47
|
+
isModelUnavailableError,
|
|
48
|
+
markPlatformFallbackFailure,
|
|
49
|
+
} from './provider-error';
|
|
50
|
+
|
|
51
|
+
/** Turn-scoped record of the BYO model ids the provider refused. */
|
|
52
|
+
export class ByoModelFallbackState {
|
|
53
|
+
private readonly refused = new Set<string>();
|
|
54
|
+
|
|
55
|
+
isRefused(modelId: string): boolean {
|
|
56
|
+
return this.refused.has(modelId);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Records a refusal; `true` only the first time for this model id. */
|
|
60
|
+
markRefused(modelId: string): boolean {
|
|
61
|
+
if (this.refused.has(modelId)) return false;
|
|
62
|
+
this.refused.add(modelId);
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface ByoModelFallbackCallOptions extends BaseChatModelCallOptions {
|
|
68
|
+
/** Tools in OpenAI function format (see `bindTools`). */
|
|
69
|
+
tools?: ToolDefinition[] | BindToolsInput[];
|
|
70
|
+
strict?: boolean;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface ByoModelFallbackFields {
|
|
74
|
+
provider: ByoProvider;
|
|
75
|
+
/** Provider-native id of the wrapped BYO model. */
|
|
76
|
+
modelId: string;
|
|
77
|
+
/** The user's BYO model for this role. */
|
|
78
|
+
byo: BaseChatModel;
|
|
79
|
+
/** Builds the platform model for the same role (called at most once). */
|
|
80
|
+
platform: () => BaseChatModel;
|
|
81
|
+
state: ByoModelFallbackState;
|
|
82
|
+
logger: Logger;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export class ByoModelFallbackChatModel extends BaseChatModel<ByoModelFallbackCallOptions> {
|
|
86
|
+
private readonly fields: ByoModelFallbackFields;
|
|
87
|
+
private platformModel: BaseChatModel | undefined;
|
|
88
|
+
|
|
89
|
+
static lc_name(): string {
|
|
90
|
+
return 'ByoModelFallbackChatModel';
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
constructor(fields: ByoModelFallbackFields) {
|
|
94
|
+
super({});
|
|
95
|
+
this.fields = fields;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** The provider-native id this wrapper stands in for. */
|
|
99
|
+
get modelId(): string {
|
|
100
|
+
return this.fields.modelId;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** The user's own model this wrapper calls first. */
|
|
104
|
+
get byoModel(): BaseChatModel {
|
|
105
|
+
return this.fields.byo;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
private platform(): BaseChatModel {
|
|
109
|
+
this.platformModel ??= this.fields.platform();
|
|
110
|
+
return this.platformModel;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** The model a call starting now goes to. */
|
|
114
|
+
private active(): BaseChatModel {
|
|
115
|
+
return this.fields.state.isRefused(this.fields.modelId)
|
|
116
|
+
? this.platform()
|
|
117
|
+
: this.fields.byo;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** Read by the base constructor, before the fields exist — a constant. */
|
|
121
|
+
_llmType(): string {
|
|
122
|
+
return 'byo-model-fallback';
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
override get profile(): ModelProfile {
|
|
126
|
+
return this.active().profile;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
override invocationParams(options?: this['ParsedCallOptions']): unknown {
|
|
130
|
+
return this.active().invocationParams(options);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
override getLsParams(options: this['ParsedCallOptions']) {
|
|
134
|
+
return this.active().getLsParams(options);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Tools travel as call options in OpenAI function format — the format
|
|
139
|
+
* `ChatOpenAI.bindTools` produces and both of its request converters
|
|
140
|
+
* (Chat Completions and Responses) accept — so the same bound call can be
|
|
141
|
+
* answered by either model. `strict` stays a call option, applied by the
|
|
142
|
+
* answering model exactly as its own `bindTools` would. Provider-specific
|
|
143
|
+
* tool kinds that only `ChatOpenAI.bindTools` special-cases on a LangChain
|
|
144
|
+
* tool (`metadata.customTool`, `extras.providerToolDefinition`,
|
|
145
|
+
* `extras.defer_loading`) become plain functions here; the runtime binds
|
|
146
|
+
* none of them. Plain OpenAI tool objects pass through unchanged.
|
|
147
|
+
*/
|
|
148
|
+
override bindTools(
|
|
149
|
+
tools: BindToolsInput[],
|
|
150
|
+
kwargs?: Partial<ByoModelFallbackCallOptions>,
|
|
151
|
+
): Runnable<
|
|
152
|
+
BaseLanguageModelInput,
|
|
153
|
+
AIMessageChunk,
|
|
154
|
+
ByoModelFallbackCallOptions
|
|
155
|
+
> {
|
|
156
|
+
return this.withConfig({
|
|
157
|
+
tools: tools.map((tool) => convertToOpenAITool(tool)),
|
|
158
|
+
...kwargs,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* A refusal: announce it once per model id per turn, log it, and remember
|
|
164
|
+
* it so every later call goes to the platform model.
|
|
165
|
+
*/
|
|
166
|
+
private async fallBack(
|
|
167
|
+
error: unknown,
|
|
168
|
+
runManager: CallbackManagerForLLMRun | undefined,
|
|
169
|
+
): Promise<void> {
|
|
170
|
+
const { provider, modelId, state, logger } = this.fields;
|
|
171
|
+
if (!state.markRefused(modelId)) return;
|
|
172
|
+
const status = classifyLlmError(error).status;
|
|
173
|
+
logger.warn(
|
|
174
|
+
`[byo] ${provider} refused model "${modelId}" (HTTP ${status ?? 'n/a'}): ${error instanceof Error ? error.message : String(error)} — this turn uses the platform model for it`,
|
|
175
|
+
);
|
|
176
|
+
await runManager?.handleCustomEvent(
|
|
177
|
+
BYO_FALLBACK_KIND,
|
|
178
|
+
buildByoFallbackNotice('model_unavailable', provider, { modelId }),
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
override async *_streamResponseChunks(
|
|
183
|
+
messages: BaseMessage[],
|
|
184
|
+
options: this['ParsedCallOptions'],
|
|
185
|
+
runManager?: CallbackManagerForLLMRun,
|
|
186
|
+
): AsyncGenerator<ChatGenerationChunk> {
|
|
187
|
+
if (!this.fields.state.isRefused(this.fields.modelId)) {
|
|
188
|
+
let produced = false;
|
|
189
|
+
try {
|
|
190
|
+
for await (const chunk of this.fields.byo._streamResponseChunks(
|
|
191
|
+
messages,
|
|
192
|
+
options,
|
|
193
|
+
runManager,
|
|
194
|
+
)) {
|
|
195
|
+
produced = true;
|
|
196
|
+
yield chunk;
|
|
197
|
+
}
|
|
198
|
+
return;
|
|
199
|
+
} catch (error) {
|
|
200
|
+
if (
|
|
201
|
+
options.signal?.aborted ||
|
|
202
|
+
!isModelUnavailableError(error, { afterOutput: produced })
|
|
203
|
+
) {
|
|
204
|
+
throw error;
|
|
205
|
+
}
|
|
206
|
+
await this.fallBack(error, runManager);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
try {
|
|
210
|
+
yield* this.platform()._streamResponseChunks(
|
|
211
|
+
messages,
|
|
212
|
+
options,
|
|
213
|
+
runManager,
|
|
214
|
+
);
|
|
215
|
+
} catch (error) {
|
|
216
|
+
markPlatformFallbackFailure(error);
|
|
217
|
+
throw error;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* The non-streamed path (no streaming handler on the call). A model that
|
|
223
|
+
* streams internally here reports tokens to handlers that do not forward
|
|
224
|
+
* them anywhere, so a refusal is always answered by the platform model.
|
|
225
|
+
*/
|
|
226
|
+
async _generate(
|
|
227
|
+
messages: BaseMessage[],
|
|
228
|
+
options: this['ParsedCallOptions'],
|
|
229
|
+
runManager?: CallbackManagerForLLMRun,
|
|
230
|
+
): Promise<ChatResult> {
|
|
231
|
+
if (!this.fields.state.isRefused(this.fields.modelId)) {
|
|
232
|
+
try {
|
|
233
|
+
return await this.fields.byo._generate(messages, options, runManager);
|
|
234
|
+
} catch (error) {
|
|
235
|
+
if (options.signal?.aborted || !isModelUnavailableError(error)) {
|
|
236
|
+
throw error;
|
|
237
|
+
}
|
|
238
|
+
await this.fallBack(error, runManager);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
try {
|
|
242
|
+
return await this.platform()._generate(messages, options, runManager);
|
|
243
|
+
} catch (error) {
|
|
244
|
+
markPlatformFallbackFailure(error);
|
|
245
|
+
throw error;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
package/src/llm/byo-service.ts
CHANGED
|
@@ -106,6 +106,15 @@ export interface ByoProviderStatus {
|
|
|
106
106
|
export interface ByoSecretsBackend {
|
|
107
107
|
getIndex(roomId: string): Promise<Array<{ name: string; eventId: string }>>;
|
|
108
108
|
getValues(roomId: string, names: string[]): Promise<Record<string, string>>;
|
|
109
|
+
/**
|
|
110
|
+
* Values for index entries already read with `getIndex`, without reading
|
|
111
|
+
* the index again. Optional: without it the service asks `getValues` for
|
|
112
|
+
* the entries' names.
|
|
113
|
+
*/
|
|
114
|
+
getValuesFor?(
|
|
115
|
+
roomId: string,
|
|
116
|
+
index: ReadonlyArray<{ name: string; eventId: string }>,
|
|
117
|
+
): Promise<Record<string, string>>;
|
|
109
118
|
putSecret(roomId: string, name: string, value: string): Promise<void>;
|
|
110
119
|
deleteSecret(roomId: string, name: string): Promise<void>;
|
|
111
120
|
}
|
|
@@ -132,12 +141,23 @@ export interface WorkersByoServiceOptions {
|
|
|
132
141
|
logger?: Logger;
|
|
133
142
|
/** Fetch used for the ChatGPT-backend reachability probe (tests). */
|
|
134
143
|
probeFetch?: typeof fetch;
|
|
144
|
+
/** Bound on the reachability probe (default `CHATGPT_PROBE_TIMEOUT_MS`). */
|
|
145
|
+
probeTimeoutMs?: number;
|
|
146
|
+
/** Bound on a key-validation call (default `KEY_CHECK_TIMEOUT_MS`). */
|
|
147
|
+
keyCheckTimeoutMs?: number;
|
|
135
148
|
/** Where ChatGPT-subscription requests go (default: the real backend). */
|
|
136
149
|
chatGptBackend?: ChatGptBackendConfig;
|
|
137
150
|
}
|
|
138
151
|
|
|
139
152
|
/** How long a ChatGPT-backend reachability verdict is trusted. */
|
|
140
153
|
const CHATGPT_PROBE_TTL_MS = 10 * 60 * 1000;
|
|
154
|
+
/**
|
|
155
|
+
* The probe runs at the start of a ChatGPT turn: a backend that does not
|
|
156
|
+
* answer within this is treated as reachable rather than holding the turn.
|
|
157
|
+
*/
|
|
158
|
+
const CHATGPT_PROBE_TIMEOUT_MS = 5_000;
|
|
159
|
+
/** How long a "check my key" call waits on the provider. */
|
|
160
|
+
const KEY_CHECK_TIMEOUT_MS = 10_000;
|
|
141
161
|
|
|
142
162
|
const NOOP: Logger = {
|
|
143
163
|
log: () => undefined,
|
|
@@ -173,6 +193,8 @@ export class WorkersByoService {
|
|
|
173
193
|
/** Per-user refresh-failure cooldown (epoch-ms deadline). */
|
|
174
194
|
private readonly refreshCooldownUntil = new Map<string, number>();
|
|
175
195
|
private readonly probeFetch: typeof fetch;
|
|
196
|
+
private readonly probeTimeoutMs: number;
|
|
197
|
+
private readonly keyCheckTimeoutMs: number;
|
|
176
198
|
readonly chatGptBackend: ChatGptBackendConfig;
|
|
177
199
|
/** Last ChatGPT-backend reachability verdict (deployment-wide, not per user). */
|
|
178
200
|
private chatGptProbe: { blocked: boolean; at: number } | null = null;
|
|
@@ -189,6 +211,8 @@ export class WorkersByoService {
|
|
|
189
211
|
// method. Resolve the global at call time so the probe really runs.
|
|
190
212
|
this.probeFetch =
|
|
191
213
|
opts.probeFetch ?? ((input, init) => globalThis.fetch(input, init));
|
|
214
|
+
this.probeTimeoutMs = opts.probeTimeoutMs ?? CHATGPT_PROBE_TIMEOUT_MS;
|
|
215
|
+
this.keyCheckTimeoutMs = opts.keyCheckTimeoutMs ?? KEY_CHECK_TIMEOUT_MS;
|
|
192
216
|
this.chatGptBackend = opts.chatGptBackend ?? DEFAULT_CHATGPT_BACKEND;
|
|
193
217
|
}
|
|
194
218
|
|
|
@@ -201,8 +225,9 @@ export class WorkersByoService {
|
|
|
201
225
|
* a failed turn with no reply, so probe once (a cheap GET the backend
|
|
202
226
|
* answers with a JSON 404/405 when reachable) and trust the verdict for
|
|
203
227
|
* `CHATGPT_PROBE_TTL_MS`. Only an HTML 403 counts as blocked — a network
|
|
204
|
-
* error
|
|
205
|
-
* the user's own
|
|
228
|
+
* error, a probe that times out (`CHATGPT_PROBE_TIMEOUT_MS`) or any JSON
|
|
229
|
+
* status is "reachable", so a probe hiccup never hides the user's own
|
|
230
|
+
* provider errors.
|
|
206
231
|
*/
|
|
207
232
|
private async chatGptBackendBlocked(accessToken: string): Promise<boolean> {
|
|
208
233
|
if (
|
|
@@ -222,6 +247,7 @@ export class WorkersByoService {
|
|
|
222
247
|
Authorization: `Bearer ${accessToken}`,
|
|
223
248
|
originator: 'codex_cli_rs',
|
|
224
249
|
},
|
|
250
|
+
signal: AbortSignal.timeout(this.probeTimeoutMs),
|
|
225
251
|
},
|
|
226
252
|
);
|
|
227
253
|
const type = res.headers.get('content-type') ?? '';
|
|
@@ -340,12 +366,20 @@ export class WorkersByoService {
|
|
|
340
366
|
const roomId = await this.resolveRoomIdFn(userDid);
|
|
341
367
|
if (!roomId) return {};
|
|
342
368
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
369
|
+
// One read of the room's secret index when the backend can decrypt the
|
|
370
|
+
// entries already read; `getValues` would read the index again.
|
|
371
|
+
const byoEntries = (await this.secrets.getIndex(roomId)).filter(
|
|
372
|
+
(entry) => providerForSecretName(entry.name) !== undefined,
|
|
373
|
+
);
|
|
347
374
|
const values =
|
|
348
|
-
|
|
375
|
+
byoEntries.length === 0
|
|
376
|
+
? {}
|
|
377
|
+
: this.secrets.getValuesFor
|
|
378
|
+
? await this.secrets.getValuesFor(roomId, byoEntries)
|
|
379
|
+
: await this.secrets.getValues(
|
|
380
|
+
roomId,
|
|
381
|
+
byoEntries.map((entry) => entry.name),
|
|
382
|
+
);
|
|
349
383
|
|
|
350
384
|
const creds: ByoCredentialMap = {};
|
|
351
385
|
for (const [name, value] of Object.entries(values)) {
|
|
@@ -789,7 +823,10 @@ export class WorkersByoService {
|
|
|
789
823
|
credential.provider,
|
|
790
824
|
credential.apiKey,
|
|
791
825
|
);
|
|
792
|
-
const res = await fetch(url, {
|
|
826
|
+
const res = await fetch(url, {
|
|
827
|
+
headers,
|
|
828
|
+
signal: AbortSignal.timeout(this.keyCheckTimeoutMs),
|
|
829
|
+
});
|
|
793
830
|
if (res.ok) return { valid: true };
|
|
794
831
|
return {
|
|
795
832
|
valid: false,
|