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