@opengeni/runtime 4.0.3 → 4.1.0-canary.36239117573001
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/dist/assets/codemode-client.json +1 -1
- package/dist/{chunk-GBVN7WSK.js → chunk-ORTF5RRC.js} +371 -28
- package/dist/chunk-ORTF5RRC.js.map +1 -0
- package/dist/{chunk-LL2YQO5H.js → chunk-YAA7WDNO.js} +638 -71
- package/dist/chunk-YAA7WDNO.js.map +1 -0
- package/dist/codex-tool-search.d.ts +12 -0
- package/dist/context-compaction.d.ts +4 -2
- package/dist/index.d.ts +27 -8
- package/dist/index.js +18 -2
- package/dist/knowledge-model-projection.d.ts +28 -0
- package/dist/metrics.d.ts +17 -0
- package/dist/model-provider-routing.d.ts +14 -0
- package/dist/operational-instructions.d.ts +1 -1
- package/dist/provider-quota.d.ts +76 -0
- package/dist/sandbox/channel-a.d.ts +43 -0
- package/dist/sandbox/index.d.ts +1 -1
- package/dist/sandbox/index.js +5 -1
- package/dist/tool-result-spill.d.ts +11 -3
- package/dist/workspace-tool-gateway.js +2 -2
- package/package.json +11 -11
- package/src/codex-tool-search.ts +54 -6
- package/src/context-compaction.ts +19 -3
- package/src/index.ts +277 -56
- package/src/knowledge-model-projection.ts +169 -0
- package/src/lazy-tool-transport.ts +4 -0
- package/src/metrics.ts +26 -0
- package/src/model-context-inspector.ts +1 -0
- package/src/model-provider-client.ts +37 -16
- package/src/model-provider-routing.ts +32 -6
- package/src/operational-instructions.ts +1 -1
- package/src/provider-quota.ts +412 -0
- package/src/sandbox/browser-control-client.ts +70 -25
- package/src/sandbox/channel-a.ts +370 -0
- package/src/sandbox/index.ts +3 -0
- package/src/sandbox/routing/routing-session.ts +25 -4
- package/src/tool-result-spill.ts +17 -5
- package/dist/chunk-GBVN7WSK.js.map +0 -1
- package/dist/chunk-LL2YQO5H.js.map +0 -1
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import {
|
|
2
|
+
KnowledgeEntryListResponse,
|
|
3
|
+
KnowledgeSavePreparationResponse,
|
|
4
|
+
type AttemptToolIdentity,
|
|
5
|
+
type AttemptToolResult as AttemptToolResultValue,
|
|
6
|
+
} from "@opengeni/contracts";
|
|
7
|
+
|
|
8
|
+
type JsonRecord = Record<string, unknown>;
|
|
9
|
+
|
|
10
|
+
type KnowledgeModelProjection = "entry_list" | "save_preparation";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The model-visible copy of a Knowledge discovery result.
|
|
14
|
+
*
|
|
15
|
+
* `knowledge_search` (first-party and Docs servers) and `knowledge_prepare_save`
|
|
16
|
+
* return the complete contract to every caller. Codemode and other programmatic
|
|
17
|
+
* callers keep those exact bytes; only the model caller receives this
|
|
18
|
+
* projection (see `projectAttemptToolResultForCaller`). It is the same JSON with
|
|
19
|
+
* bookkeeping and repeated text removed:
|
|
20
|
+
*
|
|
21
|
+
* - entry timestamps, rank score, revision lineage/session/batch/number fields;
|
|
22
|
+
* - defaults: `archived: false`, `change: "upsert"`, `sourceKind: null`, and a
|
|
23
|
+
* collection's `descriptionTruncated: false`;
|
|
24
|
+
* - `publishedRevisionId`/`latestRevisionId` and `revision.entryId` when they
|
|
25
|
+
* repeat `revision.id`/`id` (a differing value is kept);
|
|
26
|
+
* - `revision.preview` only when its complete text already appears verbatim in
|
|
27
|
+
* the title or an excerpt, and title-field excerpts contained in the title;
|
|
28
|
+
* - a collection descriptor's `revisionId` (collections are saved by `id` and
|
|
29
|
+
* `version` and read by `view`; they are never cited as evidence).
|
|
30
|
+
*
|
|
31
|
+
* Everything needed to act is kept: entry and collection IDs, `version` (the
|
|
32
|
+
* `expectedVersion` for an update), `revision.id` (evidence pins), scope,
|
|
33
|
+
* view/outcome status, titles, kinds, group/parent IDs, descriptions, excerpts
|
|
34
|
+
* with their offsets, index status and pagination cursors. Nothing is truncated.
|
|
35
|
+
* A result that is an error, carries structured content, or does not strictly
|
|
36
|
+
* match the contract is returned unchanged.
|
|
37
|
+
*/
|
|
38
|
+
export function projectKnowledgeToolResultForModel(
|
|
39
|
+
identity: AttemptToolIdentity,
|
|
40
|
+
result: AttemptToolResultValue,
|
|
41
|
+
): AttemptToolResultValue {
|
|
42
|
+
const projection = knowledgeModelProjection(identity);
|
|
43
|
+
if (!projection || result.isError === true || result.structuredContent !== undefined) {
|
|
44
|
+
return result;
|
|
45
|
+
}
|
|
46
|
+
if (result.content.length !== 1) return result;
|
|
47
|
+
const [content] = result.content;
|
|
48
|
+
if (content?.type !== "text") return result;
|
|
49
|
+
let value: unknown;
|
|
50
|
+
try {
|
|
51
|
+
value = JSON.parse(content.text);
|
|
52
|
+
} catch {
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
const compact =
|
|
56
|
+
projection === "entry_list" ? compactEntryList(value) : compactSavePreparation(value);
|
|
57
|
+
if (!compact) return result;
|
|
58
|
+
const text = JSON.stringify(compact);
|
|
59
|
+
if (text.length >= content.text.length) return result;
|
|
60
|
+
return { ...result, content: [{ ...content, text }] };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function knowledgeModelProjection(identity: AttemptToolIdentity): KnowledgeModelProjection | null {
|
|
64
|
+
if (
|
|
65
|
+
identity.toolName === "knowledge_search" &&
|
|
66
|
+
(identity.serverId === "opengeni" || identity.serverId === "docs")
|
|
67
|
+
) {
|
|
68
|
+
return "entry_list";
|
|
69
|
+
}
|
|
70
|
+
if (identity.toolName === "knowledge_prepare_save" && identity.serverId === "opengeni") {
|
|
71
|
+
return "save_preparation";
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function compactEntryList(value: unknown): JsonRecord | null {
|
|
77
|
+
if (!KnowledgeEntryListResponse.safeParse(value).success) return null;
|
|
78
|
+
return compactListResponse(value as JsonRecord);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function compactSavePreparation(value: unknown): JsonRecord | null {
|
|
82
|
+
if (!KnowledgeSavePreparationResponse.safeParse(value).success) return null;
|
|
83
|
+
const response = value as JsonRecord & {
|
|
84
|
+
collections: JsonRecord & { entries: JsonRecord[] };
|
|
85
|
+
matches: JsonRecord & { published: JsonRecord; needs_review: JsonRecord };
|
|
86
|
+
};
|
|
87
|
+
return {
|
|
88
|
+
...response,
|
|
89
|
+
collections: {
|
|
90
|
+
...response.collections,
|
|
91
|
+
entries: response.collections.entries.map(compactCollection),
|
|
92
|
+
},
|
|
93
|
+
matches: {
|
|
94
|
+
...response.matches,
|
|
95
|
+
published: compactListResponse(response.matches.published),
|
|
96
|
+
needs_review: compactListResponse(response.matches.needs_review),
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function compactListResponse(response: JsonRecord): JsonRecord {
|
|
102
|
+
return { ...response, entries: (response.entries as JsonRecord[]).map(compactEntrySummary) };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function compactCollection(collection: JsonRecord): JsonRecord {
|
|
106
|
+
const { revisionId: _revisionId, descriptionTruncated, ...kept } = collection;
|
|
107
|
+
return { ...kept, ...(descriptionTruncated === false ? {} : { descriptionTruncated }) };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function compactEntrySummary(entry: JsonRecord): JsonRecord {
|
|
111
|
+
const {
|
|
112
|
+
score: _score,
|
|
113
|
+
createdAt: _createdAt,
|
|
114
|
+
updatedAt: _updatedAt,
|
|
115
|
+
archived,
|
|
116
|
+
publishedRevisionId,
|
|
117
|
+
latestRevisionId,
|
|
118
|
+
excerpts,
|
|
119
|
+
revision,
|
|
120
|
+
...kept
|
|
121
|
+
} = entry;
|
|
122
|
+
const {
|
|
123
|
+
entryId,
|
|
124
|
+
number: _number,
|
|
125
|
+
change,
|
|
126
|
+
previousRevisionId: _previousRevisionId,
|
|
127
|
+
restoredFromRevisionId: _restoredFromRevisionId,
|
|
128
|
+
createdAt: _revisionCreatedAt,
|
|
129
|
+
createdBySessionId: _createdBySessionId,
|
|
130
|
+
reviewBatchId: _reviewBatchId,
|
|
131
|
+
sourceKind,
|
|
132
|
+
preview,
|
|
133
|
+
...keptRevision
|
|
134
|
+
} = revision as JsonRecord;
|
|
135
|
+
const revisionId = keptRevision.id;
|
|
136
|
+
const excerptList = (excerpts ?? []) as JsonRecord[];
|
|
137
|
+
return {
|
|
138
|
+
...kept,
|
|
139
|
+
...(archived === false ? {} : { archived }),
|
|
140
|
+
...(publishedRevisionId === revisionId ? {} : { publishedRevisionId }),
|
|
141
|
+
...(latestRevisionId === revisionId ? {} : { latestRevisionId }),
|
|
142
|
+
revision: {
|
|
143
|
+
...keptRevision,
|
|
144
|
+
...(entryId === entry.id ? {} : { entryId }),
|
|
145
|
+
...(change === "upsert" ? {} : { change }),
|
|
146
|
+
...(sourceKind === null ? {} : { sourceKind }),
|
|
147
|
+
...(previewAlreadyShown(preview, excerptList) ? {} : { preview }),
|
|
148
|
+
},
|
|
149
|
+
...(excerpts === undefined ? {} : { excerpts }),
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* The preview is the start of the entry's content. It is redundant only when
|
|
155
|
+
* it is empty or a content excerpt that starts at offset 0 already begins with
|
|
156
|
+
* it. The title never counts: a short content (a decision's answer) can be a
|
|
157
|
+
* substring of its title and still be the only place the answer appears.
|
|
158
|
+
*/
|
|
159
|
+
function previewAlreadyShown(preview: unknown, excerpts: readonly JsonRecord[]): boolean {
|
|
160
|
+
if (typeof preview !== "string") return false;
|
|
161
|
+
if (preview === "") return true;
|
|
162
|
+
return excerpts.some(
|
|
163
|
+
(excerpt) =>
|
|
164
|
+
excerpt.field === "content" &&
|
|
165
|
+
excerpt.start === 0 &&
|
|
166
|
+
typeof excerpt.text === "string" &&
|
|
167
|
+
excerpt.text.startsWith(preview),
|
|
168
|
+
);
|
|
169
|
+
}
|
|
@@ -38,6 +38,10 @@ const ALWAYS_VISIBLE_BASE_TOOL_NAMES: ReadonlySet<string> = new Set([
|
|
|
38
38
|
"repository_skill_read",
|
|
39
39
|
"request_human_input",
|
|
40
40
|
"list_models",
|
|
41
|
+
// Jev-backed code search. Present only when the deployment and workspace
|
|
42
|
+
// enable it; hiding it behind search would spend a model round trip to find
|
|
43
|
+
// the tool that exists to save round trips.
|
|
44
|
+
"code_search",
|
|
41
45
|
]);
|
|
42
46
|
const DISPATCH_MARKER_KEY = "opengeni.lazy_dispatch.v1";
|
|
43
47
|
const SEARCH_MARKER_KEY = "opengeni.lazy_search.v1";
|
package/src/metrics.ts
CHANGED
|
@@ -15,6 +15,23 @@ export const MCP_LIFECYCLE_PHASES = ["connect", "close"] as const;
|
|
|
15
15
|
export const MCP_LIFECYCLE_POLICIES = ["strict", "best_effort"] as const;
|
|
16
16
|
export const MCP_LIFECYCLE_OUTCOMES = ["completed", "failed"] as const;
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Closed outcomes of one fresh-box command-readiness replacement decision:
|
|
20
|
+
* the replacement became ready, it also missed readiness, it failed for another
|
|
21
|
+
* typed reason, cancellation won the pause or the replacement, or the turn
|
|
22
|
+
* attempt had already spent its single replacement.
|
|
23
|
+
*/
|
|
24
|
+
export const SANDBOX_READINESS_REPLACEMENT_OUTCOMES = [
|
|
25
|
+
"replaced",
|
|
26
|
+
"failed_again",
|
|
27
|
+
"replacement_failed",
|
|
28
|
+
"cancelled",
|
|
29
|
+
"budget_spent",
|
|
30
|
+
] as const;
|
|
31
|
+
|
|
32
|
+
export type SandboxReadinessReplacementOutcome =
|
|
33
|
+
(typeof SANDBOX_READINESS_REPLACEMENT_OUTCOMES)[number];
|
|
34
|
+
|
|
18
35
|
export type McpLifecyclePhase = (typeof MCP_LIFECYCLE_PHASES)[number];
|
|
19
36
|
export type McpLifecyclePolicy = (typeof MCP_LIFECYCLE_POLICIES)[number];
|
|
20
37
|
export type McpLifecycleOutcome = (typeof MCP_LIFECYCLE_OUTCOMES)[number];
|
|
@@ -35,6 +52,15 @@ export type RuntimeMetricsHooks = {
|
|
|
35
52
|
backend: string;
|
|
36
53
|
stage: "exec_readiness" | "sibling_warming";
|
|
37
54
|
}) => void;
|
|
55
|
+
/**
|
|
56
|
+
* One fresh-box readiness replacement decision. The first readiness miss is
|
|
57
|
+
* still counted by `onSandboxWarmingTimeout`; this hook says whether the
|
|
58
|
+
* single per-turn-attempt replacement happened and whether it worked.
|
|
59
|
+
*/
|
|
60
|
+
onSandboxReadinessReplacement?: (input: {
|
|
61
|
+
backend: string;
|
|
62
|
+
outcome: SandboxReadinessReplacementOutcome;
|
|
63
|
+
}) => void;
|
|
38
64
|
onSandboxProviderApiThrottle?: (input: {
|
|
39
65
|
backend: string;
|
|
40
66
|
operation: "create" | "renew";
|
|
@@ -101,6 +101,7 @@ const LAYER_TITLES: Record<ModelContextInstructionLayerId, string> = {
|
|
|
101
101
|
session_instructions: "Session instructions",
|
|
102
102
|
workspace_memory: "Workspace memory",
|
|
103
103
|
codemode: "Codemode",
|
|
104
|
+
code_search: "Code search",
|
|
104
105
|
git_bindings: "Git credential bindings",
|
|
105
106
|
genesis_title: "Missing-title directive",
|
|
106
107
|
sdk_capability_instructions: "SDK capability instructions",
|
|
@@ -28,6 +28,7 @@ import { isModelCallFetch, vercelGatewayRoutingFetch } from "./model-provider-tr
|
|
|
28
28
|
import { ReplayableJsonOpenAI } from "./replayable-json-body";
|
|
29
29
|
import { recordModelTransportStarted } from "./model-preparation-diagnostics";
|
|
30
30
|
import { captureProviderRequestBody } from "./model-request-capture";
|
|
31
|
+
import { withoutQuotaExhaustedRetries } from "./provider-quota";
|
|
31
32
|
|
|
32
33
|
let runtimeMetricsHooks: RuntimeMetricsHooks | null = null;
|
|
33
34
|
|
|
@@ -85,7 +86,10 @@ export function buildOpenAIClientFromSettings(
|
|
|
85
86
|
settings.azureOpenaiAdToken && !settings.azureOpenaiApiKey
|
|
86
87
|
? { Authorization: `Bearer ${settings.azureOpenaiAdToken}` }
|
|
87
88
|
: undefined,
|
|
88
|
-
fetch:
|
|
89
|
+
fetch: sdkRetryingModelFetch(
|
|
90
|
+
settings.openaiMaxRetries,
|
|
91
|
+
instrumentedModelFetch(providerId, globalThis.fetch),
|
|
92
|
+
),
|
|
89
93
|
},
|
|
90
94
|
{ modelRequestPolicy: azureModelRequestPolicy },
|
|
91
95
|
);
|
|
@@ -94,10 +98,23 @@ export function buildOpenAIClientFromSettings(
|
|
|
94
98
|
apiKey: settings.openaiApiKey ?? process.env.OPENAI_API_KEY,
|
|
95
99
|
...(settings.openaiBaseUrl ? { baseURL: settings.openaiBaseUrl } : {}),
|
|
96
100
|
maxRetries: settings.openaiMaxRetries,
|
|
97
|
-
fetch:
|
|
101
|
+
fetch: sdkRetryingModelFetch(
|
|
102
|
+
settings.openaiMaxRetries,
|
|
103
|
+
instrumentedModelFetch(providerId, globalThis.fetch),
|
|
104
|
+
),
|
|
98
105
|
});
|
|
99
106
|
}
|
|
100
107
|
|
|
108
|
+
/**
|
|
109
|
+
* A client that lets the OpenAI SDK retry 429s must not replay an exhausted
|
|
110
|
+
* provider quota: the refusal cannot clear within the SDK's backoff, and each
|
|
111
|
+
* retry only delays the typed turn failure. Clients with SDK retries disabled
|
|
112
|
+
* need no veto.
|
|
113
|
+
*/
|
|
114
|
+
function sdkRetryingModelFetch(maxRetries: number, inner: typeof fetch): typeof fetch {
|
|
115
|
+
return maxRetries > 0 ? withoutQuotaExhaustedRetries(inner) : inner;
|
|
116
|
+
}
|
|
117
|
+
|
|
101
118
|
/**
|
|
102
119
|
* One OpenAI client per deployment-scoped provider configuration, built lazily
|
|
103
120
|
* and cached for the process. Workspace-connection providers are deliberately
|
|
@@ -282,6 +299,11 @@ export function buildProviderClient(provider: ResolvedModelProvider, settings: S
|
|
|
282
299
|
throw new WorkspaceGatewayUnavailableError();
|
|
283
300
|
}
|
|
284
301
|
const anonymousProvider = provider.kind === "anonymous";
|
|
302
|
+
// Gateway and OpenRouter requests can incur upstream work or charges
|
|
303
|
+
// before a retryable response failure reaches this process. Neither
|
|
304
|
+
// transport has a provider idempotency key tied to our durable call,
|
|
305
|
+
// so never let the SDK replay them blindly.
|
|
306
|
+
const registryMaxRetries = gatewayProvider || openRouterProvider ? 0 : settings.openaiMaxRetries;
|
|
285
307
|
const client = provider.builtin
|
|
286
308
|
? buildOpenAIClientFromSettings(settings, provider.id)
|
|
287
309
|
: provider.kind === "codex-subscription"
|
|
@@ -342,24 +364,23 @@ export function buildProviderClient(provider: ResolvedModelProvider, settings: S
|
|
|
342
364
|
? { apiKey: provider.apiKey }
|
|
343
365
|
: {}),
|
|
344
366
|
...(provider.baseUrl ? { baseURL: provider.baseUrl } : {}),
|
|
345
|
-
|
|
346
|
-
// before a retryable response failure reaches this process. Neither
|
|
347
|
-
// transport has a provider idempotency key tied to our durable call,
|
|
348
|
-
// so never let the SDK replay them blindly.
|
|
349
|
-
maxRetries: gatewayProvider || openRouterProvider ? 0 : settings.openaiMaxRetries,
|
|
367
|
+
maxRetries: registryMaxRetries,
|
|
350
368
|
...(provider.defaultQuery ? { defaultQuery: provider.defaultQuery } : {}),
|
|
351
369
|
...(provider.defaultHeaders ? { defaultHeaders: provider.defaultHeaders } : {}),
|
|
352
|
-
fetch:
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
: gatewayProvider
|
|
357
|
-
? vercelGatewayRoutingFetch(
|
|
358
|
-
provider.kind as "vercel-gateway-managed" | "vercel-gateway-workspace",
|
|
370
|
+
fetch: sdkRetryingModelFetch(
|
|
371
|
+
registryMaxRetries,
|
|
372
|
+
anonymousProvider
|
|
373
|
+
? withoutAuthenticationHeaders(
|
|
359
374
|
instrumentedModelFetch(provider.id, globalThis.fetch),
|
|
360
|
-
gatewayPolicies,
|
|
361
375
|
)
|
|
362
|
-
:
|
|
376
|
+
: gatewayProvider
|
|
377
|
+
? vercelGatewayRoutingFetch(
|
|
378
|
+
provider.kind as "vercel-gateway-managed" | "vercel-gateway-workspace",
|
|
379
|
+
instrumentedModelFetch(provider.id, globalThis.fetch),
|
|
380
|
+
gatewayPolicies,
|
|
381
|
+
)
|
|
382
|
+
: instrumentedModelFetch(provider.id, globalThis.fetch),
|
|
383
|
+
),
|
|
363
384
|
},
|
|
364
385
|
{ modelRequestPolicy: modelRequestPolicyForProvider(provider, gatewayPolicies) },
|
|
365
386
|
);
|
|
@@ -163,6 +163,16 @@ export class MultiProviderModelProvider implements ModelProvider {
|
|
|
163
163
|
constructor(private readonly settings: Settings) {}
|
|
164
164
|
|
|
165
165
|
async getModel(modelName?: string): Promise<Model> {
|
|
166
|
+
return this.resolveBinding(modelName).model;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* The provider, owned client, and upstream model id that `getModel` binds.
|
|
171
|
+
* Standalone requests outside an agent run (the session-title sidecar) use
|
|
172
|
+
* this to call the provider directly instead of the runner-facing
|
|
173
|
+
* `Model.getResponse()`, which requires an active trace.
|
|
174
|
+
*/
|
|
175
|
+
resolveBinding(modelName?: string): ModelProviderBinding {
|
|
166
176
|
if (modelName) {
|
|
167
177
|
const resolved = resolveTurnModel(
|
|
168
178
|
settingsForRunScopedModelResolution(this.settings, modelName),
|
|
@@ -189,7 +199,12 @@ export class MultiProviderModelProvider implements ModelProvider {
|
|
|
189
199
|
) {
|
|
190
200
|
throw new XaiSubscriptionUnavailableError(modelName);
|
|
191
201
|
}
|
|
192
|
-
return
|
|
202
|
+
return {
|
|
203
|
+
provider: resolved.provider,
|
|
204
|
+
client: resolved.client,
|
|
205
|
+
model: resolved.model,
|
|
206
|
+
modelId: resolved.configured.upstreamModelId,
|
|
207
|
+
};
|
|
193
208
|
}
|
|
194
209
|
// A `codex/<slug>` id only resolves when the per-workspace worker overlay
|
|
195
210
|
// (settingsWithCodexCredential) has injected the synthetic codex-subscription
|
|
@@ -215,14 +230,25 @@ export class MultiProviderModelProvider implements ModelProvider {
|
|
|
215
230
|
// JSON parse/stringify transport wrapper on the fallback path.
|
|
216
231
|
const builtin = configuredProviders(this.settings)[0];
|
|
217
232
|
if (!builtin) throw new Error("Built-in model provider is unavailable");
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
builtin,
|
|
222
|
-
|
|
233
|
+
const client = buildProviderClient(builtin, this.settings);
|
|
234
|
+
const modelId = modelName ?? this.settings.openaiModel;
|
|
235
|
+
return {
|
|
236
|
+
provider: builtin,
|
|
237
|
+
client,
|
|
238
|
+
model: new OpenGeniResponsesModel(client, modelId, builtin),
|
|
239
|
+
modelId,
|
|
240
|
+
};
|
|
223
241
|
}
|
|
224
242
|
}
|
|
225
243
|
|
|
244
|
+
export type ModelProviderBinding = {
|
|
245
|
+
provider: ResolvedModelProvider;
|
|
246
|
+
client: OpenAI;
|
|
247
|
+
model: Model;
|
|
248
|
+
/** The id sent on the provider wire (the upstream id for a registry model). */
|
|
249
|
+
modelId: string;
|
|
250
|
+
};
|
|
251
|
+
|
|
226
252
|
function settingsForRunScopedModelResolution(settings: Settings, modelName: string): Settings {
|
|
227
253
|
if (modelName !== settings.openaiModel) {
|
|
228
254
|
return settings;
|
|
@@ -156,7 +156,7 @@ Skills are reusable instructions supplied dynamically for the current session. W
|
|
|
156
156
|
|
|
157
157
|
# Integration setup
|
|
158
158
|
|
|
159
|
-
Use available integration tools directly. If
|
|
159
|
+
Use available integration tools directly. If access is missing, search \`capability_catalog_search\` first. For a suitable match with \`setup.nextAction\`, call \`capability_authorization_request\` with its ID and a task-specific rationale. If no match exists and the task needs a remote MCP whose exact HTTPS URL the user supplied or reliable documentation establishes, call \`custom_mcp_setup_request\` with its name, URL, and rationale. Never invent URLs or request credentials in chat. Showing either card does not need integration-management permission and grants no access; the authenticated human must authorize setup. A card is for an integration required by the authorized design, including established or delegated choices. Resolve out-of-scope architecture choices before requesting setup. After setup, rediscover tools and verify access. If blocked or a setup tool is unavailable, explain the specific gap.
|
|
160
160
|
|
|
161
161
|
# Session coordination
|
|
162
162
|
|