@yaklabs-ai/plugin-sdk 0.8.4
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/LICENSE +21 -0
- package/README.md +205 -0
- package/THIRD-PARTY-INVENTORY.json +65 -0
- package/THIRD-PARTY-NOTICES.md +103 -0
- package/dist/disclosure.d.ts +3431 -0
- package/dist/disclosure.js +873 -0
- package/dist/kay-agent-testing.d.ts +3294 -0
- package/dist/kay-agent-testing.js +88 -0
- package/dist/kay-agent.d.ts +5286 -0
- package/dist/kay-agent.js +1341 -0
- package/dist/kay-app.d.ts +6303 -0
- package/dist/kay-app.js +424 -0
- package/dist/phase-shift.d.ts +127 -0
- package/dist/phase-shift.js +180 -0
- package/dist/transcript.d.ts +60 -0
- package/dist/transcript.js +23 -0
- package/package.json +59 -0
- package/skills/directory-plugin-authoring/SKILL.md +252 -0
|
@@ -0,0 +1,3431 @@
|
|
|
1
|
+
import type { TypeSafeClient } from '@typesafe-ai/sdk';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Who a connected account *belongs to* — the human, as the provider reports them. Derived at connect
|
|
6
|
+
* time by a scheme's `resolveAccountId` hook from the provider's own profile endpoint, and surfaced
|
|
7
|
+
* to the agent so it never has to ask "what is your email?" for a service the user already
|
|
8
|
+
* connected.
|
|
9
|
+
*
|
|
10
|
+
* Distinct from {@link AccountProvenance}: an account can be firmly identified
|
|
11
|
+
* (`externalId`/`provider`) while nothing is known about its owner, and a workspace's name is not a
|
|
12
|
+
* person's name. Field-for-field the same shape the credential authority persists, so identity
|
|
13
|
+
* crosses the daemon⇄kernel boundary without a lossy projection.
|
|
14
|
+
*/
|
|
15
|
+
declare interface AccountIdentity {
|
|
16
|
+
/** Provider handle (GitHub `login`, Slack `user`). */
|
|
17
|
+
readonly username?: string;
|
|
18
|
+
/** Verified account email, when the provider reports one (Google userinfo). */
|
|
19
|
+
readonly email?: string;
|
|
20
|
+
/** The person's name as the provider renders it (e.g. "Alexander Goddijn"). */
|
|
21
|
+
readonly displayName?: string;
|
|
22
|
+
/** Absolute URL of the account's avatar. */
|
|
23
|
+
readonly avatarUrl?: string;
|
|
24
|
+
/** Issuer-asserted beta-tester flag; only Kay's product-account service sets it. */
|
|
25
|
+
readonly betaTester?: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Provider-native session id claim (WorkOS's `sid`), needed to call the provider's own
|
|
28
|
+
* session-revocation endpoint at sign-out (`POST /user_management/sessions/revoke`, not yet
|
|
29
|
+
* wired — see `docs/kay-account-auth.md`'s "Sign-out guarantee" section). Only Kay's WorkOS
|
|
30
|
+
* product-account exchange/refresh sets it; every other provider leaves it absent.
|
|
31
|
+
*/
|
|
32
|
+
readonly providerSessionId?: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Structured, non-secret *provenance* for a connected account: which record on the provider's side
|
|
37
|
+
* this credential points at. Says nothing about the human — a Notion `displayName` here is the
|
|
38
|
+
* workspace name, a Slack one is the team name. To describe the person, see {@link AccountIdentity}.
|
|
39
|
+
*/
|
|
40
|
+
declare interface AccountProvenance {
|
|
41
|
+
/** Provider-side stable id (e.g. an account/user id), when a scheme can resolve one. */
|
|
42
|
+
readonly externalId?: string;
|
|
43
|
+
/** Provider name the `externalId` is scoped to. */
|
|
44
|
+
readonly provider?: string;
|
|
45
|
+
/** Human-facing account name a scheme can derive (e.g. a workspace/org name). */
|
|
46
|
+
readonly displayName?: string;
|
|
47
|
+
/** Audited, non-core-typed provenance extras (e.g. a provider's workspace_id/owner blob). */
|
|
48
|
+
readonly raw?: Readonly<Record<string, unknown>>;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Final or partial result produced by a tool. */
|
|
52
|
+
declare interface AgentToolResult<T = JsonValue_3 | undefined> {
|
|
53
|
+
/** Text or image content returned to the model. */
|
|
54
|
+
content: (TextContent | ImageContent)[];
|
|
55
|
+
/** Arbitrary structured details for logs or UI rendering. */
|
|
56
|
+
details: T;
|
|
57
|
+
/** Usage from the final tool execution itself, if available. Not used for main LLM context accounting. */
|
|
58
|
+
usage?: Usage;
|
|
59
|
+
/**
|
|
60
|
+
* Hint that the agent should stop after the current tool batch.
|
|
61
|
+
* Early termination only happens when every finalized tool result in the batch sets this to true.
|
|
62
|
+
*/
|
|
63
|
+
terminate?: boolean;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Callback used by tools to stream partial execution updates.
|
|
68
|
+
*
|
|
69
|
+
* The callback is scoped to the current `execute()` invocation. Calls made after
|
|
70
|
+
* the tool promise settles are ignored.
|
|
71
|
+
*/
|
|
72
|
+
declare type AgentToolUpdateCallback<T = any> = (partialResult: AgentToolResult<T>) => void;
|
|
73
|
+
|
|
74
|
+
declare interface AnthropicAllowedFallbackModel {
|
|
75
|
+
provider: ProviderId;
|
|
76
|
+
model: string;
|
|
77
|
+
cost: ModelCost;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Compatibility settings for Anthropic Messages-compatible APIs. */
|
|
81
|
+
declare interface AnthropicMessagesCompat {
|
|
82
|
+
/**
|
|
83
|
+
* Whether the provider accepts per-tool `eager_input_streaming`.
|
|
84
|
+
* When false, the Anthropic provider omits `tools[].eager_input_streaming`
|
|
85
|
+
* and sends the legacy `fine-grained-tool-streaming-2025-05-14` beta header
|
|
86
|
+
* for tool-enabled requests.
|
|
87
|
+
* Default: true.
|
|
88
|
+
*/
|
|
89
|
+
supportsEagerToolInputStreaming?: boolean;
|
|
90
|
+
/** Whether the provider supports Anthropic long cache retention (`cache_control.ttl: "1h"`). Default: true. */
|
|
91
|
+
supportsLongCacheRetention?: boolean;
|
|
92
|
+
/**
|
|
93
|
+
* Whether to send the `x-session-affinity` header from `options.sessionId`
|
|
94
|
+
* when caching is enabled. Required for providers like Fireworks that use
|
|
95
|
+
* session affinity for prompt cache routing (requests to the same replica
|
|
96
|
+
* maximize cache hits).
|
|
97
|
+
* Default: false.
|
|
98
|
+
*/
|
|
99
|
+
sendSessionAffinityHeaders?: boolean;
|
|
100
|
+
/** Session-affinity format. `"openrouter"` sends `x-session-id`; when unset, sends `x-session-affinity`. */
|
|
101
|
+
sessionAffinityFormat?: "openrouter";
|
|
102
|
+
/**
|
|
103
|
+
* Whether the provider supports Anthropic-style `cache_control` markers on
|
|
104
|
+
* tool definitions. When false, `cache_control` is omitted from tool params.
|
|
105
|
+
* Some Anthropic-compatible providers (e.g., Fireworks) do not support this
|
|
106
|
+
* field on tools and may reject or ignore it.
|
|
107
|
+
* Default: true.
|
|
108
|
+
*/
|
|
109
|
+
supportsCacheControlOnTools?: boolean;
|
|
110
|
+
/**
|
|
111
|
+
* Whether the model accepts the Anthropic `temperature` request field.
|
|
112
|
+
* Claude Opus 4.7+ rejects non-default temperature values.
|
|
113
|
+
* Default: true.
|
|
114
|
+
*/
|
|
115
|
+
supportsTemperature?: boolean;
|
|
116
|
+
/**
|
|
117
|
+
* Whether to force adaptive thinking (`thinking.type: "adaptive"` plus
|
|
118
|
+
* `output_config.effort`) regardless of the model id. Built-in models that
|
|
119
|
+
* require adaptive thinking set this in generated metadata. Custom
|
|
120
|
+
* Anthropic-compatible providers can set this to `true` for any model whose
|
|
121
|
+
* upstream requires the adaptive format. Set to `false` to
|
|
122
|
+
* opt out on overridden built-in models.
|
|
123
|
+
* Default: false.
|
|
124
|
+
*/
|
|
125
|
+
forceAdaptiveThinking?: boolean;
|
|
126
|
+
/** Whether to replay empty thinking signatures as `signature: ""` instead of converting thinking to text. Default: false. */
|
|
127
|
+
allowEmptySignature?: boolean;
|
|
128
|
+
/** Whether the provider supports Anthropic strict tool schemas. Default: false; generated Anthropic models enable it explicitly. */
|
|
129
|
+
supportsStrictTools?: boolean;
|
|
130
|
+
/** Whether the exact model transport supports effort-only system messages and thinking binding controls. Default: false. */
|
|
131
|
+
supportsMidConvoEffort?: boolean;
|
|
132
|
+
/** Whether the exact model accepts system-role messages inside the conversation. When false, later system messages are folded into the top-level system prompt. Default: false. */
|
|
133
|
+
supportsMidConvoSystemMessages?: boolean;
|
|
134
|
+
/** Whether the exact model accepts mid-conversation `tool_addition` and `tool_removal` blocks. Requires `supportsMidConvoSystemMessages`. Default: false. */
|
|
135
|
+
supportsMidConvoToolChanges?: boolean;
|
|
136
|
+
/**
|
|
137
|
+
* Models Anthropic accepts in `fallbacks` for server-side refusal fallback,
|
|
138
|
+
* with local pricing metadata for returned fallback responses. When absent or
|
|
139
|
+
* empty, callers must omit `fallbacks`; Anthropic rejects the field for models
|
|
140
|
+
* with no permitted fallback targets.
|
|
141
|
+
*/
|
|
142
|
+
allowedFallbackModels?: AnthropicAllowedFallbackModel[];
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
declare type Api = KnownApi | (string & {});
|
|
146
|
+
|
|
147
|
+
/** One labeled secret input of a multi-field api-key scheme. */
|
|
148
|
+
declare interface ApiKeyAuthSchemeField {
|
|
149
|
+
/** Stable key — the `fields` Record key on the stored material and resolved credential. */
|
|
150
|
+
readonly id: string;
|
|
151
|
+
/** Per-field prompt copy, e.g. "Access Client ID". */
|
|
152
|
+
readonly label: string;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** Provider-owned instructions for creating a credential outside Kay. */
|
|
156
|
+
declare interface ApiKeyAuthSchemeSetup {
|
|
157
|
+
readonly instructions: string;
|
|
158
|
+
/** Absolute HTTPS page where the user can create or manage the credential. */
|
|
159
|
+
readonly url: string;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** One non-secret capability observation made while validating a credential. */
|
|
163
|
+
declare interface ApiKeyCapabilityCheck {
|
|
164
|
+
/** Stable service capability id the scheme checked. */
|
|
165
|
+
readonly capabilityId: string;
|
|
166
|
+
readonly displayName: string;
|
|
167
|
+
readonly status: ApiKeyCapabilityCheckStatus;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/** Whether validation proved the credential can use a capability, proved it cannot, or could not tell. */
|
|
171
|
+
declare type ApiKeyCapabilityCheckStatus = "available" | "unavailable" | "unknown";
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Audit every hint-bearing module in `input.modules` (a module with no `agentHint` renders
|
|
175
|
+
* nothing into the prompt — see `renderUriAgentHints` — so it carries no disclosure surface
|
|
176
|
+
* to audit) against the rules #1138 locks down. Call from a plugin's own test suite, e.g.:
|
|
177
|
+
*
|
|
178
|
+
* ```ts
|
|
179
|
+
* const violations = await auditPluginDisclosure({
|
|
180
|
+
* modules: [gmailModule],
|
|
181
|
+
* declaredSkills: gmailPluginManifest.targets!.agent!.harness!.contributes!.skills!,
|
|
182
|
+
* });
|
|
183
|
+
* expect(violations).toEqual([]);
|
|
184
|
+
* ```
|
|
185
|
+
*/
|
|
186
|
+
export declare function auditPluginDisclosure(input: AuditPluginDisclosureInput): Promise<readonly DisclosureViolation[]>;
|
|
187
|
+
|
|
188
|
+
export declare interface AuditPluginDisclosureInput {
|
|
189
|
+
readonly modules: readonly UriExtensionModule[];
|
|
190
|
+
readonly declaredSkills: readonly DeclaredSkillRef[];
|
|
191
|
+
/**
|
|
192
|
+
* Skills bound to a declared slash command (`CommandContribution.handler.kind ===
|
|
193
|
+
* "skill"`). A command-bound skill has an unconditional discovery path — the command
|
|
194
|
+
* index lists `/<name>` regardless of description keywords — so the unlinked-skill
|
|
195
|
+
* scheme-in-description fallback does not apply to it.
|
|
196
|
+
*/
|
|
197
|
+
readonly commandBoundSkillIds?: readonly string[];
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* The codegen-pair equivalence check every bundled-skill plugin hand-rolled (see
|
|
202
|
+
* `plugins/ARCHITECTURE.md` → "Bundled skills", step 4): every exported `PluginSkillDefinition`
|
|
203
|
+
* must have a matching manifest declaration (same `localId`, same `description`), and the
|
|
204
|
+
* declared `SKILL.md` file's frontmatter + body must match the export byte-for-byte.
|
|
205
|
+
*/
|
|
206
|
+
export declare function auditSkillCodegenPairs(input: AuditSkillCodegenPairsInput): Promise<readonly DisclosureViolation[]>;
|
|
207
|
+
|
|
208
|
+
export declare interface AuditSkillCodegenPairsInput {
|
|
209
|
+
readonly declaredSkills: readonly DeclaredSkillContribution[];
|
|
210
|
+
readonly exportedSkills: readonly PluginSkillDefinition[];
|
|
211
|
+
/**
|
|
212
|
+
* The plugin package's root directory, as a `file:` URL — `declaredSkills[].path` resolves
|
|
213
|
+
* against it. From a test file under `test/`, pass `new URL("../", import.meta.url)`.
|
|
214
|
+
*/
|
|
215
|
+
readonly packageDir: URL;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/** A connected account under one `(serviceId, schemeId)` — the multi-account unit. */
|
|
219
|
+
declare interface AuthAccount {
|
|
220
|
+
readonly accountId: string;
|
|
221
|
+
readonly displayName: string;
|
|
222
|
+
/**
|
|
223
|
+
* Who the account belongs to, carried through from the stored credential. This is the
|
|
224
|
+
* agent-facing account shape (it feeds `auth_accounts` and the ambient facts), so it carries the
|
|
225
|
+
* human but NOT {@link AccountProvenance}: provenance's `raw` is an unbounded provider blob, and
|
|
226
|
+
* an account's provider-side record is not something the agent needs to see. Provenance stays on
|
|
227
|
+
* {@link StoredCredentialSummary} (persistence) and `AuthBrokerAccount` (the host's connection UI).
|
|
228
|
+
*/
|
|
229
|
+
readonly identity?: AccountIdentity;
|
|
230
|
+
/**
|
|
231
|
+
* Trust-lost marker carried through from {@link StoredCredentialSummary.needsReconnect}
|
|
232
|
+
* (kay#7852): the account's credential is dead (revoked or terminally unrefreshable) and the
|
|
233
|
+
* user must reconnect it. The account stays listed — with this flag — so the agent can see it
|
|
234
|
+
* and point the user at a reconnect; token resolution for it still fails closed.
|
|
235
|
+
*/
|
|
236
|
+
readonly needsReconnect?: boolean;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
declare interface BackgroundTaskAdoptOptions {
|
|
240
|
+
readonly preferredId?: string;
|
|
241
|
+
readonly toolCallId?: string;
|
|
242
|
+
readonly ownerId?: string;
|
|
243
|
+
readonly inspect?: BackgroundTaskInspector;
|
|
244
|
+
readonly meta?: Readonly<Record<string, unknown>>;
|
|
245
|
+
/** Scoped settlement hook for the task created by this adoption only. */
|
|
246
|
+
readonly onSettled?: (completion: BackgroundTaskCompletion) => void;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
declare interface BackgroundTaskCompletion {
|
|
250
|
+
readonly taskId: string;
|
|
251
|
+
readonly startedAtMs?: number;
|
|
252
|
+
readonly completedAtMs?: number;
|
|
253
|
+
readonly taskKind: BackgroundTaskKind;
|
|
254
|
+
readonly toolCallId: string;
|
|
255
|
+
readonly ownerId?: string;
|
|
256
|
+
readonly state: Exclude<BackgroundTaskStatus, "running" | "queued">;
|
|
257
|
+
/** Present when the cancelling principal suppresses the model-facing result. */
|
|
258
|
+
readonly resultDelivery?: BackgroundTaskResultDelivery;
|
|
259
|
+
readonly output?: string;
|
|
260
|
+
readonly error?: string;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
declare type BackgroundTaskInspector = () => Promise<string> | string;
|
|
264
|
+
|
|
265
|
+
declare type BackgroundTaskKind = "bash" | "agent";
|
|
266
|
+
|
|
267
|
+
declare type BackgroundTaskResultDelivery = "deliver" | "suppress";
|
|
268
|
+
|
|
269
|
+
declare type BackgroundTaskRun = (ctx: BackgroundTaskRunContext) => Promise<string>;
|
|
270
|
+
|
|
271
|
+
declare interface BackgroundTaskRunContext {
|
|
272
|
+
readonly taskId: string;
|
|
273
|
+
readonly signal: AbortSignal;
|
|
274
|
+
/** `this: void` — task bodies destructure this reporter off the context. */
|
|
275
|
+
reportProgress(this: void, text: string, details?: Record<string, unknown>): void;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
declare interface BackgroundTaskSnapshot {
|
|
279
|
+
readonly id: string;
|
|
280
|
+
readonly taskKind: BackgroundTaskKind;
|
|
281
|
+
readonly toolCallId?: string;
|
|
282
|
+
readonly label: string;
|
|
283
|
+
readonly status: BackgroundTaskStatus;
|
|
284
|
+
readonly ownerId?: string;
|
|
285
|
+
/** Agent tasks link to the ordinary child session they are driving. */
|
|
286
|
+
readonly childSessionId?: string;
|
|
287
|
+
readonly startedAtMs: number;
|
|
288
|
+
readonly updatedAtMs: number;
|
|
289
|
+
readonly meta?: Readonly<Record<string, unknown>>;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
declare type BackgroundTaskStatus = "queued" | "running" | "completed" | "failed" | "cancelled";
|
|
293
|
+
|
|
294
|
+
/** Compatibility settings for Amazon Bedrock models. */
|
|
295
|
+
declare interface BedrockCompat {
|
|
296
|
+
/** Whether the model supports Bedrock strict tool schemas. Default: false. */
|
|
297
|
+
supportsStrictMode?: boolean;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
declare type CacheRetention = "none" | "short" | "long";
|
|
301
|
+
|
|
302
|
+
/** One captured `declareApiKeyScheme(serviceId, scheme)` call, verbatim. */
|
|
303
|
+
export declare interface CapturedApiKeyDeclaration {
|
|
304
|
+
readonly serviceId: string;
|
|
305
|
+
readonly scheme: PluginApiKeyScheme;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/** One captured `declareDelegatedScheme(serviceId, scheme)` call, verbatim. */
|
|
309
|
+
declare interface CapturedDelegatedDeclaration {
|
|
310
|
+
readonly serviceId: string;
|
|
311
|
+
readonly scheme: PluginDelegatedScheme;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/** Everything the auth probe captures from one `activate` run, in call order. */
|
|
315
|
+
export declare interface CapturedKayAgentAuthDeclarations {
|
|
316
|
+
readonly services: readonly PluginServiceDescriptor[];
|
|
317
|
+
readonly oauthPkceSchemes: readonly CapturedOAuthPkceDeclaration[];
|
|
318
|
+
/**
|
|
319
|
+
* Api-key schemes declared this run — the `displayName`/`inputLabel`/`setup`/`validate`
|
|
320
|
+
* runtime declaration a manifest's identity-only scheme row cannot carry. Daemon acquisition
|
|
321
|
+
* composition maps these onto the connect dispatch's api-key roster the same way
|
|
322
|
+
* `oauthPkceSchemes` maps onto the PKCE arm — see `@kay/agent-runtime`'s `kay/composition/`.
|
|
323
|
+
* Empty for a plugin that declares none.
|
|
324
|
+
*/
|
|
325
|
+
readonly apiKeySchemes: readonly CapturedApiKeyDeclaration[];
|
|
326
|
+
/**
|
|
327
|
+
* Delegated schemes declared this run. Daemon acquisition composition maps these onto a
|
|
328
|
+
* delegated connect-roster entry the same way `oauthPkceSchemes` maps onto the PKCE arm — see
|
|
329
|
+
* `@kay/agent-runtime`'s `kay/composition/`. Empty for a plugin that declares none.
|
|
330
|
+
*/
|
|
331
|
+
readonly delegatedSchemes: readonly CapturedDelegatedDeclaration[];
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/** One captured `declareOAuthPkceScheme(serviceId, scheme)` call, verbatim. */
|
|
335
|
+
export declare interface CapturedOAuthPkceDeclaration {
|
|
336
|
+
readonly serviceId: string;
|
|
337
|
+
readonly scheme: PluginOAuthPkceScheme;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
declare type ChatTemplateKwargValue = string | number | boolean | null | {
|
|
341
|
+
$var: "thinking.enabled" | "thinking.effort" | "thinking.budget";
|
|
342
|
+
omitWhenOff?: boolean;
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Runs `activate` once against a fake, capture-only {@link KayAgentPluginApi} and returns every
|
|
347
|
+
* service it advertised and every OAuth-PKCE, api-key, and delegated scheme it declared, in call
|
|
348
|
+
* order. Every other call (`uri.registerModule`, `registerTool`, `declareExternalCliScheme`, …) is
|
|
349
|
+
* a silent no-op: those surfaces are irrelevant to acquisition composition, and a plugin that also
|
|
350
|
+
* calls them must not throw or leak a side effect from this probe.
|
|
351
|
+
*/
|
|
352
|
+
export declare function collectAuthDeclarationsFromKayAgentActivate(activate: KayAgentActivate): CapturedKayAgentAuthDeclarations;
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Runs `activate` once against a fake, capture-only {@link KayAgentPluginApi} and returns
|
|
356
|
+
* every `UriExtensionModule` it registered, in registration order. Every non-`uri.registerModule`
|
|
357
|
+
* call (`registerTool`, `registerSearchProvider`, `registerConverter`, `auth.*`) is a silent
|
|
358
|
+
* no-op: those surfaces are irrelevant to disclosure, and a plugin that also calls them must not
|
|
359
|
+
* throw or leak a side effect from this probe.
|
|
360
|
+
*/
|
|
361
|
+
export declare function collectUriModulesFromKayAgentActivate(activate: KayAgentActivate): readonly UriExtensionModule[];
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* A mime-keyed converter. Registered via the ExtensionApi and merged per session.
|
|
365
|
+
* `matches` is called on every active converter for every binary read, so keep it
|
|
366
|
+
* cheap and side-effect-free; a throw is isolated by the registry as "no match".
|
|
367
|
+
*/
|
|
368
|
+
declare interface ContentConverter {
|
|
369
|
+
/** Stable id for diagnostics/ordering (e.g. "app:pdf", "builtin:identity"). */
|
|
370
|
+
readonly id: string;
|
|
371
|
+
/**
|
|
372
|
+
* Optional; surfaced on the read tool's description when this converter is
|
|
373
|
+
* active. Omit for transparent converters that just improve fidelity without
|
|
374
|
+
* an agent-facing surface.
|
|
375
|
+
*/
|
|
376
|
+
readonly description?: string;
|
|
377
|
+
matches(probe: ContentProbe): boolean;
|
|
378
|
+
convert(bytes: Uint8Array, probe: ContentProbe, ctx: ConvertContext): Promise<ConversionResult>;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* What the converter is asked to convert. `mimeType`/`extension` may be absent if
|
|
383
|
+
* the transport could not determine them; `bytesHead` lets a `matches` predicate
|
|
384
|
+
* sniff magic bytes without holding the whole payload.
|
|
385
|
+
*/
|
|
386
|
+
declare interface ContentProbe {
|
|
387
|
+
readonly mimeType?: string;
|
|
388
|
+
readonly extension?: string;
|
|
389
|
+
readonly filename: string;
|
|
390
|
+
readonly bytesHead?: Uint8Array;
|
|
391
|
+
/**
|
|
392
|
+
* The absolute local filesystem path the bytes came from, threaded in from
|
|
393
|
+
* the transport's {@link RawResource} metadata. Absent for remote sources;
|
|
394
|
+
* lets a converter reference the on-disk file in agent-facing output (e.g.
|
|
395
|
+
* an archive converter directing shell tools at it for extraction).
|
|
396
|
+
*/
|
|
397
|
+
readonly localPath?: string;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* The convert return shape. `content` is the agent-facing
|
|
402
|
+
* {@link import("../shared.ts").ModelContent} — text and/or image blocks — and is
|
|
403
|
+
* the field the read pipeline consumes (finalize windows/truncates TEXT blocks and
|
|
404
|
+
* passes IMAGE blocks through unchanged). `contentType`/`artifacts`/`structure`/
|
|
405
|
+
* `notes` are reserved metadata that no consumer renders yet. The converter does
|
|
406
|
+
* NOT truncate or line-window; {@link finalize} owns that uniformly.
|
|
407
|
+
*/
|
|
408
|
+
declare interface ConversionResult {
|
|
409
|
+
readonly content: readonly ModelContent[];
|
|
410
|
+
/** Reserved metadata; e.g. "text/markdown". */
|
|
411
|
+
readonly contentType?: string;
|
|
412
|
+
/** Reserved metadata; files written to the artifact dir (images-to-disk manifest). */
|
|
413
|
+
readonly artifacts?: readonly ConvertedArtifact[];
|
|
414
|
+
/** Reserved metadata; pages/outline. */
|
|
415
|
+
readonly structure?: ConversionStructure;
|
|
416
|
+
/** Reserved metadata. */
|
|
417
|
+
readonly notes?: readonly string[];
|
|
418
|
+
/** Reserved metadata; whether the converter itself elided content before truncation. */
|
|
419
|
+
readonly truncated?: boolean;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/** Structural summary of the converted document. Reserved metadata. */
|
|
423
|
+
declare interface ConversionStructure {
|
|
424
|
+
readonly pageCount?: number;
|
|
425
|
+
readonly outline?: readonly {
|
|
426
|
+
readonly level: number;
|
|
427
|
+
readonly title: string;
|
|
428
|
+
readonly page?: number;
|
|
429
|
+
}[];
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Per-call context. `cwd`/`signal` mirror {@link UriResolveContext}. `artifactDir`
|
|
434
|
+
* is an app-provided, app-owned directory a converter may write derived files into
|
|
435
|
+
* (e.g. images extracted from a PDF); absent for core standalone callers, in which
|
|
436
|
+
* case a converter must degrade to text-only.
|
|
437
|
+
*/
|
|
438
|
+
declare interface ConvertContext {
|
|
439
|
+
readonly cwd: string;
|
|
440
|
+
readonly signal?: AbortSignal;
|
|
441
|
+
readonly artifactDir?: string;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/** A file a converter wrote to {@link ConvertContext.artifactDir}. Reserved metadata. */
|
|
445
|
+
declare interface ConvertedArtifact {
|
|
446
|
+
readonly id: string;
|
|
447
|
+
readonly path: string;
|
|
448
|
+
readonly kind: "image" | "other";
|
|
449
|
+
readonly page?: number;
|
|
450
|
+
readonly bbox?: readonly number[];
|
|
451
|
+
readonly caption?: string;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/** One skill entry from a plugin's manifest (`targets.kay.agent.contributes.skills`). */
|
|
455
|
+
export declare interface DeclaredSkillContribution {
|
|
456
|
+
readonly localId: string;
|
|
457
|
+
readonly description: string;
|
|
458
|
+
/** Package-relative path to the `SKILL.md`, e.g. `"./skills/email-triage/SKILL.md"`. */
|
|
459
|
+
readonly path: string;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/** The declared-skill shape {@link auditPluginDisclosure}'s cross-link rule needs. */
|
|
463
|
+
export declare interface DeclaredSkillRef {
|
|
464
|
+
readonly localId: string;
|
|
465
|
+
readonly description: string;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* What `service_connect` hands a delegated scheme's {@link DelegatedAuthScheme.connect}: the
|
|
470
|
+
* scope being connected plus the host's browser-redirect capability when one is configured.
|
|
471
|
+
* `authorize`/`redirectUri` wrap the host {@link import("./oauth-pkce.ts").OAuthRedirectTransport}
|
|
472
|
+
* — both absent when the host has none; the scheme decides whether it needs them.
|
|
473
|
+
*/
|
|
474
|
+
declare interface DelegatedConnectContext {
|
|
475
|
+
readonly serviceId: string;
|
|
476
|
+
readonly schemeId: string;
|
|
477
|
+
/** The freshly-minted accountId the material will be stored under. */
|
|
478
|
+
readonly accountId: string;
|
|
479
|
+
readonly signal?: AbortSignal;
|
|
480
|
+
/** The transport's redirect URI, for building redirect URLs; absent without a transport. */
|
|
481
|
+
readonly redirectUri?: string;
|
|
482
|
+
/** Open `authorizeUrl` for the user and resolve with the provider's callback params. */
|
|
483
|
+
readonly authorize?: (authorizeUrl: string, expectedState: string) => Promise<OAuthRedirectResult>;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/** What a delegated scheme's `connect` resolves with: the material to vault + optional label/provenance. */
|
|
487
|
+
declare interface DelegatedConnectResult {
|
|
488
|
+
readonly material: DelegatedSecretMaterial;
|
|
489
|
+
/** Account display label; a caller-supplied `service_connect` label takes precedence. */
|
|
490
|
+
readonly label?: string;
|
|
491
|
+
/** Structured provenance the scheme derived during connect; persisted via the vault's write-path. */
|
|
492
|
+
readonly provenance?: AccountProvenance;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* Token material minted by a delegated scheme's extension-provided flow. Core reads only
|
|
497
|
+
* `accessToken`/`expiresAt` (resolution + proactive refresh); `data` is an opaque blob the
|
|
498
|
+
* scheme round-trips through `refresh` (refresh token, DCR client info, …) — never read by core.
|
|
499
|
+
*/
|
|
500
|
+
declare interface DelegatedSecretMaterial {
|
|
501
|
+
readonly kind: "delegated";
|
|
502
|
+
readonly accessToken: string;
|
|
503
|
+
/** ISO-8601 access-token expiry; absent means non-expiring. */
|
|
504
|
+
readonly expiresAt?: string;
|
|
505
|
+
/** Scheme-owned state persisted verbatim alongside the token; opaque to core. */
|
|
506
|
+
readonly data?: Readonly<Record<string, unknown>>;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* The subset of a {@link ToolAction}'s rule-matching fields that a {@link ToolPermissionHint}
|
|
511
|
+
* can derive per-call via {@link ToolPermissionHint.deriveClassification}. Every field is
|
|
512
|
+
* optional: a present field overrides the static hint for that call; an absent field falls back
|
|
513
|
+
* to the static hint (then to the fail-closed default). `resources` stays a separate deriver
|
|
514
|
+
* ({@link ToolPermissionHint.deriveResources}) — these are the fields that decide which rule a
|
|
515
|
+
* call matches, not the paths it touches.
|
|
516
|
+
*/
|
|
517
|
+
declare interface DerivedToolClassification {
|
|
518
|
+
readonly kind?: ToolActionKind;
|
|
519
|
+
readonly ruleKey?: string;
|
|
520
|
+
readonly scheme?: string;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/** One authoring-quality defect found in a plugin's disclosure surfaces. */
|
|
524
|
+
export declare interface DisclosureViolation {
|
|
525
|
+
/**
|
|
526
|
+
* The disclosure surface the violation belongs to: a `UriExtensionModule.id` for every
|
|
527
|
+
* `auditPluginDisclosure` module-shaped rule, or a skill's `localId` for the rules that
|
|
528
|
+
* inspect a declared skill's own description (there is no module in play there — the
|
|
529
|
+
* `localId` is the closest thing to that surface's identity).
|
|
530
|
+
*/
|
|
531
|
+
readonly moduleId: string;
|
|
532
|
+
/** A stable, kebab-case rule id — grep this in the audit source to see exactly what fired. */
|
|
533
|
+
readonly rule: PluginDisclosureRule;
|
|
534
|
+
readonly detail: string;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* The execute-time auth surface the SDK wrappers read off `ctx.auth`. Slice 3 attaches a
|
|
539
|
+
* concrete implementation (vault + pkce refresh + active-account store) to ExtensionContext;
|
|
540
|
+
* defining the contract here lets the wrappers compile and be unit-tested against a double.
|
|
541
|
+
*/
|
|
542
|
+
declare interface ExtensionAuthContext {
|
|
543
|
+
/**
|
|
544
|
+
* Resolve a live token for a service and an account: the explicit `accountId` when given, else
|
|
545
|
+
* the session-active account. `schemeId` is a preference, not an address — an explicit account
|
|
546
|
+
* resolves against its OWN backing scheme even when a different scheme is named/bound, and an
|
|
547
|
+
* absent `schemeId` resolves service-level across the injectable schemes. Refreshes oauth near
|
|
548
|
+
* expiry under the hood. Returns an actionable {@link ResolveTokenFailure} (never throws) when
|
|
549
|
+
* there is no connection or the account is ambiguous, so wrappers can short-circuit gracefully.
|
|
550
|
+
*/
|
|
551
|
+
readonly resolveToken: (input: {
|
|
552
|
+
readonly serviceId: string;
|
|
553
|
+
readonly schemeId?: string;
|
|
554
|
+
readonly accountId?: string;
|
|
555
|
+
/**
|
|
556
|
+
* The invoking capability's `requiresAuth.requiredScopes`, threaded down to the vault load so a
|
|
557
|
+
* grant-enforcing vault can refuse a plugin whose granted scopes don't cover them. The
|
|
558
|
+
* caller identity itself is not a per-call argument — it is fixed by the owner-scoped context
|
|
559
|
+
* (see {@link CreateExtensionAuthContextInput.callerId}).
|
|
560
|
+
*/
|
|
561
|
+
readonly requiredScopes?: readonly string[];
|
|
562
|
+
/** Ordered scope alternatives; mutually exclusive with `requiredScopes`. */
|
|
563
|
+
readonly scopeRequirement?: ScopeRequirement;
|
|
564
|
+
/**
|
|
565
|
+
* The binding's front-door service id (`requiresAuth.connectServiceId`), threaded so a
|
|
566
|
+
* scope-shortfall failure can name the connect that actually grants the missing scopes
|
|
567
|
+
* instead of the credential service (whose re-connect cannot). Guidance only.
|
|
568
|
+
*/
|
|
569
|
+
readonly connectServiceId?: string;
|
|
570
|
+
}) => Promise<ResolveTokenResult>;
|
|
571
|
+
/**
|
|
572
|
+
* The session-active accountId, or `undefined`. Scheme-scoped when `schemeId` is given; with it
|
|
573
|
+
* absent, the service's most recent selection across all schemes (service-level view).
|
|
574
|
+
*/
|
|
575
|
+
readonly getActiveAccount: (input: {
|
|
576
|
+
readonly serviceId: string;
|
|
577
|
+
readonly schemeId?: string;
|
|
578
|
+
}) => Promise<string | undefined>;
|
|
579
|
+
/**
|
|
580
|
+
* Connected accounts (multi-account discovery). Scheme-scoped when `schemeId` is given; with it
|
|
581
|
+
* absent, the union across the service's injectable schemes (first scheme wins an id collision).
|
|
582
|
+
*/
|
|
583
|
+
readonly listAccounts: (input: {
|
|
584
|
+
readonly serviceId: string;
|
|
585
|
+
readonly schemeId?: string;
|
|
586
|
+
}) => Promise<readonly AuthAccount[]>;
|
|
587
|
+
/**
|
|
588
|
+
* Resolve a non-injectable external-CLI readiness marker into a protocol runner. No secret
|
|
589
|
+
* material leaves the auth subsystem; the host runner receives the stored binary plus argv.
|
|
590
|
+
*/
|
|
591
|
+
readonly resolveExternalCli: (input: {
|
|
592
|
+
readonly serviceId: string;
|
|
593
|
+
readonly schemeId: string;
|
|
594
|
+
readonly accountId?: string;
|
|
595
|
+
}) => Promise<ResolveExternalCliResult>;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
/** Optional extension-facing APIs for background task orchestration. */
|
|
599
|
+
declare interface ExtensionBackgroundTasksApi {
|
|
600
|
+
adopt(taskKind: BackgroundTaskKind, label: string, run: BackgroundTaskRun, options?: BackgroundTaskAdoptOptions): string;
|
|
601
|
+
list(): readonly BackgroundTaskSnapshot[];
|
|
602
|
+
get(taskId: string): BackgroundTaskSnapshot | undefined;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
/**
|
|
606
|
+
* Host-provided, per-extension writable data root surfaced on {@link ToolContext.paths}. Isolation is
|
|
607
|
+
* the DIRECTORY itself — exactly like the per-extension SQLite db files keyed by extension id: the host
|
|
608
|
+
* provisions a distinct path per owning extension, the extension owns everything inside it. The kernel
|
|
609
|
+
* never decides *where* this lives; it carries an opaque host-resolved path and exposes it, so an
|
|
610
|
+
* extension can read/write files (caches, scratch state, downloads) under a sanctioned root without the
|
|
611
|
+
* kernel learning any host storage policy.
|
|
612
|
+
*/
|
|
613
|
+
declare interface ExtensionPaths {
|
|
614
|
+
/** Absolute path to this extension's writable data directory (created before the context is built). */
|
|
615
|
+
readonly dataDir: string;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
/**
|
|
619
|
+
* Whether a scheme acts *as the user* (the fully-built PKCE path) or *on behalf of the user
|
|
620
|
+
* as an app* (a declared seam — see {@link OAuthPkceAuthScheme.mintToken}). It's a property of
|
|
621
|
+
* the scheme, not the call, so the resolver knows the identity without per-call ambiguity.
|
|
622
|
+
*/
|
|
623
|
+
declare type IdentityMode = "as-user" | "on-behalf-of-app";
|
|
624
|
+
|
|
625
|
+
declare interface ImageContent {
|
|
626
|
+
type: "image";
|
|
627
|
+
data: string;
|
|
628
|
+
mimeType: string;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
declare type JsonObject = {
|
|
632
|
+
[key: string]: JsonValue_3;
|
|
633
|
+
};
|
|
634
|
+
|
|
635
|
+
declare type JsonPrimitive = string | number | boolean | null;
|
|
636
|
+
|
|
637
|
+
declare type JsonSchema = Record<string, unknown>;
|
|
638
|
+
|
|
639
|
+
declare type JsonValue = JsonPrimitive | {
|
|
640
|
+
readonly [key: string]: JsonValue;
|
|
641
|
+
} | readonly JsonValue[];
|
|
642
|
+
|
|
643
|
+
declare type JsonValue_2 = string | number | boolean | null | JsonValue_2[] | {
|
|
644
|
+
[key: string]: JsonValue_2;
|
|
645
|
+
};
|
|
646
|
+
|
|
647
|
+
declare const JsonValue_2: z.ZodType<JsonValue_2>;
|
|
648
|
+
|
|
649
|
+
declare type JsonValue_3 = null | boolean | number | string | readonly JsonValue_3[] | JsonObject;
|
|
650
|
+
|
|
651
|
+
/** A plugin's Kay agent activation — receives only the brokered {@link KayAgentPluginApi}. */
|
|
652
|
+
declare type KayAgentActivate = (api: KayAgentPluginApi) => void;
|
|
653
|
+
|
|
654
|
+
/**
|
|
655
|
+
* Publishes the plugin's declared events from agent code. See {@link KayAgentPluginApi.events}.
|
|
656
|
+
*/
|
|
657
|
+
declare interface KayAgentEvents {
|
|
658
|
+
/**
|
|
659
|
+
* Publish one event the plugin declares in its `kay.app` `eventDefinitions`, with a payload that
|
|
660
|
+
* matches that definition's `payloadSchema`.
|
|
661
|
+
*
|
|
662
|
+
* The host stamps the publisher (this plugin) and the session this activation belongs to; the
|
|
663
|
+
* plugin names neither. Fire-and-forget: it returns nothing and never throws. A publish the host
|
|
664
|
+
* refuses (an undeclared event, an invalid payload, the plugin's `kay.app` target not running)
|
|
665
|
+
* is reported to the host's diagnostics and dropped, so the work that raised the event never
|
|
666
|
+
* fails because the event did not go out.
|
|
667
|
+
*/
|
|
668
|
+
publish(localId: string, payload: unknown): void;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
/** Claim-checked filesystem operations. Paths are root-relative; canonical host paths never cross the seam. */
|
|
672
|
+
declare interface KayAgentFilesystem {
|
|
673
|
+
readText(location: KayAgentFilesystemLocation): Promise<string>;
|
|
674
|
+
writeText(location: KayAgentFilesystemLocation, content: string): Promise<void>;
|
|
675
|
+
mkdir(location: KayAgentFilesystemLocation): Promise<void>;
|
|
676
|
+
readDir(location: KayAgentFilesystemLocation): Promise<readonly KayAgentFilesystemEntry[]>;
|
|
677
|
+
stat(location: KayAgentFilesystemLocation): Promise<KayAgentFilesystemStat>;
|
|
678
|
+
rename(from: KayAgentFilesystemLocation, to: KayAgentFilesystemLocation): Promise<void>;
|
|
679
|
+
/** Returns the canonical path relative to the selected grant root. */
|
|
680
|
+
realpath(location: KayAgentFilesystemLocation): Promise<string>;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
declare interface KayAgentFilesystemEntry {
|
|
684
|
+
readonly name: string;
|
|
685
|
+
readonly kind: "file" | "directory" | "symlink" | "other";
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
declare interface KayAgentFilesystemLocation {
|
|
689
|
+
/** Exact `root` selector the host bound for this target, plus a path relative to it. */
|
|
690
|
+
readonly root: string;
|
|
691
|
+
readonly path: string;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
declare interface KayAgentFilesystemStat {
|
|
695
|
+
readonly kind: "file" | "directory" | "other";
|
|
696
|
+
readonly size: number;
|
|
697
|
+
readonly modifiedAtMs: number;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
/** One knowledge-index hit — a reference and within-list rank score, never a snippet. */
|
|
701
|
+
declare interface KayAgentKnowledgeHit {
|
|
702
|
+
readonly uri: string;
|
|
703
|
+
/** Comparable only with other hits returned by this search call. */
|
|
704
|
+
readonly score?: number;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
/**
|
|
708
|
+
* Query the daemon's shared knowledge index, scoped to this plugin's own declared `kay.agent`
|
|
709
|
+
* schemes. Deny-by-default: `api.knowledgeSearch` is absent unless the manifest requests the
|
|
710
|
+
* `knowledgeSearch` grant AND the host effectively grants it — a plugin must feature-detect the
|
|
711
|
+
* capability. (It is WIRING, so absence is still the signal here, unlike the claim-checked
|
|
712
|
+
* capabilities around it.) The host pre-scopes every call to the
|
|
713
|
+
* plugin's OWN declared URI schemes; a caller-supplied `uriPrefixes` outside that scope is dropped
|
|
714
|
+
* rather than widening it (fail closed), so an all-out-of-scope request resolves an empty result
|
|
715
|
+
* rather than silently reaching another plugin's address space. Rank order is authoritative; each
|
|
716
|
+
* hit also carries the source score for display or comparison within that one returned list only.
|
|
717
|
+
*/
|
|
718
|
+
declare type KayAgentKnowledgeSearch = (query: string, opts?: {
|
|
719
|
+
readonly limit?: number;
|
|
720
|
+
readonly uriPrefixes?: readonly string[];
|
|
721
|
+
}) => Promise<readonly KayAgentKnowledgeHit[]>;
|
|
722
|
+
|
|
723
|
+
/**
|
|
724
|
+
* One row of the daemon's known-resources ledger: a resource the host has
|
|
725
|
+
* observed flowing through one of this plugin's own URI modules (or recorded
|
|
726
|
+
* through a host-side grant flow). Metadata facts, never content — content
|
|
727
|
+
* understanding stays with the knowledge index.
|
|
728
|
+
*/
|
|
729
|
+
declare interface KayAgentKnownResource {
|
|
730
|
+
/** The module's canonical URI for the resource. */
|
|
731
|
+
readonly uri: string;
|
|
732
|
+
/** The resolved credential's account id at recording time; absent for unauthed sightings. */
|
|
733
|
+
readonly accountId?: string;
|
|
734
|
+
readonly title?: string;
|
|
735
|
+
readonly summary?: string;
|
|
736
|
+
readonly mimeType?: string;
|
|
737
|
+
/** Most recent provenance; the DURABLE grant fact lives in `grantedVia`, not here. */
|
|
738
|
+
readonly seenVia: KayAgentKnownResourceSeenVia;
|
|
739
|
+
/**
|
|
740
|
+
* Durable grant provenance (`picked` for a Drive-picker grant). Unlike `seenVia`, a later
|
|
741
|
+
* ordinary sighting never erases it — the row permanently remembers it entered the corpus
|
|
742
|
+
* through an explicit user grant.
|
|
743
|
+
*/
|
|
744
|
+
readonly grantedVia?: string;
|
|
745
|
+
/** When the `grantedVia` grant was last made. */
|
|
746
|
+
readonly grantedAt?: string;
|
|
747
|
+
readonly firstSeen: string;
|
|
748
|
+
readonly lastSeen: string;
|
|
749
|
+
/** Last successful full read; absent means seen in listings but never opened. */
|
|
750
|
+
readonly lastRead?: string;
|
|
751
|
+
/** Set when the last resolution found the resource gone; cleared by any later successful sighting. */
|
|
752
|
+
readonly goneAt?: string;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
/**
|
|
756
|
+
* Read back this plugin's own rows from the daemon's known-resources ledger
|
|
757
|
+
* (the host-recorded memory that `contributes.uriModules[].knownResources`
|
|
758
|
+
* opts a module's traffic into). Deny-by-default: `api.knownResources` is
|
|
759
|
+
* absent unless the manifest requests the `knownResources` grant AND the host
|
|
760
|
+
* effectively grants it — feature-detect it exactly like
|
|
761
|
+
* {@link KayAgentKnowledgeSearch}, the other WIRING capability. Every call is host-scoped to the plugin's
|
|
762
|
+
* OWN declared module ids; there is no way to widen it. Read-only by design:
|
|
763
|
+
* rows are created only by the host's recording wrap and host-side grant
|
|
764
|
+
* writers, so a plugin can render its memory but never invent it. The ledger
|
|
765
|
+
* is a cache of CLAIMS, not an authority — a returned row proves the resource
|
|
766
|
+
* was once seen, never that it is still reachable.
|
|
767
|
+
*/
|
|
768
|
+
declare interface KayAgentKnownResources {
|
|
769
|
+
query(opts?: {
|
|
770
|
+
/** Restrict to sightings recorded under one account id. */
|
|
771
|
+
readonly accountId?: string;
|
|
772
|
+
/** Case-insensitive substring match against uri and title. */
|
|
773
|
+
readonly text?: string;
|
|
774
|
+
/** Exact match against the row's most recent `seenVia`. */
|
|
775
|
+
readonly seenVia?: string;
|
|
776
|
+
/** True: only rows carrying a durable grant (`grantedVia` set). */
|
|
777
|
+
readonly grantedOnly?: boolean;
|
|
778
|
+
/** Default false: rows marked gone are excluded unless asked for. */
|
|
779
|
+
readonly includeGone?: boolean;
|
|
780
|
+
readonly limit?: number;
|
|
781
|
+
readonly cursor?: string;
|
|
782
|
+
}): Promise<{
|
|
783
|
+
readonly rows: readonly KayAgentKnownResource[];
|
|
784
|
+
readonly nextCursor?: string;
|
|
785
|
+
}>;
|
|
786
|
+
get(uri: string, opts?: {
|
|
787
|
+
readonly accountId?: string;
|
|
788
|
+
}): Promise<KayAgentKnownResource | undefined>;
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
/**
|
|
792
|
+
* How a known-resource row was MOST RECENTLY seen: one of the recording
|
|
793
|
+
* wrap's verbs, or a host-side grant writer's `granted:<label>` (e.g. a
|
|
794
|
+
* Drive picker completion writing `granted:picked`). The closed template
|
|
795
|
+
* union keeps the contract honest without enumerating grant labels — a
|
|
796
|
+
* plain-string widening here would silently admit junk provenance.
|
|
797
|
+
*/
|
|
798
|
+
declare type KayAgentKnownResourceSeenVia = "read" | "list" | "search" | `granted:${string}`;
|
|
799
|
+
|
|
800
|
+
/**
|
|
801
|
+
* Claim-checked outbound HTTP. The handle is always present; authority is the CALL, not the handle.
|
|
802
|
+
*
|
|
803
|
+
* The signature is `fetch`'s on purpose: a plugin that already speaks `fetch` changes the object it
|
|
804
|
+
* calls, not the code around it. What differs is invisible from here — the host builds an exact
|
|
805
|
+
* `network.connect {connect}` claim from the live URL (and again from each redirect hop), checks it
|
|
806
|
+
* against the user's grant ledger, and rejects with a {@link KayAgentCapabilityError} rather than
|
|
807
|
+
* performing the request. There is no `allowedHosts` to read: a plugin cannot enumerate its own
|
|
808
|
+
* authority, because that set is live and would be stale the moment it was handed over.
|
|
809
|
+
*/
|
|
810
|
+
declare interface KayAgentNetwork {
|
|
811
|
+
fetch(input: string | URL | Request, init?: RequestInit): Promise<Response>;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
/**
|
|
815
|
+
* The API a plugin's `kay.agent` entrypoint receives. Every sensitive operation is brokered
|
|
816
|
+
* here; the plugin never touches an `ExtensionApi`, `child_process`, credentials, or Electron.
|
|
817
|
+
*/
|
|
818
|
+
declare interface KayAgentPluginApi {
|
|
819
|
+
readonly uri: {
|
|
820
|
+
/** Register a whole URI module (a scheme handler that owns its address space, e.g. `drive://`). */
|
|
821
|
+
registerModule(module: UriExtensionModule): void;
|
|
822
|
+
};
|
|
823
|
+
/**
|
|
824
|
+
* Always present; each call is checked against `filesystem.read {root}` / `filesystem.write
|
|
825
|
+
* {root}` for the root it touches, and against the host's binding for that root.
|
|
826
|
+
*/
|
|
827
|
+
readonly filesystem: KayAgentFilesystem;
|
|
828
|
+
/** Absent unless the `knowledgeSearch` grant is requested and effectively granted. */
|
|
829
|
+
readonly knowledgeSearch?: KayAgentKnowledgeSearch;
|
|
830
|
+
/** Absent unless the `knownResources` grant is requested and effectively granted. */
|
|
831
|
+
readonly knownResources?: KayAgentKnownResources;
|
|
832
|
+
/**
|
|
833
|
+
* Present when the host composes Whiteboard. Each operation is checked against its exact
|
|
834
|
+
* `whiteboard.<operation> {scope}` claim; handle presence is not consent.
|
|
835
|
+
*/
|
|
836
|
+
readonly whiteboard?: KayAgentWhiteboard;
|
|
837
|
+
/**
|
|
838
|
+
* Always present; each request — and each redirect hop — is checked against
|
|
839
|
+
* `network.connect {connect}` for the host it is about to reach.
|
|
840
|
+
*/
|
|
841
|
+
readonly network: KayAgentNetwork;
|
|
842
|
+
/**
|
|
843
|
+
* Always present; each call is checked against `web-session.request {origin, pathPrefix}` or
|
|
844
|
+
* `web-session.download {origin, pathPrefix}` for the declared row the URL matches.
|
|
845
|
+
*/
|
|
846
|
+
readonly webSession: KayAgentWebSession;
|
|
847
|
+
/**
|
|
848
|
+
* Always present; each run is checked against `process.spawn {command, argvClass}` for the
|
|
849
|
+
* command it is about to run, and against the host's executable binding.
|
|
850
|
+
*/
|
|
851
|
+
readonly process: KayAgentProcess;
|
|
852
|
+
/**
|
|
853
|
+
* Jev typed-decision inference, as an OPTIONAL accelerator with a REQUIRED fallback. Each call
|
|
854
|
+
* is checked against `jev.use` — but a refused grant or a missing saved key both
|
|
855
|
+
* produce the same outcome: your `fallback` runs.
|
|
856
|
+
*
|
|
857
|
+
* Always present, on every host. That is the point: with no absent state to feature-detect,
|
|
858
|
+
* there is no shape of code that reaches Jev without having written the baseline too. Do not
|
|
859
|
+
* guard it with `if (api.withJev !== undefined)`.
|
|
860
|
+
*/
|
|
861
|
+
readonly withJev: WithJev;
|
|
862
|
+
/**
|
|
863
|
+
* Publish the plugin's declared events, for example so a default notification rule can tell the
|
|
864
|
+
* user what the agent just did. Always present, like {@link withJev}: there is nothing to
|
|
865
|
+
* feature-detect, and a host that composed no events lane drops what is published.
|
|
866
|
+
*/
|
|
867
|
+
readonly events: KayAgentEvents;
|
|
868
|
+
/**
|
|
869
|
+
* Register a tool (a named function the agent can call). Parallel to the kernel's `registerTool`.
|
|
870
|
+
* To auth-gate a tool, build the definition with {@link withResolvedAuth}: it stamps the
|
|
871
|
+
* `requiresAuth` binding onto the definition and resolves a live token from the kernel-injected
|
|
872
|
+
* `ctx.auth` before the body runs — the same wrapper the built-in service tools use. A definition
|
|
873
|
+
* with no `requiresAuth` is an ordinary always-on tool.
|
|
874
|
+
*/
|
|
875
|
+
registerTool(definition: ToolDefinition): void;
|
|
876
|
+
/**
|
|
877
|
+
* Register a web search provider (a backend behind the single `web_search` tool). Parallel to the
|
|
878
|
+
* kernel's `registerSearchProvider`. An own-credential provider carries its `requiresAuth` binding —
|
|
879
|
+
* build it with {@link withResolvedSearchProvider}, which stamps the binding and resolves the token
|
|
880
|
+
* from `ctx.auth`. A provider without a binding reuses inference credentials or needs none.
|
|
881
|
+
*/
|
|
882
|
+
registerSearchProvider(provider: SearchProvider): void;
|
|
883
|
+
/**
|
|
884
|
+
* Register a content converter (turns raw bytes of a mime type into agent-facing content behind the
|
|
885
|
+
* read tool's finalize stage). Parallel to the kernel's `registerConverter`. Converters are
|
|
886
|
+
* auth-free: the injected credential never reaches them, so there is no `requiresAuth` binding here.
|
|
887
|
+
*/
|
|
888
|
+
registerConverter(converter: ContentConverter): void;
|
|
889
|
+
/**
|
|
890
|
+
* Always present; the registration is checked against `tool-interception.register`. Register
|
|
891
|
+
* a tool-call interceptor — the neutral around-execution seam over EVERY tool call in the session
|
|
892
|
+
* (any layer's tools, subagent sessions included): observe a call, delegate to the real tool via
|
|
893
|
+
* `next()`, or return a substitute result so the real tool never runs. Materially higher
|
|
894
|
+
* privilege than `registerTool` (which owns only its own tool), which is why the registration is
|
|
895
|
+
* a claim of its own. The permission gate still runs before execution — an interceptor can
|
|
896
|
+
* substitute results, never widen what is callable.
|
|
897
|
+
*/
|
|
898
|
+
interceptToolCall(interceptor: ToolCallInterceptor): void;
|
|
899
|
+
readonly auth: {
|
|
900
|
+
advertiseService(descriptor: PluginServiceDescriptor): void;
|
|
901
|
+
declareOAuthPkceScheme(serviceId: string, scheme: PluginOAuthPkceScheme): void;
|
|
902
|
+
declareApiKeyScheme(serviceId: string, scheme: PluginApiKeyScheme): void;
|
|
903
|
+
declareExternalCliScheme(serviceId: string, scheme: PluginExternalCliScheme): void;
|
|
904
|
+
/**
|
|
905
|
+
* Declare a delegated scheme (plugin-provided connect/refresh over opaque material) for a
|
|
906
|
+
* service. Mirrors the other three declarations; the host maps it onto the kernel's
|
|
907
|
+
* `DelegatedAuthScheme` and drives its `connect` from `service_connect`.
|
|
908
|
+
*/
|
|
909
|
+
declareDelegatedScheme(serviceId: string, scheme: PluginDelegatedScheme): void;
|
|
910
|
+
/**
|
|
911
|
+
* Register a synchronous source for delegated services whose membership may change while the
|
|
912
|
+
* session is running. Each service registry read receives the source's current snapshot. Absent
|
|
913
|
+
* on compatibility hosts; plugins must fall back to static advertisement when unavailable.
|
|
914
|
+
*/
|
|
915
|
+
registerDelegatedServiceSource?(source: () => readonly PluginDelegatedServiceEntry[]): void;
|
|
916
|
+
};
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
/**
|
|
920
|
+
* Claim-checked process execution. The handle is always present; each run is decided on its own,
|
|
921
|
+
* against an exact `process.spawn {command, argvClass}` claim and the host's executable binding.
|
|
922
|
+
*
|
|
923
|
+
* `run` only ever RESOLVES for a process the host actually started, so a non-zero
|
|
924
|
+
* {@link KayAgentProcessRunResult.exitCode} always means the command itself failed, never that
|
|
925
|
+
* permission did. Two DIFFERENT things reject, and a caller should handle both: a refusal rejects
|
|
926
|
+
* with {@link KayAgentCapabilityError}, and cancelling through
|
|
927
|
+
* {@link KayAgentProcessRunRequest.signal} rejects with an `AbortError` carrying a fixed message —
|
|
928
|
+
* the host terminates the child and settles its output first, and deliberately does not echo back
|
|
929
|
+
* the `reason` you aborted with. A user cancelling a run is not a crash. A timeout is NOT a
|
|
930
|
+
* rejection: it resolves with exit code 124. The termination bound covers the DIRECT child only: a
|
|
931
|
+
* command that forks its own helper (a wrapper script, a build tool) can leave that helper running
|
|
932
|
+
* after a timeout or an abort, even though the run itself reports it stopped (#6153).
|
|
933
|
+
*/
|
|
934
|
+
declare interface KayAgentProcess {
|
|
935
|
+
run(request: KayAgentProcessRunRequest): Promise<KayAgentProcessRunResult>;
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
/**
|
|
939
|
+
* One process the plugin asks the host to run.
|
|
940
|
+
*
|
|
941
|
+
* `argvClass` is required and is NOT decoration: it names the class of argument vector this
|
|
942
|
+
* invocation belongs to, and it must match a class the plugin declared alongside `command` in its
|
|
943
|
+
* manifest. Declaring a command alone would authorize `git log` and `git push --force` identically;
|
|
944
|
+
* the class is what a user can be shown and asked to consent to. The host judges the real `args`
|
|
945
|
+
* against the named class and refuses with `argv-outside-class` when they do not belong to it.
|
|
946
|
+
*/
|
|
947
|
+
declare interface KayAgentProcessRunRequest {
|
|
948
|
+
readonly command: string;
|
|
949
|
+
readonly argvClass: string;
|
|
950
|
+
readonly args: readonly string[];
|
|
951
|
+
/** Abort the run; the host kills the DIRECT child and rejects — a forked descendant may outlive it (#6153). */
|
|
952
|
+
readonly signal?: AbortSignal;
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
/**
|
|
956
|
+
* A finished run. `stdout`/`stderr` are decoded text, and the host CAPS them — a truncation flag is
|
|
957
|
+
* set rather than the output silently ending, so a plugin parsing the stream can tell "the command
|
|
958
|
+
* printed this" apart from "the command printed more than we kept".
|
|
959
|
+
*/
|
|
960
|
+
declare interface KayAgentProcessRunResult {
|
|
961
|
+
readonly exitCode: number;
|
|
962
|
+
readonly stdout: string;
|
|
963
|
+
readonly stderr: string;
|
|
964
|
+
readonly stdoutTruncated?: boolean;
|
|
965
|
+
readonly stderrTruncated?: boolean;
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
/**
|
|
969
|
+
* Authenticated requests through the host's embedded-browser partition. The host confines every
|
|
970
|
+
* call to the exact origin/path rows declared in the plugin manifest (segment-bounded), never
|
|
971
|
+
* follows a redirect of the call, and injects CSRF material without returning it. Downloads stream
|
|
972
|
+
* to a host-created temporary file. A malformed call throws `TypeError` before any host round-trip;
|
|
973
|
+
* everything the session or provider does wrong arrives as {@link KayAgentWebSessionError}.
|
|
974
|
+
*/
|
|
975
|
+
declare interface KayAgentWebSession {
|
|
976
|
+
request(call: KayAgentWebSessionCall): Promise<KayAgentWebSessionResponse>;
|
|
977
|
+
download(call: KayAgentWebSessionDownload): Promise<KayAgentWebSessionDownloadResult>;
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
declare interface KayAgentWebSessionCall {
|
|
981
|
+
readonly url: string;
|
|
982
|
+
readonly method: "GET" | "POST";
|
|
983
|
+
readonly headers?: Readonly<Record<string, string>>;
|
|
984
|
+
readonly body?: string;
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
declare interface KayAgentWebSessionDownload extends KayAgentWebSessionCall {
|
|
988
|
+
/** Suggested basename only. The host sanitizes it and owns the destination directory. */
|
|
989
|
+
readonly filename?: string;
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
declare interface KayAgentWebSessionDownloadResult {
|
|
993
|
+
/** The file on disk; authoritative. Its basename is host-chosen and is not `filename`. */
|
|
994
|
+
readonly localPath: string;
|
|
995
|
+
/** The provider-reported name (content-disposition) or the suggested one, sanitized. */
|
|
996
|
+
readonly filename: string;
|
|
997
|
+
readonly mimeType?: string;
|
|
998
|
+
readonly size: number;
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
declare interface KayAgentWebSessionResponse {
|
|
1002
|
+
readonly status: number;
|
|
1003
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
1004
|
+
readonly body: string;
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
declare interface KayAgentWhiteboard {
|
|
1008
|
+
listTypes(): Promise<readonly WhiteboardTypeInfo[]>;
|
|
1009
|
+
forType(typeId: string): KayAgentWhiteboardTypeHandle;
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
declare interface KayAgentWhiteboardCreateInput {
|
|
1013
|
+
readonly name: string;
|
|
1014
|
+
readonly title: string;
|
|
1015
|
+
readonly value: WhiteboardValue;
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
declare interface KayAgentWhiteboardListOptions {
|
|
1019
|
+
readonly cursor?: string;
|
|
1020
|
+
readonly limit?: number;
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
declare interface KayAgentWhiteboardListResult {
|
|
1024
|
+
readonly entries: readonly WhiteboardEntrySummary[];
|
|
1025
|
+
readonly nextCursor?: string;
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
declare interface KayAgentWhiteboardMutationInput extends KayAgentWhiteboardUpdateInput {
|
|
1029
|
+
readonly mutation: WhiteboardValue;
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
declare interface KayAgentWhiteboardPayload {
|
|
1033
|
+
/** Reads at most 1 MiB; a supplied revision rejects ranges from a changed payload. */
|
|
1034
|
+
read(name: string, range: {
|
|
1035
|
+
readonly offset: number;
|
|
1036
|
+
readonly maxBytes: number;
|
|
1037
|
+
readonly expectedRevision?: string;
|
|
1038
|
+
}): Promise<{
|
|
1039
|
+
readonly bytes: Uint8Array;
|
|
1040
|
+
readonly offset: number;
|
|
1041
|
+
readonly sizeBytes: number;
|
|
1042
|
+
readonly revision: string;
|
|
1043
|
+
}>;
|
|
1044
|
+
create(input: KayAgentWhiteboardCreateInput & {
|
|
1045
|
+
readonly bytes: Uint8Array;
|
|
1046
|
+
}): Promise<WhiteboardEntrySnapshot>;
|
|
1047
|
+
replace(name: string, input: {
|
|
1048
|
+
readonly expectedRevision: string;
|
|
1049
|
+
readonly title?: string;
|
|
1050
|
+
readonly value: WhiteboardValue;
|
|
1051
|
+
readonly bytes: Uint8Array;
|
|
1052
|
+
}): Promise<WhiteboardEntrySnapshot>;
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
declare interface KayAgentWhiteboardRemoveInput {
|
|
1056
|
+
readonly expectedRevision: string;
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
declare interface KayAgentWhiteboardTypeHandle {
|
|
1060
|
+
/** Byte-preserving operations for payload storage; other storage kinds reject them. */
|
|
1061
|
+
readonly payload: KayAgentWhiteboardPayload;
|
|
1062
|
+
/** Includes invalid and unavailable entries without loading their bodies into the inventory. */
|
|
1063
|
+
inventory(options?: KayAgentWhiteboardListOptions): Promise<{
|
|
1064
|
+
readonly entries: readonly WhiteboardEntryInfo[];
|
|
1065
|
+
readonly nextCursor?: string;
|
|
1066
|
+
}>;
|
|
1067
|
+
/** Bounded read-only recovery view; never authorizes typed mutation of an incompatible entry. */
|
|
1068
|
+
inspect(name: string): Promise<WhiteboardEntryInspection | undefined>;
|
|
1069
|
+
list(options?: KayAgentWhiteboardListOptions): Promise<KayAgentWhiteboardListResult>;
|
|
1070
|
+
read(name: string): Promise<WhiteboardEntrySnapshot | undefined>;
|
|
1071
|
+
create(input: KayAgentWhiteboardCreateInput): Promise<WhiteboardEntrySnapshot>;
|
|
1072
|
+
update(name: string, input: KayAgentWhiteboardUpdateInput, transform: (current: WhiteboardEntrySnapshot) => WhiteboardValue | Promise<WhiteboardValue>): Promise<WhiteboardEntrySnapshot>;
|
|
1073
|
+
/** Invokes the registered type's mutation under the host lock and update grant. */
|
|
1074
|
+
mutate(name: string, input: KayAgentWhiteboardMutationInput): Promise<WhiteboardEntrySnapshot>;
|
|
1075
|
+
remove(name: string, input: KayAgentWhiteboardRemoveInput): Promise<void>;
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
declare interface KayAgentWhiteboardUpdateInput {
|
|
1079
|
+
readonly expectedRevision?: string;
|
|
1080
|
+
readonly title?: string;
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
declare type KnownApi = "openai-completions" | "mistral-conversations" | "openai-responses" | "azure-openai-responses" | "openai-codex-responses" | "anthropic-messages" | "bedrock-converse-stream" | "google-generative-ai" | "google-vertex" | "pi-messages";
|
|
1084
|
+
|
|
1085
|
+
declare type KnownProvider = "amazon-bedrock" | "ant-ling" | "anthropic" | "google" | "google-vertex" | "openai" | "azure-openai-responses" | "openai-codex" | "radius" | "nvidia" | "deepseek" | "github-copilot" | "xai" | "groq" | "cerebras" | "openrouter" | "vercel-ai-gateway" | "zai" | "zai-coding-cn" | "mistral" | "minimax" | "minimax-cn" | "moonshotai" | "moonshotai-cn" | "huggingface" | "fireworks" | "together" | "baseten" | "opencode" | "opencode-go" | "kimi-coding" | "meta" | "cloudflare-workers-ai" | "cloudflare-ai-gateway" | "qwen-token-plan" | "qwen-token-plan-cn" | "qwen-token-plan-individual" | "xiaomi" | "xiaomi-token-plan-cn" | "xiaomi-token-plan-ams" | "xiaomi-token-plan-sgp";
|
|
1086
|
+
|
|
1087
|
+
declare interface LineRange {
|
|
1088
|
+
readonly start: number;
|
|
1089
|
+
readonly end: number;
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
declare interface LineSelector {
|
|
1093
|
+
readonly ranges: readonly LineRange[];
|
|
1094
|
+
readonly raw: boolean;
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
/**
|
|
1098
|
+
* The execute-time surface a tool sees. These are the only fields a {@link ToolDefinition.execute}
|
|
1099
|
+
* body can read. Runtime-optionals are absent without a live session: a tool invoked from a
|
|
1100
|
+
* session-less inventory/standalone context gets just `cwd` + `model: undefined`, with `auth`,
|
|
1101
|
+
* `backgroundTasks`, `toolExecutionPolicyDefaults`, and `paths` all absent. A tool must treat each
|
|
1102
|
+
* optional as "may be missing" and degrade accordingly (the SDK auth wrappers, for instance,
|
|
1103
|
+
* short-circuit with an actionable message when {@link auth} is undefined).
|
|
1104
|
+
*/
|
|
1105
|
+
declare type MidTurnCompactionOutcome = {
|
|
1106
|
+
readonly kind: "compacted";
|
|
1107
|
+
} | {
|
|
1108
|
+
readonly kind: "failed";
|
|
1109
|
+
} | {
|
|
1110
|
+
readonly kind: "cancelled";
|
|
1111
|
+
readonly reason: "run-stopped" | "session-disposed";
|
|
1112
|
+
};
|
|
1113
|
+
|
|
1114
|
+
declare type MidTurnCompactionRequest = {
|
|
1115
|
+
readonly kind: "scheduled";
|
|
1116
|
+
/**
|
|
1117
|
+
* Settles exactly once and never rejects, including abort and teardown paths.
|
|
1118
|
+
*
|
|
1119
|
+
* NEVER `await` this inside `execute`. The only thing that settles it is
|
|
1120
|
+
* post-run compaction, and that cannot begin until this tool call returns —
|
|
1121
|
+
* so awaiting it here hangs the run behind itself, which is the exact
|
|
1122
|
+
* deadlock this capability replaced. Attach a callback and return.
|
|
1123
|
+
*/
|
|
1124
|
+
readonly settled: Promise<MidTurnCompactionOutcome>;
|
|
1125
|
+
} | {
|
|
1126
|
+
readonly kind: "refused";
|
|
1127
|
+
readonly reason: "already-pending" | "maintenance-in-flight";
|
|
1128
|
+
};
|
|
1129
|
+
|
|
1130
|
+
/** Compatibility settings for the Mistral chat API. */
|
|
1131
|
+
declare interface MistralConversationsCompat {
|
|
1132
|
+
/** Whether the exact model accepts system messages after the conversation has started. When false, later system messages are folded into the leading system message. Default: false. */
|
|
1133
|
+
supportsMidConvoSystemMessages?: boolean;
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
declare interface Model<TApi extends Api> {
|
|
1137
|
+
id: string;
|
|
1138
|
+
name: string;
|
|
1139
|
+
api: TApi;
|
|
1140
|
+
provider: ProviderId;
|
|
1141
|
+
baseUrl: string;
|
|
1142
|
+
reasoning: boolean;
|
|
1143
|
+
/**
|
|
1144
|
+
* Maps pi thinking levels to provider/model-specific values.
|
|
1145
|
+
* Missing keys use provider defaults. null marks a level as unsupported.
|
|
1146
|
+
*/
|
|
1147
|
+
thinkingLevelMap?: ThinkingLevelMap;
|
|
1148
|
+
input: ("text" | "image")[];
|
|
1149
|
+
/** Provider input limits and cache-safe preprocessing metadata. */
|
|
1150
|
+
inputLimits?: ModelInputLimits;
|
|
1151
|
+
cost: ModelCost;
|
|
1152
|
+
/** Prompt cache lifetimes per retention tier. Unset when the provider's cache behavior is unknown. */
|
|
1153
|
+
promptCache?: ModelPromptCache;
|
|
1154
|
+
contextWindow: number;
|
|
1155
|
+
maxTokens: number;
|
|
1156
|
+
/** Default sampling parameters for this model. See {@link StreamOptions.samplingParams}; per-request keys override these. */
|
|
1157
|
+
samplingParams?: Record<string, unknown>;
|
|
1158
|
+
headers?: Record<string, string>;
|
|
1159
|
+
/** Compatibility overrides for OpenAI-compatible APIs. If not set, auto-detected from baseUrl. */
|
|
1160
|
+
compat?: TApi extends "openai-completions" ? OpenAICompletionsCompat : TApi extends "openai-responses" | "azure-openai-responses" | "openai-codex-responses" ? OpenAIResponsesCompat : TApi extends "anthropic-messages" ? AnthropicMessagesCompat : TApi extends "bedrock-converse-stream" ? BedrockCompat : TApi extends "mistral-conversations" ? MistralConversationsCompat : never;
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
/**
|
|
1164
|
+
* The canonical model-facing content type — the single source of truth for what
|
|
1165
|
+
* a `read`/`finalize` boundary emits and what an {@link AgentToolResult.content}
|
|
1166
|
+
* carries (it IS the same `(TextContent | ImageContent)` union). When pi-ai later
|
|
1167
|
+
* adds audio, this one alias widens and every consumer follows.
|
|
1168
|
+
*/
|
|
1169
|
+
declare type ModelContent = TextContent | ImageContent;
|
|
1170
|
+
|
|
1171
|
+
declare interface ModelCost extends ModelCostRates {
|
|
1172
|
+
/** Request-wide pricing tiers. The highest matching input threshold applies to the full request. */
|
|
1173
|
+
tiers?: ModelCostTier[];
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
declare interface ModelCostRates {
|
|
1177
|
+
input: number;
|
|
1178
|
+
output: number;
|
|
1179
|
+
cacheRead: number;
|
|
1180
|
+
cacheWrite: number;
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
declare interface ModelCostTier extends ModelCostRates {
|
|
1184
|
+
/** Use this tier for requests whose total input usage exceeds this token count. */
|
|
1185
|
+
inputTokensAbove: number;
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
declare interface ModelImageInputLimits {
|
|
1189
|
+
/** Cache-safe resize profile applied before a new image enters conversation history. */
|
|
1190
|
+
resize?: ModelImageResizeOptions;
|
|
1191
|
+
/** Maximum images accepted in one provider message. */
|
|
1192
|
+
maxPerMessage?: number;
|
|
1193
|
+
/** Maximum images accepted across one provider request. */
|
|
1194
|
+
maxPerRequest?: number;
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
declare interface ModelImageResizeOptions {
|
|
1198
|
+
maxWidth?: number;
|
|
1199
|
+
maxHeight?: number;
|
|
1200
|
+
/** Maximum base64-encoded payload size in bytes. */
|
|
1201
|
+
maxBytes?: number;
|
|
1202
|
+
jpegQuality?: number;
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
declare interface ModelInputLimits {
|
|
1206
|
+
/** Maximum serialized provider request size in bytes. */
|
|
1207
|
+
maxRequestBytes?: number;
|
|
1208
|
+
images?: ModelImageInputLimits;
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
/**
|
|
1212
|
+
* Best-effort prompt cache lifetime in seconds for each retention tier a request can ask for.
|
|
1213
|
+
* A missing tier means the lifetime is unknown; pi does not warm such caches.
|
|
1214
|
+
*/
|
|
1215
|
+
declare type ModelPromptCache = Partial<Record<Exclude<CacheRetention, "none">, number>>;
|
|
1216
|
+
|
|
1217
|
+
declare type ModelThinkingLevel = "off" | ThinkingLevel;
|
|
1218
|
+
|
|
1219
|
+
/**
|
|
1220
|
+
* Who may supply the OAuth client for a scheme: Kay's embedded client, or one the user pastes in
|
|
1221
|
+
* (BYOOC). An UNORDERED SET — list order carries no meaning (nothing reads it as a preference).
|
|
1222
|
+
*/
|
|
1223
|
+
declare type OAuthClientSupplier = "kay" | "user";
|
|
1224
|
+
|
|
1225
|
+
/** What the provider hands back at the redirect URI after the user approves. */
|
|
1226
|
+
declare interface OAuthRedirectResult {
|
|
1227
|
+
readonly code: string;
|
|
1228
|
+
readonly state: string;
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
/**
|
|
1232
|
+
* The two client-supply axes, structured so the illegal cell is unrepresentable: a `confidential`
|
|
1233
|
+
* exchange's secret may only ever be Kay's (held by the host's broker, never pasted by a
|
|
1234
|
+
* customer), so `confidential` types `clientSuppliers` as `readonly ["kay"]` — `confidential ×
|
|
1235
|
+
* user` is a compile-time impossibility in v1, not a runtime refusal. `pkce`'s `clientSuppliers`
|
|
1236
|
+
* is a non-empty set; {@link createOAuthPkceAuthScheme} dedupes it and refuses empty.
|
|
1237
|
+
*/
|
|
1238
|
+
declare type OAuthSchemeClientAxes = {
|
|
1239
|
+
readonly exchange: "pkce";
|
|
1240
|
+
readonly clientSuppliers: readonly OAuthClientSupplier[];
|
|
1241
|
+
} | {
|
|
1242
|
+
readonly exchange: "confidential";
|
|
1243
|
+
readonly clientSuppliers: readonly ["kay"];
|
|
1244
|
+
};
|
|
1245
|
+
|
|
1246
|
+
/**
|
|
1247
|
+
* Compatibility settings for OpenAI-compatible completions APIs.
|
|
1248
|
+
* Use this to override URL-based auto-detection for custom providers.
|
|
1249
|
+
*/
|
|
1250
|
+
declare interface OpenAICompletionsCompat {
|
|
1251
|
+
/** Whether the provider supports the `store` field. Default: auto-detected from URL. */
|
|
1252
|
+
supportsStore?: boolean;
|
|
1253
|
+
/** Whether the provider supports the `developer` role (vs `system`). Default: auto-detected from URL. */
|
|
1254
|
+
supportsDeveloperRole?: boolean;
|
|
1255
|
+
/** Whether the provider supports `reasoning_effort`. Default: auto-detected from URL. */
|
|
1256
|
+
supportsReasoningEffort?: boolean;
|
|
1257
|
+
/** Whether the provider supports `stream_options: { include_usage: true }` for token usage in streaming responses. Default: true. */
|
|
1258
|
+
supportsUsageInStreaming?: boolean;
|
|
1259
|
+
/** Whether streamed responses include `finish_reason`. When false, pi infers `stop` or `toolUse` when the stream ends. Default: true. */
|
|
1260
|
+
supportsFinishReason?: boolean;
|
|
1261
|
+
/** Which field to use for max tokens. Default: auto-detected from URL. */
|
|
1262
|
+
maxTokensField?: "max_completion_tokens" | "max_tokens";
|
|
1263
|
+
/** Whether tool results require the `name` field. Default: auto-detected from URL. */
|
|
1264
|
+
requiresToolResultName?: boolean;
|
|
1265
|
+
/** Whether a user message after tool results requires an assistant message in between. Default: auto-detected from URL. */
|
|
1266
|
+
requiresAssistantAfterToolResult?: boolean;
|
|
1267
|
+
/** Whether thinking blocks must be converted to text blocks with <thinking> delimiters. Default: auto-detected from URL. */
|
|
1268
|
+
requiresThinkingAsText?: boolean;
|
|
1269
|
+
/** Whether all replayed assistant messages must include an empty reasoning_content field when reasoning is enabled. Default: auto-detected from URL. */
|
|
1270
|
+
requiresReasoningContentOnAssistantMessages?: boolean;
|
|
1271
|
+
/** Format for reasoning/thinking parameter. "openai" uses reasoning_effort, "openrouter" uses reasoning: { effort }, "deepseek" uses thinking: { type } plus reasoning_effort when supported, "together" uses reasoning: { enabled } plus reasoning_effort when supported, "baseten" uses configurable chat_template_args plus reasoning_effort when supported, "zai" uses thinking: { type }, "qwen" uses top-level enable_thinking: boolean, "qwen-chat-template" uses chat_template_kwargs.enable_thinking and preserve_thinking, "chat-template" uses configurable chat_template_kwargs, "string-thinking" uses top-level thinking: string, and "ant-ling" uses reasoning: { effort } only when the mapped effort is non-null. Default: "openai". */
|
|
1272
|
+
thinkingFormat?: "openai" | "openrouter" | "deepseek" | "together" | "baseten" | "zai" | "qwen" | "chat-template" | "qwen-chat-template" | "string-thinking" | "ant-ling";
|
|
1273
|
+
/** Kwargs to send as `chat_template_kwargs` when `thinkingFormat` is `chat-template`. Use `{ "$var": "thinking.enabled" }`, `{ "$var": "thinking.effort" }`, or `{ "$var": "thinking.budget" }` for pi-controlled thinking values. */
|
|
1274
|
+
chatTemplateKwargs?: Record<string, ChatTemplateKwargValue>;
|
|
1275
|
+
/** Arguments to send as `chat_template_args` when `thinkingFormat` is `baseten`. Use `{ "$var": "thinking.enabled" }`, `{ "$var": "thinking.effort" }`, or `{ "$var": "thinking.budget" }` for pi-controlled thinking values. */
|
|
1276
|
+
chatTemplateArgs?: Record<string, ChatTemplateKwargValue>;
|
|
1277
|
+
/** OpenRouter-compatible routing preferences sent as the `provider` request field. */
|
|
1278
|
+
openRouterRouting?: OpenRouterRouting;
|
|
1279
|
+
/** Vercel AI Gateway routing preferences. Only used when baseUrl points to Vercel AI Gateway. */
|
|
1280
|
+
vercelGatewayRouting?: VercelGatewayRouting;
|
|
1281
|
+
/** Whether z.ai supports top-level `tool_stream: true` for streaming tool call deltas. Default: false. */
|
|
1282
|
+
zaiToolStream?: boolean;
|
|
1283
|
+
/**
|
|
1284
|
+
* Top-level request field used to cap reasoning tokens from `thinkingBudgets`.
|
|
1285
|
+
* Reasoning and the answer share `max_tokens` on these endpoints, so without a budget a
|
|
1286
|
+
* reasoning-heavy turn can consume the whole response and emit no answer.
|
|
1287
|
+
* `"thinking_token_budget"` is vLLM, `"thinking_budget"` is Qwen/DashScope/SGLang,
|
|
1288
|
+
* `"thinking_budget_tokens"` is llama.cpp. Off by default; not set on the generated catalog.
|
|
1289
|
+
*/
|
|
1290
|
+
thinkingTokenBudgetField?: ThinkingTokenBudgetField;
|
|
1291
|
+
/** Alias for `thinkingTokenBudgetField: "thinking_token_budget"` (vLLM). Prefer `thinkingTokenBudgetField`. Default: false. */
|
|
1292
|
+
supportsThinkingTokenBudget?: boolean;
|
|
1293
|
+
/** Whether the provider supports OpenAI custom tools with Lark/regex grammar formats. When false, grammar-constrained tools fall back to normal function tools. Default: false; the generated model catalog enables it for capable models. */
|
|
1294
|
+
supportsOpenAIGrammarTools?: boolean;
|
|
1295
|
+
/** Whether the exact model accepts system or developer messages after the conversation has started. When false, later system messages are folded into the leading system message. Default: false; the generated model catalog enables it for verified models. */
|
|
1296
|
+
supportsMidConvoSystemMessages?: boolean;
|
|
1297
|
+
/** Whether system messages can introduce additional tools mid-conversation. Requires `supportsMidConvoSystemMessages`. Default: false; the generated model catalog enables it for capable models. */
|
|
1298
|
+
supportsMidConvoToolAdditions?: boolean;
|
|
1299
|
+
/** Whether the provider supports the `strict` field in tool definitions. Default: false; generated capable models enable it explicitly. */
|
|
1300
|
+
supportsStrictMode?: boolean;
|
|
1301
|
+
/** Cache control convention for prompt caching. "anthropic" applies Anthropic-style `cache_control` markers to the system prompt, last tool definition, and last user, assistant, or tool-result text content. */
|
|
1302
|
+
cacheControlFormat?: "anthropic";
|
|
1303
|
+
/** Whether to send session-affinity data from `options.sessionId`. Default: true for OpenRouter endpoints, false otherwise. */
|
|
1304
|
+
sendSessionAffinityHeaders?: boolean;
|
|
1305
|
+
/** Session-affinity header format: `openai` sends `session_id`, `x-client-request-id`, and `x-session-affinity`; `openai-nosession` sends `x-client-request-id` and `x-session-affinity`; `openrouter` sends `x-session-id`. Does not affect the `prompt_cache_key` body param, which is governed by cache retention. Default: auto-detected. */
|
|
1306
|
+
sessionAffinityFormat?: SessionAffinityFormat;
|
|
1307
|
+
/** Whether the provider supports long prompt cache retention (`prompt_cache_retention: "24h"` or Anthropic-style `cache_control.ttl: "1h"`, depending on format). Default: true. */
|
|
1308
|
+
supportsLongCacheRetention?: boolean;
|
|
1309
|
+
/**
|
|
1310
|
+
* vLLM scheduler priority sent as the top-level `priority` request field (lower values are
|
|
1311
|
+
* handled earlier; server default 0). Only meaningful when vLLM runs with
|
|
1312
|
+
* `--scheduling-policy priority`; useful for keeping background/batch work from stalling
|
|
1313
|
+
* interactive sessions. Off by default; not set on the generated catalog.
|
|
1314
|
+
*/
|
|
1315
|
+
vllmPriority?: number;
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
/** Compatibility settings for OpenAI Responses APIs. */
|
|
1319
|
+
declare interface OpenAIResponsesCompat {
|
|
1320
|
+
/** Whether the provider supports the `developer` role (vs `system`). Default: true. */
|
|
1321
|
+
supportsDeveloperRole?: boolean;
|
|
1322
|
+
/** Whether the exact model accepts developer or system messages after the conversation has started. When false, later system messages are folded into the leading system message. Default: false; the generated model catalog enables it for verified models. */
|
|
1323
|
+
supportsMidConvoSystemMessages?: boolean;
|
|
1324
|
+
/** Session-affinity header format: `openai` sends `session_id` and `x-client-request-id`; `openai-nosession` sends `x-client-request-id`; `openrouter` sends `x-session-id`. Does not affect the `prompt_cache_key` body param, which is governed by cache retention. Default: auto-detected. */
|
|
1325
|
+
sessionAffinityFormat?: SessionAffinityFormat;
|
|
1326
|
+
/** Whether the provider supports long prompt cache retention. This uses `prompt_cache_options.ttl: "30m"` on GPT-5.6+ and `prompt_cache_retention: "24h"` on earlier models. Default: true. */
|
|
1327
|
+
supportsLongCacheRetention?: boolean;
|
|
1328
|
+
/** Whether the provider supports strict JSON-schema function tools. Defaults are API-specific; generated OpenAI models enable it explicitly. */
|
|
1329
|
+
supportsStrictMode?: boolean;
|
|
1330
|
+
/** Whether to emit OpenAI custom tools with Lark/regex grammar formats. When false, grammar-constrained tools fall back to normal function tools. Default: false; the generated model catalog enables it for capable models. */
|
|
1331
|
+
supportsOpenAIGrammarTools?: boolean;
|
|
1332
|
+
/** Whether the model supports message-anchored `additional_tools` input items. Default: false. */
|
|
1333
|
+
supportsAdditionalTools?: boolean;
|
|
1334
|
+
/** Whether the model supports client-executed tool search for transcript-anchored additions. Default: false. */
|
|
1335
|
+
supportsToolSearch?: boolean;
|
|
1336
|
+
/** Whether the model accepts `prompt_cache_options` (OpenAI GPT-5.6+ prompt caching). Older OpenAI models reject the parameter. Default: false. */
|
|
1337
|
+
supportsExplicitPromptCacheMode?: boolean;
|
|
1338
|
+
/** Whether the provider accepts the `max_output_tokens` parameter. Some Codex-protocol gateways reject it. Default: true. */
|
|
1339
|
+
supportsMaxOutputTokens?: boolean;
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
/**
|
|
1343
|
+
* OpenRouter provider routing preferences.
|
|
1344
|
+
* Controls which upstream providers OpenRouter routes requests to.
|
|
1345
|
+
* Sent as the `provider` field in the OpenRouter API request body.
|
|
1346
|
+
* @see https://openrouter.ai/docs/guides/routing/provider-selection
|
|
1347
|
+
*/
|
|
1348
|
+
declare interface OpenRouterRouting {
|
|
1349
|
+
/** Whether to allow backup providers to serve requests. Default: true. */
|
|
1350
|
+
allow_fallbacks?: boolean;
|
|
1351
|
+
/** Whether to filter providers to only those that support all parameters in the request. Default: false. */
|
|
1352
|
+
require_parameters?: boolean;
|
|
1353
|
+
/** Data collection setting. "allow" (default): allow providers that may store/train on data. "deny": only use providers that don't collect user data. */
|
|
1354
|
+
data_collection?: "deny" | "allow";
|
|
1355
|
+
/** Whether to restrict routing to only ZDR (Zero Data Retention) endpoints. */
|
|
1356
|
+
zdr?: boolean;
|
|
1357
|
+
/** Whether to restrict routing to only models that allow text distillation. */
|
|
1358
|
+
enforce_distillable_text?: boolean;
|
|
1359
|
+
/** An ordered list of provider names/slugs to try in sequence, falling back to the next if unavailable. */
|
|
1360
|
+
order?: string[];
|
|
1361
|
+
/** List of provider names/slugs to exclusively allow for this request. */
|
|
1362
|
+
only?: string[];
|
|
1363
|
+
/** List of provider names/slugs to skip for this request. */
|
|
1364
|
+
ignore?: string[];
|
|
1365
|
+
/** A list of quantization levels to filter providers by (e.g., ["fp16", "bf16", "fp8", "fp6", "int8", "int4", "fp4", "fp32"]). */
|
|
1366
|
+
quantizations?: string[];
|
|
1367
|
+
/** Sorting strategy. Can be a string (e.g., "price", "throughput", "latency") or an object with `by` and `partition`. */
|
|
1368
|
+
sort?: string | {
|
|
1369
|
+
/** The sorting metric: "price", "throughput", "latency". */
|
|
1370
|
+
by?: string;
|
|
1371
|
+
/** Partitioning strategy: "model" (default) or "none". */
|
|
1372
|
+
partition?: string | null;
|
|
1373
|
+
};
|
|
1374
|
+
/** Maximum price per million tokens (USD). */
|
|
1375
|
+
max_price?: {
|
|
1376
|
+
/** Price per million prompt tokens. */
|
|
1377
|
+
prompt?: number | string;
|
|
1378
|
+
/** Price per million completion tokens. */
|
|
1379
|
+
completion?: number | string;
|
|
1380
|
+
/** Price per image. */
|
|
1381
|
+
image?: number | string;
|
|
1382
|
+
/** Price per audio unit. */
|
|
1383
|
+
audio?: number | string;
|
|
1384
|
+
/** Price per request. */
|
|
1385
|
+
request?: number | string;
|
|
1386
|
+
};
|
|
1387
|
+
/** Preferred minimum throughput (tokens/second). Can be a number (applies to p50) or an object with percentile-specific cutoffs. */
|
|
1388
|
+
preferred_min_throughput?: number | {
|
|
1389
|
+
/** Minimum tokens/second at the 50th percentile. */
|
|
1390
|
+
p50?: number;
|
|
1391
|
+
/** Minimum tokens/second at the 75th percentile. */
|
|
1392
|
+
p75?: number;
|
|
1393
|
+
/** Minimum tokens/second at the 90th percentile. */
|
|
1394
|
+
p90?: number;
|
|
1395
|
+
/** Minimum tokens/second at the 99th percentile. */
|
|
1396
|
+
p99?: number;
|
|
1397
|
+
};
|
|
1398
|
+
/** Preferred maximum latency (seconds). Can be a number (applies to p50) or an object with percentile-specific cutoffs. */
|
|
1399
|
+
preferred_max_latency?: number | {
|
|
1400
|
+
/** Maximum latency in seconds at the 50th percentile. */
|
|
1401
|
+
p50?: number;
|
|
1402
|
+
/** Maximum latency in seconds at the 75th percentile. */
|
|
1403
|
+
p75?: number;
|
|
1404
|
+
/** Maximum latency in seconds at the 90th percentile. */
|
|
1405
|
+
p90?: number;
|
|
1406
|
+
/** Maximum latency in seconds at the 99th percentile. */
|
|
1407
|
+
p99?: number;
|
|
1408
|
+
};
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
/**
|
|
1412
|
+
* The version of the conformance floor this SDK enforces — bumped whenever
|
|
1413
|
+
* {@link PLUGIN_DISCLOSURE_RULES} changes, and never for anything else.
|
|
1414
|
+
*
|
|
1415
|
+
* Two readers, and the second is why a number beats a changelog entry. A plugin author compares it
|
|
1416
|
+
* against the Kay build they target. The daemon's loader NAMES it in the
|
|
1417
|
+
* `disclosure-conformance-failed` diagnostic, so a plugin that passed its own suite against an
|
|
1418
|
+
* older SDK and then failed at load says "this host enforces contract N, you audited against
|
|
1419
|
+
* something older" instead of leaving the author to guess whether their code or their SDK moved.
|
|
1420
|
+
*
|
|
1421
|
+
* `1` is the floor as published by #3210 — the first release in which an author could run this
|
|
1422
|
+
* audit at all. `2` (#6880) adds the route-contract rules and tightens
|
|
1423
|
+
* `template-operation-docs-missing` from "every verb on a multi-GROUP route" to "every verb on a
|
|
1424
|
+
* multi-verb route": the help renderer now prints a sentence per verb at the route, so a missing
|
|
1425
|
+
* one is a visible hole, not a permission-row ambiguity.
|
|
1426
|
+
*/
|
|
1427
|
+
export declare const PLUGIN_DISCLOSURE_CONTRACT = 2;
|
|
1428
|
+
|
|
1429
|
+
/**
|
|
1430
|
+
* Every rule this audit can fire, as one frozen inventory.
|
|
1431
|
+
*
|
|
1432
|
+
* It exists because this list IS the conformance floor a third-party plugin is held to, and the
|
|
1433
|
+
* daemon enforces it fail-closed. A rule added here retroactively fails plugins that were correct
|
|
1434
|
+
* yesterday, so the set may not grow as a side effect of an edit: the pinned test beside this
|
|
1435
|
+
* module (`test/disclosure/conformance-contract.test.ts`) fails on any change and makes the author
|
|
1436
|
+
* record it against a new {@link PLUGIN_DISCLOSURE_CONTRACT} — which is what forces the SDK release
|
|
1437
|
+
* that carries the new rule to the authors it now judges (#3210).
|
|
1438
|
+
*
|
|
1439
|
+
* Typing {@link DisclosureViolation.rule} as this union is the half that cannot be forgotten: a
|
|
1440
|
+
* rule id emitted but never listed does not compile.
|
|
1441
|
+
*/
|
|
1442
|
+
export declare const PLUGIN_DISCLOSURE_RULES: readonly ["agent-hint-missing", "agent-hint-summary-empty", "agent-hint-flows-empty", "agent-hint-triggers-missing", "scheme-claim-missing", "help-missing", "help-malformed", "help-throws", "help-uri-malformed", "help-uri-throws", "verb-coverage-missing", "template-operation-docs-missing", "verb-docs-missing", "template-accepts-missing", "template-address-unmatchable", "template-delete-effect-missing", "template-operations-missing", "template-parameter-unknown", "template-scheme-mismatch", "clamp-summary", "clamp-flow-label", "clamp-flow-when", "clamp-flow-example", "clamp-trigger-count", "clamp-trigger-item", "clamp-help-uri", "skill-link-dangling", "skill-link-missing-scheme", "skill-twin-count-mismatch", "skill-twin-missing-declaration", "skill-twin-description-mismatch", "skill-twin-file-unreadable", "skill-twin-frontmatter-name-mismatch", "skill-twin-frontmatter-description-mismatch", "skill-twin-body-mismatch"];
|
|
1443
|
+
|
|
1444
|
+
declare interface PluginApiKeyScheme {
|
|
1445
|
+
readonly id: string;
|
|
1446
|
+
/** Human-facing flow name; distinguish the key's purpose when sibling flows exist. */
|
|
1447
|
+
readonly displayName: string;
|
|
1448
|
+
/** What this flow unlocks and when to choose it over another. */
|
|
1449
|
+
readonly agentHint?: PluginSchemeAgentHint;
|
|
1450
|
+
/** What this flow unlocks, for the person: one plain sentence, no identifiers or URIs. */
|
|
1451
|
+
readonly summary?: string;
|
|
1452
|
+
readonly inputLabel: string;
|
|
1453
|
+
/**
|
|
1454
|
+
* Two or more labeled inputs for an irreducibly multi-part secret (e.g. a Cloudflare Access
|
|
1455
|
+
* service token's client-id + client-secret pair). The connect prompt collects one value per
|
|
1456
|
+
* field; handlers read them from `ctx.auth.credential.fields`, keyed by field id. Index 0 is
|
|
1457
|
+
* the primary field (mirrored as `accessToken`). Absent means the classic single-secret prompt;
|
|
1458
|
+
* when present, ids must be unique and non-empty and there must be at least two entries — the
|
|
1459
|
+
* kernel rejects an invalid declaration at activation.
|
|
1460
|
+
*/
|
|
1461
|
+
readonly fields?: readonly PluginApiKeySchemeField[];
|
|
1462
|
+
readonly setup?: PluginApiKeySchemeSetup;
|
|
1463
|
+
readonly validate?: ValidatePluginApiKeyCredential;
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
declare type PluginApiKeySchemeField = ApiKeyAuthSchemeField;
|
|
1467
|
+
|
|
1468
|
+
declare type PluginApiKeySchemeSetup = ApiKeyAuthSchemeSetup;
|
|
1469
|
+
|
|
1470
|
+
/** The kinds of capability a plugin service can advertise (mirrors the host capability taxonomy). */
|
|
1471
|
+
declare type PluginCapabilityKind = "uri-module" | "tool" | "search-provider";
|
|
1472
|
+
|
|
1473
|
+
/** The host-supplied connect context a delegated scheme receives, aliased (never copied) for plugins. */
|
|
1474
|
+
declare type PluginDelegatedConnectContext = DelegatedConnectContext;
|
|
1475
|
+
|
|
1476
|
+
/** What a delegated scheme's `connect` resolves with, aliased (never copied) for plugins. */
|
|
1477
|
+
declare type PluginDelegatedConnectResult = DelegatedConnectResult;
|
|
1478
|
+
|
|
1479
|
+
/** What a delegated scheme's `revoke` is told: the service and scheme being disconnected. */
|
|
1480
|
+
declare interface PluginDelegatedRevokeContext {
|
|
1481
|
+
readonly serviceId: string;
|
|
1482
|
+
readonly schemeId: string;
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
/**
|
|
1486
|
+
* A protocol-agnostic scheme whose connect/refresh choreography the plugin provides — the host owns
|
|
1487
|
+
* only vault persistence, gating, and single-flight refresh over the opaque material. Use this when
|
|
1488
|
+
* the auth dance is not a fixed OAuth-PKCE shape the host can drive itself (e.g. a runtime-discovered
|
|
1489
|
+
* authorization server: 401 sniff → RFC 9728/8414 discovery → RFC 7591 dynamic client registration →
|
|
1490
|
+
* PKCE). `connect` receives the host's browser-redirect capability (`ctx.redirectUri`/`ctx.authorize`)
|
|
1491
|
+
* when one is configured, and both are absent when the host has none — the scheme decides whether it
|
|
1492
|
+
* needs them and fails its connect with an actionable message if it does and they are missing.
|
|
1493
|
+
*/
|
|
1494
|
+
declare interface PluginDelegatedScheme {
|
|
1495
|
+
readonly id: string;
|
|
1496
|
+
readonly displayName: string;
|
|
1497
|
+
/** What this flow unlocks and when to choose it over another. */
|
|
1498
|
+
readonly agentHint?: PluginSchemeAgentHint;
|
|
1499
|
+
/** What this flow unlocks, for the person: one plain sentence, no identifiers or URIs. */
|
|
1500
|
+
readonly summary?: string;
|
|
1501
|
+
/** Run the connect flow and mint the material to store. Throw to fail the connect. */
|
|
1502
|
+
readonly connect: (ctx: PluginDelegatedConnectContext) => Promise<PluginDelegatedConnectResult>;
|
|
1503
|
+
/**
|
|
1504
|
+
* Mint fresh material from the current (expiring) material. Absent means non-refreshable:
|
|
1505
|
+
* expired material resolves as-is (like an OAuth scheme without a refresh token).
|
|
1506
|
+
*/
|
|
1507
|
+
readonly refresh?: (material: PluginDelegatedSecretMaterial) => Promise<PluginDelegatedSecretMaterial>;
|
|
1508
|
+
/**
|
|
1509
|
+
* Make the sign-in unavailable when the user disconnects: drop whatever the plugin's host holds
|
|
1510
|
+
* outside the vault (an MCP host's token, say). Throw to fail the disconnect loudly. Absent
|
|
1511
|
+
* means the vault record was all there was.
|
|
1512
|
+
*/
|
|
1513
|
+
readonly revoke?: (ctx: PluginDelegatedRevokeContext) => Promise<void>;
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
/** Opaque token material a delegated scheme mints, aliased (never copied) for plugins. */
|
|
1517
|
+
declare type PluginDelegatedSecretMaterial = DelegatedSecretMaterial;
|
|
1518
|
+
|
|
1519
|
+
/** One runtime-discovered service and its delegated auth scheme. */
|
|
1520
|
+
declare interface PluginDelegatedServiceEntry {
|
|
1521
|
+
readonly descriptor: PluginServiceDescriptor;
|
|
1522
|
+
readonly scheme: PluginDelegatedScheme;
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
/** One rule id from {@link PLUGIN_DISCLOSURE_RULES}. */
|
|
1526
|
+
export declare type PluginDisclosureRule = (typeof PLUGIN_DISCLOSURE_RULES)[number];
|
|
1527
|
+
|
|
1528
|
+
/**
|
|
1529
|
+
* An external-CLI readiness scheme the plugin declares: a non-injectable marker for a
|
|
1530
|
+
* host-probed local binary (e.g. `osascript`, `gh`). Connecting runs `binary probeArgs`
|
|
1531
|
+
* through the host's bounded spawn — success mints a readiness record, never token
|
|
1532
|
+
* material. Verb handlers gated on such a scheme receive `ctx.auth.externalCli`, a
|
|
1533
|
+
* host-owned runner pinned to the declared binary; the plugin itself never spawns.
|
|
1534
|
+
* The manifest twin must declare the binary under `permissions.byTarget["agent.harness"].process`.
|
|
1535
|
+
*/
|
|
1536
|
+
declare interface PluginExternalCliScheme {
|
|
1537
|
+
readonly id: string;
|
|
1538
|
+
readonly displayName: string;
|
|
1539
|
+
/** What this flow unlocks and when to choose it over another. */
|
|
1540
|
+
readonly agentHint?: PluginSchemeAgentHint;
|
|
1541
|
+
/** What this flow unlocks, for the person: one plain sentence, no identifiers or URIs. */
|
|
1542
|
+
readonly summary?: string;
|
|
1543
|
+
/** The local binary the host probes and runs (bare name resolved on PATH, or absolute). */
|
|
1544
|
+
readonly binary: string;
|
|
1545
|
+
/** Argv for the readiness probe; exit 0 means connected. */
|
|
1546
|
+
readonly probeArgs: readonly string[];
|
|
1547
|
+
/** Optional argv whose stdout is recorded as the CLI version on connect. */
|
|
1548
|
+
readonly versionArgs?: readonly string[];
|
|
1549
|
+
/** Probe/run timeout; the host default applies when absent. */
|
|
1550
|
+
readonly timeoutMs?: number;
|
|
1551
|
+
/** Actionable copy when the binary is missing (install path, platform requirement). */
|
|
1552
|
+
readonly installHint: string;
|
|
1553
|
+
/** Actionable copy when the probe fails (login step, OS permission grant). */
|
|
1554
|
+
readonly loginHint: string;
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1557
|
+
/**
|
|
1558
|
+
* An OAuth2-PKCE scheme the plugin declares. The host owns acquisition, refresh, and broker
|
|
1559
|
+
* routing; the plugin supplies only public metadata plus an optional account-id resolver.
|
|
1560
|
+
*
|
|
1561
|
+
* Intersects {@link PluginOAuthSchemeClientAxes} (BYOOC — bring-your-own-OAuth-client):
|
|
1562
|
+
* `clientId`/`clientSecret` are optional and mean ONLY "the Kay-supplied embedded client" —
|
|
1563
|
+
* present iff `exchange: "pkce"` and the host ships one. A `confidential` scheme never carries
|
|
1564
|
+
* them; that secret lives with the host's broker, never with the plugin.
|
|
1565
|
+
*/
|
|
1566
|
+
declare type PluginOAuthPkceScheme = PluginOAuthSchemeClientAxes & {
|
|
1567
|
+
readonly id: string;
|
|
1568
|
+
readonly displayName: string;
|
|
1569
|
+
/** What this flow unlocks and when to choose it over another. */
|
|
1570
|
+
readonly agentHint?: PluginSchemeAgentHint;
|
|
1571
|
+
/** What this flow unlocks, for the person: one plain sentence, no identifiers or URIs. */
|
|
1572
|
+
readonly summary?: string;
|
|
1573
|
+
readonly authorizationUrl: string;
|
|
1574
|
+
readonly tokenUrl: string;
|
|
1575
|
+
/** The Kay-supplied embedded client id; omit for a `confidential` scheme or a `pkce` scheme
|
|
1576
|
+
* the host ships no client for. */
|
|
1577
|
+
readonly clientId?: string;
|
|
1578
|
+
/**
|
|
1579
|
+
* An embedded, **non-confidential** client secret for a broker-free PKCE scheme — the case where a
|
|
1580
|
+
* desktop-app OAuth client requires `client_secret` at the token endpoint but the secret is public
|
|
1581
|
+
* by design (e.g. Google installed-app clients). Omit for brokered schemes, where the confidential
|
|
1582
|
+
* secret lives in the broker and never reaches the plugin. A scheme is broker-free purely by virtue
|
|
1583
|
+
* of its `id` not being in the host's brokered-scheme map; supplying this never routes it anywhere.
|
|
1584
|
+
*/
|
|
1585
|
+
readonly clientSecret?: string;
|
|
1586
|
+
readonly scopes: readonly string[];
|
|
1587
|
+
/** Extra params merged into the authorize query. */
|
|
1588
|
+
readonly extraAuthorizeParams?: Readonly<Record<string, string>>;
|
|
1589
|
+
readonly resolveAccountId?: ResolvePluginAccountId;
|
|
1590
|
+
/**
|
|
1591
|
+
* Enumerate every provider account a fresh OAuth grant powers. Present only for providers whose
|
|
1592
|
+
* consent can grant multiple accounts; the host commits one account binding per result while
|
|
1593
|
+
* keeping one shared token lifecycle.
|
|
1594
|
+
*/
|
|
1595
|
+
readonly resolveAccounts?: ResolvePluginAccounts;
|
|
1596
|
+
};
|
|
1597
|
+
|
|
1598
|
+
/** BYOOC — the herd canonical client-axes union, aliased (never copied) for plugin authors. */
|
|
1599
|
+
declare type PluginOAuthSchemeClientAxes = OAuthSchemeClientAxes;
|
|
1600
|
+
|
|
1601
|
+
/** What one connection flow unlocks and when an agent should choose it; grants no authority. */
|
|
1602
|
+
declare type PluginSchemeAgentHint = string;
|
|
1603
|
+
|
|
1604
|
+
/** A capability a service advertises (e.g. a URI module) for discovery / the Integrations page. */
|
|
1605
|
+
declare interface PluginServiceCapability {
|
|
1606
|
+
readonly id: string;
|
|
1607
|
+
readonly kind: PluginCapabilityKind;
|
|
1608
|
+
readonly summary: string;
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
/** A service the plugin owns — advertised for discovery and used to gate auth-backed contributions. */
|
|
1612
|
+
declare interface PluginServiceDescriptor {
|
|
1613
|
+
readonly id: string;
|
|
1614
|
+
readonly display: string;
|
|
1615
|
+
readonly summary: string;
|
|
1616
|
+
readonly capabilities: readonly PluginServiceCapability[];
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1619
|
+
/**
|
|
1620
|
+
* A skill the plugin ships, content included. The manifest *declares* the skill (a
|
|
1621
|
+
* `SkillContribution` naming the in-package SKILL.md); this is the runtime half — bundled
|
|
1622
|
+
* plugins are compiled in with no package directory at runtime, so the package exports the
|
|
1623
|
+
* instructions as data beside the file and an in-package equivalence test pins the two
|
|
1624
|
+
* together (the same pattern as the manifest icon). `name` must equal the contribution's
|
|
1625
|
+
* `localId`; it is the name the agent invokes, unique only within the plugin's own namespaced
|
|
1626
|
+
* skill source (skill identity is `(sourceId, name)`, so same-named skills from other sources
|
|
1627
|
+
* coexist rather than colliding). Still prefix it with the product it teaches
|
|
1628
|
+
* (`gmail-email-triage`, not `triage`) so bare-name invocation stays unambiguous.
|
|
1629
|
+
*/
|
|
1630
|
+
declare interface PluginSkillDefinition {
|
|
1631
|
+
readonly name: string;
|
|
1632
|
+
/** Model-visible one-liner: when to reach for this skill. */
|
|
1633
|
+
readonly description: string;
|
|
1634
|
+
/** The full instructions — what a filesystem skill carries below its frontmatter. */
|
|
1635
|
+
readonly content: string;
|
|
1636
|
+
}
|
|
1637
|
+
|
|
1638
|
+
declare type ProviderId = KnownProvider | string;
|
|
1639
|
+
|
|
1640
|
+
/**
|
|
1641
|
+
* The call a proxy tool stands in for: the registered tool that will actually run, and the input
|
|
1642
|
+
* it will run with. Returned by {@link ToolPermissionHint.deriveProxiedCall}.
|
|
1643
|
+
*/
|
|
1644
|
+
declare interface ProxiedToolCall {
|
|
1645
|
+
/** The registered tool the carrier will invoke. */
|
|
1646
|
+
readonly toolName: string;
|
|
1647
|
+
/** The input that tool will be invoked with — what the gate must classify. */
|
|
1648
|
+
readonly input: Record<string, unknown>;
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1651
|
+
declare interface RawResource {
|
|
1652
|
+
readonly uri: string;
|
|
1653
|
+
readonly content: string | Uint8Array;
|
|
1654
|
+
readonly mimeType: string;
|
|
1655
|
+
readonly metadata?: RawResourceMetadata;
|
|
1656
|
+
readonly selector?: SelectorDisposition;
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1659
|
+
declare interface RawResourceCacheControl {
|
|
1660
|
+
/** False marks degraded/fallback content that read-through caches must not store. */
|
|
1661
|
+
readonly store?: boolean;
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
declare interface RawResourceIdentity {
|
|
1665
|
+
readonly id: string;
|
|
1666
|
+
readonly version: string;
|
|
1667
|
+
readonly variantId?: string;
|
|
1668
|
+
}
|
|
1669
|
+
|
|
1670
|
+
declare interface RawResourceMetadata {
|
|
1671
|
+
readonly size?: number;
|
|
1672
|
+
readonly finalUrl?: string;
|
|
1673
|
+
/** Provider resource MIME type; absent when the source does not report one. */
|
|
1674
|
+
readonly sourceMimeType?: string;
|
|
1675
|
+
readonly notes?: readonly string[];
|
|
1676
|
+
readonly cache?: RawResourceCacheControl;
|
|
1677
|
+
/**
|
|
1678
|
+
* The absolute local filesystem path the resource's bytes were read from,
|
|
1679
|
+
* when the transport read them from local disk. Converter-facing
|
|
1680
|
+
* provenance: finalize threads it into the content probe so a converter
|
|
1681
|
+
* can direct shell tools at the file (e.g. archive extraction). Absent for
|
|
1682
|
+
* remote/synthesized content.
|
|
1683
|
+
*/
|
|
1684
|
+
readonly localPath?: string;
|
|
1685
|
+
/**
|
|
1686
|
+
* Opaque source-native revision suitable only for a later equality check.
|
|
1687
|
+
* A host records it as the session's witness for this resource; when a
|
|
1688
|
+
* module reports none, the host records a content digest of `content`
|
|
1689
|
+
* instead (`contentDigest`, prefixed `sha256:`), which the module can
|
|
1690
|
+
* reproduce at mutation time from the same content its read returns.
|
|
1691
|
+
*/
|
|
1692
|
+
readonly revision?: string;
|
|
1693
|
+
/**
|
|
1694
|
+
* Opaque immutable identity for the exact resource bytes returned by a
|
|
1695
|
+
* provider. Transport preserves it for observers; it never parses it.
|
|
1696
|
+
*/
|
|
1697
|
+
readonly resourceIdentity?: RawResourceIdentity;
|
|
1698
|
+
/**
|
|
1699
|
+
* The resource's stable public address when `uri` reports something more
|
|
1700
|
+
* specific than the address a consumer names it by — a retained revision
|
|
1701
|
+
* (`skill://<id>/@revision/<version>`) served for a `skill://<source>/<name>`
|
|
1702
|
+
* read. Present only when the two differ. Consumers that attribute by name
|
|
1703
|
+
* (the read tool's `skillName` detail, and everything keyed off it) read
|
|
1704
|
+
* this; consumers that need the exact bytes keep `uri`.
|
|
1705
|
+
*/
|
|
1706
|
+
readonly canonicalUri?: string;
|
|
1707
|
+
}
|
|
1708
|
+
|
|
1709
|
+
/**
|
|
1710
|
+
* The capability⇄service auth binding. A registration carrying this is gated on `serviceId`'s
|
|
1711
|
+
* connectivity; the runtime answers "is this capability authorized?" from this plus vault state.
|
|
1712
|
+
*/
|
|
1713
|
+
declare interface RequiresAuth {
|
|
1714
|
+
readonly serviceId: string;
|
|
1715
|
+
/**
|
|
1716
|
+
* Optional scheme preference. Absent means the binding is service-level: any injectable scheme
|
|
1717
|
+
* of the service satisfies it, and resolution picks each account's own backing scheme. Present,
|
|
1718
|
+
* it names the scheme resolution tries first — an account connected under a sibling injectable
|
|
1719
|
+
* scheme still satisfies the binding and resolves against its own scheme. A non-injectable
|
|
1720
|
+
* capability (`external-cli`) MUST name its scheme: it is a scheme-exact island.
|
|
1721
|
+
*/
|
|
1722
|
+
readonly schemeId?: string;
|
|
1723
|
+
/**
|
|
1724
|
+
* The scopes this capability needs to do its job. Threaded through `resolveToken` into the
|
|
1725
|
+
* vault load so a grant-enforcing vault (the daemon's authority-backed vault) can refuse a
|
|
1726
|
+
* plugin whose granted scopes don't cover them (`scopesCovered`) — per call, per plugin. Absent
|
|
1727
|
+
* means the capability declares no scope requirement (the single-user local path never checks
|
|
1728
|
+
* scopes; it is a daemon per-plugin enforcement input). Also read by the scope-aware half of
|
|
1729
|
+
* `isBindingSatisfied` when the host supplies a granted-scopes reader.
|
|
1730
|
+
*/
|
|
1731
|
+
readonly requiredScopes?: readonly string[];
|
|
1732
|
+
/**
|
|
1733
|
+
* Ordered any-of/all-of scope requirement for this capability. Mutually exclusive with the
|
|
1734
|
+
* legacy `requiredScopes`; alternatives are evaluated from provider-returned grant truth.
|
|
1735
|
+
*/
|
|
1736
|
+
readonly scopeRequirement?: ScopeRequirement;
|
|
1737
|
+
/**
|
|
1738
|
+
* The service id to NAME in connect guidance when this capability is gated — a host-composed
|
|
1739
|
+
* FRONT DOOR whose `service_connect` grants exactly what this binding needs (e.g. Gmail's
|
|
1740
|
+
* modules resolve under the shared `google.account` credential but connect via the `gmail`
|
|
1741
|
+
* front door, which expands only Gmail's scopes onto it). Guidance only: credential resolution
|
|
1742
|
+
* still keys on `(serviceId, schemeId)`. Absent, guidance names `serviceId` — which for a
|
|
1743
|
+
* scoped binding on a shared multi-product credential would send the agent to a connect that
|
|
1744
|
+
* classifies resolved and no-ops, never granting the missing scopes.
|
|
1745
|
+
*/
|
|
1746
|
+
readonly connectServiceId?: string;
|
|
1747
|
+
}
|
|
1748
|
+
|
|
1749
|
+
/**
|
|
1750
|
+
* What the resolver hands a tool body: a live token for a concrete account + identity.
|
|
1751
|
+
* `accessToken` is `apiKey` for an api-key scheme or the (refreshed) bearer for oauth.
|
|
1752
|
+
*/
|
|
1753
|
+
declare interface ResolvedAuth {
|
|
1754
|
+
readonly accessToken: string;
|
|
1755
|
+
readonly account: AuthAccount;
|
|
1756
|
+
readonly identityMode: IdentityMode;
|
|
1757
|
+
readonly schemeId: string;
|
|
1758
|
+
readonly scopes?: readonly string[];
|
|
1759
|
+
/**
|
|
1760
|
+
* Every value of a multi-field api-key credential, keyed by the scheme's field id;
|
|
1761
|
+
* `accessToken` mirrors the primary (first) field. Absent for single-secret schemes.
|
|
1762
|
+
*/
|
|
1763
|
+
readonly fields?: Readonly<Record<string, string>>;
|
|
1764
|
+
}
|
|
1765
|
+
|
|
1766
|
+
declare type ResolveExternalCliFailureReason = "not-external-cli" | "no-connection" | "account-ambiguous" | "account-not-found" | "not-authorized" | "host-unavailable";
|
|
1767
|
+
|
|
1768
|
+
declare type ResolveExternalCliResult = {
|
|
1769
|
+
readonly ok: true;
|
|
1770
|
+
readonly account: AuthAccount;
|
|
1771
|
+
readonly runner: UriExternalCliRunner;
|
|
1772
|
+
} | {
|
|
1773
|
+
readonly ok: false;
|
|
1774
|
+
readonly reason: ResolveExternalCliFailureReason;
|
|
1775
|
+
readonly message: string;
|
|
1776
|
+
readonly accounts?: readonly AuthAccount[];
|
|
1777
|
+
};
|
|
1778
|
+
|
|
1779
|
+
/**
|
|
1780
|
+
* Derive a stable provider account id (and optional label) from a freshly connected access token,
|
|
1781
|
+
* plus — when known — who the account belongs to.
|
|
1782
|
+
*
|
|
1783
|
+
* A resolver almost always calls the provider's profile endpoint to derive `accountId` at all, so
|
|
1784
|
+
* the identity fields are already in the response: return them rather than flattening the person
|
|
1785
|
+
* into `label`. Populating `identity` is what lets the agent know the user's handle/email for a
|
|
1786
|
+
* connected service without asking.
|
|
1787
|
+
*/
|
|
1788
|
+
declare type ResolvePluginAccountId = (accessToken: string) => Promise<{
|
|
1789
|
+
readonly accountId: string;
|
|
1790
|
+
readonly label?: string;
|
|
1791
|
+
readonly identity?: AccountIdentity;
|
|
1792
|
+
}>;
|
|
1793
|
+
|
|
1794
|
+
/** Enumerate every provider account a fresh OAuth grant powers. */
|
|
1795
|
+
declare type ResolvePluginAccounts = (accessToken: string) => Promise<readonly {
|
|
1796
|
+
readonly accountId: string;
|
|
1797
|
+
readonly label?: string;
|
|
1798
|
+
readonly identity?: AccountIdentity;
|
|
1799
|
+
}[]>;
|
|
1800
|
+
|
|
1801
|
+
declare interface ResolveTokenFailure {
|
|
1802
|
+
readonly ok: false;
|
|
1803
|
+
readonly reason: ResolveTokenFailureReason;
|
|
1804
|
+
/** Actionable text the wrapper surfaces verbatim to the agent. */
|
|
1805
|
+
readonly message: string;
|
|
1806
|
+
/** When ambiguous, the candidate accounts so the agent can pick one. */
|
|
1807
|
+
readonly accounts?: readonly AuthAccount[];
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
/** Reason a token could not be resolved, with a short agent-facing message. */
|
|
1811
|
+
declare type ResolveTokenFailureReason = "no-connection" | "account-ambiguous" | "account-not-found" | "refresh-failed" | "not-authorized";
|
|
1812
|
+
|
|
1813
|
+
declare type ResolveTokenResult = ResolveTokenSuccess | ResolveTokenFailure;
|
|
1814
|
+
|
|
1815
|
+
declare interface ResolveTokenSuccess {
|
|
1816
|
+
readonly ok: true;
|
|
1817
|
+
readonly auth: ResolvedAuth;
|
|
1818
|
+
}
|
|
1819
|
+
|
|
1820
|
+
/** One all-of arm in an ordered scope requirement. */
|
|
1821
|
+
declare interface ScopeAlternative {
|
|
1822
|
+
readonly allOf: readonly ScopeNeed[];
|
|
1823
|
+
}
|
|
1824
|
+
|
|
1825
|
+
/** One scope in an authorization requirement; `reason` is presentation metadata only. */
|
|
1826
|
+
declare interface ScopeNeed {
|
|
1827
|
+
readonly scope: string;
|
|
1828
|
+
readonly reason?: string;
|
|
1829
|
+
}
|
|
1830
|
+
|
|
1831
|
+
/**
|
|
1832
|
+
* The minimum OAuth authority for one action.
|
|
1833
|
+
*
|
|
1834
|
+
* Alternatives are ordered by acquisition preference. Evaluation accepts the first covered arm;
|
|
1835
|
+
* when none is covered, callers request only the first arm rather than unioning unrelated grants.
|
|
1836
|
+
*/
|
|
1837
|
+
declare interface ScopeRequirement {
|
|
1838
|
+
readonly purpose: string;
|
|
1839
|
+
readonly alternatives: readonly ScopeAlternative[];
|
|
1840
|
+
}
|
|
1841
|
+
|
|
1842
|
+
declare interface SearchCitation {
|
|
1843
|
+
readonly url: string;
|
|
1844
|
+
readonly title?: string;
|
|
1845
|
+
readonly citedText?: string;
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1848
|
+
/**
|
|
1849
|
+
* Shared request shape passed to every provider. Providers MUST ignore knobs
|
|
1850
|
+
* they do not support rather than approximate them.
|
|
1851
|
+
*/
|
|
1852
|
+
declare interface SearchParams {
|
|
1853
|
+
readonly query: string;
|
|
1854
|
+
/**
|
|
1855
|
+
* Pure time filter narrowing results to a window. Providers MUST treat this as
|
|
1856
|
+
* a time filter only — never as a signal to change topic scope, content domain,
|
|
1857
|
+
* or ranking (oh-my-pi's Tavily `topic=news` coupling silently dropped docs).
|
|
1858
|
+
* Providers without temporal filtering MUST ignore it silently.
|
|
1859
|
+
*/
|
|
1860
|
+
readonly recency?: "day" | "week" | "month" | "year";
|
|
1861
|
+
/** Max results to surface (advisory; providers clamp to their own limits). */
|
|
1862
|
+
readonly limit?: number;
|
|
1863
|
+
/** Provider-specific: max synthesis output tokens (ignored if unsupported). */
|
|
1864
|
+
readonly maxOutputTokens?: number;
|
|
1865
|
+
}
|
|
1866
|
+
|
|
1867
|
+
/**
|
|
1868
|
+
* A web search backend behind the single `web_search` tool. Directly analogous
|
|
1869
|
+
* to a `UriModule` behind `read`/`write`: registered through `ExtensionApi`,
|
|
1870
|
+
* merged per session, dispatched by the registry. Stateless and shareable —
|
|
1871
|
+
* per-call context (the abort signal and the {@link SearchProviderContext}) is
|
|
1872
|
+
* threaded into {@link search} and {@link isAvailable}.
|
|
1873
|
+
*/
|
|
1874
|
+
declare abstract class SearchProvider {
|
|
1875
|
+
/** Registry id. Builtins use the reserved `builtin:` namespace. */
|
|
1876
|
+
abstract readonly id: SearchProviderId;
|
|
1877
|
+
/** Human label for diagnostics/UI. */
|
|
1878
|
+
abstract readonly label: string;
|
|
1879
|
+
/**
|
|
1880
|
+
* LLM provider key whose credential this provider reuses (e.g. `anthropic`).
|
|
1881
|
+
* Identifies a builtin's provider preference. Empty for providers with their own key;
|
|
1882
|
+
* it does not create an active-model-first or catch-next execution order.
|
|
1883
|
+
*/
|
|
1884
|
+
abstract readonly providerId: string;
|
|
1885
|
+
/**
|
|
1886
|
+
* The capability⇄scheme binding when this provider resolves its OWN credential through the
|
|
1887
|
+
* service-extension auth system (stamped by {@link withResolvedSearchProvider}). `undefined`
|
|
1888
|
+
* for providers that reuse inference credentials (the builtins) or need no auth. The runner
|
|
1889
|
+
* reads it to surface the provider in `auth_status` as gated-until-connected, parallel to a
|
|
1890
|
+
* tool's or URI module's `requiresAuth`.
|
|
1891
|
+
*/
|
|
1892
|
+
readonly requiresAuth?: RequiresAuth;
|
|
1893
|
+
/**
|
|
1894
|
+
* Cheap availability check (typically: does a usable credential resolve?). An own-credential
|
|
1895
|
+
* provider returns `false` until its auth scheme has a connected account, so the chain skips
|
|
1896
|
+
* it gracefully — the gating counterpart to a tool/module's discoverable-but-disallowed state.
|
|
1897
|
+
*/
|
|
1898
|
+
abstract isAvailable(ctx?: SearchProviderContext): Promise<boolean> | boolean;
|
|
1899
|
+
/** Run the search. Throw {@link SearchProviderError} on a provider failure. */
|
|
1900
|
+
abstract search(params: SearchParams, signal?: AbortSignal, ctx?: SearchProviderContext): Promise<SearchResponse>;
|
|
1901
|
+
}
|
|
1902
|
+
|
|
1903
|
+
/**
|
|
1904
|
+
* Per-call context threaded into a provider's {@link SearchProvider.isAvailable} and
|
|
1905
|
+
* {@link SearchProvider.search}. Carries the execute-time auth surface — the SAME
|
|
1906
|
+
* `ctx.auth` a tool or URI module receives — scoped to the provider's OWNING extension,
|
|
1907
|
+
* so a provider that declares its own credential (an api-key/oauth scheme via the
|
|
1908
|
+
* service-extension auth system) can resolve a token. `auth` is absent for builtins
|
|
1909
|
+
* (no owning extension; they reuse inference credentials through the host model execution capability)
|
|
1910
|
+
* and outside a live session.
|
|
1911
|
+
*/
|
|
1912
|
+
declare interface SearchProviderContext {
|
|
1913
|
+
readonly auth?: ExtensionAuthContext;
|
|
1914
|
+
}
|
|
1915
|
+
|
|
1916
|
+
/** Registry id of a search provider, e.g. `builtin:anthropic`. Open-ended for extensions. */
|
|
1917
|
+
declare type SearchProviderId = string;
|
|
1918
|
+
|
|
1919
|
+
declare interface SearchResponse {
|
|
1920
|
+
/** Registry id of the provider that produced this response. */
|
|
1921
|
+
readonly provider: SearchProviderId;
|
|
1922
|
+
readonly query: string;
|
|
1923
|
+
/** Requested model. A separately reported response model is not silently substituted here. */
|
|
1924
|
+
readonly modelReference?: string;
|
|
1925
|
+
readonly responseModelReference?: string;
|
|
1926
|
+
readonly usage?: SearchUsageEvidence;
|
|
1927
|
+
/** Synthesized natural-language answer, when the provider returns one. */
|
|
1928
|
+
readonly answer?: string;
|
|
1929
|
+
readonly sources: readonly SearchSource[];
|
|
1930
|
+
readonly citations?: readonly SearchCitation[];
|
|
1931
|
+
}
|
|
1932
|
+
|
|
1933
|
+
declare interface SearchSource {
|
|
1934
|
+
readonly url: string;
|
|
1935
|
+
readonly title?: string;
|
|
1936
|
+
readonly snippet?: string;
|
|
1937
|
+
readonly publishedDate?: string;
|
|
1938
|
+
}
|
|
1939
|
+
|
|
1940
|
+
/** Evidence for the terminal native attempt; this is not the logical call's aggregate bill. */
|
|
1941
|
+
declare interface SearchUsageEvidence {
|
|
1942
|
+
/** Non-cached input; cached input and reasoning output are reported separately, never counted twice. */
|
|
1943
|
+
readonly inputTokens?: number;
|
|
1944
|
+
readonly outputTokens?: number;
|
|
1945
|
+
readonly cacheReadTokens?: number;
|
|
1946
|
+
readonly cacheWriteTokens?: number;
|
|
1947
|
+
/** Subset of outputTokens, not an additional output charge. */
|
|
1948
|
+
readonly reasoningTokens?: number;
|
|
1949
|
+
readonly nativeSearchRequests?: number;
|
|
1950
|
+
readonly modelUsdEstimate?: number;
|
|
1951
|
+
readonly reportedToolUsd?: number;
|
|
1952
|
+
readonly reportedCredits?: number;
|
|
1953
|
+
readonly complete: boolean;
|
|
1954
|
+
}
|
|
1955
|
+
|
|
1956
|
+
declare type SelectorDisposition = {
|
|
1957
|
+
readonly kind: "deferred";
|
|
1958
|
+
readonly window?: LineSelector;
|
|
1959
|
+
} | {
|
|
1960
|
+
readonly kind: "applied";
|
|
1961
|
+
} | {
|
|
1962
|
+
readonly kind: "invalid";
|
|
1963
|
+
readonly message: string;
|
|
1964
|
+
};
|
|
1965
|
+
|
|
1966
|
+
declare type SessionAffinityFormat = "openai" | "openai-nosession" | "openrouter";
|
|
1967
|
+
|
|
1968
|
+
/**
|
|
1969
|
+
* The cheap, closed snapshot of primary session facts a {@link ToolAvailability} predicate reads to
|
|
1970
|
+
* decide whether its tool applies in the current context. The kernel builds one at session start and
|
|
1971
|
+
* rebuilds it on each {@link import("../agent-session/agent-session.ts").AgentSession.reroot} (the only event that
|
|
1972
|
+
* moves `cwd`), then evaluates every tool's predicate against it exactly once per change.
|
|
1973
|
+
*
|
|
1974
|
+
* **CLOSED-CONTEXT INVARIANT (load-bearing — do not violate when extending this type).** This object
|
|
1975
|
+
* carries ONLY primary facts that are *independent of tool availability* — `cwd`, `repoRoot`, and the
|
|
1976
|
+
* planned `mode`/`platform`. It MUST NEVER carry the tool list, the active/effective set, or anything
|
|
1977
|
+
* else derived from availability. If a predicate's input could depend on another predicate's output,
|
|
1978
|
+
* evaluation would have to reach a fixpoint and could cycle. Because the context is closed, the kernel
|
|
1979
|
+
* evaluates each predicate ONCE per context change (one pass, no fixpoint) and caches the result. Keep
|
|
1980
|
+
* every future field in this same closed class so the single-pass guarantee survives.
|
|
1981
|
+
*
|
|
1982
|
+
* Predicates reading this MUST be **pure and synchronous** — same context in ⇒ same answer out, no I/O,
|
|
1983
|
+
* no side effects. The host resolves the VCS facts (`repoRoot`); the predicate only inspects them.
|
|
1984
|
+
*/
|
|
1985
|
+
declare interface SessionContext {
|
|
1986
|
+
/** The session's current working directory (the live, re-rootable cwd — not the launch dir). */
|
|
1987
|
+
readonly cwd: string;
|
|
1988
|
+
/**
|
|
1989
|
+
* The enclosing VCS repository root for {@link cwd}, or `null` when `cwd` is not inside a repo.
|
|
1990
|
+
* Host-resolved via {@link import("../agent-session/agent-session.ts").AgentSessionOptions.resolveRepoRoot};
|
|
1991
|
+
* always `null` when the host injects no resolver (the kernel never computes VCS facts itself).
|
|
1992
|
+
*/
|
|
1993
|
+
readonly repoRoot: string | null;
|
|
1994
|
+
}
|
|
1995
|
+
|
|
1996
|
+
declare interface TextContent {
|
|
1997
|
+
type: "text";
|
|
1998
|
+
text: string;
|
|
1999
|
+
textSignature?: string;
|
|
2000
|
+
}
|
|
2001
|
+
|
|
2002
|
+
declare type ThinkingLevel = "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
|
|
2003
|
+
|
|
2004
|
+
declare type ThinkingLevelMap = Partial<Record<ModelThinkingLevel, string | null>>;
|
|
2005
|
+
|
|
2006
|
+
/** Top-level request field used to cap reasoning tokens on OpenAI-compatible servers. */
|
|
2007
|
+
declare type ThinkingTokenBudgetField = "thinking_token_budget" | "thinking_budget" | "thinking_budget_tokens";
|
|
2008
|
+
|
|
2009
|
+
/** What a tool call is about to do, from the host's point of view. */
|
|
2010
|
+
declare type ToolActionKind = "read" | "mutate" | "destructive" | "execute";
|
|
2011
|
+
|
|
2012
|
+
/**
|
|
2013
|
+
* A tool's declarative availability gate. `when` is the authoritative predicate the kernel evaluates
|
|
2014
|
+
* against the live {@link SessionContext} to decide whether the tool is offered to the model here;
|
|
2015
|
+
* `description` is the always-shown, human-readable "why" a UI renders next to a tool the predicate
|
|
2016
|
+
* disables (e.g. greyed on the new-session page). The predicate runs in the main process only — a
|
|
2017
|
+
* renderer receives just the resolved `available: boolean` plus this `description`.
|
|
2018
|
+
*
|
|
2019
|
+
* `when` MUST be pure and synchronous (see {@link SessionContext}); a throw is caught by the kernel,
|
|
2020
|
+
* treated as "unavailable", and surfaced as a session diagnostic — it never crashes the tool sync.
|
|
2021
|
+
*/
|
|
2022
|
+
declare interface ToolAvailability {
|
|
2023
|
+
/** Pure, synchronous gate. `true` ⇒ the tool may be offered in this context; `false` ⇒ withheld. */
|
|
2024
|
+
when(ctx: SessionContext): boolean;
|
|
2025
|
+
/** Human-readable reason the tool is/isn't available, always shown in UI. */
|
|
2026
|
+
description: string;
|
|
2027
|
+
}
|
|
2028
|
+
|
|
2029
|
+
/**
|
|
2030
|
+
* The neutral around-execution hook: return `next()`'s result (possibly transformed) to run the
|
|
2031
|
+
* real tool, or return a result without calling `next()` to substitute one — the tool then never
|
|
2032
|
+
* executes. Every downstream consumer (the model, the session log, result clamps, failure
|
|
2033
|
+
* attribution) sees whatever is returned exactly as if the tool produced it. Throwing surfaces to
|
|
2034
|
+
* the caller as a tool failure, classified like any other.
|
|
2035
|
+
*
|
|
2036
|
+
* Interceptors compose as middleware: each receives a `next` that runs the REST of the chain, the
|
|
2037
|
+
* innermost `next` being the tool itself. `next` may be called at most the caller's own logic
|
|
2038
|
+
* demands (zero to short-circuit, once to delegate, more to retry) — the kernel does not police
|
|
2039
|
+
* arity.
|
|
2040
|
+
*/
|
|
2041
|
+
declare type ToolCallInterceptor = (call: ToolCallInvocation, next: () => Promise<AgentToolResult<unknown>>) => Promise<AgentToolResult<unknown>>;
|
|
2042
|
+
|
|
2043
|
+
/** One tool call as an interceptor sees it, before the tool's `execute` has run. */
|
|
2044
|
+
declare interface ToolCallInvocation {
|
|
2045
|
+
readonly toolCallId: string;
|
|
2046
|
+
readonly toolName: string;
|
|
2047
|
+
/** The call's arguments, already through `prepareArguments` — the shape `execute` receives. */
|
|
2048
|
+
readonly params: unknown;
|
|
2049
|
+
readonly signal: AbortSignal | undefined;
|
|
2050
|
+
/**
|
|
2051
|
+
* See {@link ToolCallTarget}. `undefined` when the session cannot say: a URI verb whose
|
|
2052
|
+
* address matches no registered module, a builtin non-URI tool, or a wrap built without a
|
|
2053
|
+
* resolver.
|
|
2054
|
+
*/
|
|
2055
|
+
readonly target: ToolCallTarget | undefined;
|
|
2056
|
+
/** See {@link ToolCallOrigin}. `"model"` for a wrap built without a resolver. */
|
|
2057
|
+
readonly origin: ToolCallOrigin;
|
|
2058
|
+
/** See {@link ToolCallModel}. Absent for a wrap built without a resolver. */
|
|
2059
|
+
readonly model?: ToolCallModel;
|
|
2060
|
+
/** Register a validation run before each real execute; unavailable on older hosts. */
|
|
2061
|
+
readonly addExecutionGuard?: (guard: () => void | Promise<void>) => void;
|
|
2062
|
+
}
|
|
2063
|
+
|
|
2064
|
+
/**
|
|
2065
|
+
* The model the call's result is about to be shown to, as the session holds it at execution
|
|
2066
|
+
* time: identity plus pi's `input` list — the SAME field pi's adapters read when they build the
|
|
2067
|
+
* next request, so a capability policy keyed on it agrees with what the provider will accept.
|
|
2068
|
+
* Absent when the wrap was built without a resolver; a policy must then treat the capability
|
|
2069
|
+
* as unknown, not as absent.
|
|
2070
|
+
*/
|
|
2071
|
+
declare interface ToolCallModel {
|
|
2072
|
+
readonly provider: string;
|
|
2073
|
+
readonly id: string;
|
|
2074
|
+
readonly input: readonly ("text" | "image")[];
|
|
2075
|
+
}
|
|
2076
|
+
|
|
2077
|
+
/**
|
|
2078
|
+
* Who issued the call: the model, through the harness loop, or a host through
|
|
2079
|
+
* `AgentSession.invokeTool` (a slash command, an RPC, a host hook). Both run the same chain, so
|
|
2080
|
+
* a policy about the MODEL's behaviour must read this rather than assume every call is its.
|
|
2081
|
+
*/
|
|
2082
|
+
declare type ToolCallOrigin = "model" | "host";
|
|
2083
|
+
|
|
2084
|
+
/**
|
|
2085
|
+
* What a tool call ADDRESSES, resolved by the session before execution from facts it already
|
|
2086
|
+
* holds for auth scoping: a URI verb's matched module (and the extension that registered it), or
|
|
2087
|
+
* an extension-contributed tool's owner. `ownerExtensionId` is absent for builtin/app-owned
|
|
2088
|
+
* modules and tools — the session owns those, no extension does. A host policy that keys on
|
|
2089
|
+
* "which integration is this call about" reads this instead of re-parsing `params`.
|
|
2090
|
+
*/
|
|
2091
|
+
declare type ToolCallTarget = {
|
|
2092
|
+
readonly kind: "uri";
|
|
2093
|
+
/** The address as the model wrote it (selector and all). */
|
|
2094
|
+
readonly uri: string;
|
|
2095
|
+
readonly moduleId: string;
|
|
2096
|
+
readonly ownerExtensionId?: string;
|
|
2097
|
+
} | {
|
|
2098
|
+
readonly kind: "tool";
|
|
2099
|
+
readonly ownerExtensionId?: string;
|
|
2100
|
+
};
|
|
2101
|
+
|
|
2102
|
+
declare interface ToolContext {
|
|
2103
|
+
readonly cwd: string;
|
|
2104
|
+
readonly model: Model<any> | undefined;
|
|
2105
|
+
/**
|
|
2106
|
+
* The execute-time auth resolver the SDK tool wrapper (`withResolvedAuth`) reads to resolve a
|
|
2107
|
+
* live token for the active (or per-call-overridden) account. Present when the session is bound
|
|
2108
|
+
* to an {@link import("./auth/runtime.ts").ExtensionAuthRuntime}; absent in always-on contexts
|
|
2109
|
+
* (the wrapper short-circuits with an actionable message when undefined).
|
|
2110
|
+
*/
|
|
2111
|
+
readonly auth?: ExtensionAuthContext;
|
|
2112
|
+
/**
|
|
2113
|
+
* Detached work whose result should re-enter the conversation as a new turn (each completion calls
|
|
2114
|
+
* `nextTurn()` and appears in the Background tasks panel). Not for fire-and-forget side effects —
|
|
2115
|
+
* use {@link ExtensionContext.runEffect}. Absent when the host wires no background-task manager.
|
|
2116
|
+
*/
|
|
2117
|
+
readonly backgroundTasks?: ExtensionBackgroundTasksApi;
|
|
2118
|
+
readonly toolExecutionPolicyDefaults?: ToolExecutionPolicyDefaults;
|
|
2119
|
+
/**
|
|
2120
|
+
* Ask the active run to stop after this tool batch, compact during post-run settlement, then
|
|
2121
|
+
* continue. This is the in-tool counterpart of idle-only `AgentSession.compact()`: it never
|
|
2122
|
+
* waits for the run that currently owns promptability admission and therefore cannot deadlock
|
|
2123
|
+
* behind itself. Present only with a live session. One request may be pending at a time.
|
|
2124
|
+
*/
|
|
2125
|
+
readonly requestMidTurnCompaction?: (customInstructions?: string, trigger?: "command" | "suggestion") => MidTurnCompactionRequest;
|
|
2126
|
+
/**
|
|
2127
|
+
* Re-root the session into `newCwd` mid-run, recomposing the cwd-scoped substrate (builtin tools,
|
|
2128
|
+
* the capability registry, the system-prompt project line) in place so subsequent tool calls operate
|
|
2129
|
+
* there — the conversation continues uninterrupted. This is the agent self-reroot seam: the
|
|
2130
|
+
* motivating caller is an app tool (e.g. `isolate_worktree`) that does its git ops then calls
|
|
2131
|
+
* `ctx.reroot(worktreePath)` from inside its own `execute`. Safe mid-run — it never gates on idle.
|
|
2132
|
+
* Present only with a live session (absent in session-less inventory/standalone contexts, like
|
|
2133
|
+
* {@link auth}). `reason` labels the transition for observability (e.g. `"worktree-isolation"`).
|
|
2134
|
+
*/
|
|
2135
|
+
readonly reroot?: (newCwd: string, reason?: string) => Promise<void>;
|
|
2136
|
+
/**
|
|
2137
|
+
* The host-provided per-extension writable data root (see {@link ExtensionPaths}). Present when the
|
|
2138
|
+
* session was built with a host {@link ExtensionDataDirResolver} AND the context is scoped to an
|
|
2139
|
+
* owning extension (a tool/handler context); absent in session-less inventory/standalone contexts
|
|
2140
|
+
* and in any unowned context, like {@link auth}. When present, `paths.dataDir` already exists on
|
|
2141
|
+
* disk (the runtime creates it before handing back the context), so a consumer can write
|
|
2142
|
+
* immediately.
|
|
2143
|
+
*/
|
|
2144
|
+
readonly paths?: ExtensionPaths;
|
|
2145
|
+
}
|
|
2146
|
+
|
|
2147
|
+
declare interface ToolDefinition<TParams extends ToolParametersWire = ToolParametersWire, TDetails = unknown> {
|
|
2148
|
+
/** Tool name (used in LLM tool calls) */
|
|
2149
|
+
name: string;
|
|
2150
|
+
/** Human-readable label for UI */
|
|
2151
|
+
label: string;
|
|
2152
|
+
/** Description for LLM */
|
|
2153
|
+
description: string;
|
|
2154
|
+
/** Optional one-line snippet for the Available tools section in the default system prompt. Custom tools are omitted from that section when this is not provided. */
|
|
2155
|
+
promptSnippet?: string;
|
|
2156
|
+
/** Optional guideline bullets appended to the default system prompt Guidelines section when this tool is active. */
|
|
2157
|
+
promptGuidelines?: string[];
|
|
2158
|
+
/** Parameter schema as a plain JSON Schema wire object (see {@link ToolParametersWire}). */
|
|
2159
|
+
parameters: TParams;
|
|
2160
|
+
/** Optional compatibility shim to prepare raw tool call arguments before schema validation. Must return an object conforming to TParams. */
|
|
2161
|
+
prepareArguments?: (args: unknown) => TParams;
|
|
2162
|
+
/**
|
|
2163
|
+
* Per-tool execution mode override.
|
|
2164
|
+
* - "sequential": this tool must execute one at a time with other tool calls.
|
|
2165
|
+
* - "parallel": this tool can execute concurrently with other tool calls.
|
|
2166
|
+
*
|
|
2167
|
+
* If omitted, the default execution mode applies.
|
|
2168
|
+
*/
|
|
2169
|
+
executionMode?: ToolExecutionMode;
|
|
2170
|
+
/**
|
|
2171
|
+
* How long a single execution may legitimately stay silent (#3785).
|
|
2172
|
+
* - `"fast"` (the default when omitted): the execution is bounded — if it
|
|
2173
|
+
* neither settles nor loses the abort race within the fast-tool deadline
|
|
2174
|
+
* (120s), the kernel fails the call with a timeout error the model can
|
|
2175
|
+
* react to. Right for local reads/writes and short network calls, which
|
|
2176
|
+
* finish in seconds or are stuck.
|
|
2177
|
+
* - `"long"`: no deadline — a build under a shell, a parent parked on a
|
|
2178
|
+
* child task. The abort race still applies: a user Stop always wins,
|
|
2179
|
+
* whether or not the tool honors its `signal`.
|
|
2180
|
+
* Declare `"long"` only when unbounded silent execution is genuinely part
|
|
2181
|
+
* of the tool's contract; a wedged `"long"` tool is only recoverable by an
|
|
2182
|
+
* explicit Stop.
|
|
2183
|
+
*/
|
|
2184
|
+
durationClass?: "fast" | "long";
|
|
2185
|
+
/**
|
|
2186
|
+
* Optional capability⇄scheme binding. When present this tool is auth-gated: discoverable when
|
|
2187
|
+
* unauthed, callable once the binding is satisfied. The SDK's {@link withResolvedAuth}
|
|
2188
|
+
* stamps this; absent means an ordinary always-on tool (additive — nothing changes for existing
|
|
2189
|
+
* tools that omit it).
|
|
2190
|
+
*/
|
|
2191
|
+
requiresAuth?: RequiresAuth;
|
|
2192
|
+
/**
|
|
2193
|
+
* Optional declarative availability gate ({@link ToolAvailability}). When present, the kernel
|
|
2194
|
+
* evaluates `availability.when` against the live {@link SessionContext} once per context change
|
|
2195
|
+
* (session start + each reroot) and withholds the tool from the model-visible set wherever it
|
|
2196
|
+
* returns `false`. Backward-compatible: **absent ⇒ always available**. Orthogonal to
|
|
2197
|
+
* mode-disallowed sets and to auth/permission gates — availability answers only "does this tool
|
|
2198
|
+
* apply in this context", and composes with the others at the same chokepoint.
|
|
2199
|
+
*/
|
|
2200
|
+
availability?: ToolAvailability;
|
|
2201
|
+
/**
|
|
2202
|
+
* Optional declarative permission classification ({@link ToolPermissionHint}). Consulted by
|
|
2203
|
+
* the permission gate ONLY for tools the classifier does not otherwise recognize (extension/app
|
|
2204
|
+
* tools), letting a tool relax the fail-closed `execute` fallback (a pure staging tool → `read`),
|
|
2205
|
+
* declare its provider `scheme`, derive the `resources` a call touches (a browser
|
|
2206
|
+
* navigation's target origin) so per-provider and per-origin rules can gate it.
|
|
2207
|
+
* Because it lives on the definition it is
|
|
2208
|
+
* provenance-bound. Backward-compatible: **absent ⇒ the classifier's default (`execute`, no
|
|
2209
|
+
* scheme)**.
|
|
2210
|
+
*/
|
|
2211
|
+
permission?: ToolPermissionHint;
|
|
2212
|
+
/** Run policies this tool supports. Defaults to ["inline"] when omitted. */
|
|
2213
|
+
supportedRunPolicies?: readonly ToolRunPolicy[];
|
|
2214
|
+
/** Tool-level default execution policy when caller/session do not override. Defaults to inline semantics when omitted. */
|
|
2215
|
+
defaultRunPolicy?: ToolExecutionPolicyDefaults;
|
|
2216
|
+
/** Execute the tool. */
|
|
2217
|
+
execute(toolCallId: string, params: ToolExecutionAwareParams<TParams>, signal: AbortSignal | undefined, onUpdate: AgentToolUpdateCallback<TDetails> | undefined, ctx: ToolContext): Promise<AgentToolResult<TDetails>>;
|
|
2218
|
+
}
|
|
2219
|
+
|
|
2220
|
+
/** Runtime execute input shape that always allows reserved execution directives. */
|
|
2221
|
+
declare type ToolExecutionAwareParams<T> = T & {
|
|
2222
|
+
readonly _execution?: ToolExecutionDirective;
|
|
2223
|
+
};
|
|
2224
|
+
|
|
2225
|
+
/** Shared per-call execution directive accepted by ToolDefinition inputs. */
|
|
2226
|
+
declare interface ToolExecutionDirective {
|
|
2227
|
+
/** Explicit execution mode override for this specific tool call. */
|
|
2228
|
+
readonly mode?: ToolRunPolicy;
|
|
2229
|
+
/** Preferred background task id when mode resolves to background. */
|
|
2230
|
+
readonly preferredTaskId?: string;
|
|
2231
|
+
/** Per-call auto-background threshold override in milliseconds. */
|
|
2232
|
+
readonly autoBackgroundAfterMs?: number;
|
|
2233
|
+
}
|
|
2234
|
+
|
|
2235
|
+
/**
|
|
2236
|
+
* Configuration for how tool calls from a single assistant message are executed.
|
|
2237
|
+
*
|
|
2238
|
+
* - "sequential": each tool call is prepared, executed, and finalized before the next one starts.
|
|
2239
|
+
* - "parallel": tool calls are prepared sequentially, then allowed tools execute concurrently.
|
|
2240
|
+
* `tool_execution_end` is emitted in tool completion order after each tool is finalized,
|
|
2241
|
+
* while tool-result message artifacts are emitted later in assistant source order.
|
|
2242
|
+
*/
|
|
2243
|
+
declare type ToolExecutionMode = "sequential" | "parallel";
|
|
2244
|
+
|
|
2245
|
+
/** Optional host/session defaults used when a call does not provide an explicit execution directive. */
|
|
2246
|
+
declare interface ToolExecutionPolicyDefaults {
|
|
2247
|
+
readonly mode?: ToolRunPolicy;
|
|
2248
|
+
readonly autoBackgroundAfterMs?: number;
|
|
2249
|
+
}
|
|
2250
|
+
|
|
2251
|
+
/**
|
|
2252
|
+
* A tool's parameters as a plain JSON Schema wire object, opaque (`unknown`) so
|
|
2253
|
+
* the tool seam stays decoupled from any one schema library.
|
|
2254
|
+
*/
|
|
2255
|
+
declare type ToolParametersWire = unknown;
|
|
2256
|
+
|
|
2257
|
+
/**
|
|
2258
|
+
* A tool's own declaration of how a call to it should classify, supplied on the tool
|
|
2259
|
+
* definition (the same shape of author-declared, kernel-read fact as `requiresAuth` and
|
|
2260
|
+
* `availability`). Consulted ONLY when the classifier does not otherwise recognize the tool
|
|
2261
|
+
* (never overrides `read`/`bash`/URI builtins), so a tool can relax the fail-closed
|
|
2262
|
+
* `execute` fallback (e.g. a pure staging tool → `read`) or scope itself for per-provider and
|
|
2263
|
+
* per-origin rules. Because it lives on the registered definition, it is provenance-bound: a
|
|
2264
|
+
* tool named the same as another carries its OWN hint, never inherits a first-party one.
|
|
2265
|
+
*
|
|
2266
|
+
* The host is trusted to supply honest values; a declared `kind` of `read` moves a call below
|
|
2267
|
+
* the protected-path hard-deny (which only fires for non-read kinds), so a tool that could
|
|
2268
|
+
* touch protected config must NOT declare `read`. Kay's embedded-experience tools never touch
|
|
2269
|
+
* `configDir`, so this is safe for them. `read` is also what every capability surface reports as
|
|
2270
|
+
* "read-only" (Kay's Permissions page groups and defaults on it), so it is a claim about what the
|
|
2271
|
+
* tool DOES — never a way to buy silence. A tool that should raise no ask on its own account
|
|
2272
|
+
* declares {@link ToolPermissionHint.quiet} and keeps its honest `kind`.
|
|
2273
|
+
*/
|
|
2274
|
+
declare interface ToolPermissionHint {
|
|
2275
|
+
readonly kind?: ToolActionKind;
|
|
2276
|
+
readonly scheme?: string;
|
|
2277
|
+
readonly ruleKey?: string;
|
|
2278
|
+
/**
|
|
2279
|
+
* Overrides the ask rail's default human-response window for THIS tool's asks, in ms.
|
|
2280
|
+
* A UX window declaration, not authority: it cannot loosen or tighten what is asked —
|
|
2281
|
+
* kind/ruleKey/scheme/rules/mode all decide that exactly as they would without it —
|
|
2282
|
+
* only how long the prompt waits before it lapses unanswered (`expired`, which the gate
|
|
2283
|
+
* already treats as `unanswered`, never `denied`). Bounded use case: a suggestion-shaped
|
|
2284
|
+
* tool whose silence means "continue" (e.g. `suggest_compaction`'s 15s ask, chosen to
|
|
2285
|
+
* stay inside the warm prompt-cache window) rather than the default window sized for a
|
|
2286
|
+
* human who may be away. Undeclared keeps the ask rail's own default.
|
|
2287
|
+
*/
|
|
2288
|
+
readonly askTimeoutMs?: number;
|
|
2289
|
+
/**
|
|
2290
|
+
* Derives the resources a call touches from its validated input (e.g. the target origin of
|
|
2291
|
+
* a browser navigation). Pure and total; a throw is treated as "no resources" and the call
|
|
2292
|
+
* falls back to the plain classification (fail-closed — never crashes the gate).
|
|
2293
|
+
*/
|
|
2294
|
+
readonly deriveResources?: (input: Record<string, unknown>) => readonly string[];
|
|
2295
|
+
/**
|
|
2296
|
+
* Derives a call's classification (`kind`/`ruleKey`/`scheme`) from its validated input, where
|
|
2297
|
+
* the static fields above fix them for every call. This is the generalization of
|
|
2298
|
+
* {@link deriveResources} (input → resources) to the rule-matching triple, and of the kernel's
|
|
2299
|
+
* built-in `deriveBashRuleKey` (a bash call's argv sharpens its key) to an author-declared tool.
|
|
2300
|
+
*
|
|
2301
|
+
* Returns a partial: each present field OVERRIDES the static hint for this call; each absent
|
|
2302
|
+
* field (or a wholly `undefined` return) falls back to the static hint, then the fail-closed
|
|
2303
|
+
* default. Pure and total; a throw is treated as "no refinement" and the call falls back to the
|
|
2304
|
+
* static hint (fail-closed — never crashes the gate).
|
|
2305
|
+
*
|
|
2306
|
+
* It grants no authority the static fields don't already grant: it computes the same
|
|
2307
|
+
* kind/ruleKey/scheme the tool could hard-code, per call rather than once. `kind` here matters
|
|
2308
|
+
* because the same tool can read on one input and execute on another, which a static `kind`
|
|
2309
|
+
* cannot express.
|
|
2310
|
+
*
|
|
2311
|
+
* Exemplar — Kay's `mcp` proxy tool, one tool spanning every server: `search`/`describe` derive
|
|
2312
|
+
* `{ kind: "read" }` (metadata lookups), while `call` derives
|
|
2313
|
+
* `{ kind: "execute", ruleKey: "mcp:<server>::<tool>", scheme: "<server>" }`, so a
|
|
2314
|
+
* `{ scheme: "<server>" }` rule gates a whole server and a `{ ruleKey: "mcp:<server>::<tool>" }`
|
|
2315
|
+
* rule gates one of its tools.
|
|
2316
|
+
*/
|
|
2317
|
+
readonly deriveClassification?: (input: Record<string, unknown>) => DerivedToolClassification | undefined;
|
|
2318
|
+
/**
|
|
2319
|
+
* Declares that this call is really a call to ANOTHER registered tool, so the gate classifies
|
|
2320
|
+
* the call that will actually run rather than the tool that carries it. Returns the target's
|
|
2321
|
+
* name and the input it will be executed with; `undefined` means "not a proxied call", and the
|
|
2322
|
+
* static hint / fail-closed default applies to the carrier itself.
|
|
2323
|
+
*
|
|
2324
|
+
* This is the parity seam a proxy tool needs and {@link deriveClassification} cannot provide: a
|
|
2325
|
+
* deriver is a pure function of its own input and cannot see the target's hint, its service
|
|
2326
|
+
* scheme, or the kernel's own recognition of names like `bash` and the URI verbs. Resolution
|
|
2327
|
+
* re-enters classification under the target's identity, so a proxied call matches EXACTLY the
|
|
2328
|
+
* rules its direct form would — same kind, same ruleKey, same scheme.
|
|
2329
|
+
*
|
|
2330
|
+
* A hop ADDS the target's rules; it never SUBTRACTS the carrier's. The carrier's own
|
|
2331
|
+
* classification rides along ({@link ToolActionPreflight.carrier}) and the controller takes the
|
|
2332
|
+
* STRICTEST verdict of the two, so a deny on the proxy tool, its scheme, or its declared
|
|
2333
|
+
* defaults still blocks, and a proxied call is never more permitted than calling the proxy
|
|
2334
|
+
* directly. Routing a call through a proxy therefore cannot reach anything either identity
|
|
2335
|
+
* alone could not.
|
|
2336
|
+
*
|
|
2337
|
+
* Not consulted at all when the CARRIER is a tool the kernel classifies itself (`bash`, the URI
|
|
2338
|
+
* verbs, the read tools) — the same invariant that governs the rest of this hint. A proxy may
|
|
2339
|
+
* still freely TARGET one of them.
|
|
2340
|
+
*
|
|
2341
|
+
* Pure and total. EVERY abnormal outcome falls back to the carrier's own classification, never
|
|
2342
|
+
* to an intermediate link: a throw, a malformed return, a blank target name, a non-object input,
|
|
2343
|
+
* a cycle, or a chain longer than 4 hops. (Falling back to an intermediate would itself be a
|
|
2344
|
+
* bypass — a crafted 5-link chain could present hop 4's benign classification for a call whose
|
|
2345
|
+
* real target is `bash rm -rf /`.) Only a chain terminating normally, at a tool declaring no
|
|
2346
|
+
* further proxy, hops at all.
|
|
2347
|
+
*
|
|
2348
|
+
* "Fail-closed" here means the carrier's own classification applies — the identity the call
|
|
2349
|
+
* really had — not that the result is always stricter than the target's. How much gate is left
|
|
2350
|
+
* on such a call is exactly how much the CARRIER carries: an `execute` carrier is stricter than
|
|
2351
|
+
* a `read` target and equal to a `mutate`/`destructive` one outside allow mode, while a
|
|
2352
|
+
* {@link quiet} carrier (Kay's own `call_tool`) contributes no mode default at all, so the
|
|
2353
|
+
* fallback leaves nothing but the carrier's rules and the protected-path deny — and the carrier
|
|
2354
|
+
* names no path, so that deny has nothing to fire on. This is why a carrier must refuse a target
|
|
2355
|
+
* that is itself a carrier: no chain long or circular enough to reach the fallback can be built,
|
|
2356
|
+
* which is the only thing that keeps the fallback from being toothless. Fail-closed is a
|
|
2357
|
+
* CLASSIFICATION rule and nothing carries it to an executor.
|
|
2358
|
+
*/
|
|
2359
|
+
readonly deriveProxiedCall?: (input: Record<string, unknown>) => ProxiedToolCall | undefined;
|
|
2360
|
+
/**
|
|
2361
|
+
* Declares that this tool's identity contributes its RULES to the gate but not its MODE
|
|
2362
|
+
* DEFAULT. A rule about it still applies in full (a deny denies, an ask asks, an allow
|
|
2363
|
+
* allows) and the protected-path hard-deny still fires; what is withheld is the verdict the
|
|
2364
|
+
* MODE would reach from `kind` alone — default/ask mode's "anything but a read asks".
|
|
2365
|
+
*
|
|
2366
|
+
* It is a statement about this identity's CONTRIBUTION, never about what the call does.
|
|
2367
|
+
* `kind` stays the honest declaration of the strongest effect a call can have, and a quiet
|
|
2368
|
+
* tool declares it exactly as loudly as a noisy one — which is the whole point: buying
|
|
2369
|
+
* silence with `kind: "read"` instead moves the call below the protected-path hard-deny and
|
|
2370
|
+
* reports the tool as read-only on every capability surface.
|
|
2371
|
+
*
|
|
2372
|
+
* It exists for a PROXY CARRIER ({@link deriveProxiedCall}), and is honest only for a tool
|
|
2373
|
+
* whose only effect is invoking the target the gate is already classifying beside it: the
|
|
2374
|
+
* target's kind, rules, resources and the protected-path deny all still decide, so a proxied
|
|
2375
|
+
* call is gated exactly like the direct one — no more, and no ask the direct call would never
|
|
2376
|
+
* have raised. Kay's `call_tool` declares `{ kind: "execute", quiet: true }` (owner ruling,
|
|
2377
|
+
* 2026-08-18): honest about everything it can reach, silent on its own account.
|
|
2378
|
+
*
|
|
2379
|
+
* Quiet is not ungated, but it IS nearly all the gate a lone carrier has. On a call that
|
|
2380
|
+
* classifies as the carrier because no hop resolved (a cycle, an over-long chain), a quiet
|
|
2381
|
+
* carrier leaves only its own rules — see {@link deriveProxiedCall}'s fail-closed paragraph
|
|
2382
|
+
* for why a carrier must therefore refuse a carrier as a target.
|
|
2383
|
+
*/
|
|
2384
|
+
readonly quiet?: boolean;
|
|
2385
|
+
/**
|
|
2386
|
+
* Plugin-declared default permission for this tool, compiled into a defaults-level rule.
|
|
2387
|
+
*
|
|
2388
|
+
* `"ask"` prompts by default and elevates over EVERY mode, `allow` included. `"never"`
|
|
2389
|
+
* compiles to a deny. `"allow"` declares the capability safe and satisfies the conservative
|
|
2390
|
+
* modes, so `default` and `ask` stop prompting for it. Undeclared leaves the mode deciding,
|
|
2391
|
+
* which for anything not classified `read` means a prompt.
|
|
2392
|
+
*
|
|
2393
|
+
* `"allow"` used to compile to nothing, which made declaring it a no-op and left the mode's
|
|
2394
|
+
* blanket ask in charge of tools their own plugin had called safe (fixed 2026-08-20). This
|
|
2395
|
+
* is a floor, never a ceiling: a user or learned setting still shadows it by level
|
|
2396
|
+
* precedence, and no declaration here can lift the protected-path deny.
|
|
2397
|
+
*/
|
|
2398
|
+
readonly defaultPermission?: "allow" | "ask" | "never";
|
|
2399
|
+
}
|
|
2400
|
+
|
|
2401
|
+
/** Controls whether a tool is expected to complete inline or via background adoption. */
|
|
2402
|
+
declare type ToolRunPolicy = "inline" | "background";
|
|
2403
|
+
|
|
2404
|
+
declare interface UriAgentHint {
|
|
2405
|
+
readonly summary: string;
|
|
2406
|
+
readonly commonFlows?: readonly UriCommonFlow[];
|
|
2407
|
+
readonly helpUri?: string;
|
|
2408
|
+
readonly triggers?: UriHintTriggers;
|
|
2409
|
+
/**
|
|
2410
|
+
* Skill invocation names (flat `localId`s, e.g. `"gmail-email-triage"`) that hold this module's
|
|
2411
|
+
* deeper workflow judgment — the name a `read skill://<name>` dispatch resolves, not a display
|
|
2412
|
+
* title or a path. Point here only at skills that actually exist in the composed skill roster;
|
|
2413
|
+
* a dangling name sends the agent to a `skill://` read for nothing.
|
|
2414
|
+
*/
|
|
2415
|
+
readonly skills?: readonly string[];
|
|
2416
|
+
}
|
|
2417
|
+
|
|
2418
|
+
declare type UriAuthActionClassification = {
|
|
2419
|
+
readonly kind: "action";
|
|
2420
|
+
readonly actionId: string;
|
|
2421
|
+
} | {
|
|
2422
|
+
readonly kind: "public";
|
|
2423
|
+
};
|
|
2424
|
+
|
|
2425
|
+
/**
|
|
2426
|
+
* Finite authorization actions plus the pure route classifier that selects one before dispatch.
|
|
2427
|
+
* An exception, an unknown action id, or `undefined` classification fails closed in the host.
|
|
2428
|
+
*/
|
|
2429
|
+
declare interface UriAuthActionPolicy {
|
|
2430
|
+
readonly actions: Readonly<Record<string, ScopeRequirement>>;
|
|
2431
|
+
classifyOperation(request: {
|
|
2432
|
+
readonly operation: UriOperation;
|
|
2433
|
+
readonly uri: string;
|
|
2434
|
+
}): UriAuthActionClassification | undefined;
|
|
2435
|
+
}
|
|
2436
|
+
|
|
2437
|
+
declare interface UriAuthRef {
|
|
2438
|
+
readonly serviceId: string;
|
|
2439
|
+
readonly schemeId: string;
|
|
2440
|
+
readonly accountId?: string;
|
|
2441
|
+
}
|
|
2442
|
+
|
|
2443
|
+
declare interface UriAuthRequirement {
|
|
2444
|
+
readonly serviceId: string;
|
|
2445
|
+
/**
|
|
2446
|
+
* Optional scheme preference. Absent binds service-level: any injectable scheme of the
|
|
2447
|
+
* service satisfies the module, and an explicit account resolves against its own backing
|
|
2448
|
+
* scheme. Present, it names the scheme resolution tries first.
|
|
2449
|
+
*/
|
|
2450
|
+
readonly schemeId?: string;
|
|
2451
|
+
/**
|
|
2452
|
+
* The scopes this module needs its resolved credential to carry. Threaded by
|
|
2453
|
+
* the host's dispatch into every credential load, so a grant-enforcing vault
|
|
2454
|
+
* can refuse the module per call. Absent declares no scope requirement.
|
|
2455
|
+
*/
|
|
2456
|
+
readonly requiredScopes?: readonly string[];
|
|
2457
|
+
/**
|
|
2458
|
+
* The service id connect guidance should NAME when this module is gated — a host-composed
|
|
2459
|
+
* front door whose connect grants exactly this module's scopes (e.g. `gmail` expanding onto
|
|
2460
|
+
* the shared `google.account` credential). Guidance only; resolution keys on
|
|
2461
|
+
* `(serviceId, schemeId)`. Absent, guidance names `serviceId`.
|
|
2462
|
+
*/
|
|
2463
|
+
readonly connectServiceId?: string;
|
|
2464
|
+
}
|
|
2465
|
+
|
|
2466
|
+
declare type UriBackingCandidate = {
|
|
2467
|
+
readonly path: string;
|
|
2468
|
+
readonly root: string;
|
|
2469
|
+
readonly relativePath?: never;
|
|
2470
|
+
/** Optional display label for where the content canonically comes from (e.g. its source repository), rendered beside the path in projection metadata. Opaque display text; never parsed. */
|
|
2471
|
+
readonly sourceRepositoryLabel?: string;
|
|
2472
|
+
} | {
|
|
2473
|
+
/** Path relative to the sole host-protected root owned by this module. */
|
|
2474
|
+
readonly relativePath: string;
|
|
2475
|
+
readonly path?: never;
|
|
2476
|
+
readonly root?: never;
|
|
2477
|
+
/** Optional display label for where the content canonically comes from (e.g. its source repository), rendered beside the path in projection metadata. Opaque display text; never parsed. */
|
|
2478
|
+
readonly sourceRepositoryLabel?: string;
|
|
2479
|
+
};
|
|
2480
|
+
|
|
2481
|
+
declare interface UriCacheInvalidation {
|
|
2482
|
+
/** Drop cached entries whose cache URI starts with this prefix. */
|
|
2483
|
+
readonly prefix?: string;
|
|
2484
|
+
/** Drop the exact cache URI. */
|
|
2485
|
+
readonly uri?: string;
|
|
2486
|
+
}
|
|
2487
|
+
|
|
2488
|
+
declare interface UriCachePolicy {
|
|
2489
|
+
/** Read-family cache TTL in milliseconds; absent or zero means not cached. */
|
|
2490
|
+
readonly ttlMs?: number;
|
|
2491
|
+
/** Mutation directive applied after a successful write/edit/delete. */
|
|
2492
|
+
readonly invalidate?: readonly UriCacheInvalidation[];
|
|
2493
|
+
}
|
|
2494
|
+
|
|
2495
|
+
/**
|
|
2496
|
+
* Resolve cache policy for a concrete URI operation. User-specific modules that
|
|
2497
|
+
* return a policy must declare `requiresAuth` or `requiresConnection` so hosts
|
|
2498
|
+
* can partition cache keys by identity.
|
|
2499
|
+
*/
|
|
2500
|
+
declare type UriCachePolicyResolver = (operation: UriOperation, cacheUri: string, ctx: UriResolveContext) => UriCachePolicy | undefined;
|
|
2501
|
+
|
|
2502
|
+
declare type UriClaim = UriSchemeClaim | UriPatternClaim | UrlPatternClaim;
|
|
2503
|
+
|
|
2504
|
+
declare interface UriCommonFlow {
|
|
2505
|
+
readonly label: string;
|
|
2506
|
+
readonly when?: string;
|
|
2507
|
+
readonly example: string;
|
|
2508
|
+
}
|
|
2509
|
+
|
|
2510
|
+
declare interface UriConnection {
|
|
2511
|
+
readonly id: string;
|
|
2512
|
+
readonly moduleId: string;
|
|
2513
|
+
readonly adapterId: string;
|
|
2514
|
+
readonly displayName: string;
|
|
2515
|
+
readonly authRef?: UriAuthRef;
|
|
2516
|
+
readonly metadata?: Record<string, unknown>;
|
|
2517
|
+
/**
|
|
2518
|
+
* Carries the vault's default flag for the account behind this row. A
|
|
2519
|
+
* selector-less address resolves to the single flagged row; the flag is per
|
|
2520
|
+
* service + scheme, so a module that accepts two schemes (GitHub OAuth and
|
|
2521
|
+
* PAT) can see two, which resolves as ambiguous rather than guessing.
|
|
2522
|
+
*/
|
|
2523
|
+
readonly isDefault?: boolean;
|
|
2524
|
+
}
|
|
2525
|
+
|
|
2526
|
+
/**
|
|
2527
|
+
* The typed answer to "can this be undone?" for a route's `delete` verb.
|
|
2528
|
+
*
|
|
2529
|
+
* - `trash`: moved somewhere recoverable; `restore` names the address or payload that
|
|
2530
|
+
* brings it back ("edit with {addLabels:['INBOX']}").
|
|
2531
|
+
* - `permanent`: gone on success.
|
|
2532
|
+
* - `conditional`: depends on the address or payload; `how` states the rule
|
|
2533
|
+
* ("trash by default; ?permanent=true deletes permanently").
|
|
2534
|
+
*/
|
|
2535
|
+
declare type UriDeleteEffect = {
|
|
2536
|
+
readonly kind: "trash";
|
|
2537
|
+
readonly restore?: string;
|
|
2538
|
+
} | {
|
|
2539
|
+
readonly kind: "permanent";
|
|
2540
|
+
} | {
|
|
2541
|
+
readonly kind: "conditional";
|
|
2542
|
+
readonly how: string;
|
|
2543
|
+
};
|
|
2544
|
+
|
|
2545
|
+
declare type UriDescribeContext = Pick<UriResolveContext, "cwd" | "sessionId">;
|
|
2546
|
+
|
|
2547
|
+
declare interface UriExample {
|
|
2548
|
+
readonly task: string;
|
|
2549
|
+
readonly uri: string;
|
|
2550
|
+
readonly operation?: UriOperation;
|
|
2551
|
+
}
|
|
2552
|
+
|
|
2553
|
+
declare interface UriExtensionModule extends Omit<UriModule, "verbs"> {
|
|
2554
|
+
readonly verbs: UriVerbSupport;
|
|
2555
|
+
/** Human-facing per-verb effect docs; see {@link UriVerbDocs}. */
|
|
2556
|
+
readonly verbDocs?: UriVerbDocs;
|
|
2557
|
+
/**
|
|
2558
|
+
* Plugin-declared default permission per verb group. A verb group set to `"ask"`
|
|
2559
|
+
* compiles into a defaults-level ask rule — the user is prompted by default but can
|
|
2560
|
+
* override with an explicit allow. Undeclared = `"allow"` (fail-open).
|
|
2561
|
+
* See {@link UriPermissionDefaults}.
|
|
2562
|
+
*/
|
|
2563
|
+
readonly permissionDefaults?: UriPermissionDefaults;
|
|
2564
|
+
/** Requires a resolved URI connection before any verb handler runs. */
|
|
2565
|
+
readonly requiresConnection?: boolean;
|
|
2566
|
+
/** Pure route-aware permission facts; may only strengthen the verb default. */
|
|
2567
|
+
readonly permission?: UriExtensionOperationPermissionPolicy;
|
|
2568
|
+
/** False rejects generic write-tool `source` copies before source fetch. */
|
|
2569
|
+
readonly acceptsSourceCopy?: boolean;
|
|
2570
|
+
}
|
|
2571
|
+
|
|
2572
|
+
declare interface UriExtensionOperationPermissionPolicy {
|
|
2573
|
+
classifyOperation(request: {
|
|
2574
|
+
readonly operation: UriOperation;
|
|
2575
|
+
readonly uri: string;
|
|
2576
|
+
}): {
|
|
2577
|
+
readonly kind: "read" | "mutate" | "destructive" | "execute";
|
|
2578
|
+
readonly ruleKey?: string;
|
|
2579
|
+
} | undefined;
|
|
2580
|
+
}
|
|
2581
|
+
|
|
2582
|
+
declare interface UriExternalCliRunner {
|
|
2583
|
+
readonly run: (request: UriExternalCliRunRequest) => Promise<UriExternalCliRunResult>;
|
|
2584
|
+
}
|
|
2585
|
+
|
|
2586
|
+
declare interface UriExternalCliRunRequest {
|
|
2587
|
+
readonly args: readonly string[];
|
|
2588
|
+
readonly cwd?: string;
|
|
2589
|
+
readonly timeoutMs?: number;
|
|
2590
|
+
readonly maxOutputChars?: number;
|
|
2591
|
+
readonly signal?: AbortSignal;
|
|
2592
|
+
}
|
|
2593
|
+
|
|
2594
|
+
declare interface UriExternalCliRunResult {
|
|
2595
|
+
readonly exitCode: number;
|
|
2596
|
+
readonly stdout: string;
|
|
2597
|
+
readonly stderr: string;
|
|
2598
|
+
readonly stdoutTruncated?: boolean;
|
|
2599
|
+
readonly stderrTruncated?: boolean;
|
|
2600
|
+
}
|
|
2601
|
+
|
|
2602
|
+
declare interface UriHelp {
|
|
2603
|
+
readonly uri: string;
|
|
2604
|
+
readonly title: string;
|
|
2605
|
+
readonly summary: string;
|
|
2606
|
+
readonly capabilities?: readonly UriNodeCapability[];
|
|
2607
|
+
readonly templates?: readonly UriTemplate[];
|
|
2608
|
+
readonly examples?: readonly UriExample[];
|
|
2609
|
+
readonly notes?: readonly string[];
|
|
2610
|
+
}
|
|
2611
|
+
|
|
2612
|
+
declare interface UriHintTriggers {
|
|
2613
|
+
readonly keywords?: readonly string[];
|
|
2614
|
+
readonly patterns?: readonly string[];
|
|
2615
|
+
}
|
|
2616
|
+
|
|
2617
|
+
declare interface UriListItem {
|
|
2618
|
+
readonly uri: string;
|
|
2619
|
+
readonly title?: string;
|
|
2620
|
+
readonly summary?: string;
|
|
2621
|
+
/** Provider resource MIME type; absent when the source does not report one. */
|
|
2622
|
+
readonly sourceMimeType?: string;
|
|
2623
|
+
readonly operations?: readonly UriOperation[];
|
|
2624
|
+
}
|
|
2625
|
+
|
|
2626
|
+
declare interface UriListMetadata {
|
|
2627
|
+
readonly totalCount?: number;
|
|
2628
|
+
readonly totalCountLabel?: string;
|
|
2629
|
+
readonly returnedCount?: number;
|
|
2630
|
+
/**
|
|
2631
|
+
* True when the provider could not search/enumerate the whole corpus for this
|
|
2632
|
+
* page, so the result may be missing matches (e.g. a federated query that spans
|
|
2633
|
+
* multiple stores and timed out part way). The caller should treat the result as
|
|
2634
|
+
* partial and, where possible, narrow the scope. Omit when results are complete.
|
|
2635
|
+
*/
|
|
2636
|
+
readonly incompleteSearch?: boolean;
|
|
2637
|
+
}
|
|
2638
|
+
|
|
2639
|
+
declare interface UriListParams {
|
|
2640
|
+
readonly cursor?: string;
|
|
2641
|
+
readonly limit?: number;
|
|
2642
|
+
}
|
|
2643
|
+
|
|
2644
|
+
declare interface UriListResult {
|
|
2645
|
+
readonly uri: string;
|
|
2646
|
+
readonly items: readonly UriListItem[];
|
|
2647
|
+
readonly nextCursor?: string;
|
|
2648
|
+
readonly metadata?: UriListMetadata;
|
|
2649
|
+
}
|
|
2650
|
+
|
|
2651
|
+
declare type UriLocateContext = Pick<UriResolveContext, "cwd" | "signal" | "connection" | "sessionId" | "paths"> & {
|
|
2652
|
+
/** Exact resource returned by the read whose supplemental backing is being located. */
|
|
2653
|
+
readonly readResource?: RawResource;
|
|
2654
|
+
};
|
|
2655
|
+
|
|
2656
|
+
declare type UriLocateResult = {
|
|
2657
|
+
readonly kind: "candidate";
|
|
2658
|
+
readonly candidate: UriBackingCandidate;
|
|
2659
|
+
} | {
|
|
2660
|
+
readonly kind: "malformed";
|
|
2661
|
+
readonly message: string;
|
|
2662
|
+
} | {
|
|
2663
|
+
readonly kind: "not-disk-backed";
|
|
2664
|
+
};
|
|
2665
|
+
|
|
2666
|
+
declare type UriMarkdownRenderInput = {
|
|
2667
|
+
readonly operation: "help";
|
|
2668
|
+
readonly uri: string;
|
|
2669
|
+
readonly result: UriHelp;
|
|
2670
|
+
} | {
|
|
2671
|
+
readonly operation: "list";
|
|
2672
|
+
readonly uri: string;
|
|
2673
|
+
readonly result: UriListResult;
|
|
2674
|
+
} | {
|
|
2675
|
+
readonly operation: "search";
|
|
2676
|
+
readonly uri: string;
|
|
2677
|
+
readonly result: UriSearchResult;
|
|
2678
|
+
} | {
|
|
2679
|
+
readonly operation: Extract<UriOperation, "write" | "edit" | "delete">;
|
|
2680
|
+
readonly uri: string;
|
|
2681
|
+
readonly result: UriMutationResult;
|
|
2682
|
+
};
|
|
2683
|
+
|
|
2684
|
+
declare interface UriModule {
|
|
2685
|
+
readonly id: string;
|
|
2686
|
+
/**
|
|
2687
|
+
* Explicit human-readable name for catalog/display purposes (the plugin
|
|
2688
|
+
* manifest's `contributes.uriModules[].displayName`, threaded through by
|
|
2689
|
+
* hand since there is no automatic manifest→runtime-module composition
|
|
2690
|
+
* step). Falls back to a mechanical id-derived title when absent — see
|
|
2691
|
+
* `uriCatalogTitle`.
|
|
2692
|
+
*/
|
|
2693
|
+
readonly title?: string;
|
|
2694
|
+
readonly fallback?: boolean;
|
|
2695
|
+
readonly claims?: readonly UriClaim[];
|
|
2696
|
+
/** Migration-only prompt summary until structured help/catalog coverage replaces prose. */
|
|
2697
|
+
readonly description?: string;
|
|
2698
|
+
readonly agentHint?: UriAgentHint;
|
|
2699
|
+
readonly requiresAuth?: UriAuthRequirement;
|
|
2700
|
+
readonly authActions?: UriAuthActionPolicy;
|
|
2701
|
+
/** Pure route/payload validation applied before a consequential permission ask. */
|
|
2702
|
+
readonly preflight?: UriOperationPreflightPolicy;
|
|
2703
|
+
/**
|
|
2704
|
+
* Per-resource observability: does a `format: "observation"` read of THIS
|
|
2705
|
+
* uri render source-native material ({@link UriResolveContext.format})?
|
|
2706
|
+
* Route-parse only — cheap, side-effect-free, no I/O, no auth (the same
|
|
2707
|
+
* discipline as `matches`); a throw is treated as `false`. Returning true
|
|
2708
|
+
* is the resource's contract that its observation read returns an
|
|
2709
|
+
* observation render and that failures under that format throw typed,
|
|
2710
|
+
* status-bearing errors rather than resolving a friendly fallback page.
|
|
2711
|
+
* The kernel stamps the answer onto the resource's access events, which is
|
|
2712
|
+
* what ambient index consumers replay on — so "PRs are observable" is
|
|
2713
|
+
* declared here, route by route, never module-wide. Absent means no read
|
|
2714
|
+
* of this module is observable. A vendor's own gone-vocabulary (HTTP 410,
|
|
2715
|
+
* a 200-body "ok:false"/"archived:true"/"status:cancelled", or a
|
|
2716
|
+
* 403/404-ambiguous lost-access response) is this module's job to
|
|
2717
|
+
* translate into `UriNotFoundError` or a `status: 404` error under the
|
|
2718
|
+
* observation format — the fold that retires a doc never learns vendor
|
|
2719
|
+
* vocabulary (see `ObservationMaterial`'s doc comment).
|
|
2720
|
+
*/
|
|
2721
|
+
observes?(uri: string): boolean;
|
|
2722
|
+
/**
|
|
2723
|
+
* Head-only revision probe: the resource's CURRENT source-native revision
|
|
2724
|
+
* without fetching its body, or `undefined` when that cannot be known
|
|
2725
|
+
* cheaply. The host calls it before a repeat `read` of an address the
|
|
2726
|
+
* session already holds, with `ctx.expectedRevision` set to the revision
|
|
2727
|
+
* that read reported (`RawResourceMetadata.revision`); a value equal to it
|
|
2728
|
+
* means "unchanged" and the fetch is skipped, anything else falls through
|
|
2729
|
+
* to the ordinary read. A module whose read revision embeds more than the
|
|
2730
|
+
* head can know (a content digest beside a stat stamp) may answer relative
|
|
2731
|
+
* to `expectedRevision` — return it when the cheap part matches, `undefined`
|
|
2732
|
+
* when it does not — but must never return a value it has not verified.
|
|
2733
|
+
* `undefined` is always safe: it costs one fetch, never a wrong answer.
|
|
2734
|
+
* Must be cheaper than the read (a metadata request, a `stat`); a throw is
|
|
2735
|
+
* treated as `undefined`.
|
|
2736
|
+
*/
|
|
2737
|
+
revision?(uri: string, ctx: UriResolveContext): Promise<string | undefined>;
|
|
2738
|
+
readonly getCachePolicy?: UriCachePolicyResolver;
|
|
2739
|
+
/** Retained matching escape hatch; future dispatch should prefer compiled claims. */
|
|
2740
|
+
matches(input: string, ctx: UriResolveContext): boolean;
|
|
2741
|
+
normalize?(input: string, ctx: UriResolveContext): UriNormalizeResult | undefined;
|
|
2742
|
+
describe?(input: string, ctx: UriDescribeContext): UriResourceDescription | undefined;
|
|
2743
|
+
locate?(input: string, ctx: UriLocateContext): Promise<UriLocateResult>;
|
|
2744
|
+
read(input: string, ctx: UriResolveContext): Promise<RawResource>;
|
|
2745
|
+
help?(input: string, ctx: UriResolveContext): Promise<UriHelp>;
|
|
2746
|
+
write?(input: string, content: UriPayload, ctx: UriResolveContext): Promise<UriMutationResult | void>;
|
|
2747
|
+
/** Opt-in structured write with one or more host-resolved resources delivered beside the content. */
|
|
2748
|
+
writeWithSources?(input: string, content: UriPayload, sources: readonly UriWriteSource[], ctx: UriResolveContext): Promise<UriMutationResult | void>;
|
|
2749
|
+
/**
|
|
2750
|
+
* Opt-in byte sink: write RAW BYTES to this address, no UTF-8 round trip.
|
|
2751
|
+
* `UriPayload` is text/JSON by contract, so a module that can store bytes
|
|
2752
|
+
* declares it here instead of stretching `write`. The only caller today is
|
|
2753
|
+
* the `write` tool's `source` copy leg, which is how a provider's binary
|
|
2754
|
+
* resource — a Gmail attachment, an image, a PDF, a zip — reaches a
|
|
2755
|
+
* byte-capable destination without ever being decoded into the model's
|
|
2756
|
+
* context. A module that omits it is text-only, and a binary copy targeting
|
|
2757
|
+
* it is refused with an error naming a byte-capable destination.
|
|
2758
|
+
*
|
|
2759
|
+
* `source` describes what the bytes are: a filesystem-backed sink can ignore
|
|
2760
|
+
* it (the target path already carries the name), while a remote provider
|
|
2761
|
+
* sink needs it to name and type the stored file. It sits before `ctx` so a
|
|
2762
|
+
* forwarding wrapper that fails to thread it is a compile error, not a
|
|
2763
|
+
* silent drop.
|
|
2764
|
+
*/
|
|
2765
|
+
writeBytes?(input: string, bytes: Uint8Array, source: UriSourceDescriptor, ctx: UriResolveContext): Promise<UriMutationResult | void>;
|
|
2766
|
+
edit?(input: string, patch: UriPayload, ctx: UriResolveContext): Promise<UriMutationResult>;
|
|
2767
|
+
list?(input: string, params: UriListParams, ctx: UriResolveContext): Promise<UriListResult>;
|
|
2768
|
+
search?(input: string, params: UriSearchParams, ctx: UriResolveContext): Promise<UriSearchResult>;
|
|
2769
|
+
delete?(input: string, ctx: UriResolveContext): Promise<UriMutationResult>;
|
|
2770
|
+
toMarkdown?(input: UriMarkdownRenderInput, ctx: UriResolveContext): string | undefined;
|
|
2771
|
+
}
|
|
2772
|
+
|
|
2773
|
+
/** Authoritative lifecycle facts for a successful record-like URI mutation. */
|
|
2774
|
+
declare interface UriMutationReceipt {
|
|
2775
|
+
readonly uri: string;
|
|
2776
|
+
readonly operation: "create" | "update" | "trash" | "restore" | "delete";
|
|
2777
|
+
readonly state: "active" | "trashed" | "deleted";
|
|
2778
|
+
readonly revision?: string;
|
|
2779
|
+
readonly changedFields?: readonly string[];
|
|
2780
|
+
readonly recoverable: boolean;
|
|
2781
|
+
readonly recoveryUri?: string;
|
|
2782
|
+
readonly originalUri?: string;
|
|
2783
|
+
readonly restoreTarget?: string;
|
|
2784
|
+
}
|
|
2785
|
+
|
|
2786
|
+
declare interface UriMutationResult {
|
|
2787
|
+
readonly uri: string;
|
|
2788
|
+
readonly summary?: string;
|
|
2789
|
+
readonly resource?: RawResource | JsonValue;
|
|
2790
|
+
readonly validation?: readonly UriValidationMessage[];
|
|
2791
|
+
readonly metadata?: Record<string, unknown>;
|
|
2792
|
+
/** Typed state/recovery evidence; successful callers may trust it without a proof read. */
|
|
2793
|
+
readonly receipt?: UriMutationReceipt;
|
|
2794
|
+
/** Opaque source-native revision after this successful mutation. */
|
|
2795
|
+
readonly revision?: string;
|
|
2796
|
+
}
|
|
2797
|
+
|
|
2798
|
+
declare interface UriNodeCapability {
|
|
2799
|
+
readonly rel: string;
|
|
2800
|
+
readonly operation: UriOperation;
|
|
2801
|
+
readonly uri: string;
|
|
2802
|
+
readonly title: string;
|
|
2803
|
+
readonly description?: string;
|
|
2804
|
+
readonly parameters?: readonly UriParameter[];
|
|
2805
|
+
readonly accepts?: UriPayloadContract;
|
|
2806
|
+
readonly returns?: UriPayloadContract;
|
|
2807
|
+
}
|
|
2808
|
+
|
|
2809
|
+
declare interface UriNormalizeResult {
|
|
2810
|
+
readonly uri: string;
|
|
2811
|
+
readonly source: string;
|
|
2812
|
+
}
|
|
2813
|
+
|
|
2814
|
+
declare type UriOperation = "help" | "read" | "write" | "edit" | "list" | "search" | "delete";
|
|
2815
|
+
|
|
2816
|
+
/**
|
|
2817
|
+
* Pure, route-aware validation that runs before permission policy can surface an ask.
|
|
2818
|
+
*
|
|
2819
|
+
* This is deliberately narrower than execution: it receives only the canonical URI,
|
|
2820
|
+
* operation, and operation payload, performs no I/O, and returns a user-actionable
|
|
2821
|
+
* rejection for calls that the module already knows cannot succeed. The handler must
|
|
2822
|
+
* still validate again when it executes because approval is not an authority boundary.
|
|
2823
|
+
*/
|
|
2824
|
+
declare interface UriOperationPreflightPolicy {
|
|
2825
|
+
/**
|
|
2826
|
+
* The rejection half. Optional since kay#7684: a module whose only preflight
|
|
2827
|
+
* fact is affirmative (`acceptsPayloadlessWrite`) must not have to stub a
|
|
2828
|
+
* validator that never rejects.
|
|
2829
|
+
*/
|
|
2830
|
+
validateOperation?(request: {
|
|
2831
|
+
readonly operation: UriOperation;
|
|
2832
|
+
readonly uri: string;
|
|
2833
|
+
readonly payload: unknown;
|
|
2834
|
+
}): string | undefined;
|
|
2835
|
+
/**
|
|
2836
|
+
* Route-level affirmative: may a `write` to this URI carry NO payload at all
|
|
2837
|
+
* — neither `content` nor `source`? (The canonical case is folder creation,
|
|
2838
|
+
* e.g. `?type=folder`.) The write tool's generic shape rule requires exactly
|
|
2839
|
+
* one payload form; a route answers `true` here when a payloadless write is
|
|
2840
|
+
* meaningful FOR THAT ROUTE, and the rule then admits the call.
|
|
2841
|
+
*
|
|
2842
|
+
* This exists because an empty body is not always sendable: on some
|
|
2843
|
+
* model/provider paths a top-level empty-string argument (`content: ""`)
|
|
2844
|
+
* never reaches the host — the key arrives absent, indistinguishable from an
|
|
2845
|
+
* omitted argument (kay#7684). A contract that can only be invoked with an
|
|
2846
|
+
* empty string is unreachable on those paths, so a route whose write is
|
|
2847
|
+
* meaningful with no body must declare it and let the caller omit the
|
|
2848
|
+
* payload instead.
|
|
2849
|
+
*
|
|
2850
|
+
* Pure and side-effect-free like {@link validateOperation}: route-parse
|
|
2851
|
+
* only, no I/O. A throw NEVER admits the write, but the two consult paths
|
|
2852
|
+
* disagree on purpose about what happens next: pre-ask (permission
|
|
2853
|
+
* resolution, which must never crash the gate) answers no and records a
|
|
2854
|
+
* module-attributed diagnostic; at execute time the write tool asks only
|
|
2855
|
+
* when the call genuinely carries no payload, and a throw there is the
|
|
2856
|
+
* module's own bug surfacing as the call's error — louder than a misleading
|
|
2857
|
+
* \"provide exactly one of content or source\" the model could never
|
|
2858
|
+
* satisfy. Absent means no route of this module accepts a payloadless
|
|
2859
|
+
* write.
|
|
2860
|
+
*/
|
|
2861
|
+
acceptsPayloadlessWrite?(request: {
|
|
2862
|
+
readonly uri: string;
|
|
2863
|
+
}): boolean;
|
|
2864
|
+
}
|
|
2865
|
+
|
|
2866
|
+
declare interface UriParameter {
|
|
2867
|
+
readonly name: string;
|
|
2868
|
+
readonly in: "uri" | "query" | "selector";
|
|
2869
|
+
readonly required: boolean;
|
|
2870
|
+
readonly description?: string;
|
|
2871
|
+
readonly schema: JsonSchema;
|
|
2872
|
+
readonly examples?: readonly unknown[];
|
|
2873
|
+
}
|
|
2874
|
+
|
|
2875
|
+
declare interface UriPatternClaim {
|
|
2876
|
+
readonly kind: "uriPattern";
|
|
2877
|
+
readonly pattern: UriPatternInit;
|
|
2878
|
+
/**
|
|
2879
|
+
* Canonical spelling rebuilt from the captures. When the template keeps the
|
|
2880
|
+
* input's scheme (the module's own addressing), the input's query string is
|
|
2881
|
+
* carried over verbatim so a route flag such as `?permanent=true` survives
|
|
2882
|
+
* canonicalization (kay#7859); a template that re-targets to another scheme
|
|
2883
|
+
* (a projection to a web link) drops it, as does a pattern that consumed the
|
|
2884
|
+
* query itself or a template that spells its own.
|
|
2885
|
+
*/
|
|
2886
|
+
readonly normalizeTo?: string;
|
|
2887
|
+
readonly examples?: readonly string[];
|
|
2888
|
+
}
|
|
2889
|
+
|
|
2890
|
+
declare interface UriPatternInit {
|
|
2891
|
+
readonly protocol?: string;
|
|
2892
|
+
readonly hostname?: string;
|
|
2893
|
+
/**
|
|
2894
|
+
* Slash-delimited path pattern. A final capture may use `:name*` to consume
|
|
2895
|
+
* the remaining path segments, preserving `/` in the captured value.
|
|
2896
|
+
*/
|
|
2897
|
+
readonly pathname?: string;
|
|
2898
|
+
readonly search?: string;
|
|
2899
|
+
}
|
|
2900
|
+
|
|
2901
|
+
declare type UriPayload = string | JsonValue;
|
|
2902
|
+
|
|
2903
|
+
declare interface UriPayloadContract {
|
|
2904
|
+
readonly name: string;
|
|
2905
|
+
readonly mediaType?: string;
|
|
2906
|
+
readonly description?: string;
|
|
2907
|
+
/** Omitted when the node accepts or returns an unstructured payload. */
|
|
2908
|
+
readonly schema?: JsonSchema;
|
|
2909
|
+
/** Cross-value invariants the module enforces but plain JSON Schema cannot express. */
|
|
2910
|
+
readonly semanticConstraints?: readonly string[];
|
|
2911
|
+
readonly examples?: readonly unknown[];
|
|
2912
|
+
}
|
|
2913
|
+
|
|
2914
|
+
/**
|
|
2915
|
+
* The three-state permission vocabulary a plugin declares for its default permissions.
|
|
2916
|
+
* Maps to `PermissionRule.effect`: `allow` → allow, `ask` → ask, `never` → deny.
|
|
2917
|
+
*/
|
|
2918
|
+
declare type UriPermissionDefault = "allow" | "ask" | "never";
|
|
2919
|
+
|
|
2920
|
+
/**
|
|
2921
|
+
* Plugin-declared default permission for each verb group (read/write/edit/delete).
|
|
2922
|
+
* Undeclared verb groups default to `"allow"` (fail-open, same optionality as
|
|
2923
|
+
* {@link UriVerbDocs}). Lives here in the protocol layer (beside `UriVerbDocs`)
|
|
2924
|
+
* so both `@herd/uri-extensions`' `UriExtensionModule` and herd-agent's kernel
|
|
2925
|
+
* `UriModule` can carry it.
|
|
2926
|
+
*/
|
|
2927
|
+
declare interface UriPermissionDefaults {
|
|
2928
|
+
readonly read?: UriPermissionDefault;
|
|
2929
|
+
readonly write?: UriPermissionDefault;
|
|
2930
|
+
readonly edit?: UriPermissionDefault;
|
|
2931
|
+
readonly delete?: UriPermissionDefault;
|
|
2932
|
+
}
|
|
2933
|
+
|
|
2934
|
+
/** The read shape a caller may request via {@link UriResolveContext.format}. */
|
|
2935
|
+
declare type UriReadFormat = "observation";
|
|
2936
|
+
|
|
2937
|
+
declare interface UriResolveContext {
|
|
2938
|
+
readonly cwd: string;
|
|
2939
|
+
readonly signal?: AbortSignal;
|
|
2940
|
+
readonly connection?: UriConnection;
|
|
2941
|
+
/** Host-populated execution auth for verb handlers; always absent during matching. */
|
|
2942
|
+
readonly auth?: UriResolvedAuth;
|
|
2943
|
+
/**
|
|
2944
|
+
* Id of the session dispatching this call, stamped by the host at the dispatch site.
|
|
2945
|
+
* It never originates from anything the caller can influence — no verb argument, no write
|
|
2946
|
+
* body — so a module may treat it as trustworthy caller identity and stamp it onto writes.
|
|
2947
|
+
*
|
|
2948
|
+
* Absent when no session owns the dispatch: registry matching and permission pre-checks
|
|
2949
|
+
* resolve a URI outside any verb call.
|
|
2950
|
+
*/
|
|
2951
|
+
readonly sessionId?: string;
|
|
2952
|
+
/**
|
|
2953
|
+
* WHO the dispatching session acts as, stamped by the host at the same dispatch site as
|
|
2954
|
+
* {@link UriResolveContext.sessionId} and just as un-influenceable by the caller — so a module
|
|
2955
|
+
* writing to a store it owns can record the actor without the agent being able to choose one.
|
|
2956
|
+
*
|
|
2957
|
+
* Free text minted by the host's own runtime (Kay's form is `user`, `subagent`,
|
|
2958
|
+
* `system/<surface>`, `followup/<surface>`) rather than a closed union: the surface half is the
|
|
2959
|
+
* host's vocabulary, and a second enumeration of it here would drift from the first.
|
|
2960
|
+
*
|
|
2961
|
+
* Absent whenever the host has nothing to say — no session owns the dispatch, or the session
|
|
2962
|
+
* predates the host recording an origin. A module must then omit the actor rather than assume
|
|
2963
|
+
* one; an invented actor written into a durable artifact can never be walked back.
|
|
2964
|
+
*/
|
|
2965
|
+
readonly actor?: string;
|
|
2966
|
+
/**
|
|
2967
|
+
* Identity of the tool invocation that owns this dispatch, stamped by the host from its
|
|
2968
|
+
* execution frame. Like `sessionId`, this is never accepted from URI arguments or payloads.
|
|
2969
|
+
* Absent for dispatches that do not run inside a session-owned tool invocation.
|
|
2970
|
+
*/
|
|
2971
|
+
readonly invocation?: {
|
|
2972
|
+
readonly toolCallId: string;
|
|
2973
|
+
readonly sessionId: string;
|
|
2974
|
+
readonly origin: "model" | "host";
|
|
2975
|
+
/**
|
|
2976
|
+
* The daemon-assigned id of the run driving this invocation, when the host's own execution
|
|
2977
|
+
* frame carried one. Absent means the frame carried none — never a reason to guess one: a
|
|
2978
|
+
* module recording this onto a durable artifact must treat absence as unresolved provenance,
|
|
2979
|
+
* not as "no run was active".
|
|
2980
|
+
*/
|
|
2981
|
+
readonly runId?: string;
|
|
2982
|
+
/** The registered name of the tool making this call, when the host's frame carried one. */
|
|
2983
|
+
readonly toolName?: string;
|
|
2984
|
+
};
|
|
2985
|
+
/**
|
|
2986
|
+
* An OPAQUE, host-captured context for the mutation this dispatch is about to make —
|
|
2987
|
+
* the artifact-provenance counterpart of `sessionId`/`actor`, and stamped at the same
|
|
2988
|
+
* un-influenceable dispatch site.
|
|
2989
|
+
*
|
|
2990
|
+
* The host composes it from facts a caller cannot supply: which session is acting, the
|
|
2991
|
+
* run driving it, the tool invocation, and whatever the host resolved about how that
|
|
2992
|
+
* session came to exist. The kernel treats it as an uninterpreted JSON value — it never
|
|
2993
|
+
* reads a field, only checks that the value is lossless JSON before it crosses the
|
|
2994
|
+
* dispatch boundary — so this layer stays ignorant of any one product's provenance
|
|
2995
|
+
* semantics. A module that owns a durable artifact carries it to its store and validates
|
|
2996
|
+
* it against its OWN schema at the point it persists, where the target revision is known.
|
|
2997
|
+
*
|
|
2998
|
+
* Absent means the host captured nothing for this dispatch (a read, a host with no
|
|
2999
|
+
* capture resolver, or a capture that failed) — never a claim that there was nothing to
|
|
3000
|
+
* capture. Present only on mutating dispatches (`write`/`edit`/`delete`); reads never pay
|
|
3001
|
+
* for it.
|
|
3002
|
+
*/
|
|
3003
|
+
readonly captureContext?: Readonly<Record<string, unknown>>;
|
|
3004
|
+
/**
|
|
3005
|
+
* Requested read shape. A module may ignore this and return its normal read; honoring
|
|
3006
|
+
* it means returning an `ObservationMaterial` via `observationResource(...)` instead.
|
|
3007
|
+
* Opt-in only — there is no advertisement surface or capability flag.
|
|
3008
|
+
*/
|
|
3009
|
+
readonly format?: UriReadFormat;
|
|
3010
|
+
/**
|
|
3011
|
+
* Host-provided writable data root for the OWNING extension of the module handling this call —
|
|
3012
|
+
* structurally identical to the per-extension `paths` a tool/handler context already receives, so
|
|
3013
|
+
* a module authored against both surfaces reads `ctx.paths?.dataDir` uniformly. Lets a URI module
|
|
3014
|
+
* own durable local state (a JSON store, a small file tree) instead of the host constructing and
|
|
3015
|
+
* injecting it. Host-populated after dispatch selects a module and keyed to that module's owner;
|
|
3016
|
+
* always absent during matching, and absent for modules with no owning extension (builtins) or a
|
|
3017
|
+
* host that wires no per-module data dir. Unlike a tool context, the host only NAMES this path for
|
|
3018
|
+
* a URI module and does NOT pre-create the directory — create it on first write and tolerate its
|
|
3019
|
+
* absence on read.
|
|
3020
|
+
*/
|
|
3021
|
+
readonly paths?: {
|
|
3022
|
+
readonly dataDir: string;
|
|
3023
|
+
};
|
|
3024
|
+
/**
|
|
3025
|
+
* What this session last saw of the resource, handed to every mutation: the
|
|
3026
|
+
* module's own `RawResourceMetadata.revision` from the last read, else the
|
|
3027
|
+
* host's content digest of what that read returned. Never model input. A
|
|
3028
|
+
* module holding the current content compares with `assertResourceUnchanged`;
|
|
3029
|
+
* absent means the host requires no witness for this call and the module
|
|
3030
|
+
* proceeds as before.
|
|
3031
|
+
*/
|
|
3032
|
+
readonly expectedRevision?: string;
|
|
3033
|
+
/** Host marks a mediated session that enforces its private revision witness ledger. */
|
|
3034
|
+
readonly requiresRevisionWitness?: boolean;
|
|
3035
|
+
/** Host-derived descriptor for a top-level copied resource; absent for inline writes. */
|
|
3036
|
+
readonly writeSource?: UriSourceDescriptor;
|
|
3037
|
+
}
|
|
3038
|
+
|
|
3039
|
+
declare interface UriResolvedAuth {
|
|
3040
|
+
readonly connection?: UriConnection;
|
|
3041
|
+
/** Present only for verb handlers of a module declaring `requiresAuth`; absent during matching. */
|
|
3042
|
+
readonly credential?: UriResolvedCredential;
|
|
3043
|
+
/** Present for handlers gated by a non-injectable external-CLI readiness scheme. */
|
|
3044
|
+
readonly externalCli?: UriExternalCliRunner;
|
|
3045
|
+
/** Opaque host metadata; not a credential channel. */
|
|
3046
|
+
readonly metadata?: Record<string, unknown>;
|
|
3047
|
+
}
|
|
3048
|
+
|
|
3049
|
+
declare interface UriResolvedCredential {
|
|
3050
|
+
/** Bearer for OAuth schemes, apiKey for api-key schemes. Host-resolved and refreshed. */
|
|
3051
|
+
readonly accessToken: string;
|
|
3052
|
+
readonly accountId: string;
|
|
3053
|
+
readonly schemeId: string;
|
|
3054
|
+
readonly scopes?: readonly string[];
|
|
3055
|
+
/**
|
|
3056
|
+
* Every value of a multi-field api-key credential, keyed by the scheme's declared field id
|
|
3057
|
+
* (`accessToken` mirrors the primary field). Absent for single-secret schemes.
|
|
3058
|
+
*/
|
|
3059
|
+
readonly fields?: Readonly<Record<string, string>>;
|
|
3060
|
+
}
|
|
3061
|
+
|
|
3062
|
+
declare interface UriResourceDescription {
|
|
3063
|
+
readonly uri: string;
|
|
3064
|
+
/** Absent when the caller should retain its existing label. */
|
|
3065
|
+
readonly title?: string;
|
|
3066
|
+
/** Provider-owned public URL when the resource also has a web address. */
|
|
3067
|
+
readonly sourceUrl?: string;
|
|
3068
|
+
}
|
|
3069
|
+
|
|
3070
|
+
declare interface UriSchemeClaim {
|
|
3071
|
+
readonly kind: "scheme";
|
|
3072
|
+
readonly scheme: string;
|
|
3073
|
+
}
|
|
3074
|
+
|
|
3075
|
+
declare interface UriSearchItem {
|
|
3076
|
+
readonly uri: string;
|
|
3077
|
+
readonly title?: string;
|
|
3078
|
+
readonly summary?: string;
|
|
3079
|
+
/** Provider resource MIME type; absent when the source does not report one. */
|
|
3080
|
+
readonly sourceMimeType?: string;
|
|
3081
|
+
readonly score?: number;
|
|
3082
|
+
readonly reason?: string;
|
|
3083
|
+
readonly operations?: readonly UriOperation[];
|
|
3084
|
+
}
|
|
3085
|
+
|
|
3086
|
+
declare interface UriSearchParams {
|
|
3087
|
+
readonly query: string;
|
|
3088
|
+
readonly limit?: number;
|
|
3089
|
+
readonly cursor?: string;
|
|
3090
|
+
}
|
|
3091
|
+
|
|
3092
|
+
declare interface UriSearchResult {
|
|
3093
|
+
readonly uri: string;
|
|
3094
|
+
readonly query: string;
|
|
3095
|
+
readonly items: readonly UriSearchItem[];
|
|
3096
|
+
readonly nextCursor?: string;
|
|
3097
|
+
/**
|
|
3098
|
+
* Optional result-set metadata, mirroring {@link UriListResult.metadata}. Lets a
|
|
3099
|
+
* search surface completeness signals such as {@link UriListMetadata.incompleteSearch}
|
|
3100
|
+
* so the caller knows the matches may be partial.
|
|
3101
|
+
*/
|
|
3102
|
+
readonly metadata?: UriListMetadata;
|
|
3103
|
+
}
|
|
3104
|
+
|
|
3105
|
+
/**
|
|
3106
|
+
* What a copied resource IS, minus its bytes: the identity half of a mediated
|
|
3107
|
+
* source. The host derives it once (filename inferred from the source address,
|
|
3108
|
+
* `mimeType` carried from the source module's own `RawResource.mimeType`) and
|
|
3109
|
+
* delivers it on both byte-bearing write hooks — inside each `UriWriteSource`,
|
|
3110
|
+
* and as the standalone third argument to `writeBytes` — so a remote sink can
|
|
3111
|
+
* name and type what it stores without re-deriving either from raw bytes.
|
|
3112
|
+
*/
|
|
3113
|
+
declare interface UriSourceDescriptor {
|
|
3114
|
+
readonly uri: string;
|
|
3115
|
+
readonly filename: string;
|
|
3116
|
+
readonly mimeType?: string;
|
|
3117
|
+
}
|
|
3118
|
+
|
|
3119
|
+
declare interface UriTemplate {
|
|
3120
|
+
readonly uri: string;
|
|
3121
|
+
readonly description: string;
|
|
3122
|
+
readonly operations?: readonly UriOperation[];
|
|
3123
|
+
/**
|
|
3124
|
+
* Per-operation effect docs for this route, keyed by operation. A route
|
|
3125
|
+
* whose shared {@link description} spans several verbs ("Read, edit, or
|
|
3126
|
+
* trash a thread") is ambiguous for any surface that groups by verb — a
|
|
3127
|
+
* permission view must not describe `read` with a sentence that mentions
|
|
3128
|
+
* trashing. Each entry says what THAT operation concretely does at THIS
|
|
3129
|
+
* route; operations without an entry fall back to the shared description.
|
|
3130
|
+
* Additive and optional — existing templates are unaffected.
|
|
3131
|
+
*/
|
|
3132
|
+
readonly operationDocs?: Partial<Record<UriOperation, string>>;
|
|
3133
|
+
/**
|
|
3134
|
+
* Per-operation default permission for this route, keyed by operation.
|
|
3135
|
+
* When present, overrides the module-level {@link UriPermissionDefaults}
|
|
3136
|
+
* for the named operations at this specific route (resource-glob scoped).
|
|
3137
|
+
* Undeclared operations fall back to the module-level default, then to
|
|
3138
|
+
* `"allow"` (fail-open).
|
|
3139
|
+
*/
|
|
3140
|
+
readonly operationDefaults?: Partial<Record<UriOperation, UriPermissionDefault>>;
|
|
3141
|
+
/**
|
|
3142
|
+
* Parameters are DERIVED from the address pattern by `deriveTemplateParameters`
|
|
3143
|
+
* (`template-route.ts`): every `<placeholder>` path segment and every `?key=<value>`
|
|
3144
|
+
* query key becomes one. Author an entry only to add a description, a tighter schema,
|
|
3145
|
+
* or examples; it overrides the derived entry with the same name. An authored name
|
|
3146
|
+
* that is not a placeholder in `uri` is a lint failure, not a hidden extra.
|
|
3147
|
+
*/
|
|
3148
|
+
readonly parameters?: readonly UriParameter[];
|
|
3149
|
+
/**
|
|
3150
|
+
* The payload contract for each mutating verb at THIS route, built with `payload()`
|
|
3151
|
+
* from the same Zod validator the handler parses with. A contract without a `schema`
|
|
3152
|
+
* is the typed "unstructured" declaration (raw text, YAML, a file body) and must be
|
|
3153
|
+
* true of the handler. This is the only place a route's write or edit shape is
|
|
3154
|
+
* disclosed; `help` renders it at the route and the tool catalog derives its facets
|
|
3155
|
+
* from it.
|
|
3156
|
+
*/
|
|
3157
|
+
readonly accepts?: Partial<Record<"write" | "edit", UriPayloadContract>>;
|
|
3158
|
+
/** What `delete` does at this route and whether it can be undone. Required by lint on
|
|
3159
|
+
* every route that lists `delete`. */
|
|
3160
|
+
readonly deleteEffect?: UriDeleteEffect;
|
|
3161
|
+
}
|
|
3162
|
+
|
|
3163
|
+
declare interface UriValidationMessage {
|
|
3164
|
+
readonly path?: string;
|
|
3165
|
+
readonly message: string;
|
|
3166
|
+
}
|
|
3167
|
+
|
|
3168
|
+
/**
|
|
3169
|
+
* Author-declared, human-facing documentation of what each supported verb
|
|
3170
|
+
* CONCRETELY does against the provider — the capability half of a module's
|
|
3171
|
+
* boolean verb-support declaration. Written for a permission/capability
|
|
3172
|
+
* surface ("what am I allowing?"), not for the model: one plain sentence per
|
|
3173
|
+
* verb naming the real-world effect ("Sends mail, creates/updates drafts,
|
|
3174
|
+
* applies label changes"), never wire mechanics or URI shapes. Verbs the
|
|
3175
|
+
* module does not support are omitted; at runtime a supported verb with no
|
|
3176
|
+
* entry simply renders undocumented (fail-open metadata) — but for a plugin's
|
|
3177
|
+
* `kay.agent` URI modules, `auditPluginDisclosure` (`@kay/plugin-sdk/
|
|
3178
|
+
* disclosure`) enforces coverage of every supported verb at authoring/load
|
|
3179
|
+
* time.
|
|
3180
|
+
*
|
|
3181
|
+
* Lives here in the protocol layer (beside {@link UriTemplate.operationDocs},
|
|
3182
|
+
* its per-route sibling) so both `@herd/uri-extensions`' `UriExtensionModule`
|
|
3183
|
+
* and herd-agent's kernel `UriModule` can carry it without inverting the
|
|
3184
|
+
* `@herd/uri` ← `@herd/uri-extensions` dependency.
|
|
3185
|
+
*/
|
|
3186
|
+
declare interface UriVerbDocs {
|
|
3187
|
+
/** What a `read` of this address space returns (also covers `help`). */
|
|
3188
|
+
readonly read?: string;
|
|
3189
|
+
/** What `list` enumerates, when it differs meaningfully from `read`. */
|
|
3190
|
+
readonly list?: string;
|
|
3191
|
+
/** What `search` queries, when it differs meaningfully from `read`. */
|
|
3192
|
+
readonly search?: string;
|
|
3193
|
+
/** What a `write` creates or overwrites at the provider. */
|
|
3194
|
+
readonly write?: string;
|
|
3195
|
+
/** What an `edit` changes in place at the provider. */
|
|
3196
|
+
readonly edit?: string;
|
|
3197
|
+
/** What a `delete` destroys or removes at the provider (and how recoverable it is). */
|
|
3198
|
+
readonly delete?: string;
|
|
3199
|
+
}
|
|
3200
|
+
|
|
3201
|
+
declare interface UriVerbSupport {
|
|
3202
|
+
readonly readable: boolean;
|
|
3203
|
+
readonly writable: boolean;
|
|
3204
|
+
readonly editable: boolean;
|
|
3205
|
+
readonly deletable?: boolean;
|
|
3206
|
+
readonly listable: boolean;
|
|
3207
|
+
}
|
|
3208
|
+
|
|
3209
|
+
/** A mediated resource supplied beside structured write content without entering model context. */
|
|
3210
|
+
declare interface UriWriteSource extends UriSourceDescriptor {
|
|
3211
|
+
readonly content: string | Uint8Array;
|
|
3212
|
+
}
|
|
3213
|
+
|
|
3214
|
+
declare interface UrlPatternClaim {
|
|
3215
|
+
readonly kind: "urlPattern";
|
|
3216
|
+
readonly pattern: UriPatternInit;
|
|
3217
|
+
readonly normalizeTo?: string;
|
|
3218
|
+
readonly examples?: readonly string[];
|
|
3219
|
+
}
|
|
3220
|
+
|
|
3221
|
+
declare interface Usage {
|
|
3222
|
+
input: number;
|
|
3223
|
+
output: number;
|
|
3224
|
+
cacheRead: number;
|
|
3225
|
+
cacheWrite: number;
|
|
3226
|
+
/** Subset of `cacheWrite` written with 1h retention. Only Anthropic reports this split. */
|
|
3227
|
+
cacheWrite1h?: number;
|
|
3228
|
+
/**
|
|
3229
|
+
* Reasoning/thinking tokens, when the provider reports them. This is a subset of
|
|
3230
|
+
* `output`: `output` already includes these tokens. Set to a number (possibly 0) by
|
|
3231
|
+
* providers that expose a reasoning breakdown; left undefined by providers that don't.
|
|
3232
|
+
*/
|
|
3233
|
+
reasoning?: number;
|
|
3234
|
+
totalTokens: number;
|
|
3235
|
+
cost: {
|
|
3236
|
+
input: number;
|
|
3237
|
+
output: number;
|
|
3238
|
+
cacheRead: number;
|
|
3239
|
+
cacheWrite: number;
|
|
3240
|
+
total: number;
|
|
3241
|
+
};
|
|
3242
|
+
}
|
|
3243
|
+
|
|
3244
|
+
/** Validate freshly-entered material and resolve the provider account before persistence. */
|
|
3245
|
+
declare type ValidateApiKeyCredential = (input: ValidateApiKeyCredentialInput) => Promise<ValidateApiKeyCredentialResult>;
|
|
3246
|
+
|
|
3247
|
+
declare interface ValidateApiKeyCredentialInput {
|
|
3248
|
+
readonly accessToken: string;
|
|
3249
|
+
/** Every declared field; empty for a classic single-secret scheme. */
|
|
3250
|
+
readonly fields: Readonly<Record<string, string>>;
|
|
3251
|
+
readonly signal: AbortSignal;
|
|
3252
|
+
}
|
|
3253
|
+
|
|
3254
|
+
declare interface ValidateApiKeyCredentialResult {
|
|
3255
|
+
readonly accountId: string;
|
|
3256
|
+
readonly label?: string;
|
|
3257
|
+
readonly identity?: AccountIdentity;
|
|
3258
|
+
/** Non-secret observations the host may surface without retaining credential material. */
|
|
3259
|
+
readonly capabilityChecks?: readonly ApiKeyCapabilityCheck[];
|
|
3260
|
+
}
|
|
3261
|
+
|
|
3262
|
+
declare type ValidatePluginApiKeyCredential = ValidateApiKeyCredential;
|
|
3263
|
+
|
|
3264
|
+
/**
|
|
3265
|
+
* Vercel AI Gateway routing preferences.
|
|
3266
|
+
* Controls which upstream providers the gateway routes requests to.
|
|
3267
|
+
* @see https://vercel.com/docs/ai-gateway/models-and-providers/provider-options
|
|
3268
|
+
*/
|
|
3269
|
+
declare interface VercelGatewayRouting {
|
|
3270
|
+
/** List of provider slugs to exclusively use for this request (e.g., ["bedrock", "anthropic"]). */
|
|
3271
|
+
only?: string[];
|
|
3272
|
+
/** List of provider slugs to try in order (e.g., ["anthropic", "openai"]). */
|
|
3273
|
+
order?: string[];
|
|
3274
|
+
}
|
|
3275
|
+
|
|
3276
|
+
declare const WhiteboardEntryEnvelope: z.ZodObject<{
|
|
3277
|
+
id: z.ZodString;
|
|
3278
|
+
typeId: z.ZodString;
|
|
3279
|
+
schemaVersion: z.ZodNumber;
|
|
3280
|
+
title: z.ZodString;
|
|
3281
|
+
authorSessionId: z.ZodNullable<z.ZodString>;
|
|
3282
|
+
}, z.core.$strict>;
|
|
3283
|
+
|
|
3284
|
+
declare type WhiteboardEntryEnvelope = Readonly<z.infer<typeof WhiteboardEntryEnvelope>>;
|
|
3285
|
+
|
|
3286
|
+
declare const WhiteboardEntryInfo: z.ZodObject<{
|
|
3287
|
+
name: z.ZodString;
|
|
3288
|
+
revision: z.ZodString;
|
|
3289
|
+
entry: z.ZodOptional<z.ZodObject<{
|
|
3290
|
+
id: z.ZodString;
|
|
3291
|
+
typeId: z.ZodString;
|
|
3292
|
+
schemaVersion: z.ZodNumber;
|
|
3293
|
+
title: z.ZodString;
|
|
3294
|
+
authorSessionId: z.ZodNullable<z.ZodString>;
|
|
3295
|
+
}, z.core.$strict>>;
|
|
3296
|
+
state: z.ZodEnum<{
|
|
3297
|
+
available: "available";
|
|
3298
|
+
unavailable: "unavailable";
|
|
3299
|
+
"unsupported-version": "unsupported-version";
|
|
3300
|
+
invalid: "invalid";
|
|
3301
|
+
}>;
|
|
3302
|
+
mutable: z.ZodBoolean;
|
|
3303
|
+
diagnostics: z.ZodArray<z.ZodString>;
|
|
3304
|
+
}, z.core.$strict>;
|
|
3305
|
+
|
|
3306
|
+
declare type WhiteboardEntryInfo = Readonly<z.infer<typeof WhiteboardEntryInfo>>;
|
|
3307
|
+
|
|
3308
|
+
declare const WhiteboardEntryInspection: z.ZodObject<{
|
|
3309
|
+
name: z.ZodString;
|
|
3310
|
+
revision: z.ZodString;
|
|
3311
|
+
entry: z.ZodOptional<z.ZodObject<{
|
|
3312
|
+
id: z.ZodString;
|
|
3313
|
+
typeId: z.ZodString;
|
|
3314
|
+
schemaVersion: z.ZodNumber;
|
|
3315
|
+
title: z.ZodString;
|
|
3316
|
+
authorSessionId: z.ZodNullable<z.ZodString>;
|
|
3317
|
+
}, z.core.$strict>>;
|
|
3318
|
+
state: z.ZodEnum<{
|
|
3319
|
+
available: "available";
|
|
3320
|
+
unavailable: "unavailable";
|
|
3321
|
+
"unsupported-version": "unsupported-version";
|
|
3322
|
+
invalid: "invalid";
|
|
3323
|
+
}>;
|
|
3324
|
+
mutable: z.ZodBoolean;
|
|
3325
|
+
diagnostics: z.ZodArray<z.ZodString>;
|
|
3326
|
+
value: z.ZodOptional<z.ZodType<JsonValue_2, unknown, z.core.$ZodTypeInternals<JsonValue_2, unknown>>>;
|
|
3327
|
+
text: z.ZodString;
|
|
3328
|
+
truncated: z.ZodBoolean;
|
|
3329
|
+
}, z.core.$strict>;
|
|
3330
|
+
|
|
3331
|
+
declare type WhiteboardEntryInspection = Readonly<z.infer<typeof WhiteboardEntryInspection>>;
|
|
3332
|
+
|
|
3333
|
+
declare const WhiteboardEntrySnapshot: z.ZodObject<{
|
|
3334
|
+
name: z.ZodString;
|
|
3335
|
+
entry: z.ZodObject<{
|
|
3336
|
+
id: z.ZodString;
|
|
3337
|
+
typeId: z.ZodString;
|
|
3338
|
+
schemaVersion: z.ZodNumber;
|
|
3339
|
+
title: z.ZodString;
|
|
3340
|
+
authorSessionId: z.ZodNullable<z.ZodString>;
|
|
3341
|
+
}, z.core.$strict>;
|
|
3342
|
+
revision: z.ZodString;
|
|
3343
|
+
value: z.ZodType<JsonValue_2, unknown, z.core.$ZodTypeInternals<JsonValue_2, unknown>>;
|
|
3344
|
+
}, z.core.$strict>;
|
|
3345
|
+
|
|
3346
|
+
declare type WhiteboardEntrySnapshot = Readonly<Omit<z.infer<typeof WhiteboardEntrySnapshot>, "entry"> & {
|
|
3347
|
+
readonly entry: WhiteboardEntryEnvelope;
|
|
3348
|
+
}>;
|
|
3349
|
+
|
|
3350
|
+
declare const WhiteboardEntrySummary: z.ZodObject<{
|
|
3351
|
+
name: z.ZodString;
|
|
3352
|
+
entry: z.ZodObject<{
|
|
3353
|
+
id: z.ZodString;
|
|
3354
|
+
typeId: z.ZodString;
|
|
3355
|
+
schemaVersion: z.ZodNumber;
|
|
3356
|
+
title: z.ZodString;
|
|
3357
|
+
authorSessionId: z.ZodNullable<z.ZodString>;
|
|
3358
|
+
}, z.core.$strict>;
|
|
3359
|
+
revision: z.ZodString;
|
|
3360
|
+
}, z.core.$strict>;
|
|
3361
|
+
|
|
3362
|
+
declare type WhiteboardEntrySummary = Readonly<Omit<z.infer<typeof WhiteboardEntrySummary>, "entry"> & {
|
|
3363
|
+
readonly entry: WhiteboardEntryEnvelope;
|
|
3364
|
+
}>;
|
|
3365
|
+
|
|
3366
|
+
declare const WhiteboardTypeInfo: z.ZodObject<{
|
|
3367
|
+
typeId: z.ZodString;
|
|
3368
|
+
displayName: z.ZodString;
|
|
3369
|
+
schemaVersion: z.ZodNumber;
|
|
3370
|
+
available: z.ZodBoolean;
|
|
3371
|
+
presentation: z.ZodOptional<z.ZodObject<{
|
|
3372
|
+
app: z.ZodString;
|
|
3373
|
+
}, z.core.$strict>>;
|
|
3374
|
+
}, z.core.$strict>;
|
|
3375
|
+
|
|
3376
|
+
declare type WhiteboardTypeInfo = Readonly<z.infer<typeof WhiteboardTypeInfo>>;
|
|
3377
|
+
|
|
3378
|
+
/** The logical plugin-defined JSON payload stored inside a host-owned entry envelope. */
|
|
3379
|
+
declare const WhiteboardValue: z.ZodType<JsonValue_2, unknown, z.core.$ZodTypeInternals<JsonValue_2, unknown>>;
|
|
3380
|
+
|
|
3381
|
+
declare type WhiteboardValue = z.infer<typeof WhiteboardValue>;
|
|
3382
|
+
|
|
3383
|
+
/**
|
|
3384
|
+
* Run `use` against a live Jev client, falling back to `fallback` whenever Jev cannot serve the
|
|
3385
|
+
* call. The wrapper exists so a feature can ASK for a typed decision without ever depending on
|
|
3386
|
+
* one: `fallback` is required, and it runs whenever no host-managed key is saved,
|
|
3387
|
+
* the user has not granted `jev.use`, the branch exceeds its budget, `use` throws, or
|
|
3388
|
+
* `use` declines by returning `undefined`. With valid options, this rejects on an explicit
|
|
3389
|
+
* `signal` abort, reaching `deadlineAt`, or `fallback` itself throwing.
|
|
3390
|
+
*
|
|
3391
|
+
* ALWAYS PRESENT on the plugin API, on every host. A host that composed no Jev lane binds
|
|
3392
|
+
* {@link createFallbackOnlyWithJev}, so a caller never branches on availability — branching is
|
|
3393
|
+
* exactly how a fallback gets skipped.
|
|
3394
|
+
*
|
|
3395
|
+
* `jev` is the vendor's own `TypeSafeClient`, configured with the host's default model and base
|
|
3396
|
+
* URL. Native `request.model` overrides remain supported. It
|
|
3397
|
+
* is valid only for the duration of the callback: a reference retained past `use`'s scope is
|
|
3398
|
+
* refused at the transport, and an in-flight request is aborted when the call stops waiting.
|
|
3399
|
+
* The callback itself is ABANDONED, not interrupted — a host cannot stop arbitrary JavaScript
|
|
3400
|
+
* that is already running, so `use` must not perform side effects it would mind completing
|
|
3401
|
+
* unobserved. The same applies to a running fallback: abort/deadline stops waiting for it but
|
|
3402
|
+
* cannot interrupt its JavaScript.
|
|
3403
|
+
*/
|
|
3404
|
+
declare type WithJev = <T>(
|
|
3405
|
+
/** Return `undefined` to decline (the fallback runs). A feature whose real result can itself be
|
|
3406
|
+
* `undefined` should return `null` or its own wrapper type instead. */
|
|
3407
|
+
use: (jev: TypeSafeClient) => T | undefined | PromiseLike<T | undefined>,
|
|
3408
|
+
/** Required. The behaviour the feature has without Jev. */
|
|
3409
|
+
fallback: () => T | PromiseLike<T>, options?: WithJevOptions) => Promise<T>;
|
|
3410
|
+
|
|
3411
|
+
/** Per-call knobs for {@link WithJev}. Every field is optional; the host applies its own default
|
|
3412
|
+
* budget when none is given, so the Jev branch is never unbounded. */
|
|
3413
|
+
declare interface WithJevOptions {
|
|
3414
|
+
/**
|
|
3415
|
+
* Explicit cancellation. Always propagates as a rejection; never substituted with `fallback`,
|
|
3416
|
+
* because an aborted call means "stop", not "use the cheaper path".
|
|
3417
|
+
*/
|
|
3418
|
+
readonly signal?: AbortSignal;
|
|
3419
|
+
/** Bounds the Jev branch alone, measured from the call. */
|
|
3420
|
+
readonly timeoutMs?: number;
|
|
3421
|
+
/**
|
|
3422
|
+
* Absolute epoch-ms hard deadline for the WHOLE call, `fallback` included. A deadline already
|
|
3423
|
+
* passed rejects (like `signal`); one still ahead derives the Jev branch's budget from it and
|
|
3424
|
+
* keeps a small reserve so `fallback` has a window to run.
|
|
3425
|
+
*/
|
|
3426
|
+
readonly deadlineAt?: number;
|
|
3427
|
+
/** Attribution slug for host usage accounting. Never sent to Jev. */
|
|
3428
|
+
readonly purpose?: string;
|
|
3429
|
+
}
|
|
3430
|
+
|
|
3431
|
+
export { }
|