@yaklabs-ai/plugin-sdk 0.15.3 → 0.26.3

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.
@@ -1,7 +1,7 @@
1
1
  import { z } from 'zod';
2
2
 
3
3
  /**
4
- * Who a connected account *belongs to* — the human, as the provider reports them. Derived at connect
4
+ * Who a connected account *belongs to* - the human, as the provider reports them. Derived at connect
5
5
  * time by a scheme's `resolveAccountId` hook from the provider's own profile endpoint, and surfaced
6
6
  * to the agent so it never has to ask "what is your email?" for a service the user already
7
7
  * connected.
@@ -9,7 +9,7 @@ import { z } from 'zod';
9
9
  * Distinct from {@link AccountProvenance}: an account can be firmly identified
10
10
  * (`externalId`/`provider`) while nothing is known about its owner, and a workspace's name is not a
11
11
  * person's name. Field-for-field the same shape the credential authority persists, so identity
12
- * crosses the daemon⇄kernel boundary without a lossy projection.
12
+ * crosses the daemon<->kernel boundary without a lossy projection.
13
13
  */
14
14
  declare interface AccountIdentity {
15
15
  /** Provider handle (GitHub `login`, Slack `user`). */
@@ -28,7 +28,7 @@ declare interface AccountIdentity {
28
28
  /**
29
29
  * Provider-native session id claim (WorkOS's `sid`), needed to call the provider's own
30
30
  * session-revocation endpoint at sign-out (`POST /user_management/sessions/revoke`, not yet
31
- * wired — see `docs/kay-account-auth.md`'s "Sign-out guarantee" section). Only Bonsai's WorkOS
31
+ * wired - see `docs/kay-account-auth.md`'s "Sign-out guarantee" section). Only Bonsai's WorkOS
32
32
  * product-account exchange/refresh sets it; every other provider leaves it absent.
33
33
  */
34
34
  readonly providerSessionId?: string;
@@ -36,7 +36,7 @@ declare interface AccountIdentity {
36
36
 
37
37
  /**
38
38
  * Structured, non-secret *provenance* for a connected account: which record on the provider's side
39
- * this credential points at. Says nothing about the human — a Notion `displayName` here is the
39
+ * this credential points at. Says nothing about the human - a Notion `displayName` here is the
40
40
  * workspace name, a Slack one is the team name. To describe the person, see {@link AccountIdentity}.
41
41
  */
42
42
  declare interface AccountProvenance {
@@ -50,6 +50,17 @@ declare interface AccountProvenance {
50
50
  readonly raw?: Readonly<Record<string, unknown>>;
51
51
  }
52
52
 
53
+ declare const AdapterPlacement: z.ZodEnum<{
54
+ "local-only": "local-only";
55
+ anywhere: "anywhere";
56
+ }>;
57
+
58
+ declare type AdapterPlacement = z.infer<typeof AdapterPlacement>;
59
+
60
+ declare const AdapterSelectionKey: z.ZodString;
61
+
62
+ declare type AdapterSelectionKey = z.infer<typeof AdapterSelectionKey>;
63
+
53
64
  /** Applies an AddImmutable action to a type. */
54
65
  declare function AddImmutable<Type extends TSchema>(type: Type, options?: TSchemaOptions): TAddImmutable<Type>;
55
66
 
@@ -146,21 +157,6 @@ declare function Any(options?: TSchemaOptions): TAny;
146
157
 
147
158
  declare type Api = KnownApi | (string & {});
148
159
 
149
- /** One labeled secret input of a multi-field api-key scheme. */
150
- declare interface ApiKeyAuthSchemeField {
151
- /** Stable key — the `fields` Record key on the stored material and resolved credential. */
152
- readonly id: string;
153
- /** Per-field prompt copy, e.g. "Access Client ID". */
154
- readonly label: string;
155
- }
156
-
157
- /** Provider-owned instructions for creating a credential outside Bonsai. */
158
- declare interface ApiKeyAuthSchemeSetup {
159
- readonly instructions: string;
160
- /** Absolute HTTPS page where the user can create or manage the credential. */
161
- readonly url: string;
162
- }
163
-
164
160
  /** One non-secret capability observation made while validating a credential. */
165
161
  declare interface ApiKeyCapabilityCheck {
166
162
  /** Stable service capability id the scheme checked. */
@@ -178,6 +174,10 @@ declare function _Array_<Type extends TSchema>(items: Type, options?: TArrayOpti
178
174
  /** Extracts options from a TArray. */
179
175
  declare function ArrayOptions(type: TArray): TArrayOptions;
180
176
 
177
+ declare const ArtifactGenerationId: z.core.$ZodBranded<z.ZodString, "ArtifactGenerationId", "out">;
178
+
179
+ declare type ArtifactGenerationId = z.infer<typeof ArtifactGenerationId>;
180
+
181
181
  /**
182
182
  * Performs an Object assign using the Left and Right object types. We track this operation as it
183
183
  * creates a new GC handle per assignment.
@@ -186,7 +186,7 @@ declare function Assign<Left extends ObjectLike, Right extends ObjectLike>(left:
186
186
 
187
187
  /**
188
188
  * Audit every hint-bearing module in `input.modules` (a module with no `agentHint` renders
189
- * nothing into the prompt — see `renderUriAgentHints` — so it carries no disclosure surface
189
+ * nothing into the prompt - see `renderUriAgentHints` - so it carries no disclosure surface
190
190
  * to audit) against the rules #1138 locks down. Call from a plugin's own test suite, e.g.:
191
191
  *
192
192
  * ```ts
@@ -204,8 +204,8 @@ export declare interface AuditPluginDisclosureInput {
204
204
  readonly declaredSkills: readonly DeclaredSkillRef[];
205
205
  /**
206
206
  * Skills bound to a declared slash command (`CommandContribution.handler.kind ===
207
- * "skill"`). A command-bound skill has an unconditional discovery path — the command
208
- * index lists `/<name>` regardless of description keywords — so the unlinked-skill
207
+ * "skill"`). A command-bound skill has an unconditional discovery path - the command
208
+ * index lists `/<name>` regardless of description keywords - so the unlinked-skill
209
209
  * scheme-in-description fallback does not apply to it.
210
210
  */
211
211
  readonly commandBoundSkillIds?: readonly string[];
@@ -213,7 +213,7 @@ export declare interface AuditPluginDisclosureInput {
213
213
 
214
214
  /**
215
215
  * The codegen-pair equivalence check every bundled-skill plugin hand-rolled (see
216
- * `plugins/ARCHITECTURE.md` → "Bundled skills", step 4): every exported `PluginSkillDefinition`
216
+ * `plugins/ARCHITECTURE.md` -> "Bundled skills", step 4): every exported `PluginSkillDefinition`
217
217
  * must have a matching manifest declaration (same `localId`, same `description`), and the
218
218
  * declared `SKILL.md` file's decoded YAML name and description must match the export.
219
219
  * The body is compared byte-for-byte after trimming surrounding whitespace.
@@ -224,33 +224,66 @@ export declare interface AuditSkillCodegenPairsInput {
224
224
  readonly declaredSkills: readonly DeclaredSkillContribution[];
225
225
  readonly exportedSkills: readonly PluginSkillDefinition[];
226
226
  /**
227
- * The plugin package's root directory, as a `file:` URL — `declaredSkills[].path` resolves
227
+ * The plugin package's root directory, as a `file:` URL - `declaredSkills[].path` resolves
228
228
  * against it. From a test file under `test/`, pass `new URL("../", import.meta.url)`.
229
229
  */
230
230
  readonly packageDir: URL;
231
231
  }
232
232
 
233
- /** A connected account under one `(serviceId, schemeId)` — the multi-account unit. */
234
- declare interface AuthAccount {
235
- readonly accountId: string;
236
- readonly displayName: string;
237
- /**
238
- * Who the account belongs to, carried through from the stored credential. This is the
239
- * agent-facing account shape (it feeds `auth_accounts` and the ambient facts), so it carries the
240
- * human but NOT {@link AccountProvenance}: provenance's `raw` is an unbounded provider blob, and
241
- * an account's provider-side record is not something the agent needs to see. Provenance stays on
242
- * `StoredCredentialSummary` (persistence) and `AuthBrokerAccount` (the host's connection UI).
243
- */
244
- readonly identity?: AccountIdentity;
245
- /**
246
- * Trust-lost marker carried through from `StoredCredentialSummary.needsReconnect`
247
- * (kay#7852): the account's credential is dead (revoked or terminally unrefreshable) and the
248
- * user must reconnect it. The account stays listed — with this flag — so the agent can see it
249
- * and point the user at a reconnect; token resolution for it still fails closed.
250
- */
251
- readonly needsReconnect?: boolean;
233
+ declare namespace authoringCommonSchemas {
234
+ export {
235
+ hostWithinDomain,
236
+ urlHostWithinDomain,
237
+ refineWebSessionScopeConfined,
238
+ KAY_PLUGIN_PROTOCOL_VERSION,
239
+ KAY_PLUGIN_PROTOCOL_PACKAGE_NAME,
240
+ KAY_PLUGIN_SDK_VERSION,
241
+ KAY_PLUGIN_COMPATIBLE_SDK_VERSIONS,
242
+ KAY_PLUGIN_TARGET_NAMES,
243
+ KayPluginProtocolVersion,
244
+ PluginTargetName,
245
+ KAY_EVENT_PUBLISHER_ID,
246
+ NonEmptyString,
247
+ PluginId,
248
+ PluginNamespace,
249
+ LocalContributionId,
250
+ GlobalContributionId,
251
+ PluginToolName,
252
+ AuthServiceReference,
253
+ AuthSchemeReference,
254
+ AdapterSelectionKey,
255
+ AdapterPlacement,
256
+ UriScheme,
257
+ MimeTypeString,
258
+ FileExtensionToken,
259
+ CliBinaryName,
260
+ CliAuthEnvVarName,
261
+ PackageAssetPath,
262
+ PluginSourceClass,
263
+ ArtifactGenerationId,
264
+ HttpsUrl,
265
+ RegistrableDomain,
266
+ GitHostName,
267
+ GitBasicAuthUser,
268
+ HttpStatusCode,
269
+ SignedInStatusCode,
270
+ JsonPointer,
271
+ TargetEntrypoint,
272
+ canonicalCommonSchemas,
273
+ JsonObject,
274
+ JsonSchema_2 as JsonSchema,
275
+ JsonValue_2 as JsonValue
276
+ }
252
277
  }
253
278
 
279
+ declare const AuthSchemeReference: z.ZodString;
280
+
281
+ declare type AuthSchemeReference = z.infer<typeof AuthSchemeReference>;
282
+
283
+ declare const AuthServiceReference: z.ZodString;
284
+
285
+ declare type AuthServiceReference = z.infer<typeof AuthServiceReference>;
286
+
254
287
  declare interface Availability {
255
288
  readonly available: boolean;
256
289
  readonly reason?: string;
@@ -302,7 +335,7 @@ declare type BackgroundTaskRun = (ctx: BackgroundTaskRunContext) => Promise<stri
302
335
  declare interface BackgroundTaskRunContext {
303
336
  readonly taskId: string;
304
337
  readonly signal: AbortSignal;
305
- /** `this: void` — task bodies destructure this reporter off the context. */
338
+ /** `this: void` - task bodies destructure this reporter off the context. */
306
339
  reportProgress(this: void, text: string, details?: Record<string, unknown>): void;
307
340
  }
308
341
 
@@ -366,50 +399,53 @@ declare function Call<Target extends TSchema, Arguments extends TSchema[]>(targe
366
399
 
367
400
  declare function CallConstruct<Target extends TSchema, Arguments extends TSchema[]>(target: Target, arguments_: [...Arguments]): TCallConstruct<Target, Arguments>;
368
401
 
402
+ declare const canonicalCommonSchemas: {
403
+ NonEmptyString: z.ZodString;
404
+ PluginId: z.ZodString;
405
+ PluginNamespace: z.ZodString;
406
+ LocalContributionId: z.ZodString;
407
+ GlobalContributionId: z.ZodString;
408
+ PluginToolName: z.ZodString;
409
+ AuthServiceReference: z.ZodString;
410
+ AuthSchemeReference: z.ZodString;
411
+ AdapterSelectionKey: z.ZodString;
412
+ AdapterPlacement: z.ZodEnum<{
413
+ "local-only": "local-only";
414
+ anywhere: "anywhere";
415
+ }>;
416
+ UriScheme: z.ZodString;
417
+ MimeTypeString: z.ZodString;
418
+ FileExtensionToken: z.ZodString;
419
+ CliBinaryName: z.ZodString;
420
+ CliAuthEnvVarName: z.ZodString;
421
+ PackageAssetPath: z.ZodString;
422
+ PluginSourceClass: z.ZodEnum<{
423
+ bundled: "bundled";
424
+ "org-shared": "org-shared";
425
+ "user-local": "user-local";
426
+ "verified-marketplace": "verified-marketplace";
427
+ freeform: "freeform";
428
+ }>;
429
+ ArtifactGenerationId: z.core.$ZodBranded<z.ZodString, "ArtifactGenerationId", "out">;
430
+ HttpsUrl: z.ZodURL;
431
+ RegistrableDomain: z.ZodString;
432
+ GitHostName: z.ZodString;
433
+ GitBasicAuthUser: z.ZodString;
434
+ HttpStatusCode: z.ZodNumber;
435
+ SignedInStatusCode: z.ZodNumber;
436
+ JsonPointer: z.ZodString;
437
+ TargetEntrypoint: z.ZodObject<{
438
+ module: z.ZodString;
439
+ export: z.ZodOptional<z.ZodString>;
440
+ }, z.core.$strict>;
441
+ };
442
+
369
443
  /** Applies a Capitalize action to the given type. */
370
444
  declare function Capitalize_2<Type extends TSchema>(type: Type, options?: TSchemaOptions): TCapitalize<Type>;
371
445
 
372
446
  /** Creates a deferred Capitalize action. */
373
447
  declare function CapitalizeDeferred<Type extends TSchema>(type: Type, options?: TSchemaOptions): TCapitalizeDeferred<Type>;
374
448
 
375
- /** One captured `declareApiKeyScheme(serviceId, scheme)` call, verbatim. */
376
- export declare interface CapturedApiKeyDeclaration {
377
- readonly serviceId: string;
378
- readonly scheme: PluginApiKeyScheme;
379
- }
380
-
381
- /** One captured `declareDelegatedScheme(serviceId, scheme)` call, verbatim. */
382
- declare interface CapturedDelegatedDeclaration {
383
- readonly serviceId: string;
384
- readonly scheme: PluginDelegatedScheme;
385
- }
386
-
387
- /** Everything the auth probe captures from one `activate` run, in call order. */
388
- export declare interface CapturedKayAgentAuthDeclarations {
389
- readonly services: readonly PluginServiceDescriptor[];
390
- readonly oauthPkceSchemes: readonly CapturedOAuthPkceDeclaration[];
391
- /**
392
- * Api-key schemes declared this run — the `displayName`/`inputLabel`/`setup`/`validate`
393
- * runtime declaration a manifest's identity-only scheme row cannot carry. Daemon acquisition
394
- * composition maps these onto the connect dispatch's api-key roster the same way
395
- * `oauthPkceSchemes` maps onto the PKCE arm — see `@bonsai/agent-runtime`'s `kay/composition/`.
396
- * Empty for a plugin that declares none.
397
- */
398
- readonly apiKeySchemes: readonly CapturedApiKeyDeclaration[];
399
- /**
400
- * Delegated schemes declared this run. Daemon acquisition composition maps these onto a
401
- * delegated connect-roster entry the same way `oauthPkceSchemes` maps onto the PKCE arm — see
402
- * `@bonsai/agent-runtime`'s `kay/composition/`. Empty for a plugin that declares none.
403
- */
404
- readonly delegatedSchemes: readonly CapturedDelegatedDeclaration[];
405
- }
406
-
407
- /** One captured `declareOAuthPkceScheme(serviceId, scheme)` call, verbatim. */
408
- export declare interface CapturedOAuthPkceDeclaration {
409
- readonly serviceId: string;
410
- readonly scheme: PluginOAuthPkceScheme;
411
- }
412
-
413
449
  declare namespace Char {
414
450
  export {
415
451
  TAlpha,
@@ -437,6 +473,14 @@ declare namespace Char {
437
473
  }
438
474
  }
439
475
 
476
+ declare const CliAuthEnvVarName: z.ZodString;
477
+
478
+ declare type CliAuthEnvVarName = z.infer<typeof CliAuthEnvVarName>;
479
+
480
+ declare const CliBinaryName: z.ZodString;
481
+
482
+ declare type CliBinaryName = z.infer<typeof CliBinaryName>;
483
+
440
484
  /**
441
485
  * Returns a Clone of the given value. This function is similar to structuredClone()
442
486
  * but also supports deep cloning instances of Map, Set and TypeArray.
@@ -450,19 +494,10 @@ declare const CloseComment = "*/";
450
494
  /** Creates a bi-directional Codec. Codec functions are called on Value.Decode and Value.Encode. */
451
495
  declare function Codec<Type extends TSchema>(type: Type): DecodeBuilder<Type, StaticDecode<Type>>;
452
496
 
453
- /**
454
- * Runs `activate` once against a fake, capture-only {@link KayAgentPluginApi} and returns every
455
- * service it advertised and every OAuth-PKCE, api-key, and delegated scheme it declared, in call
456
- * order. Every other call (`uri.registerModule`, `registerTool`, `declareExternalCliScheme`, …) is
457
- * a silent no-op: those surfaces are irrelevant to acquisition composition, and a plugin that also
458
- * calls them must not throw or leak a side effect from this probe.
459
- */
460
- export declare function collectAuthDeclarationsFromKayAgentActivate(activate: KayAgentActivate): CapturedKayAgentAuthDeclarations;
461
-
462
497
  /**
463
498
  * Runs `activate` once against a fake, capture-only {@link KayAgentPluginApi} and returns
464
499
  * every `UriExtensionModule` it registered, in registration order. Every non-`uri.registerModule`
465
- * call (`registerTool`, `registerSearchProvider`, `registerInteractionDriver`, `registerConverter`, `auth.*`) is a silent
500
+ * call (`implementTool`, `registerTool`, `registerSearchProvider`, `registerInteractionDriver`, `registerConverter`, `auth.*`) is a silent
466
501
  * no-op: those surfaces are irrelevant to disclosure, and a plugin that also calls them must not
467
502
  * throw or leak a side effect from this probe.
468
503
  */
@@ -516,7 +551,13 @@ declare interface ContentConverter {
516
551
  */
517
552
  readonly description?: string;
518
553
  matches(probe: ContentProbe): boolean;
519
- convert(bytes: Uint8Array, probe: ContentProbe, ctx: ConvertContext): Promise<ConversionResult>;
554
+ /**
555
+ * Converts the file `matches` claimed. Resolves `undefined` to decline it after all: the read
556
+ * pipeline then tries the next converter that claimed it, as if this one had not. A converter
557
+ * that answers elsewhere (the plugin host's stand-in, which learns only there which converter
558
+ * takes the file) declines this way; one that decides in `matches` never needs to.
559
+ */
560
+ convert(bytes: Uint8Array, probe: ContentProbe, ctx: ConvertContext): Promise<ConversionResult | undefined>;
520
561
  }
521
562
 
522
563
  /**
@@ -540,7 +581,7 @@ declare interface ContentProbe {
540
581
 
541
582
  /**
542
583
  * The convert return shape. `content` is the agent-facing
543
- * {@link ModelContent} — text and/or image blocks — and is
584
+ * {@link ModelContent} - text and/or image blocks - and is
544
585
  * the field the read pipeline consumes (finalize windows/truncates TEXT blocks and
545
586
  * passes IMAGE blocks through unchanged). `contentType`/`artifacts`/`structure`/
546
587
  * `notes` are reserved metadata that no consumer renders yet. The converter does
@@ -718,7 +759,7 @@ declare function Deferred<Action extends string, Types extends TSchema[]>(action
718
759
  * What `service_connect` hands a delegated scheme's {@link DelegatedAuthScheme.connect}: the
719
760
  * scope being connected plus the host's browser-redirect capability when one is configured.
720
761
  * `authorize`/`redirectUri` wrap the host `OAuthRedirectTransport` (`@herd/herd-agent`)
721
- * — both absent when the host has none; the scheme decides whether it needs them.
762
+ * - both absent when the host has none; the scheme decides whether it needs them.
722
763
  */
723
764
  declare interface DelegatedConnectContext {
724
765
  readonly serviceId: string;
@@ -751,7 +792,7 @@ declare interface DelegatedConnectResult {
751
792
  /**
752
793
  * Token material minted by a delegated scheme's extension-provided flow. Core reads only
753
794
  * `accessToken`/`expiresAt` (resolution + proactive refresh); `data` is an opaque blob the
754
- * scheme round-trips through `refresh` (refresh token, DCR client info, …) — never read by core.
795
+ * scheme round-trips through `refresh` (refresh token, DCR client info, ...) - never read by core.
755
796
  */
756
797
  declare interface DelegatedSecretMaterial {
757
798
  readonly kind: "delegated";
@@ -777,7 +818,7 @@ declare function DependentOptions(type: TDependent): TSchemaOptions;
777
818
  * can derive per-call via {@link ToolPermissionHint.deriveClassification}. Every field is
778
819
  * optional: a present field overrides the static hint for that call; an absent field falls back
779
820
  * to the static hint (then to the fail-closed default). `resources` stays a separate deriver
780
- * ({@link ToolPermissionHint.deriveResources}) — these are the fields that decide which rule a
821
+ * ({@link ToolPermissionHint.deriveResources}) - these are the fields that decide which rule a
781
822
  * call matches, not the paths it touches.
782
823
  */
783
824
  declare interface DerivedToolClassification {
@@ -796,11 +837,11 @@ export declare interface DisclosureViolation {
796
837
  /**
797
838
  * The disclosure surface the violation belongs to: a `UriExtensionModule.id` for every
798
839
  * `auditPluginDisclosure` module-shaped rule, or a skill's `localId` for the rules that
799
- * inspect a declared skill's own description (there is no module in play there — the
840
+ * inspect a declared skill's own description (there is no module in play there - the
800
841
  * `localId` is the closest thing to that surface's identity).
801
842
  */
802
843
  readonly moduleId: string;
803
- /** A stable, kebab-case rule id — grep this in the audit source to see exactly what fired. */
844
+ /** A stable, kebab-case rule id - grep this in the audit source to see exactly what fired. */
804
845
  readonly rule: PluginDisclosureRule;
805
846
  readonly detail: string;
806
847
  }
@@ -950,67 +991,6 @@ declare function ExtendsTrue<Inferred extends TProperties>(inferred: TProperties
950
991
 
951
992
  declare function ExtendsUnion<Inferred extends TProperties>(inferred: TProperties): TExtendsUnion<Inferred>;
952
993
 
953
- /**
954
- * The execute-time auth surface the SDK wrappers read off `ctx.auth`. Slice 3 attaches a
955
- * concrete implementation (vault + pkce refresh + active-account store) to ExtensionContext;
956
- * defining the contract here lets the wrappers compile and be unit-tested against a double.
957
- */
958
- declare interface ExtensionAuthContext {
959
- /**
960
- * Resolve a live token for a service and an account: the explicit `accountId` when given, else
961
- * the session-active account. `schemeId` is a preference, not an address — an explicit account
962
- * resolves against its OWN backing scheme even when a different scheme is named/bound, and an
963
- * absent `schemeId` resolves service-level across the injectable schemes. Refreshes oauth near
964
- * expiry under the hood. Returns an actionable {@link ResolveTokenFailure} (never throws) when
965
- * there is no connection or the account is ambiguous, so wrappers can short-circuit gracefully.
966
- */
967
- readonly resolveToken: (input: {
968
- readonly serviceId: string;
969
- readonly schemeId?: string;
970
- readonly accountId?: string;
971
- /**
972
- * The invoking capability's `requiresAuth.requiredScopes`, threaded down to the vault load so a
973
- * grant-enforcing vault can refuse a plugin whose granted scopes don't cover them. The
974
- * caller identity itself is not a per-call argument — it is fixed by the owner-scoped context
975
- * (see `CreateExtensionAuthContextInput.callerId`).
976
- */
977
- readonly requiredScopes?: readonly string[];
978
- /** Ordered scope alternatives; mutually exclusive with `requiredScopes`. */
979
- readonly scopeRequirement?: ScopeRequirement;
980
- /**
981
- * The binding's front-door service id (`requiresAuth.connectServiceId`), threaded so a
982
- * scope-shortfall failure can name the connect that actually grants the missing scopes
983
- * instead of the credential service (whose re-connect cannot). Guidance only.
984
- */
985
- readonly connectServiceId?: string;
986
- }) => Promise<ResolveTokenResult>;
987
- /**
988
- * The session-active accountId, or `undefined`. Scheme-scoped when `schemeId` is given; with it
989
- * absent, the service's most recent selection across all schemes (service-level view).
990
- */
991
- readonly getActiveAccount: (input: {
992
- readonly serviceId: string;
993
- readonly schemeId?: string;
994
- }) => Promise<string | undefined>;
995
- /**
996
- * Connected accounts (multi-account discovery). Scheme-scoped when `schemeId` is given; with it
997
- * absent, the union across the service's injectable schemes (first scheme wins an id collision).
998
- */
999
- readonly listAccounts: (input: {
1000
- readonly serviceId: string;
1001
- readonly schemeId?: string;
1002
- }) => Promise<readonly AuthAccount[]>;
1003
- /**
1004
- * Resolve a non-injectable external-CLI readiness marker into a protocol runner. No secret
1005
- * material leaves the auth subsystem; the host runner receives the stored binary plus argv.
1006
- */
1007
- readonly resolveExternalCli: (input: {
1008
- readonly serviceId: string;
1009
- readonly schemeId: string;
1010
- readonly accountId?: string;
1011
- }) => Promise<ResolveExternalCliResult>;
1012
- }
1013
-
1014
994
  /** Optional extension-facing APIs for background task orchestration. */
1015
995
  declare interface ExtensionBackgroundTasksApi {
1016
996
  adopt(taskKind: BackgroundTaskKind, label: string, run: BackgroundTaskRun, options?: BackgroundTaskAdoptOptions & {
@@ -1023,7 +1003,7 @@ declare interface ExtensionBackgroundTasksApi {
1023
1003
 
1024
1004
  /**
1025
1005
  * Host-provided, per-extension writable data root surfaced on {@link ToolContext.paths}. Isolation is
1026
- * the DIRECTORY itself — exactly like the per-extension SQLite db files keyed by extension id: the host
1006
+ * the DIRECTORY itself - exactly like the per-extension SQLite db files keyed by extension id: the host
1027
1007
  * provisions a distinct path per owning extension, the extension owns everything inside it. The kernel
1028
1008
  * never decides *where* this lives; it carries an opaque host-resolved path and exposes it, so an
1029
1009
  * extension can read/write files (caches, scratch state, downloads) under a sanctioned root without the
@@ -1164,6 +1144,10 @@ declare const Factor: (input: string) => [unknown, string] | [];
1164
1144
 
1165
1145
  declare function FactorMapping(input: [unknown, unknown, unknown, unknown, unknown]): unknown;
1166
1146
 
1147
+ declare const FileExtensionToken: z.ZodString;
1148
+
1149
+ declare type FileExtensionToken = z.infer<typeof FileExtensionToken>;
1150
+
1167
1151
  declare type ForecastInterval = "step" | "minute" | "hour" | "day" | "week" | "month" | "quarter" | "year";
1168
1152
 
1169
1153
  /** Creates a Function type. */
@@ -1227,6 +1211,36 @@ declare const GenericType: (input: string) => [unknown, string] | [];
1227
1211
 
1228
1212
  declare function GenericTypeMapping(input: [unknown, unknown, unknown]): unknown;
1229
1213
 
1214
+ declare const GitBasicAuthUser: z.ZodString;
1215
+
1216
+ declare type GitBasicAuthUser = z.infer<typeof GitBasicAuthUser>;
1217
+
1218
+ declare const GitHostName: z.ZodString;
1219
+
1220
+ declare type GitHostName = z.infer<typeof GitHostName>;
1221
+
1222
+ declare const GlobalContributionId: z.ZodString;
1223
+
1224
+ declare type GlobalContributionId = z.infer<typeof GlobalContributionId>;
1225
+
1226
+ /**
1227
+ * The cookie suffix rule shared by every web-session boundary: whether `host`
1228
+ * is `domain` or a subdomain of it. Coined ONCE here - the manifest's scope
1229
+ * confinement, the resolved/wire re-checks, and the desktop probe's
1230
+ * redirect-landing classification must all agree on it, or a login bounce
1231
+ * inside the declared scope could read as "unknown" on one side of a seam and
1232
+ * "signed-out" on the other.
1233
+ */
1234
+ declare function hostWithinDomain(host: string, domain: string): boolean;
1235
+
1236
+ declare const HttpStatusCode: z.ZodNumber;
1237
+
1238
+ declare type HttpStatusCode = z.infer<typeof HttpStatusCode>;
1239
+
1240
+ declare const HttpsUrl: z.ZodURL;
1241
+
1242
+ declare type HttpsUrl = z.infer<typeof HttpsUrl>;
1243
+
1230
1244
  declare const Hyphen = "-";
1231
1245
 
1232
1246
  /** Matches if next is an Ident */
@@ -1235,13 +1249,6 @@ declare function Ident<Input extends string>(input: Input): TIdent<Input>;
1235
1249
  /** Creates an Identifier. */
1236
1250
  declare function Identifier<Name extends string>(name: Name): TIdentifier<Name>;
1237
1251
 
1238
- /**
1239
- * Whether a scheme acts *as the user* (the fully-built PKCE path) or *on behalf of the user
1240
- * as an app* (a declared seam — see {@link OAuthPkceAuthScheme.mintToken}). It's a property of
1241
- * the scheme, not the call, so the resolver knows the identity without per-call ambiguity.
1242
- */
1243
- declare type IdentityMode = "as-user" | "on-behalf-of-app";
1244
-
1245
1252
  declare interface ImageContent {
1246
1253
  type: "image";
1247
1254
  data: string;
@@ -1371,6 +1378,12 @@ declare interface InteractionCandidate {
1371
1378
  readonly checked?: string | undefined;
1372
1379
  readonly selected?: string | undefined;
1373
1380
  readonly expanded?: string | undefined;
1381
+ /**
1382
+ * What a page driver found the element to be in the DOM, not what the page calls it: `link` is an
1383
+ * anchor that navigates to another http(s) page; `widget` is an element that is no form control.
1384
+ * Absent means unknown, and the engine then treats the element as any other.
1385
+ */
1386
+ readonly look?: "link" | "widget" | undefined;
1374
1387
  readonly delta?: number | undefined;
1375
1388
  /** Where an element outside the visible area sits; absent means on screen. */
1376
1389
  readonly position?: "above" | "below" | "beside" | undefined;
@@ -1703,10 +1716,22 @@ declare function IsUnsafe(value: unknown): value is TUnsafe;
1703
1716
  /** Returns true if the given value is TVoid. */
1704
1717
  declare function IsVoid(value: unknown): value is TVoid;
1705
1718
 
1719
+ declare const JsonObject: z.ZodRecord<z.ZodString, z.ZodType<JsonValue_2, unknown, z.core.$ZodTypeInternals<JsonValue_2, unknown>>>;
1720
+
1721
+ declare type JsonObject = z.infer<typeof JsonObject>;
1722
+
1723
+ declare const JsonPointer: z.ZodString;
1724
+
1725
+ declare type JsonPointer = z.infer<typeof JsonPointer>;
1726
+
1706
1727
  declare type JsonPrimitive = string | number | boolean | null;
1707
1728
 
1708
1729
  declare type JsonSchema = Record<string, unknown>;
1709
1730
 
1731
+ declare const JsonSchema_2: z.ZodRecord<z.ZodString, z.ZodType<JsonValue_2, unknown, z.core.$ZodTypeInternals<JsonValue_2, unknown>>>;
1732
+
1733
+ declare type JsonSchema_2 = z.infer<typeof JsonSchema_2>;
1734
+
1710
1735
  declare type JsonValue = JsonPrimitive | {
1711
1736
  readonly [key: string]: JsonValue;
1712
1737
  } | readonly JsonValue[];
@@ -1721,9 +1746,630 @@ declare type JsonValue_3 = null | boolean | number | string | JsonValue_3[] | {
1721
1746
  [key: string]: JsonValue_3;
1722
1747
  };
1723
1748
 
1724
- /** A plugin's Bonsai agent activation — receives only the brokered {@link KayAgentPluginApi}. */
1749
+ /**
1750
+ * The publisher id Bonsai's own subsystems publish declared events under (skill
1751
+ * changes, wallet notices, automation failures). A notification rule matches a
1752
+ * declared event by (publisher id, event local id), so no plugin may take this
1753
+ * id or it could publish events that Bonsai's default rules read as Bonsai's own.
1754
+ */
1755
+ declare const KAY_EVENT_PUBLISHER_ID: "kay";
1756
+
1757
+ /** Released SDK contracts still implemented by the host, alongside its current SDK. */
1758
+ declare const KAY_PLUGIN_COMPATIBLE_SDK_VERSIONS: readonly string[];
1759
+
1760
+ declare const KAY_PLUGIN_PROTOCOL_PACKAGE_NAME: "@bonsai/plugin-protocol";
1761
+
1762
+ declare const KAY_PLUGIN_PROTOCOL_VERSION: "kay.plugin.v1";
1763
+
1764
+ /**
1765
+ * The plugin SDK version this host ships, and the number a plugin's
1766
+ * `compatibility.sdk` range is matched against.
1767
+ *
1768
+ * -- Why this is the compatibility axis, and the app version is not ----------
1769
+ * A plugin never bundles the SDK: the host injects its own `@bonsai/plugin-sdk`
1770
+ * dist at import time. So the SDK surface a plugin was written against is the
1771
+ * thing that can break under it, and the Bonsai app version is only correlated
1772
+ * with that - it moves for reasons a plugin cannot observe.
1773
+ *
1774
+ * -- Two lines, deliberately, not one ----------------------------------------
1775
+ * This continues the published `@yaklabs-ai/plugin-sdk` line, currently in 0.x.
1776
+ * `KAY_UI_SDK_VERSION` in `@bonsai/protocol` is a SEPARATE line at its own number,
1777
+ * and the two are never compared: a `kay.ui` app package declares against the
1778
+ * UI SDK, a plugin manifest declares `compatibility.sdk` against this one, and
1779
+ * no manifest field takes both. Unifying them would have to publish the plugin
1780
+ * SDK at the UI line's 1.x, claiming a stability this surface does not have -
1781
+ * 0.x says "breaking minors" honestly, which is the truth pre-product.
1782
+ *
1783
+ * -- Bumping it --------------------------------------------------------------
1784
+ * A protocol or SDK surface break bumps this constant IN THE SAME PR, and
1785
+ * `scripts/check-sdk-release-due.mjs` then reports a release due at the new
1786
+ * number. The constant is the declared version, not a derived one: the release
1787
+ * publishes exactly what is written here.
1788
+ *
1789
+ * An ADDITION takes the patch slot, not the minor. Under 0.x npm's caret makes
1790
+ * the minor the breaking slot, so a minor bump refuses
1791
+ * (`sdk-range-unsatisfied`) every plugin still declaring the previous one - a
1792
+ * fleet-wide break, never the price of one new entrypoint. 0.7.1 carried the
1793
+ * additive `@bonsai/plugin-sdk/phase-shift` surface that way, while `^0.7.0`
1794
+ * plugins kept loading.
1795
+ *
1796
+ * 0.8.0 IS that fleet-wide break, deliberately, and it spends the one break on
1797
+ * two changes at once.
1798
+ *
1799
+ * FIRST, a plugin no longer declares the capabilities it wants, because the
1800
+ * CALL is the ask. Every capability ask left `permissions.byTarget` (an
1801
+ * installed manifest that still carries one is stripped with a
1802
+ * `retired-permission-ask` diagnostic, never refused - the same law
1803
+ * `RETIRED_COMPATIBILITY_KEYS` follows), every brokered capability object is
1804
+ * unconditionally present on the SDK surface (presence is no longer a grant
1805
+ * signal to feature-detect), and authority is one exact claim checked against
1806
+ * the user's grant ledger at the call.
1807
+ *
1808
+ * SECOND, `app.hindsight` (`KayAppHindsight` - lane registration, the lane
1809
+ * event, the lane context, and the followup DTOs) is DELETED, along with the
1810
+ * daemon-side lane dispatcher it registered onto. Its replacement is two
1811
+ * generic doors a plugin composes itself, `app.sessionEvents.subscribe` and
1812
+ * `app.agents.run`, and their authority is two claims in the same ledger
1813
+ * (`session-events.subscribe`, `agents.run`). There is no shim, because a shim
1814
+ * would have to fake a dispatcher that no longer exists.
1815
+ *
1816
+ * Either half alone breaks a `^0.7` plugin: the TYPE surface it compiles
1817
+ * against moved under it - the ask keys are gone from the manifest type, the
1818
+ * capability objects are required rather than optional, and the hindsight door
1819
+ * is gone - which is what the minor bump signals. A plugin declaring `^0.7` is
1820
+ * refused (`sdk-range-unsatisfied`) rather than loaded against a contract it
1821
+ * was not written to.
1822
+ *
1823
+ * 0.8.1 takes the PATCH slot under the addition rule above: `api.withJev`
1824
+ * (`@bonsai/plugin-sdk/jev`) - Jev typed-decision inference with a required
1825
+ * fallback - is one new member on an api a plugin RECEIVES. A `^0.8.0` plugin
1826
+ * keeps loading and keeps compiling; it simply never reaches for the member.
1827
+ * The member is non-optional for the same reason 0.8.0 made every brokered
1828
+ * capability unconditional: presence is not a grant signal, and an optional one
1829
+ * would invite the feature-detect branch that skips the required fallback.
1830
+ *
1831
+ * 0.8.2 adds the received Whiteboard handles, type contributions, and session
1832
+ * app-data readers. Existing 0.8 plugins keep loading without using them.
1833
+ *
1834
+ * 0.8.3 adds the recorded Jev signal reads, `app.signals.list` and
1835
+ * `app.signals.listDetectors`, behind the new `signals.read` claim. It is also
1836
+ * the first number that guarantees `app.signals.observe`, which reached hosts
1837
+ * still advertising 0.8.2. A plugin that reads signals declares `^0.8.3`, so an
1838
+ * older host refuses it at load instead of failing at the call. 0.8.3's
1839
+ * `observe` also answers `invalid`, recording nothing, for a detector id,
1840
+ * version, question name, choice label, or source id outside the recorded-name
1841
+ * grammar (`invalidObservation` in `@bonsai/plugin-sdk/signals`): names a reader
1842
+ * could not later address, filter on, or copy back. A 0.8.2 host recorded such
1843
+ * names, so a plugin moving to 0.8.3 checks its names against that grammar.
1844
+ *
1845
+ * 0.8.4 adds `api.events` on `kay.agent` (publish a declared event from agent
1846
+ * code, host-stamped with the session) and the `kay.app` manifest key
1847
+ * `notificationRules` (default notification rules for a plugin's own events).
1848
+ * Both are additions: a `^0.8` plugin keeps loading. The manifest key is new
1849
+ * schema, so an older host refuses a manifest that uses it at parse. `api.events`
1850
+ * carries no such schema backstop - it is a call, not a manifest field - so a
1851
+ * plugin that calls it, or declares `notificationRules`, declares `^0.8.4`: a
1852
+ * host still advertising 0.8.3 (signals only, landed separately) refuses it at
1853
+ * load instead of failing at the call.
1854
+ *
1855
+ * 0.8.5 republishes `@yaklabs-ai/plugin-sdk` to include the `./jev` and
1856
+ * `./signals` entrypoints: the workspace package has exported them since 0.8.1
1857
+ * (`jev`) and 0.8.3 (`signals`), but `scripts/build-publishable-packages.mjs`'s
1858
+ * hand-maintained `entrypoints` allowlist had omitted both. This packages
1859
+ * EXISTING apis; the type contract does not change. It still takes the patch
1860
+ * slot: nothing a `^0.8.1`/`^0.8.3` plugin already compiles against changes
1861
+ * shape, and the fix is which bytes reach npm, not what the contract promises.
1862
+ *
1863
+ * 0.9.0 adds timer_cancelled and requires workflow descriptions and summary
1864
+ * returns. This is an authoring-type break only: the host retains the 0.8
1865
+ * runtime contracts.
1866
+ *
1867
+ * 0.9.1 adds bounded corpus invocation reads (`app.corpus.listResourceInvocations`).
1868
+ * Existing received APIs are unchanged, so this is additive only.
1869
+ *
1870
+ * 0.10.0 removes the `spawn-session` variant from `KayAppAutomationEffectInput`
1871
+ * (and its daemon-facing mirror, `KayAutomationEffectInput`): an agent or
1872
+ * plugin can no longer create a spawn-session automation binding (owner
1873
+ * ruling, 2026-09-28) - durable recurring agent work is a plugin workflow
1874
+ * (`app.workflows.define` + `ctx.runAgent`) or a direct `session/run`. A
1875
+ * binding created before this version, or by daemon-internal code, still
1876
+ * stores and reads the effect fine (`KayAppAutomationEffect` keeps it); only
1877
+ * the create-time input type is narrower. Mostly an authoring-type break -
1878
+ * 0.9.0 and 0.9.1 stay in the runtime-contract retention list below, so a
1879
+ * plugin still running bytecode compiled against either keeps loading and
1880
+ * working - except for the one call this ruling actually closes:
1881
+ * `app.automations.create` with a `spawn-session` effect now throws
1882
+ * explicitly at the call site instead of being accepted, for any plugin
1883
+ * regardless of its declared SDK range.
1884
+ *
1885
+ * 0.11.0 adds `producedResources` to `KayAppAutomationRunDetail` (every URI a
1886
+ * run wrote, edited or declared, as one list deduplicated by URI) and the
1887
+ * `produced-resource-unnamed` member to `KayAppProvenanceGapCode`. An
1888
+ * authoring-type break only: code that builds a run detail must supply the
1889
+ * new list, and an exhaustive switch over gap codes must handle the new one.
1890
+ * 0.10.0 stays in the runtime-contract retention list below, because a plugin
1891
+ * compiled against it reads the same run detail and simply ignores the list.
1892
+ *
1893
+ * 0.11.1 adds read-only skill metadata (`app.skills`: `list`, `read`, `history`),
1894
+ * checked per call against the new `skills.read` claim. Additive: a plugin
1895
+ * compiled against 0.11.0 never reads the member. A plugin that calls it
1896
+ * declares `^0.11.1`, so an older host refuses it at load instead of the call
1897
+ * failing.
1898
+ *
1899
+ * 0.12.0 replaces the vendor client `api.withJev` hands its callback. `use`
1900
+ * now receives Bonsai's own `JevClient` (`@bonsai/plugin-sdk/jev`): one method,
1901
+ * `classify({state, questions})`, in Bonsai's question and answer vocabulary
1902
+ * (yes/no is `bool` answering `probability`; instructions and criteria are
1903
+ * text; the state is a JSON object), rejecting with a classified
1904
+ * `JevCallError`. TypeSafe's `TypeSafeClient`, `SystemOneRequest`,
1905
+ * `SystemOneResult`, `Questions`, and the `NoulResponse`/`ChoiceResponse`/
1906
+ * `ScoreResponse` re-exports are gone, and the SDK no longer depends on
1907
+ * `@typesafe-ai/sdk`. `app.signals.observe` takes the same questions, answers
1908
+ * `invalid` for a state that is not a JSON object, and its reads type stored
1909
+ * answers as `KayAppSignalAnswer` (a `JevAnswer`, or a yes/no answer recorded
1910
+ * before 0.12.0 as `{type: "noul", noul}`).
1911
+ *
1912
+ * A clean break with no shim, and deliberately NOT a load refusal: every
1913
+ * earlier release stays in the retention list below. `withJev`'s contract was
1914
+ * always that `use` may fail and the fallback then runs, so a plugin compiled
1915
+ * against 0.11 that calls `jev.systemOne` throws inside `use` and runs its
1916
+ * fallback on this host. This fallback guarantee does not cover `signals.observe`:
1917
+ * old text/array states now return `invalid`; old object states with `noul`
1918
+ * questions can classify but receive `bool` answers, so reading `.noul` yields
1919
+ * undefined. Stored pre-0.12 answers retain `noul`. Retention allows activation,
1920
+ * not vocabulary compatibility: old plugins must migrate both surfaces.
1921
+ *
1922
+ * 0.12.1 adds `app.time` (`KayAppTime`): `snapshot()` returns the daemon's clock and the time
1923
+ * zone the user is in, as `{ nowMs, zone, zoneSource }`, where `zoneSource: "utc-fallback"` marks a
1924
+ * zone the host does not know. The member is always present and ungated, like `app.paths`.
1925
+ * Additive: a plugin compiled against 0.12.0 never reads it. A plugin that calls it declares
1926
+ * `^0.12.1`, so an older host refuses a directory or repository plugin at load instead of the
1927
+ * call failing. That check needs a host that knows its own version, and it never runs for a
1928
+ * bundled plugin, which ships with the SDK it calls.
1929
+ *
1930
+ * 0.12.2 adds the `ui-action` session event (`KayAppUiActionSessionEvent`), a fourth member of the
1931
+ * `KayAppSessionEvent` union: the daemon publishes it once per accepted answer to a card the agent
1932
+ * drew with `ui_render` (pressing Send on a drafted message, submitting a form), carrying the
1933
+ * projected entry's id and the answered call's id and no payload. A typed message still publishes
1934
+ * only `human-message`. Additive: a plugin compiled against 0.12.1 never subscribes to it, and
1935
+ * code that narrows the union by `type` meets one more member, as it must for any future one. A
1936
+ * plugin that subscribes to it declares `^0.12.2`, so an older host refuses a directory or
1937
+ * repository plugin at load instead of the subscription never firing. That check never runs for a
1938
+ * bundled plugin, which ships with the SDK it calls.
1939
+ *
1940
+ * 0.12.3 adds the `page` reference open kind (`{ kind: "page", pageId }` on a
1941
+ * `referencePresentations` entry), and lets a presentation name a `kay.app` URI module as well as a
1942
+ * `kay.agent` one. A plugin's own `scheme://` references then pill with its icon and the module's
1943
+ * `describe` title, and a click opens the plugin's own registered page with the reference URI as
1944
+ * the page's `objectId`. Additive: a `^0.12.2` plugin keeps loading. The value is new schema, so an
1945
+ * older host refuses the whole manifest at parse (`manifest-schema-invalid`), before any range
1946
+ * check runs. A plugin that declares it still declares `^0.12.3`, so the install-time version
1947
+ * checks name the mismatch instead of a schema error.
1948
+ *
1949
+ * 0.12.4 adds `app.repos` (`KayAppRepos`): `resolve(folder)` answers which git checkout encloses a
1950
+ * folder (repo root, primary checkout, common dir, remotes, origin), and `linkedWorktrees(commonDir)`
1951
+ * lists git's linked-worktree registry, both read by the host asynchronously under a time limit and
1952
+ * answering `unknown` rather than throwing. Always present and ungated, like `app.paths`. Additive:
1953
+ * a plugin compiled against 0.12.3 never reads it. A plugin that calls it declares `^0.12.4`, so an
1954
+ * older host refuses a directory or repository plugin at load instead of the call failing.
1955
+ *
1956
+ * 0.12.5 adds `userFiles` (`KayAppUserFiles`) on both `app` and the `kay.agent` api: stat, read,
1957
+ * list, walk, realpath, write, mkdir and rename in user folders, run by the host on its own worker
1958
+ * threads under a deadline and answering a typed result rather than throwing. Always present and
1959
+ * ungated, like `app.paths`. Additive: a plugin compiled against 0.12.4 never reads it. A plugin
1960
+ * that calls it declares `^0.12.5`, so an older host refuses a directory or repository plugin at
1961
+ * load instead of the call failing.
1962
+ *
1963
+ * 0.13.0 adds `@bonsai/plugin-sdk/fs`, Bonsai's own `node:fs/promises`: the same functions, arguments,
1964
+ * return types and errors, with every call run on the host's user-files workers under a deadline
1965
+ * (`ERR_KAY_FS_DEADLINE` and siblings when the host gives up or does not try), plus
1966
+ * `writeFileAtomic` and `withTimeout`. With no host installed it is Node's own module. It adds
1967
+ * `api.familyFolder` (`KayAgentFamilyFolder`), whose `path()` answers the plugin's folder in its
1968
+ * session family's Whiteboard, and makes it a required member of `KayAgentPluginApi`. It removes
1969
+ * the `kay.agent` api's `filesystem` broker, the `filesystem.read` / `filesystem.write`
1970
+ * permissions it was gated by, the `{ root: "filesystem", selector }` URI backing and the URI
1971
+ * module `verbs` that went with it: plugins use `@bonsai/plugin-sdk/fs`, `userFiles` or
1972
+ * `familyFolder` instead. No plugin used them. Stored grants naming the retired permissions are
1973
+ * ignored, not refused. For the api surface this is a clean break with no shim and deliberately
1974
+ * NOT a load refusal, like 0.12.0: every earlier release stays in the retention list, so a
1975
+ * plugin compiled against 0.12.x that reads `api.filesystem` gets `undefined` at the call. The
1976
+ * manifest is different: a manifest that still declares URI-module `verbs` or
1977
+ * `filesystemBacking: { root: "filesystem" }` fails to parse and is refused at load (no plugin
1978
+ * declared either). A plugin that imports
1979
+ * `@bonsai/plugin-sdk/fs` or calls `familyFolder` declares `^0.13.0`, so an older host refuses a
1980
+ * directory or repository plugin at load.
1981
+ *
1982
+ * 0.13.1 adds the signal engine to `@bonsai/plugin-sdk/signals`: `SignalDefinition`,
1983
+ * `createSignalEngine` and `signalAnswersPass` (a plugin declares its Jev signals as data and the
1984
+ * engine evaluates them, upstreams first, through `app.signals.observe`), plus `signalDigest`.
1985
+ * `observe` gains three optional input fields, `subject`, `after` and `reuse`, and an observation
1986
+ * and a read signal carry the stored `subject` and `upstreamId` back. Additive: a plugin compiled
1987
+ * against 0.13.0 never sends or reads them. A plugin that imports the engine declares `^0.13.1`,
1988
+ * so an older host refuses a directory or repository plugin at load.
1989
+ *
1990
+ * 0.13.2 adds `app.tools` (`KayAppTools`): `describe(sessionId, refs)` returns, for each tool name
1991
+ * or address a live session can use, the text the agent sees for it, where it comes from, and
1992
+ * whether it is one of Bonsai's own (`ownedByBonsai`). Checked per call against the new
1993
+ * `tools.describe` claim. It also adds the manifest's top-level `ownedByBonsai` flag, honoured only
1994
+ * for a plugin that ships with Bonsai. Additive: a plugin compiled against 0.13.1 never reads the
1995
+ * member. A plugin that calls it declares `^0.13.2`, so an older host refuses a directory or
1996
+ * repository plugin at load. The flag is new schema, so an older host refuses a manifest that sets
1997
+ * it at parse, like 0.12.3's page open kind.
1998
+ *
1999
+ * 0.13.3 adds `app.usage` (`KayAppUsage`, `createUnavailableUsage`): `uriActivity({ prefix })`
2000
+ * returns read and change activity for resources under one of the plugin's OWN registered URI
2001
+ * schemes, checked per call (any other prefix answers `refused`). It also adds
2002
+ * `KayAppAgentDefinition.maintains`, the URI schemes a registered agent definition's runs maintain;
2003
+ * reads by such a run and its session family are not counted. Additive: a plugin compiled against
2004
+ * 0.13.2 never reads the member or sets the field. A plugin that calls `app.usage` or declares
2005
+ * `maintains` declares `^0.13.3`, so an older host refuses a directory or repository plugin at
2006
+ * load; an older host's strict definition schema would refuse a definition that sets `maintains`.
2007
+ *
2008
+ * 0.13.4 caps reads through `@bonsai/plugin-sdk/fs`: a whole-file `readFile` (a file handle's too)
2009
+ * and a single `read` reject past 64 MiB with `EFBIG` instead of copying the bytes into the daemon.
2010
+ * No API changes: a plugin compiled against 0.13.3 runs unchanged unless it reads more than 64 MiB
2011
+ * in one call, and such a plugin reads in bounded chunks (a file handle's `read`,
2012
+ * `createReadStream`, `readLines`), which work on every 0.13 host. File calls sent to a host worker
2013
+ * that fail are also logged to the daemon log as `runtime.user-files.*` events.
2014
+ *
2015
+ * 0.13.5 loads a `kay.agent` or `kay.app` entrypoint's default export when the manifest's
2016
+ * `entrypoint.export` names none, the way Pi writes extensions; a module with no default export
2017
+ * still loads from a named `activate`. A plugin whose entrypoint has only a default
2018
+ * export declares `^0.13.5`, so an older host, which reads only `activate`, refuses it at load
2019
+ * instead of failing to find the export. It also renamed three published types with no aliases,
2020
+ * a break in the authoring types that shipped in this patch: `kay-app`'s `SessionTreeEntry`
2021
+ * became `TranscriptEntry`, and `transcript`'s `TranscriptEntry` and `TranscriptSourceEntry`
2022
+ * became `TranscriptEntryLike` and `TranscriptSourceEntryLike`.
2023
+ *
2024
+ * 0.13.6 publishes the pure contracts Bonsai's own plugins took from its internal libraries, so a
2025
+ * plugin needs less beside the SDK. A new `@bonsai/plugin-sdk/time` carries `Temporal` (the
2026
+ * host's own instance, like `z`), `canonicalZone`, `isValidZone` and `sameZone`, and the time
2027
+ * door's types. `kay-app` adds
2028
+ * the `JsonSchema` and `JsonValue` schemas, `PluginAgentDefinitionPermissionRule`,
2029
+ * `KAY_APP_DATA_SNAPSHOT_MAX_BYTES`, and the URI types `UriHelp`, `RawResource`, `UriPayload`,
2030
+ * `UriListParams`, `UriListResult`, `UriSearchParams` and `UriSearchResult`. `kay-agent` adds
2031
+ * `compileUriClaim`, `kay-agent/testing` adds `createTestClock`, and `transcript` adds the turn walk
2032
+ * (`turnSegments`), the run records and the work-block and work-headline helpers. Additive: a
2033
+ * plugin compiled against 0.13.5 never imports them. A plugin that imports one declares `^0.13.6`,
2034
+ * so an older host refuses a directory or repository plugin at load instead of the import failing.
2035
+ *
2036
+ * 0.13.7 adds four subpaths. `@bonsai/plugin-sdk/vendored-mcp` re-exports
2037
+ * `@bonsai/vendored-mcp-integration` (`createVendoredMcpUriModule` and its descriptor types; not
2038
+ * the MCP read-port types). `transcript-review` and `agent-conversation` re-export the session
2039
+ * folds Bonsai's own Sessions and Hindsight plugins share. `app-data` carries the app-data size
2040
+ * caps on an entry with no other import, for code a `kay.ui` page also loads. The protocol it
2041
+ * ships also carries three new capability-claim slugs (`mcp.host-server`, `prompt.resident`,
2042
+ * `skills.declared-source`). Additive: a plugin compiled against 0.13.6 never imports the new
2043
+ * subpaths, and its manifest parses unchanged. A plugin that imports one declares `^0.13.7`, so
2044
+ * an older host refuses a directory or repository plugin at load instead of the import failing.
2045
+ *
2046
+ * 0.13.8 takes the PATCH slot under the addition rule, as `api.withJev` did in 0.8.1: one new
2047
+ * member on the api a plugin RECEIVES, `api.systemOne` / `app.systemOne` (`@bonsai/plugin-sdk/
2048
+ * system-one`), the System One client for `decide` and `extract` on Jev, Clef, Clef Flash and
2049
+ * Clairic. It is non-optional for the same reason `withJev` is, and a host with no System One
2050
+ * binds a client that throws `not-served`. `withJev` stays for every compatible plugin. A
2051
+ * plugin that calls `systemOne` declares `^0.13.8`, so an older host refuses it at load instead
2052
+ * of the member being absent.
2053
+ *
2054
+ * 0.13.9 adds `@bonsai/plugin-sdk/zod`: the host's `z` on an entry with no other import, for
2055
+ * code a `kay.ui` page also loads (`kay-agent` exports the same `z`). Additive: a plugin compiled
2056
+ * against 0.13.8 never imports it. A plugin that imports it declares `^0.13.9`, so an older host
2057
+ * refuses a directory or repository plugin at load instead of the import failing.
2058
+ *
2059
+ * 0.13.10 adds two optional manifest fields. An `external-cli` auth scheme's `cli` says how the host
2060
+ * connects it (the binary, its probe and sign-in argv, the hints, the install command), which the
2061
+ * host read from its own tables before. An auth grant's `frontDoor` names the product a person
2062
+ * connects a shared identity through (`gmail` over `google.account`), with an optional file picker.
2063
+ * Additive: a manifest without them parses unchanged. A plugin that declares one declares
2064
+ * `^0.13.10`, so an older host refuses it at load with that reason rather than a schema error.
2065
+ *
2066
+ * 0.14.0 removes `withJev`: `api.withJev` on `kay.agent`, `app.withJev` on `kay.app`, the
2067
+ * `@bonsai/plugin-sdk/jev` subpath and every `Jev*` type it exported. A plugin uses `api.systemOne`
2068
+ * / `app.systemOne` (`@bonsai/plugin-sdk/system-one`) instead. The `jev.use` capability claim is
2069
+ * renamed `systemOne.use`, and stored grants are rewritten to the new slug. The signals subpath
2070
+ * types its own surface now: observe options by `KayAppSignalObserveOptions`, state by
2071
+ * `KayAppSignalState`, and questions and answers by `DecideQuestions` and `DecideAnswer`. For the
2072
+ * api surface this is a clean break with no shim and deliberately NOT a load refusal, like 0.13.0:
2073
+ * every earlier release stays in the retention list, so a plugin compiled against 0.13.x that
2074
+ * reads `api.withJev` gets `undefined` at the call, and one that imports a value from
2075
+ * `@bonsai/plugin-sdk/jev` fails at load. No plugin used either.
2076
+ *
2077
+ * 0.14.1 adds the owned-service `kay.app` auth list operation and `listAccounts`.
2078
+ * Existing resolve-only `has` callers retain account bounds and now probe metadata without
2079
+ * resolving credentials. Plugins using list declare `^0.14.1` so older hosts refuse at load.
2080
+ *
2081
+ * 0.15.0 replaces plugin-event publish arguments and requires resource URI declarations.
2082
+ * Pre-0.15 SDK contracts are no longer retained: their publishers cannot satisfy this contract.
2083
+ *
2084
+ * 0.15.1 adds plugin-event automation conditions and public event-identity run matches.
2085
+ * The host retains 0.15.0; plugins using the new condition declare ^0.15.1.
2086
+ *
2087
+ * 0.15.2 lets a plugin workflow start on the plugin's own events (a `plugin-event` trigger, with
2088
+ * the event handed to the run as `ctx.match`) and declare `concurrency`. The host retains 0.15.0
2089
+ * and 0.15.1; a plugin using either declares ^0.15.2, so an older host refuses it at load instead
2090
+ * of `define` throwing inside activation.
2091
+ *
2092
+ * 0.15.3 adds `owner: "user"` to `app.automations.create`, with the `spawn-session` effect it
2093
+ * allows: an automation the user owns, which outlives the calling session. Additive; plugins
2094
+ * using it declare ^0.15.3 so an older host, which would ignore the field and make the calling
2095
+ * session the owner, refuses them at load.
2096
+ *
2097
+ * 0.16.0 adds declared app-lifetime approval types and their brokered registration API.
2098
+ * Plugins using approval types declare ^0.16.0; the host retains the 0.15 contracts.
2099
+ *
2100
+ * 0.16.1 adds `api.log` on both targets: a logger whose lines land in Bonsai's own log under the
2101
+ * plugin's id. Additive; plugins calling it declare ^0.16.1 so an older host, which has no
2102
+ * `api.log`, refuses them at load instead of activation throwing.
2103
+ *
2104
+ * 0.16.2 adds `api.vendoredMcp` on `kay.agent`: a port to a vendored MCP server the plugin declares,
2105
+ * bound to that plugin, with its `VendoredMcpPort` / `VendoredMcpOutcome` types on
2106
+ * `@bonsai/plugin-sdk/vendored-mcp`. Additive; plugins calling it declare ^0.16.2 so an older host,
2107
+ * which has no `api.vendoredMcp`, refuses them at load instead of activation throwing.
2108
+ *
2109
+ * 0.16.3 adds `app.sessionEvents.subscribeSessionDeleted`: a notice for every deleted session, so
2110
+ * a plugin can delete what it derived from it. Additive; plugins calling it declare ^0.16.3.
2111
+ *
2112
+ * 0.17.0 consolidates `app.corpus` into the always-present `app.sessions`. Read methods retain
2113
+ * the `corpus.read` claim; control methods retain their own claims and are absent on read-only
2114
+ * hosts. The summary helper takes `sessions` and stored-session DTOs use session names. The host
2115
+ * no longer implements earlier runtime contracts, so declared 0.15/0.16 ranges are refused.
2116
+ *
2117
+ * 0.17.1 adds `app.skills.diff` and `app.skills.revision` under `skills.read`, `uncommitted` on a
2118
+ * history result, refs by app-inventory row id, and `app.skills.writeFile`, `install` and `delete`
2119
+ * under the new `skills.write` claim. Additive; plugins calling them declare ^0.17.1.
2120
+ *
2121
+ * 0.17.2 adds `app.account.activeOrganization()` (ungated) and `app.account.refreshBilling()`
2122
+ * (under the `account.assert` grant), and an optional `refresh()` on an app-data source definition.
2123
+ * Additive; plugins using them declare ^0.17.2.
2124
+ *
2125
+ * 0.17.3 adds `app.sealing` on `kay.app` (encrypt small values under a key only the plugin's id
2126
+ * can use; the daemon holds it) and `api.endpoints` on `kay.agent` (call an endpoint the plugin's
2127
+ * own `kay.app` part registered, by local id). Additive; plugins calling either declare ^0.17.3 so
2128
+ * an older host, which has neither member, refuses them at load instead of activation throwing.
2129
+ *
2130
+ * 0.17.4 adds the optional `app.sessions.readCost(sessionId)` (a stored session's spend, models,
2131
+ * and providers, under the same `corpus.read` grant as the other session reads). Additive.
2132
+ *
2133
+ * 0.17.5 adds `app.modelSelection.registerPolicy` on `kay.app`, a private method under the
2134
+ * `model-selection.register` claim (only plugins Bonsai ships). Additive; a host without it leaves
2135
+ * `app.modelSelection` undefined.
2136
+ *
2137
+ * 0.17.6 renames the stored-session read claim `corpus.read` to `sessions.read`, next to
2138
+ * `sessions.run`, `sessions.create` and `sessions.update`; stored grants are rewritten to the new
2139
+ * slug. The manifest's `contributes.corpusConsumers` becomes `contributes.sessionReaders` (kind
2140
+ * `sessionReader`), a clean break: no plugin outside this repo declares it.
2141
+ *
2142
+ * 0.18.0 adds the calls hindsight moved onto (#14533): `app.sessions.annotate` and
2143
+ * `app.sessions.compact` (claims `sessions.annotate`, `sessions.compact`), the session-state read,
2144
+ * `app.models.listTiers({ provider })`, `app.usage.skillInvocations`, an optional `cause` on
2145
+ * `app.models.complete` and `app.systemOne.decide`, `app.skills.registerSuggestionExplainer`, and
2146
+ * the private `app.soul` (claim `soul.write`). Additive; plugins calling them declare ^0.18.0.
2147
+ *
2148
+ * 0.18.1 adds `servedSimulators` on both targets (`KayServedSimulators`): the simulators a
2149
+ * simulating Bonsai daemon (evals, dev, tests) serves a plugin, keyed by adapter id. Optional and
2150
+ * absent unless such a daemon serves it, so no plugin needs it to load; additive, so it takes the
2151
+ * patch slot. A plugin need not declare a range to read it: an older host serves none, the same as
2152
+ * a production daemon, and code that reads it already runs without it. It also deletes the other
2153
+ * route a plugin could get a simulator by: a per-dispatch handle on a URI call's `ctx.auth`, and
2154
+ * the type that `@bonsai/plugin-sdk/kay-agent` exported for it. Only Slack read it. It entered the
2155
+ * source after 0.17.3's bump, and no SDK since 0.15.3 has been published, so no released contract
2156
+ * loses a member and the removal does not spend the minor slot.
2157
+ *
2158
+ * 0.18.2 adds the marketplace calls the kay plugin moved onto (#14530):
2159
+ * `app.skills.sourceContributors`, `app.skills.shareDestinations` and `app.skills.prepareShare`
2160
+ * under `skills.read`, `app.skills.setMarketplaceAdded` under `skills.write`, and two private
2161
+ * methods, `app.skills.confirmShare` (claim `skills.share`) and `app.plugins.setEnabled` (claim
2162
+ * `plugins.set-enabled`). Additive; plugins calling them declare ^0.18.2.
2163
+ *
2164
+ * 0.18.3 adds `app.systemPrompt.register` on `kay.app`, a private method under the
2165
+ * `system-prompt.register` claim (only plugins Bonsai ships): text a plugin adds to every chat's
2166
+ * system prompt, rendered by the daemon and reused across chats. Additive; a host without it leaves
2167
+ * `app.systemPrompt` undefined.
2168
+ *
2169
+ * 0.18.4 adds `api.compiler.bundleBrowser` on `kay.agent` (#14896): the host compiles
2170
+ * caller-supplied bytes with caller-owned import resolution, without filesystem or network
2171
+ * fallback. Additive and ungated; plugins calling it declare ^0.18.4.
2172
+ *
2173
+ * 0.19.0 makes the host resolve the credential of a tool or search provider that declares
2174
+ * `requiresAuth` before calling it, as it already did for URI verbs (#15102). The credential
2175
+ * arrives on the call as `ctx.credential` (`ResolvedCredential`, the one type all three share,
2176
+ * replacing `ResolvedAuth` and `UriResolvedCredential`); tools and search providers no longer
2177
+ * receive the `ctx.auth` resolver. `defineAuthedTool`, `withResolvedAuth` and
2178
+ * `withResolvedSearchProvider` keep their shapes and read the credential off the call. A plugin
2179
+ * built against an earlier SDK carries wrappers that ask `ctx.auth`, so the host no longer
2180
+ * implements earlier runtime contracts and declared 0.17/0.18 ranges are refused.
2181
+ *
2182
+ * 0.19.1 adds `app.memory` note operations and `app.skills.propose` for pending suggestions.
2183
+ *
2184
+ * 0.19.2 adds the optional `app.sessions.readPage` (one bounded page of a stored transcript, or a
2185
+ * typed reason there is none) and `app.sessions.listSessionPage` (a page of the session index, or
2186
+ * rows by id), both under `sessions.read`; `app.knowledgeSearch` on `kay.app` under the `kay.app`
2187
+ * `knowledgeSearch` grant; and re-exports the record-kind helpers (`storedTranscriptKindOf`,
2188
+ * `parseStoredTranscriptKindFilter`) the daemon's reader numbers records with. Additive and
2189
+ * optional, so it takes the patch slot; plugins calling them feature-detect.
2190
+ *
2191
+ * 0.20.0 removes the unused activation-free advertisement helpers and their types from
2192
+ * `kay-agent` (#15160). No plugin used them, so 0.19.x stays served rather than refused, as with
2193
+ * 0.14.0; a 0.19.x plugin that imported one would now fail at load.
2194
+ *
2195
+ * 0.20.1 deletes `preflight.acceptsPayloadlessWrite` from URI modules (#15164). A `write` call
2196
+ * with neither `content` nor `source` now reaches the module's `write` with `content: undefined`,
2197
+ * and the module decides: a route that takes no body (a folder create) proceeds, and one that
2198
+ * needs a body refuses, naming what is missing. Only gdrive and microsoft declared the member,
2199
+ * and no SDK since 0.15.3 has been published, so the removal does not spend the minor slot.
2200
+ *
2201
+ * 0.20.2 lets a `kay.agent` manifest describe a tool whole (#15152): `parameters` (and with it a
2202
+ * required `description`), an `authService`/`authScheme` binding, and a `permission` (kind, rule
2203
+ * key, and the string arguments that name its resources). The host builds the tool from that entry,
2204
+ * and code supplies only the call through `api.implementTool(localId, execute)`. A plugin that runs
2205
+ * in the plugin host must describe its tools this way, because the daemon never loads its code.
2206
+ * Additive for every other plugin, so it takes the patch slot; plugins describing a tool declare
2207
+ * ^0.20.2.
2208
+ *
2209
+ * 0.21.0 makes sign-in manifest data (#15151). The host builds every auth service and scheme from
2210
+ * the manifest's `auth.services`, which gains OAuth endpoints, a public `clientId`, api-key
2211
+ * `inputLabel`/`fields`/`setup`/`validates` and a service `summary`. `api.auth` keeps only the
2212
+ * functions: `registerValidator` and `registerAccountResolver` replace `advertiseService` and the
2213
+ * `declare{OAuthPkce,ApiKey,ExternalCli}Scheme` calls. 0.19.x and 0.20.x stay served, as with
2214
+ * 0.20.0: most plugins declare no sign-in in code and are unaffected. One that does stops working
2215
+ * until it is rebuilt against 0.21: a manifest `oauth2` scheme without the now-required endpoints
2216
+ * is refused at load, and an activation that calls a removed member throws, so the plugin loses
2217
+ * its tools and URI modules too.
2218
+ *
2219
+ * 0.21.1 lets a URI module's `read` return its index form on `RawResource.observation`, from the
2220
+ * same fetch as the content, and deletes what steered the old second read (#15163): `observes`
2221
+ * on URI modules, `format: "observation"` on the read context, and `owns` on knowledge sources.
2222
+ * A plugin that still declares `owns` or `observes` loads, and the member is ignored. One that
2223
+ * omits `owns` needs this host, since an older one requires it. No SDK since 0.15.3 has been
2224
+ * published, so the removals take the patch slot, as in 0.20.1. It also adds `knownResourcesView`,
2225
+ * which marks a list or search rendered from the known-resources ledger so the host does not
2226
+ * record it back.
2227
+ *
2228
+ * 0.21.2 adds same-plugin informational tooltip apps to reference presentations, matched by
2229
+ * canonical resource URI patterns, and context-only apps excluded from standalone navigation.
2230
+ * Plugins declaring either require ^0.21.2.
2231
+ *
2232
+ * 0.22.0 deletes URI-module members the daemon used to call in a plugin's code (#15150):
2233
+ * `authActions.classifyOperation` (sign-in actions come from `authActions.rules`), `preflight` (the
2234
+ * verb rejects a bad call when it runs), `permission`, and `toMarkdown` (a verb's result carries
2235
+ * its own `markdown`); `@bonsai/plugin-sdk/kay-agent` stops re-exporting
2236
+ * `UriOperationPreflightPolicy`. A `kay.app` module also loses `matches`, `normalize` and
2237
+ * `describe`: it routes, spells and links its addresses through its claims' `normalizeTo`, `title`
2238
+ * and `sourceUrl`. The host drops these members from a 0.20.x or 0.21.x module rather than calling
2239
+ * them, and a module already claims every scheme it declares, so both stay served.
2240
+ *
2241
+ * 0.22.1 renames `RawResource.observation` to `observations`, a list (#15168), so one read can
2242
+ * index several documents: a Gmail thread read indexes each of its messages. A module that still
2243
+ * returns `observation` loads, and its read is not indexed. No SDK since 0.15.3 has been
2244
+ * published, so the rename takes the patch slot, as in 0.21.1. Plugins that return observations
2245
+ * and declare a floor require ^0.22.1.
2246
+ *
2247
+ * 0.22.2 lets a converter decline a file its `matches` claimed (#15153): `convert` may resolve
2248
+ * `undefined`, and the read pipeline tries the next converter that claimed the file. A plugin
2249
+ * whose converters only convert is unaffected; one that declines requires ^0.22.2, since an older
2250
+ * host reads the result's `content`. Additive, so it takes the patch slot.
2251
+ *
2252
+ * 0.23.0 trims the message approval type (#15154): `prepare` returns a promise, so the host can
2253
+ * call it across a process boundary; `tighten` is gone (no plugin set it, and an unknown member
2254
+ * is ignored); `resolveResource` is optional, for a provider that keeps no saved draft. A 0.22
2255
+ * plugin's synchronous `prepare` is still read correctly, because the host awaits what it
2256
+ * returns, so 0.22.x stays served.
2257
+ *
2258
+ * 0.23.0 also deletes `definePoller`'s `resolveCredential` option (#15154): the helper asks
2259
+ * `app.backgroundAuth` itself, and `requiredScopes` on a background credential request becomes
2260
+ * optional. Omitted, the host asks for the first scope of the manifest's `background-job` grant
2261
+ * (`allowedScopes`) that the account holds, and answers `requires-auth` when it holds none. A
2262
+ * 0.22.x plugin that passes `resolveCredential` still loads; the option is ignored, so its poller
2263
+ * needs a `background-job` grant whose `allowedScopes` the host can choose from.
2264
+ *
2265
+ * 0.23.0 also deletes `timezone` from a cron schedule (#15394): every schedule, a workflow trigger's
2266
+ * and an automation's, runs in the computer's current time zone and follows it when it changes.
2267
+ * A `define` or create that still passes `timezone` is refused, so a 0.22.x plugin that pins a
2268
+ * cron zone stops loading its workflow until it drops the field; every other 0.22.x plugin is
2269
+ * unaffected and stays served. Plugins that declare a cron trigger declare ^0.23.0, since an
2270
+ * older host requires the zone.
2271
+ *
2272
+ * 0.24.0 lets the daemon build a hosted plugin's URI module from its manifest alone (#15141).
2273
+ * The module facts gain `verbs.searchable`; resolved modules carry `displayName` and
2274
+ * `authServiceId`. Asana and Figma use their services' single auth scheme.
2275
+ *
2276
+ * 0.25.0 unifies URI writes (#15402): `write(input, content, sources, ctx)` receives an
2277
+ * empty sources array for ordinary writes. Modules that accept files declare
2278
+ * `acceptsWriteSources: true` in their manifest. Earlier SDK contracts are no longer served.
2279
+ *
2280
+ * 0.26.0 folds `writeBytes` into `write` (#15433): a binary `source` copy reaches `write` as a
2281
+ * `Uint8Array` content, named and typed by `ctx.writeSource`, and only for a module that declares
2282
+ * `acceptsBytes: true` in its manifest. A module that still defines `writeBytes` never receives
2283
+ * bytes. Earlier SDK contracts are no longer served.
2284
+ *
2285
+ * 0.26.1 turns the revision check into a conditional `read` (#15423). `ctx.knownRevision` carries
2286
+ * the revision the session holds, and a module that has verified the source has not moved returns
2287
+ * `{ content: "", unchanged: true, metadata: { revision } }` with no body. The module's `revision`
2288
+ * member is gone (the host drops a leftover one), so is `fallback` and `locate` (the host's own
2289
+ * modules keep them), and so is the manifest's `filesystemBacking`, which no plugin declared.
2290
+ * Additive for a plugin that never used them. A plugin that answers `unchanged` declares ^0.26.1.
2291
+ *
2292
+ * 0.26.2 lets a hosted URI module describe its calls for the approval card (#15423): a manifest
2293
+ * URI module sets `describesOperations: true` when its code defines `describeOperation`, and the
2294
+ * daemon calls the plugin host for it. `amendOperation` is gone: the host applies an "Allow with
2295
+ * edits" answer itself, replacing top-level string properties of the payload, so a preview field
2296
+ * is editable only when the payload is an object with that key as a string. `labelResource` is
2297
+ * gone: a verb's result carries the transcript row's name as `label`. The SDK stops re-exporting
2298
+ * `UriOperationAmendment`, `UriResourceLabelRequest` and `UriPreviewEdit`. The host ignores a
2299
+ * leftover `amendOperation` or `labelResource`, so this is additive for a plugin that never sets
2300
+ * the flag; a plugin whose manifest sets `describesOperations` declares ^0.26.2.
2301
+ *
2302
+ * 0.26.3 gives a plugin's own sign-in back what #15151 left behind (#15195). Every `oauth2` sign-in a
2303
+ * plugin runs says where its account comes from: `account` is `"resolveAccountId"`,
2304
+ * `"resolveAccounts"` or, on a hosted (`confidential`) sign-in whose broker names the account,
2305
+ * `"sign-in"`, so each account a person signs in with is saved apart. `api.auth.registerAccountResolver`
2306
+ * registers the named function on any such scheme, its own client's included, and the SDK refuses a
2307
+ * plugin whose lookup and manifest disagree. An own-client (`pkce`) scheme may carry the
2308
+ * `clientSecret` that goes with its `clientId`. A plugin with an `oauth2` sign-in declares ^0.26.3.
2309
+ */
2310
+ declare const KAY_PLUGIN_SDK_VERSION: "0.26.3";
2311
+
2312
+ declare const KAY_PLUGIN_TARGET_NAMES: readonly ["kay.agent", "kay.app", "kay.ui"];
2313
+
2314
+ /** A plugin's Bonsai agent activation - receives only the brokered {@link KayAgentPluginApi}. */
1725
2315
  declare type KayAgentActivate = (api: KayAgentPluginApi) => void;
1726
2316
 
2317
+ /** Browser compilation over caller-supplied bytes, without filesystem or network authority. */
2318
+ declare interface KayAgentCompiler {
2319
+ /** Rejects on compilation or source callback failure, and when the host composed no compiler. */
2320
+ bundleBrowser(request: KayBrowserBundleRequest, sources: KayBrowserSources): Promise<KayBrowserBundle>;
2321
+ }
2322
+
2323
+ /**
2324
+ * The plugin-facing `kay.agent` own-endpoint call: a plugin's agent part calls an endpoint its own
2325
+ * `kay.app` part registered. SDK 0.17.3; a plugin that calls it declares `compatibility.sdk`
2326
+ * `^0.19.0`.
2327
+ *
2328
+ * A plugin's two parts load as separate modules and share no memory, so this is how agent code
2329
+ * reaches state its app part keeps (a worker, a database). The manifest says which endpoints the
2330
+ * agent part may call: the `kay.app` target declares the endpoint with a `register` grant, and
2331
+ * `permissions.byTarget["kay.agent"].endpoints` lists it with `invoke`. No other plugin's endpoint
2332
+ * is nameable.
2333
+ *
2334
+ * An endpoint whose only `invoke` grant is the plugin's own `kay.agent` target is not reachable
2335
+ * from Bonsai's surfaces (its pages and runtime clients), so an action the agent takes after the
2336
+ * session's approval card cannot be taken around it.
2337
+ */
2338
+ /** Why an {@link KayAgentEndpoints.invoke} call produced no value. */
2339
+ declare type KayAgentEndpointFailureReason =
2340
+ /** The manifest grants this plugin's `kay.agent` target no `invoke` on this endpoint. */
2341
+ "not-declared"
2342
+ /** The plugin's `kay.app` part is not running, or registered no handler for it. */
2343
+ | "unavailable"
2344
+ /** The handler did not answer within the host's fixed deadline (10 seconds). It may still finish. */
2345
+ | "timed-out"
2346
+ /** The caller's `signal` aborted. The handler may still finish. */
2347
+ | "cancelled"
2348
+ /** The handler threw; `message` is its error's message. */
2349
+ | "failed"
2350
+ /** The request or the handler's response does not match the endpoint's declared schema. */
2351
+ | "invalid";
2352
+
2353
+ declare type KayAgentEndpointOutcome = {
2354
+ readonly ok: true;
2355
+ readonly value: unknown;
2356
+ } | {
2357
+ readonly ok: false;
2358
+ readonly reason: KayAgentEndpointFailureReason;
2359
+ readonly message: string;
2360
+ };
2361
+
2362
+ declare interface KayAgentEndpoints {
2363
+ /**
2364
+ * Call `endpoint`, the local id this plugin's manifest declares on its `kay.app` target. Never
2365
+ * rejects: every failure is an outcome. Nothing is queued: when the app part is not running the
2366
+ * answer is `unavailable` at once.
2367
+ */
2368
+ invoke(endpoint: string, payload: unknown, options?: {
2369
+ readonly signal?: AbortSignal;
2370
+ }): Promise<KayAgentEndpointOutcome>;
2371
+ }
2372
+
1727
2373
  /**
1728
2374
  * Publishes the plugin's declared events from agent code. See {@link KayAgentPluginApi.events}.
1729
2375
  */
@@ -1767,7 +2413,7 @@ declare interface KayAgentInteractionSurfaces {
1767
2413
  readonly desktop: InteractionSurface;
1768
2414
  }
1769
2415
 
1770
- /** One knowledge-index hit — a reference and within-list rank score, never a snippet. */
2416
+ /** One knowledge-index hit - a reference and within-list rank score, never a snippet. */
1771
2417
  declare interface KayAgentKnowledgeHit {
1772
2418
  readonly uri: string;
1773
2419
  /** Comparable only with other hits returned by this search call. */
@@ -1777,7 +2423,7 @@ declare interface KayAgentKnowledgeHit {
1777
2423
  /**
1778
2424
  * Query the daemon's shared knowledge index, scoped to this plugin's own declared `kay.agent`
1779
2425
  * schemes. Deny-by-default: `api.knowledgeSearch` is absent unless the manifest requests the
1780
- * `knowledgeSearch` grant AND the host effectively grants it — a plugin must feature-detect the
2426
+ * `knowledgeSearch` grant AND the host effectively grants it - a plugin must feature-detect the
1781
2427
  * capability. (It is WIRING, so absence is still the signal here, unlike the claim-checked
1782
2428
  * capabilities around it.) The host pre-scopes every call to the
1783
2429
  * plugin's OWN declared URI schemes; a caller-supplied `uriPrefixes` outside that scope is dropped
@@ -1793,7 +2439,7 @@ declare type KayAgentKnowledgeSearch = (query: string, opts?: {
1793
2439
  /**
1794
2440
  * One row of the daemon's known-resources ledger: a resource the host has
1795
2441
  * observed flowing through one of this plugin's own URI modules (or recorded
1796
- * through a host-side grant flow). Metadata facts, never content — content
2442
+ * through a host-side grant flow). Metadata facts, never content - content
1797
2443
  * understanding stays with the knowledge index.
1798
2444
  */
1799
2445
  declare interface KayAgentKnownResource {
@@ -1808,7 +2454,7 @@ declare interface KayAgentKnownResource {
1808
2454
  readonly seenVia: KayAgentKnownResourceSeenVia;
1809
2455
  /**
1810
2456
  * Durable grant provenance (`picked` for a Drive-picker grant). Unlike `seenVia`, a later
1811
- * ordinary sighting never erases it — the row permanently remembers it entered the corpus
2457
+ * ordinary sighting never erases it - the row permanently remembers it entered the corpus
1812
2458
  * through an explicit user grant.
1813
2459
  */
1814
2460
  readonly grantedVia?: string;
@@ -1827,12 +2473,12 @@ declare interface KayAgentKnownResource {
1827
2473
  * (the host-recorded memory that `contributes.uriModules[].knownResources`
1828
2474
  * opts a module's traffic into). Deny-by-default: `api.knownResources` is
1829
2475
  * absent unless the manifest requests the `knownResources` grant AND the host
1830
- * effectively grants it — feature-detect it exactly like
2476
+ * effectively grants it - feature-detect it exactly like
1831
2477
  * {@link KayAgentKnowledgeSearch}, the other WIRING capability. Every call is host-scoped to the plugin's
1832
2478
  * OWN declared module ids; there is no way to widen it. Read-only by design:
1833
2479
  * rows are created only by the host's recording wrap and host-side grant
1834
2480
  * writers, so a plugin can render its memory but never invent it. The ledger
1835
- * is a cache of CLAIMS, not an authority — a returned row proves the resource
2481
+ * is a cache of CLAIMS, not an authority - a returned row proves the resource
1836
2482
  * was once seen, never that it is still reachable.
1837
2483
  */
1838
2484
  declare interface KayAgentKnownResources {
@@ -1862,7 +2508,7 @@ declare interface KayAgentKnownResources {
1862
2508
  * How a known-resource row was MOST RECENTLY seen: one of the recording
1863
2509
  * wrap's verbs, or a host-side grant writer's `granted:<label>` (e.g. a
1864
2510
  * Drive picker completion writing `granted:picked`). The closed template
1865
- * union keeps the contract honest without enumerating grant labels — a
2511
+ * union keeps the contract honest without enumerating grant labels - a
1866
2512
  * plain-string widening here would silently admit junk provenance.
1867
2513
  */
1868
2514
  declare type KayAgentKnownResourceSeenVia = "read" | "list" | "search" | `granted:${string}`;
@@ -1871,7 +2517,7 @@ declare type KayAgentKnownResourceSeenVia = "read" | "list" | "search" | `grante
1871
2517
  * Claim-checked outbound HTTP. The handle is always present; authority is the CALL, not the handle.
1872
2518
  *
1873
2519
  * The signature is `fetch`'s on purpose: a plugin that already speaks `fetch` changes the object it
1874
- * calls, not the code around it. What differs is invisible from here — the host builds an exact
2520
+ * calls, not the code around it. What differs is invisible from here - the host builds an exact
1875
2521
  * `network.connect {connect}` claim from the live URL (and again from each redirect hop), checks it
1876
2522
  * against the user's grant ledger, and rejects with a {@link KayAgentCapabilityError} rather than
1877
2523
  * performing the request. There is no `allowedHosts` to read: a plugin cannot enumerate its own
@@ -1896,13 +2542,19 @@ declare interface KayAgentPluginApi {
1896
2542
  readonly knowledgeSearch?: KayAgentKnowledgeSearch;
1897
2543
  /** Absent unless the `knownResources` grant is requested and effectively granted. */
1898
2544
  readonly knownResources?: KayAgentKnownResources;
2545
+ /**
2546
+ * Optional: absent unless a simulating Bonsai daemon serves this plugin a simulator (SDK 0.18.1).
2547
+ * A production daemon never does. In this version only plugins the release carries are served;
2548
+ * any plugin may ship a `./simulator` later. See {@link KayServedSimulators}.
2549
+ */
2550
+ readonly servedSimulators?: KayServedSimulators;
1899
2551
  /**
1900
2552
  * Present when the host composes Whiteboard. Each operation is checked against its exact
1901
2553
  * `whiteboard.<operation> {scope}` claim; handle presence is not consent.
1902
2554
  */
1903
2555
  readonly whiteboard?: KayAgentWhiteboard;
1904
2556
  /**
1905
- * Always present; each request — and each redirect hop — is checked against
2557
+ * Always present; each request - and each redirect hop - is checked against
1906
2558
  * `network.connect {connect}` for the host it is about to reach.
1907
2559
  */
1908
2560
  readonly network: KayAgentNetwork;
@@ -1933,6 +2585,8 @@ declare interface KayAgentPluginApi {
1933
2585
  * (SDK 0.13.8).
1934
2586
  */
1935
2587
  readonly systemOne: SystemOneClient;
2588
+ /** Always present and ungated; compiles only caller-supplied bytes. SDK 0.18.4. */
2589
+ readonly compiler: KayAgentCompiler;
1936
2590
  /**
1937
2591
  * The daemon's clock and the time zone the user is in, read together on each `snapshot()` call.
1938
2592
  * Always present and ungated: it confers no authority, since a plugin runs in the daemon's
@@ -1955,18 +2609,50 @@ declare interface KayAgentPluginApi {
1955
2609
  */
1956
2610
  readonly events: KayAgentEvents;
1957
2611
  /**
1958
- * Register a tool (a named function the agent can call). Parallel to the kernel's `registerTool`.
1959
- * To auth-gate a tool, build the definition with {@link withResolvedAuth}: it stamps the
1960
- * `requiresAuth` binding onto the definition and resolves a live token from the kernel-injected
1961
- * `ctx.auth` before the body runs — the same wrapper the built-in service tools use. A definition
1962
- * with no `requiresAuth` is an ordinary always-on tool.
2612
+ * A logger whose lines land in Bonsai's own log under this plugin's id, filed on this
2613
+ * activation's session. Always present and ungated, like {@link time}. Use it instead of
2614
+ * `console`; see {@link KayPluginLog}.
2615
+ */
2616
+ readonly log: KayPluginLog;
2617
+ /**
2618
+ * Calls tools on the vendored MCP servers this plugin declares (`kay.app`
2619
+ * `contributes.mcpServers`), signed in through the plugin's own auth service. A port reaches
2620
+ * only this plugin's own server, and only while Bonsai hosts it for this plugin under its
2621
+ * `mcp.host-server` grant; it never reaches another plugin's server or one the user configured.
2622
+ * It calls any tool the server has, writes included. Always present; a host with no MCP host
2623
+ * answers each call `failed`. Types: `@bonsai/plugin-sdk/vendored-mcp` (SDK 0.16.2).
2624
+ */
2625
+ readonly vendoredMcp: KayAgentVendoredMcp;
2626
+ /**
2627
+ * Calls the endpoints this plugin's own `kay.app` part registered and the manifest grants this
2628
+ * target `invoke` on, by local id. Never another plugin's. Every failure is an outcome, never a
2629
+ * rejection; the host fixes a 10-second deadline. Always present; a host with no app part
2630
+ * answers each call `unavailable`. See {@link KayAgentEndpoints} (SDK 0.17.3).
2631
+ */
2632
+ readonly endpoints: KayAgentEndpoints;
2633
+ /**
2634
+ * Supply the `execute` of a tool kay-plugin.json describes (its entry declares `parameters`),
2635
+ * by the entry's `localId`. The host builds the rest of the tool - name, label, description,
2636
+ * parameters, auth binding and permission - from that entry. Build `execute` with
2637
+ * {@link toolExecute}. For a tool declaring `authService`, a session resolves the credential
2638
+ * onto `ctx.credential` before each call (for the account its `accountId` argument names, when
2639
+ * the tool declares one) and never calls `execute` without one; outside a session it may be
2640
+ * absent (SDK 0.20.2).
2641
+ */
2642
+ implementTool(localId: string, execute: KayAgentToolExecute): void;
2643
+ /**
2644
+ * Register a whole tool definition, for a declared tool whose entry has no `parameters` because
2645
+ * part of its policy is code. Such a plugin cannot run in the plugin host; prefer
2646
+ * {@link implementTool}. To auth-gate one, build the definition with {@link withResolvedAuth}
2647
+ * (or `defineAuthedTool`).
1963
2648
  */
1964
2649
  registerTool(definition: ToolDefinition): void;
1965
2650
  /**
1966
2651
  * Register a web search provider (a backend behind the single `web_search` tool). Parallel to the
1967
- * kernel's `registerSearchProvider`. An own-credential provider carries its `requiresAuth` binding —
1968
- * build it with {@link withResolvedSearchProvider}, which stamps the binding and resolves the token
1969
- * from `ctx.auth`. A provider without a binding reuses inference credentials or needs none.
2652
+ * kernel's `registerSearchProvider`. An own-credential provider carries its `requiresAuth` binding -
2653
+ * build it with {@link withResolvedSearchProvider}, which stamps the binding; the host resolves the
2654
+ * credential before each call and hands it to the body. A provider without a binding reuses
2655
+ * inference credentials or needs none.
1970
2656
  */
1971
2657
  registerSearchProvider(provider: SearchProvider): void;
1972
2658
  /**
@@ -1985,23 +2671,34 @@ declare interface KayAgentPluginApi {
1985
2671
  registerConverter(converter: ContentConverter): void;
1986
2672
  /**
1987
2673
  * Always present; the registration is checked against `tool-interception.register`. Register
1988
- * a tool-call interceptor — the neutral around-execution seam over EVERY tool call in the session
2674
+ * a tool-call interceptor - the neutral around-execution seam over EVERY tool call in the session
1989
2675
  * (any layer's tools, subagent sessions included): observe a call, delegate to the real tool via
1990
2676
  * `next()`, or return a substitute result so the real tool never runs. Materially higher
1991
2677
  * privilege than `registerTool` (which owns only its own tool), which is why the registration is
1992
- * a claim of its own. The permission gate still runs before execution — an interceptor can
2678
+ * a claim of its own. The permission gate still runs before execution - an interceptor can
1993
2679
  * substitute results, never widen what is callable.
1994
2680
  */
1995
2681
  interceptToolCall(interceptor: ToolCallInterceptor): void;
2682
+ /**
2683
+ * Sign-in. The plugin's manifest (`auth.services`) declares every service and scheme and the
2684
+ * facts the host shows and runs; plugin code registers only the functions a scheme needs.
2685
+ */
1996
2686
  readonly auth: {
1997
- advertiseService(descriptor: PluginServiceDescriptor): void;
1998
- declareOAuthPkceScheme(serviceId: string, scheme: PluginOAuthPkceScheme): void;
1999
- declareApiKeyScheme(serviceId: string, scheme: PluginApiKeyScheme): void;
2000
- declareExternalCliScheme(serviceId: string, scheme: PluginExternalCliScheme): void;
2687
+ /**
2688
+ * The check for an `api-key` scheme the manifest marks `validates: true`. `serviceId` and
2689
+ * `schemeId` are the manifest's global ids (`github.account`, `github.pat`).
2690
+ */
2691
+ registerValidator(serviceId: string, schemeId: string, validate: ValidatePluginApiKeyCredential): void;
2692
+ /**
2693
+ * The account lookup for an `oauth2` scheme whose manifest names it as where the account comes
2694
+ * from (`account: "resolveAccountId"` or `"resolveAccounts"`), registering that function only.
2695
+ * Refused on any other scheme.
2696
+ */
2697
+ registerAccountResolver(serviceId: string, schemeId: string, resolver: PluginAccountResolver): void;
2001
2698
  /**
2002
2699
  * Declare a delegated scheme (plugin-provided connect/refresh over opaque material) for a
2003
- * service. Mirrors the other three declarations; the host maps it onto the kernel's
2004
- * `DelegatedAuthScheme` and drives its `connect` from `service_connect`.
2700
+ * service. The host maps it onto the kernel's `DelegatedAuthScheme` and drives its `connect`
2701
+ * from `service_connect`.
2005
2702
  */
2006
2703
  declareDelegatedScheme(serviceId: string, scheme: PluginDelegatedScheme): void;
2007
2704
  /**
@@ -2021,7 +2718,7 @@ declare interface KayAgentPluginApi {
2021
2718
  * {@link KayAgentProcessRunResult.exitCode} always means the command itself failed, never that
2022
2719
  * permission did. Two DIFFERENT things reject, and a caller should handle both: a refusal rejects
2023
2720
  * with {@link KayAgentCapabilityError}, and cancelling through
2024
- * {@link KayAgentProcessRunRequest.signal} rejects with an `AbortError` carrying a fixed message —
2721
+ * {@link KayAgentProcessRunRequest.signal} rejects with an `AbortError` carrying a fixed message -
2025
2722
  * the host terminates the child and settles its output first, and deliberately does not echo back
2026
2723
  * the `reason` you aborted with. A user cancelling a run is not a crash. A timeout is NOT a
2027
2724
  * rejection: it resolves with exit code 124. The termination bound covers the DIRECT child only: a
@@ -2045,12 +2742,12 @@ declare interface KayAgentProcessRunRequest {
2045
2742
  readonly command: string;
2046
2743
  readonly argvClass: string;
2047
2744
  readonly args: readonly string[];
2048
- /** Abort the run; the host kills the DIRECT child and rejects — a forked descendant may outlive it (#6153). */
2745
+ /** Abort the run; the host kills the DIRECT child and rejects - a forked descendant may outlive it (#6153). */
2049
2746
  readonly signal?: AbortSignal;
2050
2747
  }
2051
2748
 
2052
2749
  /**
2053
- * A finished run. `stdout`/`stderr` are decoded text, and the host CAPS them — a truncation flag is
2750
+ * A finished run. `stdout`/`stderr` are decoded text, and the host CAPS them - a truncation flag is
2054
2751
  * set rather than the output silently ending, so a plugin parsing the stream can tell "the command
2055
2752
  * printed this" apart from "the command printed more than we kept".
2056
2753
  */
@@ -2062,6 +2759,28 @@ declare interface KayAgentProcessRunResult {
2062
2759
  readonly stderrTruncated?: boolean;
2063
2760
  }
2064
2761
 
2762
+ /** The `execute` a plugin supplies for a tool its manifest describes. */
2763
+ declare type KayAgentToolExecute = ToolDefinition["execute"];
2764
+
2765
+ /**
2766
+ * Calls tools on the plugin's own vendored MCP servers. See {@link KayAgentPluginApi.vendoredMcp}.
2767
+ */
2768
+ declare interface KayAgentVendoredMcp {
2769
+ /**
2770
+ * The port to `server`, a name this plugin declares in its `kay.app` target's
2771
+ * `contributes.mcpServers`. Throws when the plugin declares no server of that name. A declared
2772
+ * server that Bonsai does not host for this plugin still returns a port; each call then answers
2773
+ * `unknown-server`. Its message names the plugin that holds the name, or else lists the possible
2774
+ * reasons (no approved `mcp.host-server` grant, a sign-in the server cannot use, or a name that
2775
+ * a plugin Bonsai ships or another installed plugin also claims) and points at the plugin's load
2776
+ * diagnostic.
2777
+ *
2778
+ * A port reaches this plugin's own server only. To use another plugin's data, declare that
2779
+ * plugin in the manifest's `dependencies.plugins`; a later SDK surface reaches it.
2780
+ */
2781
+ port<TTool extends string = string>(server: string): VendoredMcpPort<TTool>;
2782
+ }
2783
+
2065
2784
  /**
2066
2785
  * Authenticated requests through the host's embedded-browser partition. The host confines every
2067
2786
  * call to the exact origin/path rows declared in the plugin manifest (segment-bounded), never
@@ -2180,7 +2899,7 @@ declare interface KayAgentWhiteboardUpdateInput {
2180
2899
  /**
2181
2900
  * `app.time` / `api.time`: host facts about time. Added to `kay.app` in SDK 0.12.1 and to
2182
2901
  * `kay.agent` with the calendar zone-of-record work: a plugin that calls it declares
2183
- * `compatibility.sdk` `^0.12.1`, so an older Bonsai refuses a directory or repository plugin at
2902
+ * `compatibility.sdk` `^0.19.0`, so an older Bonsai refuses a directory or repository plugin at
2184
2903
  * load instead of the call failing.
2185
2904
  */
2186
2905
  declare interface KayAppTime {
@@ -2190,9 +2909,7 @@ declare interface KayAppTime {
2190
2909
  * Every call asks the host again and returns a new object; nothing is cached. Call it where you
2191
2910
  * need the value (inside a tool, a handler or a workflow step), not inside `activate()`: a value
2192
2911
  * read there is as old as the activation, and the host can run a bundled plugin's `activate()`
2193
- * against a recording api that has no `time`, so a read there throws. The exception is a
2194
- * directory plugin that declares a workflow with a cron `timezone`: `workflows.define` is legal
2195
- * only during `activate()`, so it reads the zone there, and the schedule then keeps that zone.
2912
+ * against a recording api that has no `time`, so a read there throws.
2196
2913
  * Keep the result for as long as you use it, and not across a long wait (a sleep, a timer, a
2197
2914
  * queued run): the zone can change while you wait. On a daemon that runs on the user's machine,
2198
2915
  * a change of the process's `TZ` shows in the next call. A `TZ` that names a zone wins over the
@@ -2283,7 +3000,7 @@ declare interface KayAppUserFileListing {
2283
3000
  * File I/O in user folders, run by the host off the daemon's main thread. Every path is absolute.
2284
3001
  * No method rejects; each answers a {@link KayAppUserFilesResult}. This is not a sandbox: it does
2285
3002
  * not limit which paths a plugin may touch. Added in SDK 0.12.5: a plugin that calls it declares
2286
- * `compatibility.sdk` `^0.12.5`, so an older Bonsai refuses a directory or repository plugin at
3003
+ * `compatibility.sdk` `^0.19.0`, so an older Bonsai refuses a directory or repository plugin at
2287
3004
  * load instead of the call failing.
2288
3005
  */
2289
3006
  declare interface KayAppUserFiles {
@@ -2379,6 +3096,119 @@ declare interface KayAppUserFileWalkEntry {
2379
3096
  readonly kind: KayAppUserFileKind;
2380
3097
  }
2381
3098
 
3099
+ declare interface KayBrowserBundle {
3100
+ readonly outputs: readonly {
3101
+ readonly path: string;
3102
+ readonly text: string;
3103
+ }[];
3104
+ readonly warnings: readonly string[];
3105
+ }
3106
+
3107
+ declare interface KayBrowserBundleRequest {
3108
+ readonly entry: {
3109
+ readonly path: string;
3110
+ } | {
3111
+ readonly path: string;
3112
+ readonly contents: string;
3113
+ readonly loader: "js" | "css";
3114
+ };
3115
+ readonly target: string;
3116
+ readonly format: "iife" | "esm";
3117
+ readonly charset: "ascii" | "utf8";
3118
+ readonly minifyWhitespace: boolean;
3119
+ readonly legalComments: "none" | "inline";
3120
+ }
3121
+
3122
+ /** Import edges the host reports to a browser bundle's source resolver. */
3123
+ declare type KayBrowserImportKind = "entry-point" | "import-statement" | "require-call" | "dynamic-import" | "require-resolve" | "import-rule" | "composes-from" | "url-token";
3124
+
3125
+ /** All imports and bytes come from these callbacks; the host performs no fallback I/O. */
3126
+ declare interface KayBrowserSources {
3127
+ resolve(request: {
3128
+ readonly path: string;
3129
+ readonly importer: string;
3130
+ readonly kind: KayBrowserImportKind;
3131
+ }): Promise<{
3132
+ readonly path: string;
3133
+ readonly external?: boolean;
3134
+ readonly suffix?: string;
3135
+ }>;
3136
+ load(request: {
3137
+ readonly path: string;
3138
+ }): Promise<{
3139
+ readonly contents: string | Uint8Array;
3140
+ readonly loader: "js" | "css" | "json" | "dataurl";
3141
+ }>;
3142
+ }
3143
+
3144
+ /**
3145
+ * Each method writes one line. `name` is a stable event name (`sync-failed`), the same for every
3146
+ * line of one kind, so the line can be found by name; put what varies in `message` and
3147
+ * `attributes`. A call never throws, and it keeps working after the plugin is disabled.
3148
+ */
3149
+ declare interface KayPluginLog {
3150
+ debug(name: string, message: string, details?: KayPluginLogDetails): void;
3151
+ info(name: string, message: string, details?: KayPluginLogDetails): void;
3152
+ warn(name: string, message: string, details?: KayPluginLogDetails): void;
3153
+ error(name: string, message: string, details?: KayPluginLogDetails): void;
3154
+ /** A logger that adds these attributes to every line it writes, after the line's own. */
3155
+ child(attributes: KayPluginLogAttributes): KayPluginLog;
3156
+ }
3157
+
3158
+ /**
3159
+ * The plugin-facing log contract: a logger each plugin gets as `api.log`, whose lines land in
3160
+ * Bonsai's own log beside the daemon's.
3161
+ *
3162
+ * Ungated and always present, like `api.time`. It confers no authority: a line is written under the
3163
+ * plugin's own id, which the host stamps and the plugin cannot set, and a plugin can already print
3164
+ * to the console. The host records each line as `<pluginId>.<name>`, files it on a session's log
3165
+ * when it knows the session, and keeps an error's stack, so a built-in plugin's failure reaches
3166
+ * Error Tracking. Lines from a plugin the user added stay on the user's machine.
3167
+ *
3168
+ * This is a leaf module. It lives beside both target SDKs (rather than inside `kay-app`) so the
3169
+ * `kay.agent` SDK can expose the same logger without importing the `kay.app` surface.
3170
+ */
3171
+ /**
3172
+ * Metadata only: ids, closed values, counts. Never session content. The host drops any value that
3173
+ * is not a string, a boolean or a finite number.
3174
+ */
3175
+ declare type KayPluginLogAttributes = Readonly<Record<string, string | number | boolean>>;
3176
+
3177
+ declare interface KayPluginLogDetails {
3178
+ /** The error object. The host keeps its message, its stack and its `cause` chain. */
3179
+ readonly error?: unknown;
3180
+ /**
3181
+ * The session this line is about. Defaults to the session of the call the plugin is handling,
3182
+ * when the host knows it (a tool call or a URI read); the line then lands on that session's
3183
+ * `kay-log://events`. Pass it from an endpoint, a session-event handler or a timer. Work a call
3184
+ * starts (a timer, a subscription) keeps that call's session unless a line names another.
3185
+ */
3186
+ readonly sessionId?: string;
3187
+ readonly attributes?: KayPluginLogAttributes;
3188
+ }
3189
+
3190
+ declare const KayPluginProtocolVersion: z.ZodLiteral<"kay.plugin.v1">;
3191
+
3192
+ declare type KayPluginProtocolVersion = z.infer<typeof KayPluginProtocolVersion>;
3193
+
3194
+ /**
3195
+ * The simulators a simulating Bonsai daemon (evals, dev, tests) serves this plugin, keyed by the
3196
+ * adapter id its simulated URI connection rows name. The daemon builds each from the plugin's own
3197
+ * dev-only `./simulator` subpath; the plugin never imports its simulator.
3198
+ *
3199
+ * The member that carries this, `servedSimulators` on both plugin APIs, is optional and absent
3200
+ * unless a simulating daemon serves it. A production daemon never does, so treat absence as the
3201
+ * normal case. In this version the daemon serves it only to plugins the release carries; the seam
3202
+ * is not specific to them, and any plugin may ship a `./simulator` later.
3203
+ */
3204
+ declare interface KayServedSimulators {
3205
+ /**
3206
+ * What this plugin's simulator built for `adapterId`, or `undefined` when none was served for
3207
+ * that id. The shape is the plugin's own simulator contract; narrow it before use.
3208
+ */
3209
+ get(adapterId: string): unknown;
3210
+ }
3211
+
2382
3212
  /** Applies a KeyOf action to the given type. */
2383
3213
  declare function KeyOf<Type extends TSchema>(type: Type, options?: TSchemaOptions): TKeyOf_2<Type>;
2384
3214
 
@@ -2484,6 +3314,10 @@ declare function LiteralStringMapping(input: string): unknown;
2484
3314
 
2485
3315
  declare function LiteralTypeName<Value extends TLiteralValue>(value: Value): TLiteralTypeName<Value>;
2486
3316
 
3317
+ declare const LocalContributionId: z.ZodString;
3318
+
3319
+ declare type LocalContributionId = z.infer<typeof LocalContributionId>;
3320
+
2487
3321
  /** Applies a Lowercase action to the given type. */
2488
3322
  declare function Lowercase_2<Type extends TSchema>(type: Type, options?: TSchemaOptions): TLowercase<Type>;
2489
3323
 
@@ -2557,7 +3391,7 @@ declare type MidTurnCompactionRequest = {
2557
3391
  * Settles exactly once and never rejects, including abort and teardown paths.
2558
3392
  *
2559
3393
  * NEVER `await` this inside `execute`. The only thing that settles it is
2560
- * post-run compaction, and that cannot begin until this tool call returns —
3394
+ * post-run compaction, and that cannot begin until this tool call returns -
2561
3395
  * so awaiting it here hangs the run behind itself, which is the exact
2562
3396
  * deadlock this capability replaced. Attach a callback and return.
2563
3397
  */
@@ -2567,6 +3401,10 @@ declare type MidTurnCompactionRequest = {
2567
3401
  readonly reason: "already-pending" | "maintenance-in-flight";
2568
3402
  };
2569
3403
 
3404
+ declare const MimeTypeString: z.ZodString;
3405
+
3406
+ declare type MimeTypeString = z.infer<typeof MimeTypeString>;
3407
+
2570
3408
  /** Compatibility settings for the Mistral chat API. */
2571
3409
  declare interface MistralConversationsCompat extends Static<typeof MistralConversationsCompatSchema> {
2572
3410
  }
@@ -2602,7 +3440,7 @@ declare interface Model<TApi extends Api> extends BaseModel<TApi> {
2602
3440
  }
2603
3441
 
2604
3442
  /**
2605
- * The canonical model-facing content type — the single source of truth for what
3443
+ * The canonical model-facing content type - the single source of truth for what
2606
3444
  * a `read`/`finalize` boundary emits and what an {@link AgentToolResult.content}
2607
3445
  * carries (it IS the same `(TextContent | ImageContent)` union). When pi-ai later
2608
3446
  * adds audio, this one alias widens and every consumer follows.
@@ -2687,6 +3525,8 @@ declare const NeverPattern = "(?!)";
2687
3525
 
2688
3526
  declare const NewLine = "\n";
2689
3527
 
3528
+ declare const NonEmptyString: z.ZodString;
3529
+
2690
3530
  /** Applies a NonNullable action to the given type. */
2691
3531
  declare function NonNullable_2<Type extends TSchema>(type: Type, options?: TSchemaOptions): TNonNullable<Type>;
2692
3532
 
@@ -2708,33 +3548,12 @@ declare const NumberKey = "^-?(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?$";
2708
3548
 
2709
3549
  declare const NumberPattern = "-?(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?";
2710
3550
 
2711
- /**
2712
- * Who may supply the OAuth client for a scheme: Bonsai's embedded client, or one the user pastes in
2713
- * (BYOOC). An UNORDERED SET — list order carries no meaning (nothing reads it as a preference).
2714
- */
2715
- declare type OAuthClientSupplier = "kay" | "user";
2716
-
2717
3551
  /** What the provider hands back at the redirect URI after the user approves. */
2718
3552
  declare interface OAuthRedirectResult {
2719
3553
  readonly code: string;
2720
3554
  readonly state: string;
2721
3555
  }
2722
3556
 
2723
- /**
2724
- * The two client-supply axes, structured so the illegal cell is unrepresentable: a `confidential`
2725
- * exchange's secret may only ever be Bonsai's (held by the host's broker, never pasted by a
2726
- * customer), so `confidential` types `clientSuppliers` as `readonly ["kay"]` — `confidential ×
2727
- * user` is a compile-time impossibility in v1, not a runtime refusal. `pkce`'s `clientSuppliers`
2728
- * is a non-empty set; {@link createOAuthPkceAuthScheme} dedupes it and refuses empty.
2729
- */
2730
- declare type OAuthSchemeClientAxes = {
2731
- readonly exchange: "pkce";
2732
- readonly clientSuppliers: readonly OAuthClientSupplier[];
2733
- } | {
2734
- readonly exchange: "confidential";
2735
- readonly clientSuppliers: readonly ["kay"];
2736
- };
2737
-
2738
3557
  /** Creates an Object type. */
2739
3558
  declare function _Object_<Properties extends TProperties>(properties: Properties, options?: TObjectOptions): TObject<Properties>;
2740
3559
 
@@ -2753,6 +3572,79 @@ declare type ObjectLike_4 = Record<PropertyKey, any>;
2753
3572
  /** Extracts options from a TObject. */
2754
3573
  declare function ObjectOptions(type: TObject): TObjectOptions;
2755
3574
 
3575
+ /**
3576
+ * A source-native read of one URI, shaped for downstream indexing rather than
3577
+ * direct model consumption. A module's read returns it in
3578
+ * `RawResource.observations`, built from the same fetch as the content, once
3579
+ * per document it wants indexed; absence means "not indexed".
3580
+ *
3581
+ * Gone signal: the index retires a doc when a read throws `UriNotFoundError`
3582
+ * or an error whose `.status === 404`. A module whose vendor signals "gone"
3583
+ * differently (HTTP 410, a 200-body `{ok:false}`/`archived:true`, a
3584
+ * 403/404-ambiguous lost-access response) must translate that into one of
3585
+ * those two shapes itself; losing access to a resource counts as gone too.
3586
+ * A read that cannot build a complete observation (a secondary fetch failed)
3587
+ * returns none rather than a partial one.
3588
+ *
3589
+ * Account-qualified identity: a module whose read can resolve against more
3590
+ * than one connected account (the account selector is stripped before route
3591
+ * parsing, so two accounts reading the same opaque provider id would
3592
+ * otherwise collide on one doc key) must fold `ctx.auth.credential.accountId`
3593
+ * into `uri` - e.g. `gmail://@<accountId>/threads/<threadId>` - never the
3594
+ * user-renamable connection/selector label. `ctx.auth.credential` is the
3595
+ * reliable source of the resolved account at a `requiresAuth` module's
3596
+ * `read()`: it is always populated once auth resolution runs (an
3597
+ * `authContext`-style check already throws first when no credential
3598
+ * resolved). `ctx.connection` may ALSO be populated by dispatch - a
3599
+ * connection-required module receives it whenever the host has a tracked
3600
+ * connection for the account - so do not assume it is absent; a module that
3601
+ * wants the human-facing label rather than the account id reads
3602
+ * `ctx.connection?.displayName`.
3603
+ *
3604
+ * `accountId` is guaranteed stable only for the life of the stored credential
3605
+ * record, not necessarily across a disconnect/reconnect of the same
3606
+ * real-world provider account. A scheme that mints an opaque id at connect
3607
+ * time (the default for an api-key or delegated scheme, absent a
3608
+ * provider-derived override) gets a fresh id on every connect; a scheme that
3609
+ * instead derives the id from provider data (an oauth2-pkce scheme supplying
3610
+ * `resolveAccountId`, e.g. reading an OIDC `sub`) keeps the same id across
3611
+ * reconnect. A module on a scheme without a provider-stable id has two honest
3612
+ * choices: derive its own stable identity from provider data at read time
3613
+ * (call the provider's own account/"who am I" endpoint with the resolved
3614
+ * credential and fold THAT into `uri` instead of `accountId`), or
3615
+ * account-qualify with `accountId` knowing a disconnect/reconnect of the same
3616
+ * real-world account re-keys its documents under a new id rather than
3617
+ * updating them in place - nothing on the host side reconciles the old and
3618
+ * new keys.
3619
+ */
3620
+ declare interface ObservationMaterial {
3621
+ /** Canonical URI of the observed resource - may differ from the input URI. */
3622
+ readonly uri: string;
3623
+ /** Scheme-prefixed content type, e.g. `"github.pr"`, `"gmail.thread"`, `"wikipedia.article"`. */
3624
+ readonly kind: string;
3625
+ readonly title?: string;
3626
+ /** Source-native seams: one element per message/comment/section, in source order. */
3627
+ readonly text?: readonly string[];
3628
+ /** ISO-8601 creation time; provenance only - not persisted by the store yet. */
3629
+ readonly createdAt?: string;
3630
+ /** ISO-8601 last-update time; provenance only - not persisted by the store yet. */
3631
+ readonly updatedAt?: string;
3632
+ /** Source-native revision/version marker, when the source exposes one. */
3633
+ readonly version?: string;
3634
+ /**
3635
+ * How long this resource stays worth semantically searching, measured from
3636
+ * the LAST time it was observed - not from when it was first indexed. Every
3637
+ * observation of an unchanged resource pushes the deadline out again, so a
3638
+ * thread that keeps getting read keeps living and a quiet one ages out.
3639
+ *
3640
+ * Retention is opt-in per resource: absent means the index keeps the
3641
+ * resource until its source says it is gone. A route declares this only
3642
+ * when staleness is real for that shape - a chat thread or an inbox
3643
+ * conversation goes cold, a durable note the user wrote does not.
3644
+ */
3645
+ readonly retainForMs?: number;
3646
+ }
3647
+
2756
3648
  /** Applies a Omit action using the given types. */
2757
3649
  declare function Omit_2<Type extends TSchema, Indexer extends PropertyKey[]>(type: Type, indexer: readonly [...Indexer], options?: TSchemaOptions): TOmit<Type, TKeysToIndexer<Indexer>>;
2758
3650
 
@@ -2875,6 +3767,10 @@ declare const OptionalSemiColon: (input: string) => [unknown, string] | [];
2875
3767
 
2876
3768
  declare function OptionalSemiColonMapping(input: [unknown] | []): unknown;
2877
3769
 
3770
+ declare const PackageAssetPath: z.ZodString;
3771
+
3772
+ declare type PackageAssetPath = z.infer<typeof PackageAssetPath>;
3773
+
2878
3774
  /** Creates a Parameter type. */
2879
3775
  declare function Parameter<Name extends string, Extends extends TSchema, Equals extends TSchema>(name: Name, extends_: Extends, equals: Equals): TParameter<Name, Extends, Equals>;
2880
3776
 
@@ -3243,7 +4139,7 @@ declare function Pick_2<Type extends TSchema, Indexer extends TSchema>(type: Typ
3243
4139
  declare function PickDeferred<Type extends TSchema, Indexer extends TSchema>(type: Type, indexer: Indexer, options?: TSchemaOptions): TPickDeferred<Type, Indexer>;
3244
4140
 
3245
4141
  /**
3246
- * The version of the conformance floor this SDK enforces — bumped whenever
4142
+ * The version of the conformance floor this SDK enforces - bumped whenever
3247
4143
  * {@link PLUGIN_DISCLOSURE_RULES} changes, and never for anything else.
3248
4144
  *
3249
4145
  * Two readers, and the second is why a number beats a changelog entry. A plugin author compares it
@@ -3252,7 +4148,7 @@ declare function PickDeferred<Type extends TSchema, Indexer extends TSchema>(typ
3252
4148
  * older SDK and then failed at load says "this host enforces contract N, you audited against
3253
4149
  * something older" instead of leaving the author to guess whether their code or their SDK moved.
3254
4150
  *
3255
- * `1` is the floor as published by #3210 — the first release in which an author could run this
4151
+ * `1` is the floor as published by #3210 - the first release in which an author could run this
3256
4152
  * audit at all. `2` (#6880) adds the route-contract rules and tightens
3257
4153
  * `template-operation-docs-missing` from "every verb on a multi-GROUP route" to "every verb on a
3258
4154
  * multi-verb route": the help renderer now prints a sentence per verb at the route, so a missing
@@ -3267,7 +4163,7 @@ export declare const PLUGIN_DISCLOSURE_CONTRACT = 2;
3267
4163
  * daemon enforces it fail-closed. A rule added here retroactively fails plugins that were correct
3268
4164
  * yesterday, so the set may not grow as a side effect of an edit: the pinned test beside this
3269
4165
  * module (`test/disclosure/conformance-contract.test.ts`) fails on any change and makes the author
3270
- * record it against a new {@link PLUGIN_DISCLOSURE_CONTRACT} — which is what forces the SDK release
4166
+ * record it against a new {@link PLUGIN_DISCLOSURE_CONTRACT} - which is what forces the SDK release
3271
4167
  * that carries the new rule to the authors it now judges (#3210).
3272
4168
  *
3273
4169
  * Typing {@link DisclosureViolation.rule} as this union is the half that cannot be forgotten: a
@@ -3275,32 +4171,15 @@ export declare const PLUGIN_DISCLOSURE_CONTRACT = 2;
3275
4171
  */
3276
4172
  export declare const PLUGIN_DISCLOSURE_RULES: readonly ["agent-hint-missing", "agent-hint-summary-empty", "agent-hint-flows-empty", "agent-hint-triggers-missing", "scheme-claim-missing", "help-missing", "help-malformed", "help-throws", "help-uri-malformed", "help-uri-throws", "verb-coverage-missing", "template-operation-docs-missing", "verb-docs-missing", "template-accepts-missing", "template-address-unmatchable", "template-delete-effect-missing", "template-operations-missing", "template-parameter-unknown", "template-scheme-mismatch", "clamp-summary", "clamp-flow-label", "clamp-flow-when", "clamp-flow-example", "clamp-trigger-count", "clamp-trigger-item", "clamp-help-uri", "skill-link-dangling", "skill-link-missing-scheme", "skill-twin-count-mismatch", "skill-twin-missing-declaration", "skill-twin-description-mismatch", "skill-twin-file-unreadable", "skill-twin-frontmatter-name-mismatch", "skill-twin-frontmatter-description-mismatch", "skill-twin-body-mismatch"];
3277
4173
 
3278
- declare interface PluginApiKeyScheme {
3279
- readonly id: string;
3280
- /** Human-facing flow name; distinguish the key's purpose when sibling flows exist. */
3281
- readonly displayName: string;
3282
- /** What this flow unlocks and when to choose it over another. */
3283
- readonly agentHint?: PluginSchemeAgentHint;
3284
- /** What this flow unlocks, for the person: one plain sentence, no identifiers or URIs. */
3285
- readonly summary?: string;
3286
- readonly inputLabel: string;
3287
- /**
3288
- * Two or more labeled inputs for an irreducibly multi-part secret (e.g. a Cloudflare Access
3289
- * service token's client-id + client-secret pair). The connect prompt collects one value per
3290
- * field; handlers read them from `ctx.auth.credential.fields`, keyed by field id. Index 0 is
3291
- * the primary field (mirrored as `accessToken`). Absent means the classic single-secret prompt;
3292
- * when present, ids must be unique and non-empty and there must be at least two entries — the
3293
- * kernel rejects an invalid declaration at activation.
3294
- */
3295
- readonly fields?: readonly PluginApiKeySchemeField[];
3296
- readonly setup?: PluginApiKeySchemeSetup;
3297
- readonly validate?: ValidatePluginApiKeyCredential;
4174
+ /**
4175
+ * How a plugin learns which provider account a fresh OAuth token belongs to: one account, or every
4176
+ * account the grant reaches. Registered with `api.auth.registerAccountResolver`.
4177
+ */
4178
+ declare interface PluginAccountResolver {
4179
+ readonly resolveAccountId?: ResolvePluginAccountId;
4180
+ readonly resolveAccounts?: ResolvePluginAccounts;
3298
4181
  }
3299
4182
 
3300
- declare type PluginApiKeySchemeField = ApiKeyAuthSchemeField;
3301
-
3302
- declare type PluginApiKeySchemeSetup = ApiKeyAuthSchemeSetup;
3303
-
3304
4183
  /** The kinds of capability a plugin service can advertise (mirrors the host capability taxonomy). */
3305
4184
  declare type PluginCapabilityKind = "uri-module" | "tool" | "search-provider";
3306
4185
 
@@ -3317,12 +4196,12 @@ declare interface PluginDelegatedRevokeContext {
3317
4196
  }
3318
4197
 
3319
4198
  /**
3320
- * A protocol-agnostic scheme whose connect/refresh choreography the plugin provides — the host owns
4199
+ * A protocol-agnostic scheme whose connect/refresh choreography the plugin provides - the host owns
3321
4200
  * only vault persistence, gating, and single-flight refresh over the opaque material. Use this when
3322
4201
  * the auth dance is not a fixed OAuth-PKCE shape the host can drive itself (e.g. a runtime-discovered
3323
- * authorization server: 401 sniff → RFC 9728/8414 discovery → RFC 7591 dynamic client registration →
4202
+ * authorization server: 401 sniff -> RFC 9728/8414 discovery -> RFC 7591 dynamic client registration ->
3324
4203
  * PKCE). `connect` receives the host's browser-redirect capability (`ctx.redirectUri`/`ctx.authorize`)
3325
- * when one is configured, and both are absent when the host has none — the scheme decides whether it
4204
+ * when one is configured, and both are absent when the host has none - the scheme decides whether it
3326
4205
  * needs them and fails its connect with an actionable message if it does and they are missing.
3327
4206
  */
3328
4207
  declare interface PluginDelegatedScheme {
@@ -3375,83 +4254,18 @@ declare const PluginEventPublishInput: z.ZodObject<{
3375
4254
  poller: z.ZodOptional<z.ZodString>;
3376
4255
  runId: z.ZodOptional<z.ZodString>;
3377
4256
  }, z.core.$strict>>;
3378
- payload: z.ZodOptional<z.ZodType<JsonValue_2, unknown, z.core.$ZodTypeInternals<JsonValue_2, unknown>>>;
4257
+ payload: z.ZodOptional<z.ZodType<authoringCommonSchemas.JsonValue, unknown, z.core.$ZodTypeInternals<authoringCommonSchemas.JsonValue, unknown>>>;
3379
4258
  }, z.core.$strict>;
3380
4259
 
3381
4260
  declare type PluginEventPublishInput = z.infer<typeof PluginEventPublishInput>;
3382
4261
 
3383
- /**
3384
- * An external-CLI readiness scheme the plugin declares: a non-injectable marker for a
3385
- * host-probed local binary (e.g. `osascript`, `gh`). Connecting runs `binary probeArgs`
3386
- * through the host's bounded spawn — success mints a readiness record, never token
3387
- * material. Verb handlers gated on such a scheme receive `ctx.auth.externalCli`, a
3388
- * host-owned runner pinned to the declared binary; the plugin itself never spawns.
3389
- * The manifest twin must declare the binary under `permissions.byTarget["agent.harness"].process`.
3390
- */
3391
- declare interface PluginExternalCliScheme {
3392
- readonly id: string;
3393
- readonly displayName: string;
3394
- /** What this flow unlocks and when to choose it over another. */
3395
- readonly agentHint?: PluginSchemeAgentHint;
3396
- /** What this flow unlocks, for the person: one plain sentence, no identifiers or URIs. */
3397
- readonly summary?: string;
3398
- /** The local binary the host probes and runs (bare name resolved on PATH, or absolute). */
3399
- readonly binary: string;
3400
- /** Argv for the readiness probe; exit 0 means connected. */
3401
- readonly probeArgs: readonly string[];
3402
- /** Optional argv whose stdout is recorded as the CLI version on connect. */
3403
- readonly versionArgs?: readonly string[];
3404
- /** Probe/run timeout; the host default applies when absent. */
3405
- readonly timeoutMs?: number;
3406
- /** Actionable copy when the binary is missing (install path, platform requirement). */
3407
- readonly installHint: string;
3408
- /** Actionable copy when the probe fails (login step, OS permission grant). */
3409
- readonly loginHint: string;
3410
- }
4262
+ declare const PluginId: z.ZodString;
3411
4263
 
3412
- /**
3413
- * An OAuth2-PKCE scheme the plugin declares. The host owns acquisition, refresh, and broker
3414
- * routing; the plugin supplies only public metadata plus an optional account-id resolver.
3415
- *
3416
- * Intersects {@link PluginOAuthSchemeClientAxes} (BYOOC — bring-your-own-OAuth-client):
3417
- * `clientId`/`clientSecret` are optional and mean ONLY "the Kay-supplied embedded client" —
3418
- * present iff `exchange: "pkce"` and the host ships one. A `confidential` scheme never carries
3419
- * them; that secret lives with the host's broker, never with the plugin.
3420
- */
3421
- declare type PluginOAuthPkceScheme = PluginOAuthSchemeClientAxes & {
3422
- readonly id: string;
3423
- readonly displayName: string;
3424
- /** What this flow unlocks and when to choose it over another. */
3425
- readonly agentHint?: PluginSchemeAgentHint;
3426
- /** What this flow unlocks, for the person: one plain sentence, no identifiers or URIs. */
3427
- readonly summary?: string;
3428
- readonly authorizationUrl: string;
3429
- readonly tokenUrl: string;
3430
- /** The Bonsai-supplied embedded client id; omit for a `confidential` scheme or a `pkce` scheme
3431
- * the host ships no client for. */
3432
- readonly clientId?: string;
3433
- /**
3434
- * An embedded, **non-confidential** client secret for a broker-free PKCE scheme — the case where a
3435
- * desktop-app OAuth client requires `client_secret` at the token endpoint but the secret is public
3436
- * by design (e.g. Google installed-app clients). Omit for brokered schemes, where the confidential
3437
- * secret lives in the broker and never reaches the plugin. A scheme is broker-free purely by virtue
3438
- * of its `id` not being in the host's brokered-scheme map; supplying this never routes it anywhere.
3439
- */
3440
- readonly clientSecret?: string;
3441
- readonly scopes: readonly string[];
3442
- /** Extra params merged into the authorize query. */
3443
- readonly extraAuthorizeParams?: Readonly<Record<string, string>>;
3444
- readonly resolveAccountId?: ResolvePluginAccountId;
3445
- /**
3446
- * Enumerate every provider account a fresh OAuth grant powers. Present only for providers whose
3447
- * consent can grant multiple accounts; the host commits one account binding per result while
3448
- * keeping one shared token lifecycle.
3449
- */
3450
- readonly resolveAccounts?: ResolvePluginAccounts;
3451
- };
4264
+ declare type PluginId = z.infer<typeof PluginId>;
3452
4265
 
3453
- /** BYOOC — the herd canonical client-axes union, aliased (never copied) for plugin authors. */
3454
- declare type PluginOAuthSchemeClientAxes = OAuthSchemeClientAxes;
4266
+ declare const PluginNamespace: z.ZodString;
4267
+
4268
+ declare type PluginNamespace = z.infer<typeof PluginNamespace>;
3455
4269
 
3456
4270
  /** What one connection flow unlocks and when an agent should choose it; grants no authority. */
3457
4271
  declare type PluginSchemeAgentHint = string;
@@ -3463,7 +4277,7 @@ declare interface PluginServiceCapability {
3463
4277
  readonly summary: string;
3464
4278
  }
3465
4279
 
3466
- /** A service the plugin owns — advertised for discovery and used to gate auth-backed contributions. */
4280
+ /** A service the plugin owns - advertised for discovery and used to gate auth-backed contributions. */
3467
4281
  declare interface PluginServiceDescriptor {
3468
4282
  readonly id: string;
3469
4283
  readonly display: string;
@@ -3473,7 +4287,7 @@ declare interface PluginServiceDescriptor {
3473
4287
 
3474
4288
  /**
3475
4289
  * A skill the plugin ships, content included. The manifest *declares* the skill (a
3476
- * `SkillContribution` naming the in-package SKILL.md); this is the runtime half — bundled
4290
+ * `SkillContribution` naming the in-package SKILL.md); this is the runtime half - bundled
3477
4291
  * plugins are compiled in with no package directory at runtime, so the package exports the
3478
4292
  * instructions as data beside the file and an in-package equivalence test pins the two
3479
4293
  * together (the same pattern as the manifest icon). `name` must equal the contribution's
@@ -3486,10 +4300,32 @@ declare interface PluginSkillDefinition {
3486
4300
  readonly name: string;
3487
4301
  /** Model-visible one-liner: when to reach for this skill. */
3488
4302
  readonly description: string;
3489
- /** The full instructions — what a filesystem skill carries below its frontmatter. */
4303
+ /** The full instructions - what a filesystem skill carries below its frontmatter. */
3490
4304
  readonly content: string;
3491
4305
  }
3492
4306
 
4307
+ declare const PluginSourceClass: z.ZodEnum<{
4308
+ bundled: "bundled";
4309
+ "org-shared": "org-shared";
4310
+ "user-local": "user-local";
4311
+ "verified-marketplace": "verified-marketplace";
4312
+ freeform: "freeform";
4313
+ }>;
4314
+
4315
+ declare type PluginSourceClass = z.infer<typeof PluginSourceClass>;
4316
+
4317
+ declare const PluginTargetName: z.ZodEnum<{
4318
+ "kay.agent": "kay.agent";
4319
+ "kay.app": "kay.app";
4320
+ "kay.ui": "kay.ui";
4321
+ }>;
4322
+
4323
+ declare type PluginTargetName = z.infer<typeof PluginTargetName>;
4324
+
4325
+ declare const PluginToolName: z.ZodString;
4326
+
4327
+ declare type PluginToolName = z.infer<typeof PluginToolName>;
4328
+
3493
4329
  declare const Properties: (input: string) => [unknown, string] | [];
3494
4330
 
3495
4331
  declare function PropertiesMapping(input: [unknown, unknown, unknown]): unknown;
@@ -3543,7 +4379,7 @@ declare type ProviderId = KnownProvider | string;
3543
4379
  declare interface ProxiedToolCall {
3544
4380
  /** The registered tool the carrier will invoke. */
3545
4381
  readonly toolName: string;
3546
- /** The input that tool will be invoked with — what the gate must classify. */
4382
+ /** The input that tool will be invoked with - what the gate must classify. */
3547
4383
  readonly input: Record<string, unknown>;
3548
4384
  }
3549
4385
 
@@ -3553,6 +4389,28 @@ declare interface RawResource {
3553
4389
  readonly mimeType: string;
3554
4390
  readonly metadata?: RawResourceMetadata;
3555
4391
  readonly selector?: SelectorDisposition;
4392
+ /**
4393
+ * The indexable forms of this resource, built from the same fetch as
4394
+ * `content`: one per document the read wants indexed (a thread read offers
4395
+ * one per message). Absent or empty means nothing is indexed. The read tool
4396
+ * takes them off before the cache and the model; other host reads
4397
+ * (out-of-band host reads) return them untouched.
4398
+ */
4399
+ readonly observations?: readonly ObservationMaterial[];
4400
+ /** How long the host may cache this result; see {@link UriCachePolicy.ttlMs}. */
4401
+ readonly cachePolicy?: Pick<UriCachePolicy, "ttlMs">;
4402
+ /**
4403
+ * A short name for the resource this call touched, for the transcript row; absent shows the
4404
+ * address. Render-only: the host stamps it on the tool result's `details`, never in `content`.
4405
+ */
4406
+ readonly label?: UriResourceLabel;
4407
+ /**
4408
+ * Answer to a `read` called with `ctx.knownRevision`: the module checked the source's current
4409
+ * revision and it equals `knownRevision`, so `content` is empty and `metadata.revision` equals
4410
+ * `knownRevision`. Set it only when that check was made and matched; a module that cannot
4411
+ * check cheaply, or that found a different revision, returns the full resource without it.
4412
+ */
4413
+ readonly unchanged?: true;
3556
4414
  }
3557
4415
 
3558
4416
  declare interface RawResourceCacheControl {
@@ -3596,7 +4454,7 @@ declare interface RawResourceMetadata {
3596
4454
  readonly resourceIdentity?: RawResourceIdentity;
3597
4455
  /**
3598
4456
  * The resource's stable public address when `uri` reports something more
3599
- * specific than the address a consumer names it by — a retained revision
4457
+ * specific than the address a consumer names it by - a retained revision
3600
4458
  * (`skill://<id>/@revision/<version>`) served for a `skill://<source>/<name>`
3601
4459
  * read. Present only when the two differ. Consumers that attribute by name
3602
4460
  * (the read tool's `skillName` detail, and everything keyed off it) read
@@ -3671,6 +4529,29 @@ declare function Refine<Type extends TSchema, Value = Static<Type>>(type: Type,
3671
4529
  /** Applies a Refine check to the given type. */
3672
4530
  declare function RefineAdd<Type extends TSchema>(type: Type, refinement: TRefinement<Type>): TRefineAdd<Type>;
3673
4531
 
4532
+ /**
4533
+ * The web-session scope-confinement refinement, coined ONCE and shared by both
4534
+ * web-session boundaries: `loginUrl` and `probe.url` must each fall within a
4535
+ * declared `cookieDomains` entry. The manifest boundary
4536
+ * (`PluginWebSessionDeclaration`) enforces it because those URLs are
4537
+ * authenticated-navigation/request primitives a third-party manifest hands the
4538
+ * host; the resolved/wire boundary (`ResolvedPluginWebSession`) re-runs the SAME
4539
+ * check rather than trust it survived resolution (its parse is the surface's
4540
+ * only validation against a skewed daemon). Two copies of the check itself would
4541
+ * be one edit away from disagreeing across those hops.
4542
+ */
4543
+ declare function refineWebSessionScopeConfined(webSession: {
4544
+ readonly cookieDomains: readonly string[];
4545
+ readonly loginUrl: string;
4546
+ readonly probe: {
4547
+ readonly url: string;
4548
+ };
4549
+ }, ctx: z.RefinementCtx): void;
4550
+
4551
+ declare const RegistrableDomain: z.ZodString;
4552
+
4553
+ declare type RegistrableDomain = z.infer<typeof RegistrableDomain>;
4554
+
3674
4555
  /** Applies an RemoveImmutable action to a type. */
3675
4556
  declare function RemoveImmutable<Type extends TSchema>(type: Type, options?: TSchemaOptions): TRemoveImmutable<Type>;
3676
4557
 
@@ -3701,7 +4582,7 @@ declare function RequiredDeferred<Type extends TSchema>(type: Type, options?: TS
3701
4582
  declare type RequiredKeys<Properties extends TProperties, Result extends PropertyKey = keyof Omit<Properties, ReadonlyOptionalKeys<Properties> | ReadonlyKeys<Properties> | OptionalKeys<Properties>>> = Result;
3702
4583
 
3703
4584
  /**
3704
- * The capability⇄service auth binding. A registration carrying this is gated on `serviceId`'s
4585
+ * The capability<->service auth binding. A registration carrying this is gated on `serviceId`'s
3705
4586
  * connectivity; the runtime answers "is this capability authorized?" from this plus vault state.
3706
4587
  */
3707
4588
  declare interface RequiresAuth {
@@ -3709,7 +4590,7 @@ declare interface RequiresAuth {
3709
4590
  /**
3710
4591
  * Optional scheme preference. Absent means the binding is service-level: any injectable scheme
3711
4592
  * of the service satisfies it, and resolution picks each account's own backing scheme. Present,
3712
- * it names the scheme resolution tries first — an account connected under a sibling injectable
4593
+ * it names the scheme resolution tries first - an account connected under a sibling injectable
3713
4594
  * scheme still satisfies the binding and resolves against its own scheme. A non-injectable
3714
4595
  * capability (`external-cli`) MUST name its scheme: it is a scheme-exact island.
3715
4596
  */
@@ -3717,7 +4598,7 @@ declare interface RequiresAuth {
3717
4598
  /**
3718
4599
  * The scopes this capability needs to do its job. Threaded through `resolveToken` into the
3719
4600
  * vault load so a grant-enforcing vault (the daemon's authority-backed vault) can refuse a
3720
- * plugin whose granted scopes don't cover them (`scopesCovered`) — per call, per plugin. Absent
4601
+ * plugin whose granted scopes don't cover them (`scopesCovered`) - per call, per plugin. Absent
3721
4602
  * means the capability declares no scope requirement (the single-user local path never checks
3722
4603
  * scopes; it is a daemon per-plugin enforcement input). Also read by the scope-aware half of
3723
4604
  * `isBindingSatisfied` when the host supplies a granted-scopes reader.
@@ -3729,11 +4610,11 @@ declare interface RequiresAuth {
3729
4610
  */
3730
4611
  readonly scopeRequirement?: ScopeRequirement;
3731
4612
  /**
3732
- * The service id to NAME in connect guidance when this capability is gated — a host-composed
4613
+ * The service id to NAME in connect guidance when this capability is gated - a host-composed
3733
4614
  * FRONT DOOR whose `service_connect` grants exactly what this binding needs (e.g. Gmail's
3734
4615
  * modules resolve under the shared `google.account` credential but connect via the `gmail`
3735
4616
  * front door, which expands only Gmail's scopes onto it). Guidance only: credential resolution
3736
- * still keys on `(serviceId, schemeId)`. Absent, guidance names `serviceId` — which for a
4617
+ * still keys on `(serviceId, schemeId)`. Absent, guidance names `serviceId` - which for a
3737
4618
  * scoped binding on a shared multi-product credential would send the agent to a connect that
3738
4619
  * classifies resolved and no-ops, never granting the missing scopes.
3739
4620
  */
@@ -3741,38 +4622,26 @@ declare interface RequiresAuth {
3741
4622
  }
3742
4623
 
3743
4624
  /**
3744
- * What the resolver hands a tool body: a live token for a concrete account + identity.
3745
- * `accessToken` is `apiKey` for an api-key scheme or the (refreshed) bearer for oauth.
4625
+ * The credential the host resolved for one call to plugin code that declares `requiresAuth`: a URI
4626
+ * verb (`ctx.auth.credential`), a tool (`ctx.credential`) or a search provider (`ctx.credential`).
4627
+ * Plain data, so it crosses a process boundary with the call; plugin code never asks for one.
3746
4628
  */
3747
- declare interface ResolvedAuth {
4629
+ declare interface ResolvedCredential {
4630
+ /** Bearer for OAuth schemes, apiKey for api-key schemes. Host-resolved and refreshed. */
3748
4631
  readonly accessToken: string;
3749
- readonly account: AuthAccount;
3750
- readonly identityMode: IdentityMode;
4632
+ readonly accountId: string;
3751
4633
  readonly schemeId: string;
3752
4634
  readonly scopes?: readonly string[];
3753
4635
  /**
3754
- * Every value of a multi-field api-key credential, keyed by the scheme's field id;
3755
- * `accessToken` mirrors the primary (first) field. Absent for single-secret schemes.
4636
+ * Every value of a multi-field api-key credential, keyed by the scheme's declared field id
4637
+ * (`accessToken` mirrors the primary field). Absent for single-secret schemes.
3756
4638
  */
3757
4639
  readonly fields?: Readonly<Record<string, string>>;
3758
4640
  }
3759
4641
 
3760
- declare type ResolveExternalCliFailureReason = "not-external-cli" | "no-connection" | "account-ambiguous" | "account-not-found" | "not-authorized" | "host-unavailable";
3761
-
3762
- declare type ResolveExternalCliResult = {
3763
- readonly ok: true;
3764
- readonly account: AuthAccount;
3765
- readonly runner: UriExternalCliRunner;
3766
- } | {
3767
- readonly ok: false;
3768
- readonly reason: ResolveExternalCliFailureReason;
3769
- readonly message: string;
3770
- readonly accounts?: readonly AuthAccount[];
3771
- };
3772
-
3773
4642
  /**
3774
4643
  * Derive a stable provider account id (and optional label) from a freshly connected access token,
3775
- * plus — when known — who the account belongs to.
4644
+ * plus - when known - who the account belongs to.
3776
4645
  *
3777
4646
  * A resolver almost always calls the provider's profile endpoint to derive `accountId` at all, so
3778
4647
  * the identity fields are already in the response: return them rather than flattening the person
@@ -3792,25 +4661,6 @@ declare type ResolvePluginAccounts = (accessToken: string) => Promise<readonly {
3792
4661
  readonly identity?: AccountIdentity;
3793
4662
  }[]>;
3794
4663
 
3795
- declare interface ResolveTokenFailure {
3796
- readonly ok: false;
3797
- readonly reason: ResolveTokenFailureReason;
3798
- /** Actionable text the wrapper surfaces verbatim to the agent. */
3799
- readonly message: string;
3800
- /** When ambiguous, the candidate accounts so the agent can pick one. */
3801
- readonly accounts?: readonly AuthAccount[];
3802
- }
3803
-
3804
- /** Reason a token could not be resolved, with a short agent-facing message. */
3805
- declare type ResolveTokenFailureReason = "no-connection" | "account-ambiguous" | "account-not-found" | "refresh-failed" | "not-authorized";
3806
-
3807
- declare type ResolveTokenResult = ResolveTokenSuccess | ResolveTokenFailure;
3808
-
3809
- declare interface ResolveTokenSuccess {
3810
- readonly ok: true;
3811
- readonly auth: ResolvedAuth;
3812
- }
3813
-
3814
4664
  /** Match remaining characters in the buffer until end. If no characters are in buffer, no match */
3815
4665
  declare function Rest<Input extends string>(input: Input): TRest<Input>;
3816
4666
 
@@ -3824,7 +4674,7 @@ declare function ReturnType_2<Type extends TSchema>(type: Type, options?: TSchem
3824
4674
  declare function ReturnTypeDeferred<Type extends TSchema>(type: Type, options?: TSchemaOptions): TReturnTypeDeferred<Type>;
3825
4675
 
3826
4676
  /** Observed cause of a failed request or interrupted run. */
3827
- declare const RUN_FAILURE_REASONS: readonly ["auth", "cancelled", "content_blocked", "context_overflow", "invalid_request", "model_disabled", "model_unavailable", "network", "organization_unavailable", "overloaded", "quota_exhausted", "rate_limit", "runtime_interrupted", "safeguard_block", "seat_unpaid", "server_error", "sign_in_required", "stream_interrupted", "timeout", "unknown"];
4677
+ declare const RUN_FAILURE_REASONS: readonly ["account_unreachable", "auth", "cancelled", "content_blocked", "context_overflow", "final_output_missing", "invalid_request", "model_disabled", "model_unavailable", "network", "organization_unavailable", "overloaded", "quota_exhausted", "rate_limit", "runtime_interrupted", "safeguard_block", "seat_unpaid", "server_error", "sign_in_required", "stream_interrupted", "timeout", "unknown"];
3828
4678
 
3829
4679
  declare type RunFailureReason = (typeof RUN_FAILURE_REASONS)[number];
3830
4680
 
@@ -4250,7 +5100,7 @@ declare interface SearchParams {
4250
5100
  readonly query: string;
4251
5101
  /**
4252
5102
  * Pure time filter narrowing results to a window. Providers MUST treat this as
4253
- * a time filter only — never as a signal to change topic scope, content domain,
5103
+ * a time filter only - never as a signal to change topic scope, content domain,
4254
5104
  * or ranking (oh-my-pi's Tavily `topic=news` coupling silently dropped docs).
4255
5105
  * Providers without temporal filtering MUST ignore it silently.
4256
5106
  */
@@ -4264,7 +5114,7 @@ declare interface SearchParams {
4264
5114
  /**
4265
5115
  * A web search backend behind the single `web_search` tool. Directly analogous
4266
5116
  * to a `UriModule` behind `read`/`write`: registered through `ExtensionApi`,
4267
- * merged per session, dispatched by the registry. Stateless and shareable —
5117
+ * merged per session, dispatched by the registry. Stateless and shareable -
4268
5118
  * per-call context (the abort signal and the {@link SearchProviderContext}) is
4269
5119
  * threaded into `search` and `isAvailable`.
4270
5120
  */
@@ -4280,17 +5130,18 @@ declare abstract class SearchProvider {
4280
5130
  */
4281
5131
  abstract readonly providerId: string;
4282
5132
  /**
4283
- * The capability⇄scheme binding when this provider resolves its OWN credential through the
5133
+ * The capability<->scheme binding when this provider resolves its OWN credential through the
4284
5134
  * service-extension auth system (stamped by {@link withResolvedSearchProvider}). `undefined`
4285
- * for providers that reuse inference credentials (the builtins) or need no auth. The runner
4286
- * reads it to surface the provider in `auth_status` as gated-until-connected, parallel to a
4287
- * tool's or URI module's `requiresAuth`.
5135
+ * for providers that reuse inference credentials (the builtins) or need no auth. When set, the
5136
+ * host resolves the credential onto {@link SearchProviderContext.credential} before it asks or
5137
+ * calls the provider, and `auth_status` lists the provider as gated-until-connected, parallel to
5138
+ * a tool's or URI module's `requiresAuth`.
4288
5139
  */
4289
5140
  readonly requiresAuth?: RequiresAuth;
4290
5141
  /**
4291
- * Cheap availability check (typically: does a usable credential resolve?). An own-credential
4292
- * provider returns `false` until its auth scheme has a connected account, so the chain skips
4293
- * it gracefully — the gating counterpart to a tool/module's discoverable-but-disallowed state.
5142
+ * Cheap availability check. The host never asks a provider with `requiresAuth` whose credential
5143
+ * did not resolve: it is unavailable, so the chain skips it - the gating counterpart to a
5144
+ * tool/module's discoverable-but-disallowed state.
4294
5145
  */
4295
5146
  abstract isAvailable(ctx?: SearchProviderContext): Promise<boolean> | boolean;
4296
5147
  /** Run the search. Throw {@link SearchProviderError} on a provider failure. */
@@ -4299,15 +5150,16 @@ declare abstract class SearchProvider {
4299
5150
 
4300
5151
  /**
4301
5152
  * Per-call context threaded into a provider's {@link SearchProvider.isAvailable} and
4302
- * {@link SearchProvider.search}. Carries the execute-time auth surface — the SAME
4303
- * `ctx.auth` a tool or URI module receives — scoped to the provider's OWNING extension,
4304
- * so a provider that declares its own credential (an api-key/oauth scheme via the
4305
- * service-extension auth system) can resolve a token. `auth` is absent for builtins
4306
- * (no owning extension; they reuse inference credentials through the host model execution capability)
4307
- * and outside a live session.
5153
+ * {@link SearchProvider.search}.
4308
5154
  */
4309
5155
  declare interface SearchProviderContext {
4310
- readonly auth?: ExtensionAuthContext;
5156
+ /**
5157
+ * The credential the host resolved for a provider that declares
5158
+ * {@link SearchProvider.requiresAuth}, for the session-active account. The host neither asks
5159
+ * nor calls such a provider when none resolves. Absent for a provider without `requiresAuth`
5160
+ * (builtins reuse inference credentials through the host model execution capability).
5161
+ */
5162
+ readonly credential?: ResolvedCredential;
4311
5163
  }
4312
5164
 
4313
5165
  /** Registry id of a search provider, e.g. `builtin:anthropic`. Open-ended for extensions. */
@@ -4366,19 +5218,19 @@ declare type SelectorDisposition = {
4366
5218
  * rebuilds it on each `AgentSession.reroot` (`@herd/herd-agent`) (the only event that
4367
5219
  * moves `cwd`), then evaluates every tool's predicate against it exactly once per change.
4368
5220
  *
4369
- * **CLOSED-CONTEXT INVARIANT (load-bearing — do not violate when extending this type).** This object
4370
- * carries ONLY primary facts that are *independent of tool availability* — `cwd`, `repoRoot`, and the
5221
+ * **CLOSED-CONTEXT INVARIANT (load-bearing - do not violate when extending this type).** This object
5222
+ * carries ONLY primary facts that are *independent of tool availability* - `cwd`, `repoRoot`, and the
4371
5223
  * planned `mode`/`platform`. It MUST NEVER carry the tool list, the active/effective set, or anything
4372
5224
  * else derived from availability. If a predicate's input could depend on another predicate's output,
4373
5225
  * evaluation would have to reach a fixpoint and could cycle. Because the context is closed, the kernel
4374
5226
  * evaluates each predicate ONCE per context change (one pass, no fixpoint) and caches the result. Keep
4375
5227
  * every future field in this same closed class so the single-pass guarantee survives.
4376
5228
  *
4377
- * Predicates reading this MUST be **pure and synchronous** — same context in ⇒ same answer out, no I/O,
5229
+ * Predicates reading this MUST be **pure and synchronous** - same context in => same answer out, no I/O,
4378
5230
  * no side effects. The host resolves the VCS facts (`repoRoot`); the predicate only inspects them.
4379
5231
  */
4380
5232
  declare interface SessionContext {
4381
- /** The session's current working directory (the live, re-rootable cwd — not the launch dir). */
5233
+ /** The session's current working directory (the live, re-rootable cwd - not the launch dir). */
4382
5234
  readonly cwd: string;
4383
5235
  /**
4384
5236
  * The enclosing VCS repository root for {@link cwd}, or `null` when `cwd` is not inside a repo.
@@ -4388,6 +5240,10 @@ declare interface SessionContext {
4388
5240
  readonly repoRoot: string | null;
4389
5241
  }
4390
5242
 
5243
+ declare const SignedInStatusCode: z.ZodNumber;
5244
+
5245
+ declare type SignedInStatusCode = z.infer<typeof SignedInStatusCode>;
5246
+
4391
5247
  /** Matches from Start and End capturing everything in-between. Start and End are consumed. */
4392
5248
  declare function Span<Start extends string, End extends string, MultiLine extends boolean, Input extends string>(start: Start, end: End, multiLine: MultiLine, input: Input): TSpan<Start, End, MultiLine, Input>;
4393
5249
 
@@ -4500,12 +5356,25 @@ declare const StringPattern = ".*";
4500
5356
  /** Creates a Symbol type. */
4501
5357
  declare function Symbol_2(options?: TSchemaOptions): TSymbol;
4502
5358
 
5359
+ /** Structurally the plugin SDK's `KayAppSessionEventCause`: only the fields the host reads. */
5360
+ declare interface SystemOneCallCause {
5361
+ readonly kind?: "session-event" | "backfill";
5362
+ readonly handler: string;
5363
+ readonly sourceSessionId: string;
5364
+ }
5365
+
4503
5366
  declare interface SystemOneCallOptions {
4504
5367
  readonly signal?: AbortSignal;
4505
5368
  /** Bounds the whole call, retries included. */
4506
5369
  readonly timeoutMs?: number;
4507
5370
  /** What the call is for, carried to the host's usage accounting. Never sent to a provider. */
4508
5371
  readonly purpose?: string;
5372
+ /**
5373
+ * The session event a plugin's call answers, so the host charges the call to that session.
5374
+ * The host checks the plugin is handling that session's event right now. Never sent to a
5375
+ * provider.
5376
+ */
5377
+ readonly cause?: SystemOneCallCause;
4509
5378
  }
4510
5379
 
4511
5380
  declare interface SystemOneClient {
@@ -4959,6 +5828,13 @@ declare type TApplyReadonly<Readonly extends TModifierOperation, Type extends T.
4959
5828
 
4960
5829
  declare type TApplyReverse<Types extends TSchema[], Reversed extends boolean> = Reversed extends true ? TReverse<Types> : Types;
4961
5830
 
5831
+ declare const TargetEntrypoint: z.ZodObject<{
5832
+ module: z.ZodString;
5833
+ export: z.ZodOptional<z.ZodString>;
5834
+ }, z.core.$strict>;
5835
+
5836
+ declare type TargetEntrypoint = z.infer<typeof TargetEntrypoint>;
5837
+
4962
5838
  /** Represents an Array type. */
4963
5839
  declare interface TArray<Type extends TSchema = TSchema> extends TSchema {
4964
5840
  '~kind': 'Array';
@@ -6722,7 +7598,7 @@ declare type TOmitInstantiate<Context extends TProperties, State extends TState,
6722
7598
  * classification (the engine records the rejection on an unexplained failure),
6723
7599
  * and a stored record with one reads as none.
6724
7600
  */
6725
- declare const TOOL_CALL_FAILURE_CODES: readonly ["tool.invalid_input", "tool.blocked", "tool.cancelled", "tool.timeout", "tool.not_found", "tool.unavailable", "tool.execution_failed", "provider.http", "provider.api", "provider.transport", "uri.not_found", "uri.unsupported_operation", "uri.not_connected", "uri.auth_unavailable", "permission.denied", "edit.empty_edits", "edit.text_not_found", "edit.text_not_unique", "edit.empty_old_text", "edit.overlapping_edits", "edit.no_change"];
7601
+ declare const TOOL_CALL_FAILURE_CODES: readonly ["tool.invalid_input", "tool.blocked", "tool.cancelled", "tool.timeout", "tool.not_found", "tool.unavailable", "tool.execution_failed", "tool.auth_unavailable", "tool.replies_owed", "provider.http", "provider.api", "provider.transport", "uri.not_found", "uri.unsupported_operation", "uri.not_connected", "uri.auth_unavailable", "permission.denied", "edit.empty_edits", "edit.text_not_found", "edit.text_not_unique", "edit.empty_old_text", "edit.overlapping_edits", "edit.no_change"];
6726
7602
 
6727
7603
  /**
6728
7604
  * ---
@@ -6740,14 +7616,14 @@ declare type ToolActionKind = "read" | "mutate" | "destructive" | "execute";
6740
7616
  * A tool's declarative availability gate. `when` is the authoritative predicate the kernel evaluates
6741
7617
  * against the live {@link SessionContext} to decide whether the tool is offered to the model here;
6742
7618
  * `description` is the always-shown, human-readable "why" a UI renders next to a tool the predicate
6743
- * disables (e.g. greyed on the new-session page). The predicate runs in the main process only — a
7619
+ * disables (e.g. greyed on the new-session page). The predicate runs in the main process only - a
6744
7620
  * renderer receives just the resolved `available: boolean` plus this `description`.
6745
7621
  *
6746
7622
  * `when` MUST be pure and synchronous (see {@link SessionContext}); a throw is caught by the kernel,
6747
- * treated as "unavailable", and surfaced as a session diagnostic — it never crashes the tool sync.
7623
+ * treated as "unavailable", and surfaced as a session diagnostic - it never crashes the tool sync.
6748
7624
  */
6749
7625
  declare interface ToolAvailability {
6750
- /** Pure, synchronous gate. `true` ⇒ the tool may be offered in this context; `false` ⇒ withheld. */
7626
+ /** Pure, synchronous gate. `true` => the tool may be offered in this context; `false` => withheld. */
6751
7627
  when(ctx: SessionContext): boolean;
6752
7628
  /** Human-readable reason the tool is/isn't available, always shown in UI. */
6753
7629
  description: string;
@@ -6775,6 +7651,7 @@ declare const toolCallFailureCauseSchema: z.ZodObject<{
6775
7651
  "not-found": "not-found";
6776
7652
  permission: "permission";
6777
7653
  unavailable: "unavailable";
7654
+ waiting: "waiting";
6778
7655
  }>;
6779
7656
  retryable: z.ZodBoolean;
6780
7657
  action: z.ZodEnum<{
@@ -6783,6 +7660,7 @@ declare const toolCallFailureCauseSchema: z.ZodObject<{
6783
7660
  retry: "retry";
6784
7661
  "correct-input": "correct-input";
6785
7662
  connect: "connect";
7663
+ wait: "wait";
6786
7664
  }>;
6787
7665
  details: z.ZodOptional<z.ZodObject<{
6788
7666
  moduleId: z.ZodOptional<z.ZodString>;
@@ -6791,6 +7669,7 @@ declare const toolCallFailureCauseSchema: z.ZodObject<{
6791
7669
  uri: z.ZodOptional<z.ZodString>;
6792
7670
  path: z.ZodOptional<z.ZodString>;
6793
7671
  validationField: z.ZodOptional<z.ZodString>;
7672
+ validationRule: z.ZodOptional<z.ZodString>;
6794
7673
  errorName: z.ZodOptional<z.ZodString>;
6795
7674
  errorCode: z.ZodOptional<z.ZodString>;
6796
7675
  providerCode: z.ZodOptional<z.ZodString>;
@@ -6805,14 +7684,14 @@ declare type ToolCallFailureCode = (typeof TOOL_CALL_FAILURE_CODES)[number];
6805
7684
 
6806
7685
  /**
6807
7686
  * The neutral around-execution hook: return `next()`'s result (possibly transformed) to run the
6808
- * real tool, or return a result without calling `next()` to substitute one — the tool then never
7687
+ * real tool, or return a result without calling `next()` to substitute one - the tool then never
6809
7688
  * executes. Every downstream consumer (the model, the session log, result clamps, failure
6810
7689
  * attribution) sees whatever is returned exactly as if the tool produced it. Throwing surfaces to
6811
7690
  * the caller as a tool failure, classified like any other.
6812
7691
  *
6813
7692
  * Interceptors compose as middleware: each receives a `next` that runs the REST of the chain, the
6814
7693
  * innermost `next` being the tool itself. `next` may be called at most the caller's own logic
6815
- * demands (zero to short-circuit, once to delegate, more to retry) — the kernel does not police
7694
+ * demands (zero to short-circuit, once to delegate, more to retry) - the kernel does not police
6816
7695
  * arity.
6817
7696
  */
6818
7697
  declare type ToolCallInterceptor = (call: ToolCallInvocation, next: () => Promise<AgentToolResult<unknown>>) => Promise<AgentToolResult<unknown>>;
@@ -6821,7 +7700,7 @@ declare type ToolCallInterceptor = (call: ToolCallInvocation, next: () => Promis
6821
7700
  declare interface ToolCallInvocation {
6822
7701
  readonly toolCallId: string;
6823
7702
  readonly toolName: string;
6824
- /** The call's arguments, already through `prepareArguments` — the shape `execute` receives. */
7703
+ /** The call's arguments, already through `prepareArguments` - the shape `execute` receives. */
6825
7704
  readonly params: unknown;
6826
7705
  readonly signal: AbortSignal | undefined;
6827
7706
  /**
@@ -6840,7 +7719,7 @@ declare interface ToolCallInvocation {
6840
7719
 
6841
7720
  /**
6842
7721
  * The model the call's result is about to be shown to, as the session holds it at execution
6843
- * time: identity plus pi's `input` list — the SAME field pi's adapters read when they build the
7722
+ * time: identity plus pi's `input` list - the SAME field pi's adapters read when they build the
6844
7723
  * next request, so a capability policy keyed on it agrees with what the provider will accept.
6845
7724
  * Absent when the wrap was built without a resolver; a policy must then treat the capability
6846
7725
  * as unknown, not as absent.
@@ -6875,7 +7754,7 @@ declare type ToolCallPermissionVerdict = {
6875
7754
  * What a tool call ADDRESSES, resolved by the session before execution from facts it already
6876
7755
  * holds for auth scoping: a URI verb's matched module (and the extension that registered it), or
6877
7756
  * an extension-contributed tool's owner. `ownerExtensionId` is absent for builtin/app-owned
6878
- * modules and tools — the session owns those, no extension does. A host policy that keys on
7757
+ * modules and tools - the session owns those, no extension does. A host policy that keys on
6879
7758
  * "which integration is this call about" reads this instead of re-parsing `params`.
6880
7759
  */
6881
7760
  declare type ToolCallTarget = {
@@ -6893,15 +7772,15 @@ declare interface ToolContext {
6893
7772
  readonly cwd: string;
6894
7773
  readonly model: Model<any> | undefined;
6895
7774
  /**
6896
- * The execute-time auth resolver the SDK tool wrapper (`withResolvedAuth`) reads to resolve a
6897
- * live token for the active (or per-call-overridden) account. Present when the session is bound
6898
- * to an `ExtensionAuthRuntime` (`@herd/herd-agent`); absent in always-on contexts
6899
- * (the wrapper short-circuits with an actionable message when undefined).
7775
+ * The credential the host resolved for this call, for a tool that declares
7776
+ * {@link ToolDefinition.requiresAuth}: the account named by the call's `accountId` argument, else
7777
+ * the session-active one. The host does not call the tool when none resolves. Absent for a tool
7778
+ * without `requiresAuth`, and outside a live session.
6900
7779
  */
6901
- readonly auth?: ExtensionAuthContext;
7780
+ readonly credential?: ResolvedCredential;
6902
7781
  /**
6903
7782
  * Detached work whose result should re-enter the conversation as a new turn (each completion calls
6904
- * `nextTurn()` and appears in the Background tasks panel). Not for fire-and-forget side effects —
7783
+ * `nextTurn()` and appears in the Background tasks panel). Not for fire-and-forget side effects -
6905
7784
  * use `ExtensionContext.runEffect`. Absent when the host wires no background-task manager.
6906
7785
  */
6907
7786
  readonly backgroundTasks?: ExtensionBackgroundTasksApi;
@@ -6923,9 +7802,9 @@ declare interface ToolContext {
6923
7802
  /**
6924
7803
  * Re-root the session into `newCwd` mid-run, recomposing the cwd-scoped substrate (builtin tools,
6925
7804
  * the capability registry, the system-prompt project line) in place so subsequent tool calls operate
6926
- * there — the conversation continues uninterrupted. This is the agent self-reroot seam: the
7805
+ * there - the conversation continues uninterrupted. This is the agent self-reroot seam: the
6927
7806
  * motivating caller is an app tool (e.g. `isolate_worktree`) that does its git ops then calls
6928
- * `ctx.reroot(worktreePath)` from inside its own `execute`. Safe mid-run — it never gates on idle.
7807
+ * `ctx.reroot(worktreePath)` from inside its own `execute`. Safe mid-run - it never gates on idle.
6929
7808
  * Present only with a live session (absent in session-less inventory/standalone contexts, like
6930
7809
  * {@link auth}). `reason` labels the transition for observability (e.g. `"worktree-isolation"`).
6931
7810
  */
@@ -6966,12 +7845,12 @@ declare interface ToolDefinition<TParams extends ToolParametersWire = ToolParame
6966
7845
  executionMode?: ToolExecutionMode;
6967
7846
  /**
6968
7847
  * How long a single execution may legitimately stay silent (#3785).
6969
- * - `"fast"` (the default when omitted): the execution is bounded — if it
7848
+ * - `"fast"` (the default when omitted): the execution is bounded - if it
6970
7849
  * neither settles nor loses the abort race within the fast-tool deadline
6971
7850
  * (120s), the kernel fails the call with a timeout error the model can
6972
7851
  * react to. Right for local reads/writes and short network calls, which
6973
7852
  * finish in seconds or are stuck.
6974
- * - `"long"`: no deadline — a build under a shell, a parent parked on a
7853
+ * - `"long"`: no deadline - a build under a shell, a parent parked on a
6975
7854
  * child task. The abort race still applies: a user Stop always wins,
6976
7855
  * whether or not the tool honors its `signal`.
6977
7856
  * Declare `"long"` only when unbounded silent execution is genuinely part
@@ -6988,29 +7867,29 @@ declare interface ToolDefinition<TParams extends ToolParametersWire = ToolParame
6988
7867
  */
6989
7868
  replaySafe?: true;
6990
7869
  /**
6991
- * Optional capability⇄scheme binding. When present this tool is auth-gated: discoverable when
6992
- * unauthed, callable once the binding is satisfied. The SDK's `withResolvedAuth`
6993
- * stamps this; absent means an ordinary always-on tool (additive — nothing changes for existing
6994
- * tools that omit it).
7870
+ * Optional capability<->scheme binding. When present this tool is auth-gated: discoverable when
7871
+ * unauthed, callable once the binding is satisfied, and the host resolves its credential onto
7872
+ * {@link ToolContext.credential} before each call. The SDK's `withResolvedAuth` stamps this; absent
7873
+ * means an ordinary always-on tool.
6995
7874
  */
6996
7875
  requiresAuth?: RequiresAuth;
6997
7876
  /**
6998
7877
  * Optional declarative availability gate ({@link ToolAvailability}). When present, the kernel
6999
7878
  * evaluates `availability.when` against the live {@link SessionContext} once per context change
7000
7879
  * (session start + each reroot) and withholds the tool from the model-visible set wherever it
7001
- * returns `false`. Backward-compatible: **absent ⇒ always available**. Orthogonal to
7002
- * mode-disallowed sets and to auth/permission gates — availability answers only "does this tool
7880
+ * returns `false`. Backward-compatible: **absent => always available**. Orthogonal to
7881
+ * mode-disallowed sets and to auth/permission gates - availability answers only "does this tool
7003
7882
  * apply in this context", and composes with the others at the same chokepoint.
7004
7883
  */
7005
7884
  availability?: ToolAvailability;
7006
7885
  /**
7007
7886
  * Optional declarative permission classification ({@link ToolPermissionHint}). Consulted by
7008
7887
  * the permission gate ONLY for tools the classifier does not otherwise recognize (extension/app
7009
- * tools), letting a tool relax the fail-closed `execute` fallback (a pure staging tool → `read`),
7888
+ * tools), letting a tool relax the fail-closed `execute` fallback (a pure staging tool -> `read`),
7010
7889
  * declare its provider `scheme`, derive the `resources` a call touches (a browser
7011
7890
  * navigation's target origin) so per-provider and per-origin rules can gate it.
7012
7891
  * Because it lives on the definition it is
7013
- * provenance-bound. Backward-compatible: **absent ⇒ the classifier's default (`execute`, no
7892
+ * provenance-bound. Backward-compatible: **absent => the classifier's default (`execute`, no
7014
7893
  * scheme)**.
7015
7894
  */
7016
7895
  permission?: ToolPermissionHint;
@@ -7064,7 +7943,7 @@ declare type ToolParametersWire = unknown;
7064
7943
  * definition (the same shape of author-declared, kernel-read fact as `requiresAuth` and
7065
7944
  * `availability`). Consulted ONLY when the classifier does not otherwise recognize the tool
7066
7945
  * (never overrides `read`/`bash`/URI builtins), so a tool can relax the fail-closed
7067
- * `execute` fallback (e.g. a pure staging tool → `read`) or scope itself for per-provider and
7946
+ * `execute` fallback (e.g. a pure staging tool -> `read`) or scope itself for per-provider and
7068
7947
  * per-origin rules. Because it lives on the registered definition, it is provenance-bound: a
7069
7948
  * tool named the same as another carries its OWN hint, never inherits a first-party one.
7070
7949
  *
@@ -7073,7 +7952,7 @@ declare type ToolParametersWire = unknown;
7073
7952
  * touch protected config must NOT declare `read`. Bonsai's embedded-experience tools never touch
7074
7953
  * `configDir`, so this is safe for them. `read` is also what every capability surface reports as
7075
7954
  * "read-only" (Bonsai's Permissions page groups and defaults on it), so it is a claim about what the
7076
- * tool DOES — never a way to buy silence. A tool that should raise no ask on its own account
7955
+ * tool DOES - never a way to buy silence. A tool that should raise no ask on its own account
7077
7956
  * declares {@link ToolPermissionHint.quiet} and keeps its honest `kind`.
7078
7957
  */
7079
7958
  declare interface ToolPermissionHint {
@@ -7090,8 +7969,8 @@ declare interface ToolPermissionHint {
7090
7969
  readonly ruleKey?: string;
7091
7970
  /**
7092
7971
  * Overrides the ask rail's default human-response window for THIS tool's asks, in ms.
7093
- * A UX window declaration, not authority: it cannot loosen or tighten what is asked —
7094
- * kind/ruleKey/scheme/rules/mode all decide that exactly as they would without it —
7972
+ * A UX window declaration, not authority: it cannot loosen or tighten what is asked -
7973
+ * kind/ruleKey/scheme/rules/mode all decide that exactly as they would without it -
7095
7974
  * only how long the prompt waits before it lapses unanswered (`expired`, which the gate
7096
7975
  * already treats as `unanswered`, never `denied`). Bounded use case: a suggestion-shaped
7097
7976
  * tool whose silence means "continue" (e.g. `suggest_compaction`'s 15s ask, chosen to
@@ -7102,26 +7981,26 @@ declare interface ToolPermissionHint {
7102
7981
  /**
7103
7982
  * Derives the resources a call touches from its validated input (e.g. the target origin of
7104
7983
  * a browser navigation). Pure and total; a throw is treated as "no resources" and the call
7105
- * falls back to the plain classification (fail-closed — never crashes the gate).
7984
+ * falls back to the plain classification (fail-closed - never crashes the gate).
7106
7985
  */
7107
7986
  readonly deriveResources?: (input: Record<string, unknown>) => readonly string[];
7108
7987
  /**
7109
7988
  * Derives a call's classification (`kind`/`ruleKey`/`scheme`) from its validated input, where
7110
7989
  * the static fields above fix them for every call. This is the generalization of
7111
- * {@link deriveResources} (input → resources) to the rule-matching triple, and of the kernel's
7990
+ * {@link deriveResources} (input -> resources) to the rule-matching triple, and of the kernel's
7112
7991
  * built-in `deriveBashRuleKey` (a bash call's argv sharpens its key) to an author-declared tool.
7113
7992
  *
7114
7993
  * Returns a partial: each present field OVERRIDES the static hint for this call; each absent
7115
7994
  * field (or a wholly `undefined` return) falls back to the static hint, then the fail-closed
7116
7995
  * default. Pure and total; a throw is treated as "no refinement" and the call falls back to the
7117
- * static hint (fail-closed — never crashes the gate).
7996
+ * static hint (fail-closed - never crashes the gate).
7118
7997
  *
7119
7998
  * It grants no authority the static fields don't already grant: it computes the same
7120
7999
  * kind/ruleKey/scheme the tool could hard-code, per call rather than once. `kind` here matters
7121
8000
  * because the same tool can read on one input and execute on another, which a static `kind`
7122
8001
  * cannot express.
7123
8002
  *
7124
- * Exemplar — Bonsai's `mcp` proxy tool, one tool spanning every server: `search`/`describe` derive
8003
+ * Exemplar - Bonsai's `mcp` proxy tool, one tool spanning every server: `search`/`describe` derive
7125
8004
  * `{ kind: "read" }` (metadata lookups), while `call` derives
7126
8005
  * `{ kind: "execute", ruleKey: "mcp:<server>::<tool>", scheme: "<server>" }`, so a
7127
8006
  * `{ scheme: "<server>" }` rule gates a whole server and a `{ ruleKey: "mcp:<server>::<tool>" }`
@@ -7138,7 +8017,7 @@ declare interface ToolPermissionHint {
7138
8017
  * deriver is a pure function of its own input and cannot see the target's hint, its service
7139
8018
  * scheme, or the kernel's own recognition of names like `bash` and the URI verbs. Resolution
7140
8019
  * re-enters classification under the target's identity, so a proxied call matches EXACTLY the
7141
- * rules its direct form would — same kind, same ruleKey, same scheme.
8020
+ * rules its direct form would - same kind, same ruleKey, same scheme.
7142
8021
  *
7143
8022
  * A hop ADDS the target's rules; it never SUBTRACTS the carrier's. The carrier's own
7144
8023
  * classification rides along (`ToolActionPreflight.carrier`) and the controller takes the
@@ -7148,22 +8027,22 @@ declare interface ToolPermissionHint {
7148
8027
  * alone could not.
7149
8028
  *
7150
8029
  * Not consulted at all when the CARRIER is a tool the kernel classifies itself (`bash`, the URI
7151
- * verbs, the read tools) — the same invariant that governs the rest of this hint. A proxy may
8030
+ * verbs, the read tools) - the same invariant that governs the rest of this hint. A proxy may
7152
8031
  * still freely TARGET one of them.
7153
8032
  *
7154
8033
  * Pure and total. EVERY abnormal outcome falls back to the carrier's own classification, never
7155
8034
  * to an intermediate link: a throw, a malformed return, a blank target name, a non-object input,
7156
8035
  * a cycle, or a chain longer than 4 hops. (Falling back to an intermediate would itself be a
7157
- * bypass — a crafted 5-link chain could present hop 4's benign classification for a call whose
8036
+ * bypass - a crafted 5-link chain could present hop 4's benign classification for a call whose
7158
8037
  * real target is `bash rm -rf /`.) Only a chain terminating normally, at a tool declaring no
7159
8038
  * further proxy, hops at all.
7160
8039
  *
7161
- * "Fail-closed" here means the carrier's own classification applies — the identity the call
7162
- * really had — not that the result is always stricter than the target's. How much gate is left
8040
+ * "Fail-closed" here means the carrier's own classification applies - the identity the call
8041
+ * really had - not that the result is always stricter than the target's. How much gate is left
7163
8042
  * on such a call is exactly how much the CARRIER carries: an `execute` carrier is stricter than
7164
8043
  * a `read` target and equal to a `mutate`/`destructive` one outside allow mode, while a
7165
8044
  * {@link quiet} carrier (Bonsai's own `call_tool`) contributes no mode default at all, so the
7166
- * fallback leaves nothing but the carrier's rules and the protected-path deny — and the carrier
8045
+ * fallback leaves nothing but the carrier's rules and the protected-path deny - and the carrier
7167
8046
  * names no path, so that deny has nothing to fire on. This is why a carrier must refuse a target
7168
8047
  * that is itself a carrier: no chain long or circular enough to reach the fallback can be built,
7169
8048
  * which is the only thing that keeps the fallback from being toothless. Fail-closed is a
@@ -7174,24 +8053,24 @@ declare interface ToolPermissionHint {
7174
8053
  * Declares that this tool's identity contributes its RULES to the gate but not its MODE
7175
8054
  * DEFAULT. A rule about it still applies in full (a deny denies, an ask asks, an allow
7176
8055
  * allows) and the protected-path hard-deny still fires; what is withheld is the verdict the
7177
- * MODE would reach from `kind` alone — default/ask mode's "anything but a read asks".
8056
+ * MODE would reach from `kind` alone - default/ask mode's "anything but a read asks".
7178
8057
  *
7179
8058
  * It is a statement about this identity's CONTRIBUTION, never about what the call does.
7180
8059
  * `kind` stays the honest declaration of the strongest effect a call can have, and a quiet
7181
- * tool declares it exactly as loudly as a noisy one — which is the whole point: buying
8060
+ * tool declares it exactly as loudly as a noisy one - which is the whole point: buying
7182
8061
  * silence with `kind: "read"` instead moves the call below the protected-path hard-deny and
7183
8062
  * reports the tool as read-only on every capability surface.
7184
8063
  *
7185
8064
  * It exists for a PROXY CARRIER ({@link deriveProxiedCall}), and is honest only for a tool
7186
8065
  * whose only effect is invoking the target the gate is already classifying beside it: the
7187
8066
  * target's kind, rules, resources and the protected-path deny all still decide, so a proxied
7188
- * call is gated exactly like the direct one — no more, and no ask the direct call would never
8067
+ * call is gated exactly like the direct one - no more, and no ask the direct call would never
7189
8068
  * have raised. Bonsai's `call_tool` declares `{ kind: "execute", quiet: true }` (owner ruling,
7190
8069
  * 2026-08-18): honest about everything it can reach, silent on its own account.
7191
8070
  *
7192
8071
  * Quiet is not ungated, but it IS nearly all the gate a lone carrier has. On a call that
7193
8072
  * classifies as the carrier because no hop resolved (a cycle, an over-long chain), a quiet
7194
- * carrier leaves only its own rules — see {@link deriveProxiedCall}'s fail-closed paragraph
8073
+ * carrier leaves only its own rules - see {@link deriveProxiedCall}'s fail-closed paragraph
7195
8074
  * for why a carrier must therefore refuse a carrier as a target.
7196
8075
  */
7197
8076
  readonly quiet?: boolean;
@@ -8394,32 +9273,43 @@ declare interface UriAgentHint {
8394
9273
  readonly triggers?: UriHintTriggers;
8395
9274
  /**
8396
9275
  * Skill invocation names (flat `localId`s, e.g. `"gmail-email-triage"`) that hold this module's
8397
- * deeper workflow judgment — the name a `read skill://<name>` dispatch resolves, not a display
9276
+ * deeper workflow judgment - the name a `read skill://<name>` dispatch resolves, not a display
8398
9277
  * title or a path. Point here only at skills that actually exist in the composed skill roster;
8399
9278
  * a dangling name sends the agent to a `skill://` read for nothing.
8400
9279
  */
8401
9280
  readonly skills?: readonly string[];
8402
9281
  }
8403
9282
 
8404
- declare type UriAuthActionClassification = {
8405
- readonly kind: "action";
8406
- readonly actionId: string;
8407
- } | {
8408
- readonly kind: "public";
8409
- };
8410
-
8411
9283
  /**
8412
- * Finite authorization actions plus the pure route classifier that selects one before dispatch.
8413
- * An exception, an unknown action id, or `undefined` classification fails closed in the host.
9284
+ * Finite authorization actions plus the rules that select one before dispatch. A call no rule
9285
+ * matches, or a rule naming an action that is not in `actions`, fails closed in the host.
8414
9286
  */
8415
9287
  declare interface UriAuthActionPolicy {
8416
9288
  readonly actions: Readonly<Record<string, ScopeRequirement>>;
8417
- classifyOperation(request: {
8418
- readonly operation: UriOperation;
8419
- readonly uri: string;
8420
- }): UriAuthActionClassification | undefined;
9289
+ /** Absent matches no call, so every call fails closed. */
9290
+ readonly rules?: readonly UriAuthActionRule[];
8421
9291
  }
8422
9292
 
9293
+ /**
9294
+ * One row of a module's sign-in action rules: which calls need which action. A row matches a call
9295
+ * when every condition it names holds; a condition it leaves out matches anything. The first
9296
+ * matching row decides, so a row with no conditions at the end means "everything else".
9297
+ */
9298
+ declare type UriAuthActionRule = {
9299
+ /** The verbs this row covers. */
9300
+ readonly operations?: readonly UriOperation[];
9301
+ /** The claim `route` ids this row covers, matched against the call's address. */
9302
+ readonly routes?: readonly string[];
9303
+ /** Query parameters the address must carry with exactly these values (`{ permanent: "true" }`). */
9304
+ readonly query?: Readonly<Record<string, string>>;
9305
+ } & ({
9306
+ /** The {@link UriAuthActionPolicy.actions} entry the call needs. */
9307
+ readonly action: string;
9308
+ } | {
9309
+ /** The call needs no credential. */
9310
+ readonly public: true;
9311
+ });
9312
+
8423
9313
  declare interface UriAuthRef {
8424
9314
  readonly serviceId: string;
8425
9315
  readonly schemeId: string;
@@ -8441,7 +9331,7 @@ declare interface UriAuthRequirement {
8441
9331
  */
8442
9332
  readonly requiredScopes?: readonly string[];
8443
9333
  /**
8444
- * The service id connect guidance should NAME when this module is gated — a host-composed
9334
+ * The service id connect guidance should NAME when this module is gated - a host-composed
8445
9335
  * front door whose connect grants exactly this module's scopes (e.g. `gmail` expanding onto
8446
9336
  * the shared `google.account` credential). Guidance only; resolution keys on
8447
9337
  * `(serviceId, schemeId)`. Absent, guidance names `serviceId`.
@@ -8487,6 +9377,21 @@ declare type UriCachePolicyResolver = (operation: UriOperation, cacheUri: string
8487
9377
 
8488
9378
  declare type UriClaim = UriSchemeClaim | UriPatternClaim | UrlPatternClaim;
8489
9379
 
9380
+ /**
9381
+ * A URL with `:name` placeholders for the claim's captures. Each capture is escaped for where it
9382
+ * lands: lowercased in the host, one path segment (a `:name*` capture keeps its `/`), or a query
9383
+ * or fragment value. `{transform(...)}` runs one named, built-in transform over the text inside
9384
+ * it, captures pasted in unescaped, and escapes the output for where it lands:
9385
+ *
9386
+ * - `base64`: unpadded base64 of the UTF-8 text (`{base64(:id :calendar)}`).
9387
+ * - `slackTimestamp`: a Slack archive message token as the API's timestamp
9388
+ * (`p1783350000000100` becomes `1783350000.000100`).
9389
+ *
9390
+ * A transform that cannot read its input fills nothing. A claim whose `normalizeTo` fills nothing
9391
+ * does not match; a `sourceUrl` that fills nothing paints no link.
9392
+ */
9393
+ declare type UriClaimTemplate = string;
9394
+
8490
9395
  declare interface UriCommonFlow {
8491
9396
  readonly label: string;
8492
9397
  readonly when?: string;
@@ -8536,41 +9441,28 @@ declare interface UriExample {
8536
9441
  readonly operation?: UriOperation;
8537
9442
  }
8538
9443
 
8539
- declare interface UriExtensionModule extends Omit<UriModule, "verbs"> {
9444
+ /**
9445
+ * A plugin's URI module. Its claims route, spell and describe its addresses, so it has no
9446
+ * `matches`, `normalize` or `describe`. It also has no `fallback` (a plugin's addresses are always
9447
+ * claimed) and no `locate` (a plugin has no file on disk for the daemon to open).
9448
+ */
9449
+ declare interface UriExtensionModule extends Omit<UriModule, "verbs" | "normalize" | "matches" | "describe" | "fallback" | "locate"> {
8540
9450
  readonly verbs: UriVerbSupport;
8541
9451
  /** Human-facing per-verb effect docs; see {@link UriVerbDocs}. */
8542
9452
  readonly verbDocs?: UriVerbDocs;
8543
9453
  /**
8544
9454
  * Plugin-declared default permission per verb group. A verb group set to `"ask"`
8545
- * compiles into a defaults-level ask rule — the user is prompted by default but can
9455
+ * compiles into a defaults-level ask rule - the user is prompted by default but can
8546
9456
  * override with an explicit allow. Undeclared = `"allow"` (fail-open).
8547
9457
  * See {@link UriPermissionDefaults}.
8548
9458
  */
8549
9459
  readonly permissionDefaults?: UriPermissionDefaults;
8550
9460
  /** Requires a resolved URI connection before any verb handler runs. */
8551
9461
  readonly requiresConnection?: boolean;
8552
- /** Pure route-aware permission facts; see {@link UriExtensionOperationPermissionPolicy}. */
8553
- readonly permission?: UriExtensionOperationPermissionPolicy;
8554
9462
  /** False rejects generic write-tool `source` copies before source fetch. */
8555
9463
  readonly acceptsSourceCopy?: boolean;
8556
9464
  }
8557
9465
 
8558
- declare interface UriExtensionOperationPermissionPolicy {
8559
- /**
8560
- * Classify one canonical route, purely. `write`/`edit`/`delete` may only be made stricter
8561
- * than the verb default; `help`/`read`/`list`/`search` must stay `"read"` (a `ruleKey` is
8562
- * allowed), because the kernel reruns a crash-interrupted read without asking again. Any
8563
- * other answer, or a throw, fails the call closed.
8564
- */
8565
- classifyOperation(request: {
8566
- readonly operation: UriOperation;
8567
- readonly uri: string;
8568
- }): {
8569
- readonly kind: "read" | "mutate" | "destructive" | "execute";
8570
- readonly ruleKey?: string;
8571
- } | undefined;
8572
- }
8573
-
8574
9466
  declare interface UriExternalCliRunner {
8575
9467
  readonly run: (request: UriExternalCliRunRequest) => Promise<UriExternalCliRunResult>;
8576
9468
  }
@@ -8605,6 +9497,8 @@ declare interface UriHelp {
8605
9497
  * than text the module's author wrote. Absent: the module's own text.
8606
9498
  */
8607
9499
  readonly thirdPartyContent?: true;
9500
+ /** The module's own markdown for this result, shown instead of the host's default rendering. */
9501
+ readonly markdown?: string;
8608
9502
  }
8609
9503
 
8610
9504
  declare interface UriHintTriggers {
@@ -8644,6 +9538,12 @@ declare interface UriListResult {
8644
9538
  readonly items: readonly UriListItem[];
8645
9539
  readonly nextCursor?: string;
8646
9540
  readonly metadata?: UriListMetadata;
9541
+ /** How long the host may cache this result; see {@link UriCachePolicy.ttlMs}. */
9542
+ readonly cachePolicy?: Pick<UriCachePolicy, "ttlMs">;
9543
+ /** The module's own markdown for this result, shown instead of the host's default rendering. */
9544
+ readonly markdown?: string;
9545
+ /** The touched resource's name for the transcript row, as on `RawResource.label`. */
9546
+ readonly label?: UriResourceLabel;
8647
9547
  }
8648
9548
 
8649
9549
  declare type UriLocateContext = Pick<UriResolveContext, "cwd" | "signal" | "connection" | "sessionId" | "paths"> & {
@@ -8661,31 +9561,13 @@ declare type UriLocateResult = {
8661
9561
  readonly kind: "not-disk-backed";
8662
9562
  };
8663
9563
 
8664
- declare type UriMarkdownRenderInput = {
8665
- readonly operation: "help";
8666
- readonly uri: string;
8667
- readonly result: UriHelp;
8668
- } | {
8669
- readonly operation: "list";
8670
- readonly uri: string;
8671
- readonly result: UriListResult;
8672
- } | {
8673
- readonly operation: "search";
8674
- readonly uri: string;
8675
- readonly result: UriSearchResult;
8676
- } | {
8677
- readonly operation: Extract<UriOperation, "write" | "edit" | "delete">;
8678
- readonly uri: string;
8679
- readonly result: UriMutationResult;
8680
- };
8681
-
8682
9564
  declare interface UriModule {
8683
9565
  readonly id: string;
8684
9566
  /**
8685
9567
  * Explicit human-readable name for catalog/display purposes (the plugin
8686
9568
  * manifest's `contributes.uriModules[].displayName`, threaded through by
8687
- * hand since there is no automatic manifest→runtime-module composition
8688
- * step). Falls back to a mechanical id-derived title when absent — see
9569
+ * hand since there is no automatic manifest->runtime-module composition
9570
+ * step). Falls back to a mechanical id-derived title when absent - see
8689
9571
  * `uriCatalogTitle`.
8690
9572
  */
8691
9573
  readonly title?: string;
@@ -8705,14 +9587,26 @@ declare interface UriModule {
8705
9587
  * calls `parseLineSelector` itself and reports what it did through `RawResource.selector`.
8706
9588
  */
8707
9589
  readonly ownsLineSelectors?: boolean;
9590
+ /** Opt in to host-resolved supplementary sources; absent means unsupported. */
9591
+ readonly acceptsWriteSources?: boolean;
9592
+ /**
9593
+ * Opt in to raw bytes as `write`'s content; absent means text and JSON only. The `write`
9594
+ * tool's `source` copy leg is the only producer: a provider's binary resource (an attachment,
9595
+ * an image, a PDF) reaches a module that declares this as a `Uint8Array`, never decoded into
9596
+ * the model's context, and a binary copy to any other module is refused before it runs.
9597
+ */
9598
+ readonly acceptsBytes?: boolean;
8708
9599
  readonly claims?: readonly UriClaim[];
9600
+ /**
9601
+ * The routes the module's scheme-root `help` lists, as data, so a host can list them without
9602
+ * calling `help`.
9603
+ */
9604
+ readonly templates?: readonly UriTemplate[];
8709
9605
  /** Migration-only prompt summary until structured help/catalog coverage replaces prose. */
8710
9606
  readonly description?: string;
8711
9607
  readonly agentHint?: UriAgentHint;
8712
9608
  readonly requiresAuth?: UriAuthRequirement;
8713
9609
  readonly authActions?: UriAuthActionPolicy;
8714
- /** Pure route/payload validation applied before a consequential permission ask. */
8715
- readonly preflight?: UriOperationPreflightPolicy;
8716
9610
  /**
8717
9611
  * Structured facts for approval surfaces, or `undefined` when unavailable.
8718
9612
  * A one-line description must name the whole consequence; `preview` instead
@@ -8722,120 +9616,46 @@ declare interface UriModule {
8722
9616
  * of covered payload fields and query parameters. Unknown fields cost the summary.
8723
9617
  * The host owns framing, the whole-quote fit (no elision) and display-safety admission.
8724
9618
  *
8725
- * Unlike preflight, this hook is async and may do I/O. Resolve names through `ctx`,
9619
+ * This hook is async and may do I/O. Resolve names through `ctx`,
8726
9620
  * the same dispatch context used by execution, never an ambient or cached account. A name
8727
9621
  * the module resolved under that account may be cached for a bounded time, keyed by that
8728
9622
  * account's identity and never answered to another account or after a failed lookup.
8729
- * The host calls it once before raising the ask, enforces the short `ctx.signal`
8730
- * deadline and discards late answers. A throw yields no summary and a module-attributed
8731
- * diagnostic; declining costs a click into the app, never an under-described approval.
8732
- */
8733
- describeOperation?(request: {
8734
- readonly operation: UriOperation;
8735
- readonly uri: string;
8736
- readonly payload: unknown;
8737
- }, ctx: UriResolveContext): Promise<UriOperationDescription | undefined>;
8738
- /**
8739
- * Rebuild a call from the person's "Allow with edits" answer: the same payload
8740
- * {@link describeOperation} was given, with the values the person typed into the preview's
8741
- * `editable` fields put in their place. Implement it beside any preview that marks a field
8742
- * `editable`; without it the host drops every editable marker from that module's previews, so
8743
- * a field is never offered for editing that cannot be honoured.
9623
+ * The host calls it once before raising the ask and waits for the answer, with no deadline;
9624
+ * `ctx.signal` fires when the host stops waiting (the session closes, or a caller gives up),
9625
+ * and a late answer is discarded. A throw yields no
9626
+ * summary and a module-attributed diagnostic; declining costs a click into the app, never an
9627
+ * under-described approval.
8744
9628
  *
8745
- * Pure and fast: it must not send, write or otherwise act, and it should not do I/O (it gets
8746
- * the dispatch context `ctx` only so a name can be resolved the way `describeOperation` does).
8747
- * Return the COMPLETE amended payload in the shape the module's own handler reads. Validate it
8748
- * with the handler's own schema, so an amended call is exactly as valid as one the model made.
8749
- * Decline by returning `undefined` or throwing; either denies the call with nothing done.
8750
- *
8751
- * The host calls it once, after the person allowed the call, never for a denial, and only for
8752
- * a `write` whose payload this module described. The kernel passes the `edits` through
8753
- * unchanged. Bonsai's daemon has already refused, before the ask settled, a key that is not an
8754
- * `editable` field of the preview it showed, a repeated key, a blank value and a value past the
8755
- * field's `maxLength`, but another host may not, so refuse each of those here yourself (return
8756
- * `undefined`).
8757
- *
8758
- * The host then holds the amended call to what the person was shown, in two steps, and runs it
8759
- * only if both pass. It classifies the amended call again and requires the same tool, rule key,
8760
- * kind, resources, scheme and target address, so an edit cannot reach a different route. It then
8761
- * calls {@link describeOperation} on the amended call and requires that preview to read exactly
8762
- * as the one the person saw, apart from the values of the fields they edited: the same title, the
8763
- * same fields in the same order, each with the same label, key, `editable` marker and
9629
+ * "Allow with edits": a field marked `editable` may be rewritten by the person on the card.
9630
+ * The host rebuilds the call itself (see the field's `editable`), then holds the rebuilt call to
9631
+ * what the person was shown, in two steps, and runs it only if both pass. It classifies the
9632
+ * rebuilt call again and requires the same tool, rule key, kind, resources, scheme and target
9633
+ * address. It then calls this hook on the rebuilt call and requires that preview to read exactly
9634
+ * as the one the person saw, apart from the values of the fields they edited: the same title,
9635
+ * the same fields in the same order, each with the same label, key, `editable` marker and
8764
9636
  * `maxLength`, and every read-only field with the same value. Anything else refuses the call
8765
- * with nothing done: a mismatch, and also a describer that is missing, declines, throws or
8766
- * overruns its deadline. The `description` line is not compared.
8767
- *
8768
- * So rebuild from the original payload and change only the fields you offered. A recipient, a
8769
- * thread or an attachment list that an amendment changed is caught by the second step, but only
8770
- * where the preview shows it: put whatever says where a call goes, or what it does beyond its
9637
+ * with nothing done: a mismatch, and also an answer of `undefined` or a throw. The `description`
9638
+ * line is not compared. So put whatever says where a call goes, or what it does beyond its
8771
9639
  * editable fields, in the preview's `title` or a field, never only in its `description`, and
8772
9640
  * keep recipients, destinations and thread bindings out of `editable` fields.
8773
9641
  */
8774
- amendOperation?(request: {
9642
+ describeOperation?(request: {
8775
9643
  readonly operation: UriOperation;
8776
9644
  readonly uri: string;
8777
9645
  readonly payload: unknown;
8778
- readonly edits: readonly UriPreviewEdit[];
8779
- }, ctx: UriResolveContext): Promise<UriOperationAmendment | undefined>;
8780
- /**
8781
- * A short, render-only name for the resource a settled call on `request.uri` touched, which the
8782
- * host stamps on the tool result's `details` so a transcript row can name it where it would
8783
- * otherwise print the address (`#lane-analytics` and `thread`, not a channel id). Answer
8784
- * `undefined` for any address you cannot honestly name.
8785
- *
8786
- * Called once per successful URI verb call, AFTER the verb handler has returned, with the same
8787
- * dispatch context. Resolve names through `ctx` and nothing else, and prefer a cache your own
8788
- * verb handlers warm, keyed by the executing account: a name one account saw is never answered
8789
- * to another, and a failed, blank or aborted lookup is never cached. The host bounds the wait
8790
- * (a finished result is held at most a quarter of a second), gives `ctx.signal` a hard deadline,
8791
- * and drops a late, thrown or refused answer without failing the call; a throw is reported as a
8792
- * module-attributed diagnostic. The label is a label, never an identity: the address remains the
8793
- * click target, hover text and accessible name. It is UI-only, so it never enters the tool
8794
- * result's content and no telemetry reads it.
8795
- *
8796
- * Unlike `describeOperation` this is not an approval surface: it carries no consequence, no
8797
- * quote and no coverage claim, and nothing may gate a permission on it.
8798
- */
8799
- labelResource?(request: UriResourceLabelRequest, ctx: UriResolveContext): Promise<UriResourceLabel | undefined>;
8800
- /**
8801
- * Per-resource observability: does a `format: "observation"` read of THIS
8802
- * uri render source-native material ({@link UriResolveContext.format})?
8803
- * Route-parse only — cheap, side-effect-free, no I/O, no auth (the same
8804
- * discipline as `matches`); a throw is treated as `false`. Returning true
8805
- * is the resource's contract that its observation read returns an
8806
- * observation render and that failures under that format throw typed,
8807
- * status-bearing errors rather than resolving a friendly fallback page.
8808
- * The kernel stamps the answer onto the resource's access events, which is
8809
- * what ambient index consumers replay on — so "PRs are observable" is
8810
- * declared here, route by route, never module-wide. Absent means no read
8811
- * of this module is observable. A vendor's own gone-vocabulary (HTTP 410,
8812
- * a 200-body "ok:false"/"archived:true"/"status:cancelled", or a
8813
- * 403/404-ambiguous lost-access response) is this module's job to
8814
- * translate into `UriNotFoundError` or a `status: 404` error under the
8815
- * observation format — the fold that retires a doc never learns vendor
8816
- * vocabulary (see `ObservationMaterial`'s doc comment).
8817
- */
8818
- observes?(uri: string): boolean;
8819
- /**
8820
- * Head-only revision probe: the resource's CURRENT source-native revision
8821
- * without fetching its body, or `undefined` when that cannot be known
8822
- * cheaply. The host calls it before a repeat `read` of an address the
8823
- * session already holds, with `ctx.expectedRevision` set to the revision
8824
- * that read reported (`RawResourceMetadata.revision`); a value equal to it
8825
- * means "unchanged" and the fetch is skipped, anything else falls through
8826
- * to the ordinary read. A module whose read revision embeds more than the
8827
- * head can know (a content digest beside a stat stamp) may answer relative
8828
- * to `expectedRevision` — return it when the cheap part matches, `undefined`
8829
- * when it does not — but must never return a value it has not verified.
8830
- * `undefined` is always safe: it costs one fetch, never a wrong answer.
8831
- * Must be cheaper than the read (a metadata request, a `stat`); a throw is
8832
- * treated as `undefined`.
8833
- */
8834
- revision?(uri: string, ctx: UriResolveContext): Promise<string | undefined>;
9646
+ }, ctx: UriResolveContext): Promise<UriOperationDescription | undefined>;
8835
9647
  readonly getCachePolicy?: UriCachePolicyResolver;
8836
- /** Retained matching escape hatch; future dispatch should prefer compiled claims. */
8837
- matches(input: string, ctx: UriResolveContext): boolean;
9648
+ /**
9649
+ * Code matching, for the host's own modules whose addresses no claim can describe (a bare file
9650
+ * path). A plugin module has none; it routes through its `claims` alone.
9651
+ */
9652
+ matches?(input: string, ctx: UriResolveContext): boolean;
9653
+ /** The official spelling, for the host's own modules; a plugin's comes from its claims' `normalizeTo`. */
8838
9654
  normalize?(input: string, ctx: UriResolveContext): UriNormalizeResult | undefined;
9655
+ /**
9656
+ * The pill's title and link, for the host's own modules only; a plugin module has none, and its
9657
+ * pill comes from its claims' `title` and `sourceUrl`.
9658
+ */
8839
9659
  describe?(input: string, ctx: UriDescribeContext): UriResourceDescription | undefined;
8840
9660
  locate?(input: string, ctx: UriLocateContext): Promise<UriLocateResult>;
8841
9661
  /**
@@ -8848,33 +9668,21 @@ declare interface UriModule {
8848
9668
  read(input: string, ctx: UriResolveContext): Promise<RawResource>;
8849
9669
  /** Observes only; see {@link read}. */
8850
9670
  help?(input: string, ctx: UriResolveContext): Promise<UriHelp>;
8851
- write?(input: string, content: UriPayload, ctx: UriResolveContext): Promise<UriMutationResult | void>;
8852
- /** Opt-in structured write with one or more host-resolved resources delivered beside the content. */
8853
- writeWithSources?(input: string, content: UriPayload, sources: readonly UriWriteSource[], ctx: UriResolveContext): Promise<UriMutationResult | void>;
8854
- /**
8855
- * Opt-in byte sink: write RAW BYTES to this address, no UTF-8 round trip.
8856
- * `UriPayload` is text/JSON by contract, so a module that can store bytes
8857
- * declares it here instead of stretching `write`. The only caller today is
8858
- * the `write` tool's `source` copy leg, which is how a provider's binary
8859
- * resource — a Gmail attachment, an image, a PDF, a zip — reaches a
8860
- * byte-capable destination without ever being decoded into the model's
8861
- * context. A module that omits it is text-only, and a binary copy targeting
8862
- * it is refused with an error naming a byte-capable destination.
8863
- *
8864
- * `source` describes what the bytes are: a filesystem-backed sink can ignore
8865
- * it (the target path already carries the name), while a remote provider
8866
- * sink needs it to name and type the stored file. It sits before `ctx` so a
8867
- * forwarding wrapper that fails to thread it is a compile error, not a
8868
- * silent drop.
9671
+ /**
9672
+ * `sources` is empty for ordinary writes; populated only with `acceptsWriteSources: true`.
9673
+ * `content` is a `Uint8Array` only with `acceptsBytes: true`, and then `ctx.writeSource`
9674
+ * names and types it. It is `undefined` when the call carried neither `content` nor `source`.
9675
+ * The module decides what that means: a route that takes no body (a folder
9676
+ * create) proceeds, and a route that needs one refuses with an error naming
9677
+ * what is missing. Never treat it as empty content.
8869
9678
  */
8870
- writeBytes?(input: string, bytes: Uint8Array, source: UriSourceDescriptor, ctx: UriResolveContext): Promise<UriMutationResult | void>;
9679
+ write?(input: string, content: UriPayload | Uint8Array | undefined, sources: readonly UriWriteSource[], ctx: UriResolveContext): Promise<UriMutationResult | void>;
8871
9680
  edit?(input: string, patch: UriPayload, ctx: UriResolveContext): Promise<UriMutationResult>;
8872
9681
  /** Observes only; see {@link read}. */
8873
9682
  list?(input: string, params: UriListParams, ctx: UriResolveContext): Promise<UriListResult>;
8874
9683
  /** Observes only; see {@link read}. */
8875
9684
  search?(input: string, params: UriSearchParams, ctx: UriResolveContext): Promise<UriSearchResult>;
8876
9685
  delete?(input: string, ctx: UriResolveContext): Promise<UriMutationResult>;
8877
- toMarkdown?(input: UriMarkdownRenderInput, ctx: UriResolveContext): string | undefined;
8878
9686
  }
8879
9687
 
8880
9688
  /** Authoritative lifecycle facts for a successful record-like URI mutation. */
@@ -8900,6 +9708,12 @@ declare interface UriMutationResult {
8900
9708
  readonly receipt?: UriMutationReceipt;
8901
9709
  /** Opaque source-native revision after this successful mutation. */
8902
9710
  readonly revision?: string;
9711
+ /** Cached reads this mutation made stale; see {@link UriCachePolicy.invalidate}. */
9712
+ readonly cachePolicy?: Pick<UriCachePolicy, "invalidate">;
9713
+ /** The module's own markdown for this result, shown instead of the host's default rendering. */
9714
+ readonly markdown?: string;
9715
+ /** The touched resource's name for the transcript row, as on `RawResource.label`. */
9716
+ readonly label?: UriResourceLabel;
8903
9717
  }
8904
9718
 
8905
9719
  declare interface UriNodeCapability {
@@ -8920,11 +9734,6 @@ declare interface UriNormalizeResult {
8920
9734
 
8921
9735
  declare type UriOperation = "help" | "read" | "write" | "edit" | "list" | "search" | "delete";
8922
9736
 
8923
- /** The call {@link UriModule.amendOperation} rebuilt: the whole payload, in the shape the module reads. */
8924
- declare interface UriOperationAmendment {
8925
- readonly payload: unknown;
8926
- }
8927
-
8928
9737
  declare interface UriOperationDescription {
8929
9738
  /** Static verb phrase authored by the module. Never interpolated from input. */
8930
9739
  readonly action: string;
@@ -8947,9 +9756,11 @@ declare interface UriOperationDescription {
8947
9756
  */
8948
9757
  readonly key?: string;
8949
9758
  /**
8950
- * `true` offers this field to the person for editing on the approval card. Offer it only
8951
- * when the module implements {@link UriModule.amendOperation} for this call and can honour
8952
- * any value within `maxLength`; the host drops the offer otherwise. Absent means read-only.
9759
+ * `true` offers this field to the person for editing on the approval card. The host applies
9760
+ * an edit itself: it copies the payload and replaces the value of the top-level string
9761
+ * property named `key`. So offer a field only when the payload is an object, `key` is one
9762
+ * of its string properties, and the module would send any value within `maxLength` as it
9763
+ * sends the model's; the host drops the offer otherwise. Absent means read-only.
8953
9764
  */
8954
9765
  readonly editable?: true;
8955
9766
  /** Longest edited value, in UTF-16 code units. Meaningful only with `editable`. */
@@ -8967,56 +9778,6 @@ declare interface UriOperationDescription {
8967
9778
  };
8968
9779
  }
8969
9780
 
8970
- /**
8971
- * Pure, route-aware validation that runs before permission policy can surface an ask.
8972
- *
8973
- * This is deliberately narrower than execution: it receives only the canonical URI,
8974
- * operation, and operation payload, performs no I/O, and returns a user-actionable
8975
- * rejection for calls that the module already knows cannot succeed. The handler must
8976
- * still validate again when it executes because approval is not an authority boundary.
8977
- */
8978
- declare interface UriOperationPreflightPolicy {
8979
- /**
8980
- * The rejection half. Optional since kay#7684: a module whose only preflight
8981
- * fact is affirmative (`acceptsPayloadlessWrite`) must not have to stub a
8982
- * validator that never rejects.
8983
- */
8984
- validateOperation?(request: {
8985
- readonly operation: UriOperation;
8986
- readonly uri: string;
8987
- readonly payload: unknown;
8988
- }): string | undefined;
8989
- /**
8990
- * Route-level affirmative: may a `write` to this URI carry NO payload at all
8991
- * — neither `content` nor `source`? (The canonical case is folder creation,
8992
- * e.g. `?type=folder`.) The write tool's generic shape rule requires exactly
8993
- * one payload form; a route answers `true` here when a payloadless write is
8994
- * meaningful FOR THAT ROUTE, and the rule then admits the call.
8995
- *
8996
- * This exists because an empty body is not always sendable: on some
8997
- * model/provider paths a top-level empty-string argument (`content: ""`)
8998
- * never reaches the host — the key arrives absent, indistinguishable from an
8999
- * omitted argument (kay#7684). A contract that can only be invoked with an
9000
- * empty string is unreachable on those paths, so a route whose write is
9001
- * meaningful with no body must declare it and let the caller omit the
9002
- * payload instead.
9003
- *
9004
- * Pure and side-effect-free like {@link validateOperation}: route-parse
9005
- * only, no I/O. A throw NEVER admits the write, but the two consult paths
9006
- * disagree on purpose about what happens next: pre-ask (permission
9007
- * resolution, which must never crash the gate) answers no and records a
9008
- * module-attributed diagnostic; at execute time the write tool asks only
9009
- * when the call genuinely carries no payload, and a throw there is the
9010
- * module's own bug surfacing as the call's error — louder than a misleading
9011
- * \"provide exactly one of content or source\" the model could never
9012
- * satisfy. Absent means no route of this module accepts a payloadless
9013
- * write.
9014
- */
9015
- acceptsPayloadlessWrite?(request: {
9016
- readonly uri: string;
9017
- }): boolean;
9018
- }
9019
-
9020
9781
  declare interface UriParameter {
9021
9782
  readonly name: string;
9022
9783
  readonly in: "uri" | "query" | "selector";
@@ -9026,29 +9787,60 @@ declare interface UriParameter {
9026
9787
  readonly examples?: readonly unknown[];
9027
9788
  }
9028
9789
 
9790
+ /**
9791
+ * A claim on a module's own custom-scheme addresses. The pattern matches with or without an
9792
+ * account prefix: `gcal://@work/events/x` is matched as `gcal://events/x`, and the prefix is put
9793
+ * back on the official spelling, so one claim covers both spellings.
9794
+ */
9029
9795
  declare interface UriPatternClaim {
9030
9796
  readonly kind: "uriPattern";
9031
9797
  readonly pattern: UriPatternInit;
9032
9798
  /**
9033
- * Canonical spelling rebuilt from the captures. When the template keeps the
9034
- * input's scheme (the module's own addressing), the input's query string is
9799
+ * Canonical spelling rebuilt from the captures (see {@link UriClaimTemplate}). When the template
9800
+ * keeps the input's scheme (the module's own addressing), the input's query string is
9035
9801
  * carried over verbatim so a route flag such as `?permanent=true` survives
9036
9802
  * canonicalization (kay#7859); a template that re-targets to another scheme
9037
9803
  * (a projection to a web link) drops it, as does a pattern that consumed the
9038
9804
  * query itself or a template that spells its own.
9039
9805
  */
9040
- readonly normalizeTo?: string;
9806
+ readonly normalizeTo?: UriClaimTemplate;
9807
+ /**
9808
+ * Names the route this claim matches, so data keyed by route (a module's sign-in action rules)
9809
+ * can say which addresses it means. Several claims may name the same route.
9810
+ */
9811
+ readonly route?: string;
9812
+ /**
9813
+ * The web page a matched address opens, filled from the captures (see
9814
+ * {@link UriClaimTemplate}). The host matches an address's official spelling and paints its
9815
+ * reference with the source URL of the first matching claim that declares one.
9816
+ */
9817
+ readonly sourceUrl?: UriClaimTemplate;
9818
+ /**
9819
+ * The name a matched address's reference shows: literal text, with each `:name` replaced by
9820
+ * that capture as decoded (`"Flow :flow"`, or a fixed `"BFS"`). The first matching claim that
9821
+ * declares one names the address.
9822
+ */
9823
+ readonly title?: string;
9041
9824
  readonly examples?: readonly string[];
9042
9825
  }
9043
9826
 
9044
9827
  declare interface UriPatternInit {
9045
9828
  readonly protocol?: string;
9829
+ /**
9830
+ * Dot-delimited host pattern. A pattern that is one capture (`:owner`) takes the whole host,
9831
+ * dots included, as a `github://some.org/repo` owner must.
9832
+ */
9046
9833
  readonly hostname?: string;
9047
9834
  /**
9048
9835
  * Slash-delimited path pattern. A final capture may use `:name*` to consume
9049
- * the remaining path segments, preserving `/` in the captured value.
9836
+ * the remaining path segments, preserving `/` in the captured value. Unless the pattern itself
9837
+ * ends in `/`, an input path ending in one `/` matches as if the slash were absent, so
9838
+ * `threads/t1/` is `threads/t1` (captures and official spelling included) and a lone `/` after a
9839
+ * host is the empty path; a doubled `//` is never dropped, nor the root path of an address with
9840
+ * no host (`bfs:///`).
9050
9841
  */
9051
9842
  readonly pathname?: string;
9843
+ /** `&`-delimited query pattern; `""` matches only an address with no query. */
9052
9844
  readonly search?: string;
9053
9845
  }
9054
9846
 
@@ -9067,7 +9859,7 @@ declare interface UriPayloadContract {
9067
9859
 
9068
9860
  /**
9069
9861
  * The three-state permission vocabulary a plugin declares for its default permissions.
9070
- * Maps to `PermissionRule.effect`: `allow` → allow, `ask` → ask, `never` → deny.
9862
+ * Maps to `PermissionRule.effect`: `allow` -> allow, `ask` -> ask, `never` -> deny.
9071
9863
  */
9072
9864
  declare type UriPermissionDefault = "allow" | "ask" | "never";
9073
9865
 
@@ -9085,19 +9877,6 @@ declare interface UriPermissionDefaults {
9085
9877
  readonly delete?: UriPermissionDefault;
9086
9878
  }
9087
9879
 
9088
- /**
9089
- * One change a person made to an `editable` field of a call's action preview ("Allow with
9090
- * edits"): the field's `key` and the value they want it to hold. Values are message content, so
9091
- * a module must not log or persist them.
9092
- */
9093
- declare interface UriPreviewEdit {
9094
- readonly key: string;
9095
- readonly value: string;
9096
- }
9097
-
9098
- /** The read shape a caller may request via {@link UriResolveContext.format}. */
9099
- declare type UriReadFormat = "observation";
9100
-
9101
9880
  declare interface UriResolveContext {
9102
9881
  readonly cwd: string;
9103
9882
  readonly signal?: AbortSignal;
@@ -9106,8 +9885,8 @@ declare interface UriResolveContext {
9106
9885
  readonly auth?: UriResolvedAuth;
9107
9886
  /**
9108
9887
  * Id of the session dispatching this call, stamped by the host at the dispatch site.
9109
- * It never originates from anything the caller can influence — no verb argument, no write
9110
- * body — so a module may treat it as trustworthy caller identity and stamp it onto writes.
9888
+ * It never originates from anything the caller can influence - no verb argument, no write
9889
+ * body - so a module may treat it as trustworthy caller identity and stamp it onto writes.
9111
9890
  *
9112
9891
  * Absent when no session owns the dispatch: registry matching and permission pre-checks
9113
9892
  * resolve a URI outside any verb call.
@@ -9115,14 +9894,14 @@ declare interface UriResolveContext {
9115
9894
  readonly sessionId?: string;
9116
9895
  /**
9117
9896
  * WHO the dispatching session acts as, stamped by the host at the same dispatch site as
9118
- * {@link UriResolveContext.sessionId} and just as un-influenceable by the caller — so a module
9897
+ * {@link UriResolveContext.sessionId} and just as un-influenceable by the caller - so a module
9119
9898
  * writing to a store it owns can record the actor without the agent being able to choose one.
9120
9899
  *
9121
9900
  * Free text minted by the host's own runtime (Bonsai's form is `user`, `subagent`,
9122
9901
  * `system/<surface>`, `followup/<surface>`) rather than a closed union: the surface half is the
9123
9902
  * host's vocabulary, and a second enumeration of it here would drift from the first.
9124
9903
  *
9125
- * Absent whenever the host has nothing to say — no session owns the dispatch, or the session
9904
+ * Absent whenever the host has nothing to say - no session owns the dispatch, or the session
9126
9905
  * predates the host recording an origin. A module must then omit the actor rather than assume
9127
9906
  * one; an invented actor written into a durable artifact can never be walked back.
9128
9907
  */
@@ -9138,7 +9917,7 @@ declare interface UriResolveContext {
9138
9917
  readonly origin: "model" | "host";
9139
9918
  /**
9140
9919
  * The daemon-assigned id of the run driving this invocation, when the host's own execution
9141
- * frame carried one. Absent means the frame carried none — never a reason to guess one: a
9920
+ * frame carried one. Absent means the frame carried none - never a reason to guess one: a
9142
9921
  * module recording this onto a durable artifact must treat absence as unresolved provenance,
9143
9922
  * not as "no run was active".
9144
9923
  */
@@ -9147,39 +9926,33 @@ declare interface UriResolveContext {
9147
9926
  readonly toolName?: string;
9148
9927
  };
9149
9928
  /**
9150
- * An OPAQUE, host-captured context for the mutation this dispatch is about to make —
9929
+ * An OPAQUE, host-captured context for the mutation this dispatch is about to make -
9151
9930
  * the artifact-provenance counterpart of `sessionId`/`actor`, and stamped at the same
9152
9931
  * un-influenceable dispatch site.
9153
9932
  *
9154
9933
  * The host composes it from facts a caller cannot supply: which session is acting, the
9155
9934
  * run driving it, the tool invocation, and whatever the host resolved about how that
9156
- * session came to exist. The kernel treats it as an uninterpreted JSON value — it never
9935
+ * session came to exist. The kernel treats it as an uninterpreted JSON value - it never
9157
9936
  * reads a field, only checks that the value is lossless JSON before it crosses the
9158
- * dispatch boundary — so this layer stays ignorant of any one product's provenance
9937
+ * dispatch boundary - so this layer stays ignorant of any one product's provenance
9159
9938
  * semantics. A module that owns a durable artifact carries it to its store and validates
9160
9939
  * it against its OWN schema at the point it persists, where the target revision is known.
9161
9940
  *
9162
9941
  * Absent means the host captured nothing for this dispatch (a read, a host with no
9163
- * capture resolver, or a capture that failed) — never a claim that there was nothing to
9942
+ * capture resolver, or a capture that failed) - never a claim that there was nothing to
9164
9943
  * capture. Present only on mutating dispatches (`write`/`edit`/`delete`); reads never pay
9165
9944
  * for it.
9166
9945
  */
9167
9946
  readonly captureContext?: Readonly<Record<string, unknown>>;
9168
9947
  /**
9169
- * Requested read shape. A module may ignore this and return its normal read; honoring
9170
- * it means returning an `ObservationMaterial` via `observationResource(...)` instead.
9171
- * Opt-in only — there is no advertisement surface or capability flag.
9172
- */
9173
- readonly format?: UriReadFormat;
9174
- /**
9175
- * Host-provided writable data root for the OWNING extension of the module handling this call —
9948
+ * Host-provided writable data root for the OWNING extension of the module handling this call -
9176
9949
  * structurally identical to the per-extension `paths` a tool/handler context already receives, so
9177
9950
  * a module authored against both surfaces reads `ctx.paths?.dataDir` uniformly. Lets a URI module
9178
9951
  * own durable local state (a JSON store, a small file tree) instead of the host constructing and
9179
9952
  * injecting it. Host-populated after dispatch selects a module and keyed to that module's owner;
9180
9953
  * always absent during matching, and absent for modules with no owning extension (builtins) or a
9181
9954
  * host that wires no per-module data dir. Unlike a tool context, the host only NAMES this path for
9182
- * a URI module and does NOT pre-create the directory — create it on first write and tolerate its
9955
+ * a URI module and does NOT pre-create the directory - create it on first write and tolerate its
9183
9956
  * absence on read.
9184
9957
  */
9185
9958
  readonly paths?: {
@@ -9194,6 +9967,15 @@ declare interface UriResolveContext {
9194
9967
  * proceeds as before.
9195
9968
  */
9196
9969
  readonly expectedRevision?: string;
9970
+ /**
9971
+ * The revision this session already holds for the address a `read` is about to fetch: the
9972
+ * `RawResourceMetadata.revision` its last read reported. Set only on a repeat `read`, never
9973
+ * on a mutation (`expectedRevision` is the mutation witness). A module with a cheap
9974
+ * current-revision check (a metadata request, a `stat`) may compare first and, when it has
9975
+ * verified the two are equal, answer {@link RawResource.unchanged} with no body. A module
9976
+ * with no such check ignores it and reads in full, which is always correct.
9977
+ */
9978
+ readonly knownRevision?: string;
9197
9979
  /** Host marks a mediated session that enforces its private revision witness ledger. */
9198
9980
  readonly requiresRevisionWitness?: boolean;
9199
9981
  /** Host-derived descriptor for a top-level copied resource; absent for inline writes. */
@@ -9203,26 +9985,13 @@ declare interface UriResolveContext {
9203
9985
  declare interface UriResolvedAuth {
9204
9986
  readonly connection?: UriConnection;
9205
9987
  /** Present only for verb handlers of a module declaring `requiresAuth`; absent during matching. */
9206
- readonly credential?: UriResolvedCredential;
9988
+ readonly credential?: ResolvedCredential;
9207
9989
  /** Present for handlers gated by a non-injectable external-CLI readiness scheme. */
9208
9990
  readonly externalCli?: UriExternalCliRunner;
9209
9991
  /** Opaque host metadata; not a credential channel. */
9210
9992
  readonly metadata?: Record<string, unknown>;
9211
9993
  }
9212
9994
 
9213
- declare interface UriResolvedCredential {
9214
- /** Bearer for OAuth schemes, apiKey for api-key schemes. Host-resolved and refreshed. */
9215
- readonly accessToken: string;
9216
- readonly accountId: string;
9217
- readonly schemeId: string;
9218
- readonly scopes?: readonly string[];
9219
- /**
9220
- * Every value of a multi-field api-key credential, keyed by the scheme's declared field id
9221
- * (`accessToken` mirrors the primary field). Absent for single-secret schemes.
9222
- */
9223
- readonly fields?: Readonly<Record<string, string>>;
9224
- }
9225
-
9226
9995
  declare interface UriResourceDescription {
9227
9996
  readonly uri: string;
9228
9997
  /** Absent when the caller should retain its existing label. */
@@ -9242,12 +10011,9 @@ declare interface UriResourceLabel {
9242
10011
  readonly section?: string;
9243
10012
  }
9244
10013
 
9245
- /** The call a module is asked to label. */
9246
- declare interface UriResourceLabelRequest {
9247
- readonly operation: UriOperation;
9248
- /** The dispatch address: selector-stripped, the exact form the module's verb handler received. */
9249
- readonly uri: string;
9250
- }
10014
+ declare const UriScheme: z.ZodString;
10015
+
10016
+ declare type UriScheme = z.infer<typeof UriScheme>;
9251
10017
 
9252
10018
  declare interface UriSchemeClaim {
9253
10019
  readonly kind: "scheme";
@@ -9282,15 +10048,21 @@ declare interface UriSearchResult {
9282
10048
  * so the caller knows the matches may be partial.
9283
10049
  */
9284
10050
  readonly metadata?: UriListMetadata;
10051
+ /** How long the host may cache this result; see {@link UriCachePolicy.ttlMs}. */
10052
+ readonly cachePolicy?: Pick<UriCachePolicy, "ttlMs">;
10053
+ /** The module's own markdown for this result, shown instead of the host's default rendering. */
10054
+ readonly markdown?: string;
10055
+ /** The touched resource's name for the transcript row, as on `RawResource.label`. */
10056
+ readonly label?: UriResourceLabel;
9285
10057
  }
9286
10058
 
9287
10059
  /**
9288
10060
  * What a copied resource IS, minus its bytes: the identity half of a mediated
9289
10061
  * source. The host derives it once (filename inferred from the source address,
9290
10062
  * `mimeType` carried from the source module's own `RawResource.mimeType`) and
9291
- * delivers it on both byte-bearing write hooks — inside each `UriWriteSource`,
9292
- * and as the standalone third argument to `writeBytes` — so a remote sink can
9293
- * name and type what it stores without re-deriving either from raw bytes.
10063
+ * delivers it inside each `UriWriteSource`, and on `ctx.writeSource` for a
10064
+ * `source` copy - so a remote sink can name and type what it stores without
10065
+ * re-deriving either from raw bytes.
9294
10066
  */
9295
10067
  declare interface UriSourceDescriptor {
9296
10068
  readonly uri: string;
@@ -9305,11 +10077,11 @@ declare interface UriTemplate {
9305
10077
  /**
9306
10078
  * Per-operation effect docs for this route, keyed by operation. A route
9307
10079
  * whose shared {@link description} spans several verbs ("Read, edit, or
9308
- * trash a thread") is ambiguous for any surface that groups by verb — a
10080
+ * trash a thread") is ambiguous for any surface that groups by verb - a
9309
10081
  * permission view must not describe `read` with a sentence that mentions
9310
10082
  * trashing. Each entry says what THAT operation concretely does at THIS
9311
10083
  * route; operations without an entry fall back to the shared description.
9312
- * Additive and optional — existing templates are unaffected.
10084
+ * Additive and optional - existing templates are unaffected.
9313
10085
  */
9314
10086
  readonly operationDocs?: Partial<Record<UriOperation, string>>;
9315
10087
  /**
@@ -9349,13 +10121,13 @@ declare interface UriValidationMessage {
9349
10121
 
9350
10122
  /**
9351
10123
  * Author-declared, human-facing documentation of what each supported verb
9352
- * CONCRETELY does against the provider — the capability half of a module's
10124
+ * CONCRETELY does against the provider - the capability half of a module's
9353
10125
  * boolean verb-support declaration. Written for a permission/capability
9354
10126
  * surface ("what am I allowing?"), not for the model: one plain sentence per
9355
10127
  * verb naming the real-world effect ("Sends mail, creates/updates drafts,
9356
10128
  * applies label changes"), never wire mechanics or URI shapes. Verbs the
9357
10129
  * module does not support are omitted; at runtime a supported verb with no
9358
- * entry simply renders undocumented (fail-open metadata) — but for a plugin's
10130
+ * entry simply renders undocumented (fail-open metadata) - but for a plugin's
9359
10131
  * `kay.agent` URI modules, `auditPluginDisclosure` (`@bonsai/plugin-sdk/
9360
10132
  * disclosure`) enforces coverage of every supported verb at authoring/load
9361
10133
  * time.
@@ -9363,7 +10135,7 @@ declare interface UriValidationMessage {
9363
10135
  * Lives here in the protocol layer (beside {@link UriTemplate.operationDocs},
9364
10136
  * its per-route sibling) so both `@herd/uri-extensions`' `UriExtensionModule`
9365
10137
  * and herd-agent's kernel `UriModule` can carry it without inverting the
9366
- * `@herd/uri` ← `@herd/uri-extensions` dependency.
10138
+ * `@herd/uri` <- `@herd/uri-extensions` dependency.
9367
10139
  */
9368
10140
  declare interface UriVerbDocs {
9369
10141
  /** What a `read` of this address space returns (also covers `help`). */
@@ -9386,6 +10158,7 @@ declare interface UriVerbSupport {
9386
10158
  readonly editable: boolean;
9387
10159
  readonly deletable?: boolean;
9388
10160
  readonly listable: boolean;
10161
+ readonly searchable?: boolean;
9389
10162
  }
9390
10163
 
9391
10164
  /** A mediated resource supplied beside structured write content without entering model context. */
@@ -9393,10 +10166,15 @@ declare interface UriWriteSource extends UriSourceDescriptor {
9393
10166
  readonly content: string | Uint8Array;
9394
10167
  }
9395
10168
 
10169
+ /** {@link hostWithinDomain} over a URL's hostname; an unparseable URL is never within. */
10170
+ declare function urlHostWithinDomain(url: string, domain: string): boolean;
10171
+
10172
+ /** A claim on a public web link (`http` or `https`), usually folded onto the module's own scheme. */
9396
10173
  declare interface UrlPatternClaim {
9397
10174
  readonly kind: "urlPattern";
9398
10175
  readonly pattern: UriPatternInit;
9399
- readonly normalizeTo?: string;
10176
+ readonly normalizeTo?: UriClaimTemplate;
10177
+ readonly route?: string;
9400
10178
  readonly examples?: readonly string[];
9401
10179
  }
9402
10180
 
@@ -9443,6 +10221,66 @@ declare interface ValidateApiKeyCredentialResult {
9443
10221
 
9444
10222
  declare type ValidatePluginApiKeyCredential = ValidateApiKeyCredential;
9445
10223
 
10224
+ /**
10225
+ * ---
10226
+ * responsibility: The one interface through which a vendored-MCP plugin's code calls a tool on its
10227
+ * own vendored MCP server, so every read-through plugin and the daemon that implements the port
10228
+ * agree on the same shape by construction.
10229
+ * owns:
10230
+ * - {@link VendoredMcpPort} - what a plugin's URI module and tools call.
10231
+ * - {@link VendoredMcpOutcome} - the typed result a plugin decodes, including every failure it
10232
+ * must surface to the agent rather than swallow.
10233
+ * does_not_own:
10234
+ * - The implementation. A plugin never dials MCP itself: it takes the port from
10235
+ * `api.vendoredMcp.port(server)` (`@bonsai/plugin-sdk/kay-agent`), which the daemon binds to the
10236
+ * plugin's own hosted server over its MCP host. Plugin source stays host-free, and a plugin
10237
+ * under test substitutes a fake port.
10238
+ * - Which tool names exist. `TTool` is the plugin's own union of the vendor tools it calls; the
10239
+ * port is generic so a typo in a tool name fails typecheck inside the plugin.
10240
+ * ---
10241
+ */
10242
+ /**
10243
+ * Why a call failed, as the plugin must tell the agent. `needs-auth` carries the exact service to
10244
+ * reconnect (`serviceId`) so a plugin can name it in its error instead of returning an empty
10245
+ * result; today's plugins name their statically declared `requiresAuth` service instead, which is
10246
+ * the same service for a single-service plugin. `unknown-server` and `unknown-tool` are wiring
10247
+ * faults (a server Bonsai does not host for this plugin, or a tool name the server does not have)
10248
+ * and stay loud for the same reason: an unreachable host must never decode as "no data".
10249
+ */
10250
+ declare type VendoredMcpFailureReason = "needs-auth" | "unknown-server" | "unknown-tool" | "failed";
10251
+
10252
+ declare type VendoredMcpOutcome =
10253
+ /**
10254
+ * `value` is the tool's `structuredContent` when it sent one, else its text content parsed as
10255
+ * JSON, else that text as a string: a vendor tool that answers in prose (a transcript, a
10256
+ * markdown summary) is a result, not a failure. A route that needs a record rejects a string
10257
+ * itself, with its own message.
10258
+ */
10259
+ {
10260
+ readonly ok: true;
10261
+ readonly value: unknown;
10262
+ } | {
10263
+ readonly ok: false;
10264
+ readonly reason: VendoredMcpFailureReason;
10265
+ readonly message: string;
10266
+ /** Exact reconnect target supplied by the MCP host for an auth failure. */
10267
+ readonly serviceId?: string;
10268
+ };
10269
+
10270
+ /**
10271
+ * One vendored MCP server, as the plugin that declares it calls it. `invoke` calls any tool the
10272
+ * server has, whether it reads or writes. The port checks no permission of its own: a call is as
10273
+ * permitted as the plugin tool or URI verb that makes it.
10274
+ */
10275
+ declare interface VendoredMcpPort<TTool extends string = string> {
10276
+ invoke(input: {
10277
+ /** The session's working directory; the host resolves the server binding per cwd. */
10278
+ readonly cwd: string;
10279
+ readonly tool: TTool;
10280
+ readonly args: Readonly<Record<string, unknown>>;
10281
+ }): Promise<VendoredMcpOutcome>;
10282
+ }
10283
+
9446
10284
  /** Creates a Void type. */
9447
10285
  declare function Void(options?: TSchemaOptions): TVoid;
9448
10286
 
@@ -9477,8 +10315,8 @@ declare const WhiteboardEntryInfo: z.ZodObject<{
9477
10315
  authorSessionId: z.ZodNullable<z.ZodString>;
9478
10316
  }, z.core.$strict>>;
9479
10317
  state: z.ZodEnum<{
9480
- available: "available";
9481
10318
  unavailable: "unavailable";
10319
+ available: "available";
9482
10320
  "unsupported-version": "unsupported-version";
9483
10321
  invalid: "invalid";
9484
10322
  }>;
@@ -9499,8 +10337,8 @@ declare const WhiteboardEntryInspection: z.ZodObject<{
9499
10337
  authorSessionId: z.ZodNullable<z.ZodString>;
9500
10338
  }, z.core.$strict>>;
9501
10339
  state: z.ZodEnum<{
9502
- available: "available";
9503
10340
  unavailable: "unavailable";
10341
+ available: "available";
9504
10342
  "unsupported-version": "unsupported-version";
9505
10343
  invalid: "invalid";
9506
10344
  }>;