@yaklabs-ai/plugin-sdk 0.15.3 → 0.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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,636 @@ 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
+ * 0.27.0 makes a message approval card manifest data (#14574). `approvalTypes` declares the card's
2311
+ * facts and forms, and Bonsai draws it with the module's `describeOperation` and sends it with its
2312
+ * `write`. `app.approvals`, its `prepare`, `finalize` and `resolveResource` are gone, and a write can
2313
+ * refuse in the person's words with `UriProviderError.refusalMessage`. No plugin outside this repo
2314
+ * declared a card, so 0.26.3 stays served. A plugin that declares a card declares ^0.27.0.
2315
+ */
2316
+ declare const KAY_PLUGIN_SDK_VERSION: "0.27.0";
2317
+
2318
+ declare const KAY_PLUGIN_TARGET_NAMES: readonly ["kay.agent", "kay.app", "kay.ui"];
2319
+
2320
+ /** A plugin's Bonsai agent activation - receives only the brokered {@link KayAgentPluginApi}. */
1725
2321
  declare type KayAgentActivate = (api: KayAgentPluginApi) => void;
1726
2322
 
2323
+ /** Browser compilation over caller-supplied bytes, without filesystem or network authority. */
2324
+ declare interface KayAgentCompiler {
2325
+ /** Rejects on compilation or source callback failure, and when the host composed no compiler. */
2326
+ bundleBrowser(request: KayBrowserBundleRequest, sources: KayBrowserSources): Promise<KayBrowserBundle>;
2327
+ }
2328
+
2329
+ /**
2330
+ * The plugin-facing `kay.agent` own-endpoint call: a plugin's agent part calls an endpoint its own
2331
+ * `kay.app` part registered. SDK 0.17.3; a plugin that calls it declares `compatibility.sdk`
2332
+ * `^0.19.0`.
2333
+ *
2334
+ * A plugin's two parts load as separate modules and share no memory, so this is how agent code
2335
+ * reaches state its app part keeps (a worker, a database). The manifest says which endpoints the
2336
+ * agent part may call: the `kay.app` target declares the endpoint with a `register` grant, and
2337
+ * `permissions.byTarget["kay.agent"].endpoints` lists it with `invoke`. No other plugin's endpoint
2338
+ * is nameable.
2339
+ *
2340
+ * An endpoint whose only `invoke` grant is the plugin's own `kay.agent` target is not reachable
2341
+ * from Bonsai's surfaces (its pages and runtime clients), so an action the agent takes after the
2342
+ * session's approval card cannot be taken around it.
2343
+ */
2344
+ /** Why an {@link KayAgentEndpoints.invoke} call produced no value. */
2345
+ declare type KayAgentEndpointFailureReason =
2346
+ /** The manifest grants this plugin's `kay.agent` target no `invoke` on this endpoint. */
2347
+ "not-declared"
2348
+ /** The plugin's `kay.app` part is not running, or registered no handler for it. */
2349
+ | "unavailable"
2350
+ /** The handler did not answer within the host's fixed deadline (10 seconds). It may still finish. */
2351
+ | "timed-out"
2352
+ /** The caller's `signal` aborted. The handler may still finish. */
2353
+ | "cancelled"
2354
+ /** The handler threw; `message` is its error's message. */
2355
+ | "failed"
2356
+ /** The request or the handler's response does not match the endpoint's declared schema. */
2357
+ | "invalid";
2358
+
2359
+ declare type KayAgentEndpointOutcome = {
2360
+ readonly ok: true;
2361
+ readonly value: unknown;
2362
+ } | {
2363
+ readonly ok: false;
2364
+ readonly reason: KayAgentEndpointFailureReason;
2365
+ readonly message: string;
2366
+ };
2367
+
2368
+ declare interface KayAgentEndpoints {
2369
+ /**
2370
+ * Call `endpoint`, the local id this plugin's manifest declares on its `kay.app` target. Never
2371
+ * rejects: every failure is an outcome. Nothing is queued: when the app part is not running the
2372
+ * answer is `unavailable` at once.
2373
+ */
2374
+ invoke(endpoint: string, payload: unknown, options?: {
2375
+ readonly signal?: AbortSignal;
2376
+ }): Promise<KayAgentEndpointOutcome>;
2377
+ }
2378
+
1727
2379
  /**
1728
2380
  * Publishes the plugin's declared events from agent code. See {@link KayAgentPluginApi.events}.
1729
2381
  */
@@ -1767,7 +2419,7 @@ declare interface KayAgentInteractionSurfaces {
1767
2419
  readonly desktop: InteractionSurface;
1768
2420
  }
1769
2421
 
1770
- /** One knowledge-index hit — a reference and within-list rank score, never a snippet. */
2422
+ /** One knowledge-index hit - a reference and within-list rank score, never a snippet. */
1771
2423
  declare interface KayAgentKnowledgeHit {
1772
2424
  readonly uri: string;
1773
2425
  /** Comparable only with other hits returned by this search call. */
@@ -1777,7 +2429,7 @@ declare interface KayAgentKnowledgeHit {
1777
2429
  /**
1778
2430
  * Query the daemon's shared knowledge index, scoped to this plugin's own declared `kay.agent`
1779
2431
  * 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
2432
+ * `knowledgeSearch` grant AND the host effectively grants it - a plugin must feature-detect the
1781
2433
  * capability. (It is WIRING, so absence is still the signal here, unlike the claim-checked
1782
2434
  * capabilities around it.) The host pre-scopes every call to the
1783
2435
  * plugin's OWN declared URI schemes; a caller-supplied `uriPrefixes` outside that scope is dropped
@@ -1793,7 +2445,7 @@ declare type KayAgentKnowledgeSearch = (query: string, opts?: {
1793
2445
  /**
1794
2446
  * One row of the daemon's known-resources ledger: a resource the host has
1795
2447
  * 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
2448
+ * through a host-side grant flow). Metadata facts, never content - content
1797
2449
  * understanding stays with the knowledge index.
1798
2450
  */
1799
2451
  declare interface KayAgentKnownResource {
@@ -1808,7 +2460,7 @@ declare interface KayAgentKnownResource {
1808
2460
  readonly seenVia: KayAgentKnownResourceSeenVia;
1809
2461
  /**
1810
2462
  * 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
2463
+ * ordinary sighting never erases it - the row permanently remembers it entered the corpus
1812
2464
  * through an explicit user grant.
1813
2465
  */
1814
2466
  readonly grantedVia?: string;
@@ -1827,12 +2479,12 @@ declare interface KayAgentKnownResource {
1827
2479
  * (the host-recorded memory that `contributes.uriModules[].knownResources`
1828
2480
  * opts a module's traffic into). Deny-by-default: `api.knownResources` is
1829
2481
  * absent unless the manifest requests the `knownResources` grant AND the host
1830
- * effectively grants it — feature-detect it exactly like
2482
+ * effectively grants it - feature-detect it exactly like
1831
2483
  * {@link KayAgentKnowledgeSearch}, the other WIRING capability. Every call is host-scoped to the plugin's
1832
2484
  * OWN declared module ids; there is no way to widen it. Read-only by design:
1833
2485
  * rows are created only by the host's recording wrap and host-side grant
1834
2486
  * 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
2487
+ * is a cache of CLAIMS, not an authority - a returned row proves the resource
1836
2488
  * was once seen, never that it is still reachable.
1837
2489
  */
1838
2490
  declare interface KayAgentKnownResources {
@@ -1862,7 +2514,7 @@ declare interface KayAgentKnownResources {
1862
2514
  * How a known-resource row was MOST RECENTLY seen: one of the recording
1863
2515
  * wrap's verbs, or a host-side grant writer's `granted:<label>` (e.g. a
1864
2516
  * Drive picker completion writing `granted:picked`). The closed template
1865
- * union keeps the contract honest without enumerating grant labels — a
2517
+ * union keeps the contract honest without enumerating grant labels - a
1866
2518
  * plain-string widening here would silently admit junk provenance.
1867
2519
  */
1868
2520
  declare type KayAgentKnownResourceSeenVia = "read" | "list" | "search" | `granted:${string}`;
@@ -1871,7 +2523,7 @@ declare type KayAgentKnownResourceSeenVia = "read" | "list" | "search" | `grante
1871
2523
  * Claim-checked outbound HTTP. The handle is always present; authority is the CALL, not the handle.
1872
2524
  *
1873
2525
  * 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
2526
+ * calls, not the code around it. What differs is invisible from here - the host builds an exact
1875
2527
  * `network.connect {connect}` claim from the live URL (and again from each redirect hop), checks it
1876
2528
  * against the user's grant ledger, and rejects with a {@link KayAgentCapabilityError} rather than
1877
2529
  * performing the request. There is no `allowedHosts` to read: a plugin cannot enumerate its own
@@ -1896,13 +2548,19 @@ declare interface KayAgentPluginApi {
1896
2548
  readonly knowledgeSearch?: KayAgentKnowledgeSearch;
1897
2549
  /** Absent unless the `knownResources` grant is requested and effectively granted. */
1898
2550
  readonly knownResources?: KayAgentKnownResources;
2551
+ /**
2552
+ * Optional: absent unless a simulating Bonsai daemon serves this plugin a simulator (SDK 0.18.1).
2553
+ * A production daemon never does. In this version only plugins the release carries are served;
2554
+ * any plugin may ship a `./simulator` later. See {@link KayServedSimulators}.
2555
+ */
2556
+ readonly servedSimulators?: KayServedSimulators;
1899
2557
  /**
1900
2558
  * Present when the host composes Whiteboard. Each operation is checked against its exact
1901
2559
  * `whiteboard.<operation> {scope}` claim; handle presence is not consent.
1902
2560
  */
1903
2561
  readonly whiteboard?: KayAgentWhiteboard;
1904
2562
  /**
1905
- * Always present; each request — and each redirect hop — is checked against
2563
+ * Always present; each request - and each redirect hop - is checked against
1906
2564
  * `network.connect {connect}` for the host it is about to reach.
1907
2565
  */
1908
2566
  readonly network: KayAgentNetwork;
@@ -1933,6 +2591,8 @@ declare interface KayAgentPluginApi {
1933
2591
  * (SDK 0.13.8).
1934
2592
  */
1935
2593
  readonly systemOne: SystemOneClient;
2594
+ /** Always present and ungated; compiles only caller-supplied bytes. SDK 0.18.4. */
2595
+ readonly compiler: KayAgentCompiler;
1936
2596
  /**
1937
2597
  * The daemon's clock and the time zone the user is in, read together on each `snapshot()` call.
1938
2598
  * Always present and ungated: it confers no authority, since a plugin runs in the daemon's
@@ -1955,18 +2615,50 @@ declare interface KayAgentPluginApi {
1955
2615
  */
1956
2616
  readonly events: KayAgentEvents;
1957
2617
  /**
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.
2618
+ * A logger whose lines land in Bonsai's own log under this plugin's id, filed on this
2619
+ * activation's session. Always present and ungated, like {@link time}. Use it instead of
2620
+ * `console`; see {@link KayPluginLog}.
2621
+ */
2622
+ readonly log: KayPluginLog;
2623
+ /**
2624
+ * Calls tools on the vendored MCP servers this plugin declares (`kay.app`
2625
+ * `contributes.mcpServers`), signed in through the plugin's own auth service. A port reaches
2626
+ * only this plugin's own server, and only while Bonsai hosts it for this plugin under its
2627
+ * `mcp.host-server` grant; it never reaches another plugin's server or one the user configured.
2628
+ * It calls any tool the server has, writes included. Always present; a host with no MCP host
2629
+ * answers each call `failed`. Types: `@bonsai/plugin-sdk/vendored-mcp` (SDK 0.16.2).
2630
+ */
2631
+ readonly vendoredMcp: KayAgentVendoredMcp;
2632
+ /**
2633
+ * Calls the endpoints this plugin's own `kay.app` part registered and the manifest grants this
2634
+ * target `invoke` on, by local id. Never another plugin's. Every failure is an outcome, never a
2635
+ * rejection; the host fixes a 10-second deadline. Always present; a host with no app part
2636
+ * answers each call `unavailable`. See {@link KayAgentEndpoints} (SDK 0.17.3).
2637
+ */
2638
+ readonly endpoints: KayAgentEndpoints;
2639
+ /**
2640
+ * Supply the `execute` of a tool kay-plugin.json describes (its entry declares `parameters`),
2641
+ * by the entry's `localId`. The host builds the rest of the tool - name, label, description,
2642
+ * parameters, auth binding and permission - from that entry. Build `execute` with
2643
+ * {@link toolExecute}. For a tool declaring `authService`, a session resolves the credential
2644
+ * onto `ctx.credential` before each call (for the account its `accountId` argument names, when
2645
+ * the tool declares one) and never calls `execute` without one; outside a session it may be
2646
+ * absent (SDK 0.20.2).
2647
+ */
2648
+ implementTool(localId: string, execute: KayAgentToolExecute): void;
2649
+ /**
2650
+ * Register a whole tool definition, for a declared tool whose entry has no `parameters` because
2651
+ * part of its policy is code. Such a plugin cannot run in the plugin host; prefer
2652
+ * {@link implementTool}. To auth-gate one, build the definition with {@link withResolvedAuth}
2653
+ * (or `defineAuthedTool`).
1963
2654
  */
1964
2655
  registerTool(definition: ToolDefinition): void;
1965
2656
  /**
1966
2657
  * 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.
2658
+ * kernel's `registerSearchProvider`. An own-credential provider carries its `requiresAuth` binding -
2659
+ * build it with {@link withResolvedSearchProvider}, which stamps the binding; the host resolves the
2660
+ * credential before each call and hands it to the body. A provider without a binding reuses
2661
+ * inference credentials or needs none.
1970
2662
  */
1971
2663
  registerSearchProvider(provider: SearchProvider): void;
1972
2664
  /**
@@ -1985,23 +2677,34 @@ declare interface KayAgentPluginApi {
1985
2677
  registerConverter(converter: ContentConverter): void;
1986
2678
  /**
1987
2679
  * 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
2680
+ * a tool-call interceptor - the neutral around-execution seam over EVERY tool call in the session
1989
2681
  * (any layer's tools, subagent sessions included): observe a call, delegate to the real tool via
1990
2682
  * `next()`, or return a substitute result so the real tool never runs. Materially higher
1991
2683
  * 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
2684
+ * a claim of its own. The permission gate still runs before execution - an interceptor can
1993
2685
  * substitute results, never widen what is callable.
1994
2686
  */
1995
2687
  interceptToolCall(interceptor: ToolCallInterceptor): void;
2688
+ /**
2689
+ * Sign-in. The plugin's manifest (`auth.services`) declares every service and scheme and the
2690
+ * facts the host shows and runs; plugin code registers only the functions a scheme needs.
2691
+ */
1996
2692
  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;
2693
+ /**
2694
+ * The check for an `api-key` scheme the manifest marks `validates: true`. `serviceId` and
2695
+ * `schemeId` are the manifest's global ids (`github.account`, `github.pat`).
2696
+ */
2697
+ registerValidator(serviceId: string, schemeId: string, validate: ValidatePluginApiKeyCredential): void;
2698
+ /**
2699
+ * The account lookup for an `oauth2` scheme whose manifest names it as where the account comes
2700
+ * from (`account: "resolveAccountId"` or `"resolveAccounts"`), registering that function only.
2701
+ * Refused on any other scheme.
2702
+ */
2703
+ registerAccountResolver(serviceId: string, schemeId: string, resolver: PluginAccountResolver): void;
2001
2704
  /**
2002
2705
  * 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`.
2706
+ * service. The host maps it onto the kernel's `DelegatedAuthScheme` and drives its `connect`
2707
+ * from `service_connect`.
2005
2708
  */
2006
2709
  declareDelegatedScheme(serviceId: string, scheme: PluginDelegatedScheme): void;
2007
2710
  /**
@@ -2021,7 +2724,7 @@ declare interface KayAgentPluginApi {
2021
2724
  * {@link KayAgentProcessRunResult.exitCode} always means the command itself failed, never that
2022
2725
  * permission did. Two DIFFERENT things reject, and a caller should handle both: a refusal rejects
2023
2726
  * with {@link KayAgentCapabilityError}, and cancelling through
2024
- * {@link KayAgentProcessRunRequest.signal} rejects with an `AbortError` carrying a fixed message —
2727
+ * {@link KayAgentProcessRunRequest.signal} rejects with an `AbortError` carrying a fixed message -
2025
2728
  * the host terminates the child and settles its output first, and deliberately does not echo back
2026
2729
  * the `reason` you aborted with. A user cancelling a run is not a crash. A timeout is NOT a
2027
2730
  * rejection: it resolves with exit code 124. The termination bound covers the DIRECT child only: a
@@ -2045,12 +2748,12 @@ declare interface KayAgentProcessRunRequest {
2045
2748
  readonly command: string;
2046
2749
  readonly argvClass: string;
2047
2750
  readonly args: readonly string[];
2048
- /** Abort the run; the host kills the DIRECT child and rejects — a forked descendant may outlive it (#6153). */
2751
+ /** Abort the run; the host kills the DIRECT child and rejects - a forked descendant may outlive it (#6153). */
2049
2752
  readonly signal?: AbortSignal;
2050
2753
  }
2051
2754
 
2052
2755
  /**
2053
- * A finished run. `stdout`/`stderr` are decoded text, and the host CAPS them — a truncation flag is
2756
+ * A finished run. `stdout`/`stderr` are decoded text, and the host CAPS them - a truncation flag is
2054
2757
  * set rather than the output silently ending, so a plugin parsing the stream can tell "the command
2055
2758
  * printed this" apart from "the command printed more than we kept".
2056
2759
  */
@@ -2062,6 +2765,28 @@ declare interface KayAgentProcessRunResult {
2062
2765
  readonly stderrTruncated?: boolean;
2063
2766
  }
2064
2767
 
2768
+ /** The `execute` a plugin supplies for a tool its manifest describes. */
2769
+ declare type KayAgentToolExecute = ToolDefinition["execute"];
2770
+
2771
+ /**
2772
+ * Calls tools on the plugin's own vendored MCP servers. See {@link KayAgentPluginApi.vendoredMcp}.
2773
+ */
2774
+ declare interface KayAgentVendoredMcp {
2775
+ /**
2776
+ * The port to `server`, a name this plugin declares in its `kay.app` target's
2777
+ * `contributes.mcpServers`. Throws when the plugin declares no server of that name. A declared
2778
+ * server that Bonsai does not host for this plugin still returns a port; each call then answers
2779
+ * `unknown-server`. Its message names the plugin that holds the name, or else lists the possible
2780
+ * reasons (no approved `mcp.host-server` grant, a sign-in the server cannot use, or a name that
2781
+ * a plugin Bonsai ships or another installed plugin also claims) and points at the plugin's load
2782
+ * diagnostic.
2783
+ *
2784
+ * A port reaches this plugin's own server only. To use another plugin's data, declare that
2785
+ * plugin in the manifest's `dependencies.plugins`; a later SDK surface reaches it.
2786
+ */
2787
+ port<TTool extends string = string>(server: string): VendoredMcpPort<TTool>;
2788
+ }
2789
+
2065
2790
  /**
2066
2791
  * Authenticated requests through the host's embedded-browser partition. The host confines every
2067
2792
  * call to the exact origin/path rows declared in the plugin manifest (segment-bounded), never
@@ -2180,7 +2905,7 @@ declare interface KayAgentWhiteboardUpdateInput {
2180
2905
  /**
2181
2906
  * `app.time` / `api.time`: host facts about time. Added to `kay.app` in SDK 0.12.1 and to
2182
2907
  * `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
2908
+ * `compatibility.sdk` `^0.19.0`, so an older Bonsai refuses a directory or repository plugin at
2184
2909
  * load instead of the call failing.
2185
2910
  */
2186
2911
  declare interface KayAppTime {
@@ -2190,9 +2915,7 @@ declare interface KayAppTime {
2190
2915
  * Every call asks the host again and returns a new object; nothing is cached. Call it where you
2191
2916
  * need the value (inside a tool, a handler or a workflow step), not inside `activate()`: a value
2192
2917
  * 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.
2918
+ * against a recording api that has no `time`, so a read there throws.
2196
2919
  * Keep the result for as long as you use it, and not across a long wait (a sleep, a timer, a
2197
2920
  * queued run): the zone can change while you wait. On a daemon that runs on the user's machine,
2198
2921
  * a change of the process's `TZ` shows in the next call. A `TZ` that names a zone wins over the
@@ -2283,7 +3006,7 @@ declare interface KayAppUserFileListing {
2283
3006
  * File I/O in user folders, run by the host off the daemon's main thread. Every path is absolute.
2284
3007
  * No method rejects; each answers a {@link KayAppUserFilesResult}. This is not a sandbox: it does
2285
3008
  * 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
3009
+ * `compatibility.sdk` `^0.19.0`, so an older Bonsai refuses a directory or repository plugin at
2287
3010
  * load instead of the call failing.
2288
3011
  */
2289
3012
  declare interface KayAppUserFiles {
@@ -2379,6 +3102,119 @@ declare interface KayAppUserFileWalkEntry {
2379
3102
  readonly kind: KayAppUserFileKind;
2380
3103
  }
2381
3104
 
3105
+ declare interface KayBrowserBundle {
3106
+ readonly outputs: readonly {
3107
+ readonly path: string;
3108
+ readonly text: string;
3109
+ }[];
3110
+ readonly warnings: readonly string[];
3111
+ }
3112
+
3113
+ declare interface KayBrowserBundleRequest {
3114
+ readonly entry: {
3115
+ readonly path: string;
3116
+ } | {
3117
+ readonly path: string;
3118
+ readonly contents: string;
3119
+ readonly loader: "js" | "css";
3120
+ };
3121
+ readonly target: string;
3122
+ readonly format: "iife" | "esm";
3123
+ readonly charset: "ascii" | "utf8";
3124
+ readonly minifyWhitespace: boolean;
3125
+ readonly legalComments: "none" | "inline";
3126
+ }
3127
+
3128
+ /** Import edges the host reports to a browser bundle's source resolver. */
3129
+ declare type KayBrowserImportKind = "entry-point" | "import-statement" | "require-call" | "dynamic-import" | "require-resolve" | "import-rule" | "composes-from" | "url-token";
3130
+
3131
+ /** All imports and bytes come from these callbacks; the host performs no fallback I/O. */
3132
+ declare interface KayBrowserSources {
3133
+ resolve(request: {
3134
+ readonly path: string;
3135
+ readonly importer: string;
3136
+ readonly kind: KayBrowserImportKind;
3137
+ }): Promise<{
3138
+ readonly path: string;
3139
+ readonly external?: boolean;
3140
+ readonly suffix?: string;
3141
+ }>;
3142
+ load(request: {
3143
+ readonly path: string;
3144
+ }): Promise<{
3145
+ readonly contents: string | Uint8Array;
3146
+ readonly loader: "js" | "css" | "json" | "dataurl";
3147
+ }>;
3148
+ }
3149
+
3150
+ /**
3151
+ * Each method writes one line. `name` is a stable event name (`sync-failed`), the same for every
3152
+ * line of one kind, so the line can be found by name; put what varies in `message` and
3153
+ * `attributes`. A call never throws, and it keeps working after the plugin is disabled.
3154
+ */
3155
+ declare interface KayPluginLog {
3156
+ debug(name: string, message: string, details?: KayPluginLogDetails): void;
3157
+ info(name: string, message: string, details?: KayPluginLogDetails): void;
3158
+ warn(name: string, message: string, details?: KayPluginLogDetails): void;
3159
+ error(name: string, message: string, details?: KayPluginLogDetails): void;
3160
+ /** A logger that adds these attributes to every line it writes, after the line's own. */
3161
+ child(attributes: KayPluginLogAttributes): KayPluginLog;
3162
+ }
3163
+
3164
+ /**
3165
+ * The plugin-facing log contract: a logger each plugin gets as `api.log`, whose lines land in
3166
+ * Bonsai's own log beside the daemon's.
3167
+ *
3168
+ * Ungated and always present, like `api.time`. It confers no authority: a line is written under the
3169
+ * plugin's own id, which the host stamps and the plugin cannot set, and a plugin can already print
3170
+ * to the console. The host records each line as `<pluginId>.<name>`, files it on a session's log
3171
+ * when it knows the session, and keeps an error's stack, so a built-in plugin's failure reaches
3172
+ * Error Tracking. Lines from a plugin the user added stay on the user's machine.
3173
+ *
3174
+ * This is a leaf module. It lives beside both target SDKs (rather than inside `kay-app`) so the
3175
+ * `kay.agent` SDK can expose the same logger without importing the `kay.app` surface.
3176
+ */
3177
+ /**
3178
+ * Metadata only: ids, closed values, counts. Never session content. The host drops any value that
3179
+ * is not a string, a boolean or a finite number.
3180
+ */
3181
+ declare type KayPluginLogAttributes = Readonly<Record<string, string | number | boolean>>;
3182
+
3183
+ declare interface KayPluginLogDetails {
3184
+ /** The error object. The host keeps its message, its stack and its `cause` chain. */
3185
+ readonly error?: unknown;
3186
+ /**
3187
+ * The session this line is about. Defaults to the session of the call the plugin is handling,
3188
+ * when the host knows it (a tool call or a URI read); the line then lands on that session's
3189
+ * `kay-log://events`. Pass it from an endpoint, a session-event handler or a timer. Work a call
3190
+ * starts (a timer, a subscription) keeps that call's session unless a line names another.
3191
+ */
3192
+ readonly sessionId?: string;
3193
+ readonly attributes?: KayPluginLogAttributes;
3194
+ }
3195
+
3196
+ declare const KayPluginProtocolVersion: z.ZodLiteral<"kay.plugin.v1">;
3197
+
3198
+ declare type KayPluginProtocolVersion = z.infer<typeof KayPluginProtocolVersion>;
3199
+
3200
+ /**
3201
+ * The simulators a simulating Bonsai daemon (evals, dev, tests) serves this plugin, keyed by the
3202
+ * adapter id its simulated URI connection rows name. The daemon builds each from the plugin's own
3203
+ * dev-only `./simulator` subpath; the plugin never imports its simulator.
3204
+ *
3205
+ * The member that carries this, `servedSimulators` on both plugin APIs, is optional and absent
3206
+ * unless a simulating daemon serves it. A production daemon never does, so treat absence as the
3207
+ * normal case. In this version the daemon serves it only to plugins the release carries; the seam
3208
+ * is not specific to them, and any plugin may ship a `./simulator` later.
3209
+ */
3210
+ declare interface KayServedSimulators {
3211
+ /**
3212
+ * What this plugin's simulator built for `adapterId`, or `undefined` when none was served for
3213
+ * that id. The shape is the plugin's own simulator contract; narrow it before use.
3214
+ */
3215
+ get(adapterId: string): unknown;
3216
+ }
3217
+
2382
3218
  /** Applies a KeyOf action to the given type. */
2383
3219
  declare function KeyOf<Type extends TSchema>(type: Type, options?: TSchemaOptions): TKeyOf_2<Type>;
2384
3220
 
@@ -2484,6 +3320,10 @@ declare function LiteralStringMapping(input: string): unknown;
2484
3320
 
2485
3321
  declare function LiteralTypeName<Value extends TLiteralValue>(value: Value): TLiteralTypeName<Value>;
2486
3322
 
3323
+ declare const LocalContributionId: z.ZodString;
3324
+
3325
+ declare type LocalContributionId = z.infer<typeof LocalContributionId>;
3326
+
2487
3327
  /** Applies a Lowercase action to the given type. */
2488
3328
  declare function Lowercase_2<Type extends TSchema>(type: Type, options?: TSchemaOptions): TLowercase<Type>;
2489
3329
 
@@ -2557,7 +3397,7 @@ declare type MidTurnCompactionRequest = {
2557
3397
  * Settles exactly once and never rejects, including abort and teardown paths.
2558
3398
  *
2559
3399
  * 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 —
3400
+ * post-run compaction, and that cannot begin until this tool call returns -
2561
3401
  * so awaiting it here hangs the run behind itself, which is the exact
2562
3402
  * deadlock this capability replaced. Attach a callback and return.
2563
3403
  */
@@ -2567,6 +3407,10 @@ declare type MidTurnCompactionRequest = {
2567
3407
  readonly reason: "already-pending" | "maintenance-in-flight";
2568
3408
  };
2569
3409
 
3410
+ declare const MimeTypeString: z.ZodString;
3411
+
3412
+ declare type MimeTypeString = z.infer<typeof MimeTypeString>;
3413
+
2570
3414
  /** Compatibility settings for the Mistral chat API. */
2571
3415
  declare interface MistralConversationsCompat extends Static<typeof MistralConversationsCompatSchema> {
2572
3416
  }
@@ -2602,7 +3446,7 @@ declare interface Model<TApi extends Api> extends BaseModel<TApi> {
2602
3446
  }
2603
3447
 
2604
3448
  /**
2605
- * The canonical model-facing content type — the single source of truth for what
3449
+ * The canonical model-facing content type - the single source of truth for what
2606
3450
  * a `read`/`finalize` boundary emits and what an {@link AgentToolResult.content}
2607
3451
  * carries (it IS the same `(TextContent | ImageContent)` union). When pi-ai later
2608
3452
  * adds audio, this one alias widens and every consumer follows.
@@ -2687,6 +3531,8 @@ declare const NeverPattern = "(?!)";
2687
3531
 
2688
3532
  declare const NewLine = "\n";
2689
3533
 
3534
+ declare const NonEmptyString: z.ZodString;
3535
+
2690
3536
  /** Applies a NonNullable action to the given type. */
2691
3537
  declare function NonNullable_2<Type extends TSchema>(type: Type, options?: TSchemaOptions): TNonNullable<Type>;
2692
3538
 
@@ -2708,33 +3554,12 @@ declare const NumberKey = "^-?(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?$";
2708
3554
 
2709
3555
  declare const NumberPattern = "-?(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?";
2710
3556
 
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
3557
  /** What the provider hands back at the redirect URI after the user approves. */
2718
3558
  declare interface OAuthRedirectResult {
2719
3559
  readonly code: string;
2720
3560
  readonly state: string;
2721
3561
  }
2722
3562
 
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
3563
  /** Creates an Object type. */
2739
3564
  declare function _Object_<Properties extends TProperties>(properties: Properties, options?: TObjectOptions): TObject<Properties>;
2740
3565
 
@@ -2753,6 +3578,79 @@ declare type ObjectLike_4 = Record<PropertyKey, any>;
2753
3578
  /** Extracts options from a TObject. */
2754
3579
  declare function ObjectOptions(type: TObject): TObjectOptions;
2755
3580
 
3581
+ /**
3582
+ * A source-native read of one URI, shaped for downstream indexing rather than
3583
+ * direct model consumption. A module's read returns it in
3584
+ * `RawResource.observations`, built from the same fetch as the content, once
3585
+ * per document it wants indexed; absence means "not indexed".
3586
+ *
3587
+ * Gone signal: the index retires a doc when a read throws `UriNotFoundError`
3588
+ * or an error whose `.status === 404`. A module whose vendor signals "gone"
3589
+ * differently (HTTP 410, a 200-body `{ok:false}`/`archived:true`, a
3590
+ * 403/404-ambiguous lost-access response) must translate that into one of
3591
+ * those two shapes itself; losing access to a resource counts as gone too.
3592
+ * A read that cannot build a complete observation (a secondary fetch failed)
3593
+ * returns none rather than a partial one.
3594
+ *
3595
+ * Account-qualified identity: a module whose read can resolve against more
3596
+ * than one connected account (the account selector is stripped before route
3597
+ * parsing, so two accounts reading the same opaque provider id would
3598
+ * otherwise collide on one doc key) must fold `ctx.auth.credential.accountId`
3599
+ * into `uri` - e.g. `gmail://@<accountId>/threads/<threadId>` - never the
3600
+ * user-renamable connection/selector label. `ctx.auth.credential` is the
3601
+ * reliable source of the resolved account at a `requiresAuth` module's
3602
+ * `read()`: it is always populated once auth resolution runs (an
3603
+ * `authContext`-style check already throws first when no credential
3604
+ * resolved). `ctx.connection` may ALSO be populated by dispatch - a
3605
+ * connection-required module receives it whenever the host has a tracked
3606
+ * connection for the account - so do not assume it is absent; a module that
3607
+ * wants the human-facing label rather than the account id reads
3608
+ * `ctx.connection?.displayName`.
3609
+ *
3610
+ * `accountId` is guaranteed stable only for the life of the stored credential
3611
+ * record, not necessarily across a disconnect/reconnect of the same
3612
+ * real-world provider account. A scheme that mints an opaque id at connect
3613
+ * time (the default for an api-key or delegated scheme, absent a
3614
+ * provider-derived override) gets a fresh id on every connect; a scheme that
3615
+ * instead derives the id from provider data (an oauth2-pkce scheme supplying
3616
+ * `resolveAccountId`, e.g. reading an OIDC `sub`) keeps the same id across
3617
+ * reconnect. A module on a scheme without a provider-stable id has two honest
3618
+ * choices: derive its own stable identity from provider data at read time
3619
+ * (call the provider's own account/"who am I" endpoint with the resolved
3620
+ * credential and fold THAT into `uri` instead of `accountId`), or
3621
+ * account-qualify with `accountId` knowing a disconnect/reconnect of the same
3622
+ * real-world account re-keys its documents under a new id rather than
3623
+ * updating them in place - nothing on the host side reconciles the old and
3624
+ * new keys.
3625
+ */
3626
+ declare interface ObservationMaterial {
3627
+ /** Canonical URI of the observed resource - may differ from the input URI. */
3628
+ readonly uri: string;
3629
+ /** Scheme-prefixed content type, e.g. `"github.pr"`, `"gmail.thread"`, `"wikipedia.article"`. */
3630
+ readonly kind: string;
3631
+ readonly title?: string;
3632
+ /** Source-native seams: one element per message/comment/section, in source order. */
3633
+ readonly text?: readonly string[];
3634
+ /** ISO-8601 creation time; provenance only - not persisted by the store yet. */
3635
+ readonly createdAt?: string;
3636
+ /** ISO-8601 last-update time; provenance only - not persisted by the store yet. */
3637
+ readonly updatedAt?: string;
3638
+ /** Source-native revision/version marker, when the source exposes one. */
3639
+ readonly version?: string;
3640
+ /**
3641
+ * How long this resource stays worth semantically searching, measured from
3642
+ * the LAST time it was observed - not from when it was first indexed. Every
3643
+ * observation of an unchanged resource pushes the deadline out again, so a
3644
+ * thread that keeps getting read keeps living and a quiet one ages out.
3645
+ *
3646
+ * Retention is opt-in per resource: absent means the index keeps the
3647
+ * resource until its source says it is gone. A route declares this only
3648
+ * when staleness is real for that shape - a chat thread or an inbox
3649
+ * conversation goes cold, a durable note the user wrote does not.
3650
+ */
3651
+ readonly retainForMs?: number;
3652
+ }
3653
+
2756
3654
  /** Applies a Omit action using the given types. */
2757
3655
  declare function Omit_2<Type extends TSchema, Indexer extends PropertyKey[]>(type: Type, indexer: readonly [...Indexer], options?: TSchemaOptions): TOmit<Type, TKeysToIndexer<Indexer>>;
2758
3656
 
@@ -2875,6 +3773,10 @@ declare const OptionalSemiColon: (input: string) => [unknown, string] | [];
2875
3773
 
2876
3774
  declare function OptionalSemiColonMapping(input: [unknown] | []): unknown;
2877
3775
 
3776
+ declare const PackageAssetPath: z.ZodString;
3777
+
3778
+ declare type PackageAssetPath = z.infer<typeof PackageAssetPath>;
3779
+
2878
3780
  /** Creates a Parameter type. */
2879
3781
  declare function Parameter<Name extends string, Extends extends TSchema, Equals extends TSchema>(name: Name, extends_: Extends, equals: Equals): TParameter<Name, Extends, Equals>;
2880
3782
 
@@ -3243,7 +4145,7 @@ declare function Pick_2<Type extends TSchema, Indexer extends TSchema>(type: Typ
3243
4145
  declare function PickDeferred<Type extends TSchema, Indexer extends TSchema>(type: Type, indexer: Indexer, options?: TSchemaOptions): TPickDeferred<Type, Indexer>;
3244
4146
 
3245
4147
  /**
3246
- * The version of the conformance floor this SDK enforces — bumped whenever
4148
+ * The version of the conformance floor this SDK enforces - bumped whenever
3247
4149
  * {@link PLUGIN_DISCLOSURE_RULES} changes, and never for anything else.
3248
4150
  *
3249
4151
  * Two readers, and the second is why a number beats a changelog entry. A plugin author compares it
@@ -3252,7 +4154,7 @@ declare function PickDeferred<Type extends TSchema, Indexer extends TSchema>(typ
3252
4154
  * older SDK and then failed at load says "this host enforces contract N, you audited against
3253
4155
  * something older" instead of leaving the author to guess whether their code or their SDK moved.
3254
4156
  *
3255
- * `1` is the floor as published by #3210 — the first release in which an author could run this
4157
+ * `1` is the floor as published by #3210 - the first release in which an author could run this
3256
4158
  * audit at all. `2` (#6880) adds the route-contract rules and tightens
3257
4159
  * `template-operation-docs-missing` from "every verb on a multi-GROUP route" to "every verb on a
3258
4160
  * multi-verb route": the help renderer now prints a sentence per verb at the route, so a missing
@@ -3267,7 +4169,7 @@ export declare const PLUGIN_DISCLOSURE_CONTRACT = 2;
3267
4169
  * daemon enforces it fail-closed. A rule added here retroactively fails plugins that were correct
3268
4170
  * yesterday, so the set may not grow as a side effect of an edit: the pinned test beside this
3269
4171
  * 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
4172
+ * record it against a new {@link PLUGIN_DISCLOSURE_CONTRACT} - which is what forces the SDK release
3271
4173
  * that carries the new rule to the authors it now judges (#3210).
3272
4174
  *
3273
4175
  * Typing {@link DisclosureViolation.rule} as this union is the half that cannot be forgotten: a
@@ -3275,32 +4177,15 @@ export declare const PLUGIN_DISCLOSURE_CONTRACT = 2;
3275
4177
  */
3276
4178
  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
4179
 
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;
4180
+ /**
4181
+ * How a plugin learns which provider account a fresh OAuth token belongs to: one account, or every
4182
+ * account the grant reaches. Registered with `api.auth.registerAccountResolver`.
4183
+ */
4184
+ declare interface PluginAccountResolver {
4185
+ readonly resolveAccountId?: ResolvePluginAccountId;
4186
+ readonly resolveAccounts?: ResolvePluginAccounts;
3298
4187
  }
3299
4188
 
3300
- declare type PluginApiKeySchemeField = ApiKeyAuthSchemeField;
3301
-
3302
- declare type PluginApiKeySchemeSetup = ApiKeyAuthSchemeSetup;
3303
-
3304
4189
  /** The kinds of capability a plugin service can advertise (mirrors the host capability taxonomy). */
3305
4190
  declare type PluginCapabilityKind = "uri-module" | "tool" | "search-provider";
3306
4191
 
@@ -3317,12 +4202,12 @@ declare interface PluginDelegatedRevokeContext {
3317
4202
  }
3318
4203
 
3319
4204
  /**
3320
- * A protocol-agnostic scheme whose connect/refresh choreography the plugin provides — the host owns
4205
+ * A protocol-agnostic scheme whose connect/refresh choreography the plugin provides - the host owns
3321
4206
  * only vault persistence, gating, and single-flight refresh over the opaque material. Use this when
3322
4207
  * 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 →
4208
+ * authorization server: 401 sniff -> RFC 9728/8414 discovery -> RFC 7591 dynamic client registration ->
3324
4209
  * 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
4210
+ * when one is configured, and both are absent when the host has none - the scheme decides whether it
3326
4211
  * needs them and fails its connect with an actionable message if it does and they are missing.
3327
4212
  */
3328
4213
  declare interface PluginDelegatedScheme {
@@ -3375,83 +4260,18 @@ declare const PluginEventPublishInput: z.ZodObject<{
3375
4260
  poller: z.ZodOptional<z.ZodString>;
3376
4261
  runId: z.ZodOptional<z.ZodString>;
3377
4262
  }, z.core.$strict>>;
3378
- payload: z.ZodOptional<z.ZodType<JsonValue_2, unknown, z.core.$ZodTypeInternals<JsonValue_2, unknown>>>;
4263
+ payload: z.ZodOptional<z.ZodType<authoringCommonSchemas.JsonValue, unknown, z.core.$ZodTypeInternals<authoringCommonSchemas.JsonValue, unknown>>>;
3379
4264
  }, z.core.$strict>;
3380
4265
 
3381
4266
  declare type PluginEventPublishInput = z.infer<typeof PluginEventPublishInput>;
3382
4267
 
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
- }
4268
+ declare const PluginId: z.ZodString;
3411
4269
 
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
- };
4270
+ declare type PluginId = z.infer<typeof PluginId>;
3452
4271
 
3453
- /** BYOOC — the herd canonical client-axes union, aliased (never copied) for plugin authors. */
3454
- declare type PluginOAuthSchemeClientAxes = OAuthSchemeClientAxes;
4272
+ declare const PluginNamespace: z.ZodString;
4273
+
4274
+ declare type PluginNamespace = z.infer<typeof PluginNamespace>;
3455
4275
 
3456
4276
  /** What one connection flow unlocks and when an agent should choose it; grants no authority. */
3457
4277
  declare type PluginSchemeAgentHint = string;
@@ -3463,7 +4283,7 @@ declare interface PluginServiceCapability {
3463
4283
  readonly summary: string;
3464
4284
  }
3465
4285
 
3466
- /** A service the plugin owns — advertised for discovery and used to gate auth-backed contributions. */
4286
+ /** A service the plugin owns - advertised for discovery and used to gate auth-backed contributions. */
3467
4287
  declare interface PluginServiceDescriptor {
3468
4288
  readonly id: string;
3469
4289
  readonly display: string;
@@ -3473,7 +4293,7 @@ declare interface PluginServiceDescriptor {
3473
4293
 
3474
4294
  /**
3475
4295
  * 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
4296
+ * `SkillContribution` naming the in-package SKILL.md); this is the runtime half - bundled
3477
4297
  * plugins are compiled in with no package directory at runtime, so the package exports the
3478
4298
  * instructions as data beside the file and an in-package equivalence test pins the two
3479
4299
  * together (the same pattern as the manifest icon). `name` must equal the contribution's
@@ -3486,10 +4306,32 @@ declare interface PluginSkillDefinition {
3486
4306
  readonly name: string;
3487
4307
  /** Model-visible one-liner: when to reach for this skill. */
3488
4308
  readonly description: string;
3489
- /** The full instructions — what a filesystem skill carries below its frontmatter. */
4309
+ /** The full instructions - what a filesystem skill carries below its frontmatter. */
3490
4310
  readonly content: string;
3491
4311
  }
3492
4312
 
4313
+ declare const PluginSourceClass: z.ZodEnum<{
4314
+ bundled: "bundled";
4315
+ "org-shared": "org-shared";
4316
+ "user-local": "user-local";
4317
+ "verified-marketplace": "verified-marketplace";
4318
+ freeform: "freeform";
4319
+ }>;
4320
+
4321
+ declare type PluginSourceClass = z.infer<typeof PluginSourceClass>;
4322
+
4323
+ declare const PluginTargetName: z.ZodEnum<{
4324
+ "kay.agent": "kay.agent";
4325
+ "kay.app": "kay.app";
4326
+ "kay.ui": "kay.ui";
4327
+ }>;
4328
+
4329
+ declare type PluginTargetName = z.infer<typeof PluginTargetName>;
4330
+
4331
+ declare const PluginToolName: z.ZodString;
4332
+
4333
+ declare type PluginToolName = z.infer<typeof PluginToolName>;
4334
+
3493
4335
  declare const Properties: (input: string) => [unknown, string] | [];
3494
4336
 
3495
4337
  declare function PropertiesMapping(input: [unknown, unknown, unknown]): unknown;
@@ -3543,7 +4385,7 @@ declare type ProviderId = KnownProvider | string;
3543
4385
  declare interface ProxiedToolCall {
3544
4386
  /** The registered tool the carrier will invoke. */
3545
4387
  readonly toolName: string;
3546
- /** The input that tool will be invoked with — what the gate must classify. */
4388
+ /** The input that tool will be invoked with - what the gate must classify. */
3547
4389
  readonly input: Record<string, unknown>;
3548
4390
  }
3549
4391
 
@@ -3553,6 +4395,28 @@ declare interface RawResource {
3553
4395
  readonly mimeType: string;
3554
4396
  readonly metadata?: RawResourceMetadata;
3555
4397
  readonly selector?: SelectorDisposition;
4398
+ /**
4399
+ * The indexable forms of this resource, built from the same fetch as
4400
+ * `content`: one per document the read wants indexed (a thread read offers
4401
+ * one per message). Absent or empty means nothing is indexed. The read tool
4402
+ * takes them off before the cache and the model; other host reads
4403
+ * (out-of-band host reads) return them untouched.
4404
+ */
4405
+ readonly observations?: readonly ObservationMaterial[];
4406
+ /** How long the host may cache this result; see {@link UriCachePolicy.ttlMs}. */
4407
+ readonly cachePolicy?: Pick<UriCachePolicy, "ttlMs">;
4408
+ /**
4409
+ * A short name for the resource this call touched, for the transcript row; absent shows the
4410
+ * address. Render-only: the host stamps it on the tool result's `details`, never in `content`.
4411
+ */
4412
+ readonly label?: UriResourceLabel;
4413
+ /**
4414
+ * Answer to a `read` called with `ctx.knownRevision`: the module checked the source's current
4415
+ * revision and it equals `knownRevision`, so `content` is empty and `metadata.revision` equals
4416
+ * `knownRevision`. Set it only when that check was made and matched; a module that cannot
4417
+ * check cheaply, or that found a different revision, returns the full resource without it.
4418
+ */
4419
+ readonly unchanged?: true;
3556
4420
  }
3557
4421
 
3558
4422
  declare interface RawResourceCacheControl {
@@ -3596,7 +4460,7 @@ declare interface RawResourceMetadata {
3596
4460
  readonly resourceIdentity?: RawResourceIdentity;
3597
4461
  /**
3598
4462
  * The resource's stable public address when `uri` reports something more
3599
- * specific than the address a consumer names it by — a retained revision
4463
+ * specific than the address a consumer names it by - a retained revision
3600
4464
  * (`skill://<id>/@revision/<version>`) served for a `skill://<source>/<name>`
3601
4465
  * read. Present only when the two differ. Consumers that attribute by name
3602
4466
  * (the read tool's `skillName` detail, and everything keyed off it) read
@@ -3671,6 +4535,29 @@ declare function Refine<Type extends TSchema, Value = Static<Type>>(type: Type,
3671
4535
  /** Applies a Refine check to the given type. */
3672
4536
  declare function RefineAdd<Type extends TSchema>(type: Type, refinement: TRefinement<Type>): TRefineAdd<Type>;
3673
4537
 
4538
+ /**
4539
+ * The web-session scope-confinement refinement, coined ONCE and shared by both
4540
+ * web-session boundaries: `loginUrl` and `probe.url` must each fall within a
4541
+ * declared `cookieDomains` entry. The manifest boundary
4542
+ * (`PluginWebSessionDeclaration`) enforces it because those URLs are
4543
+ * authenticated-navigation/request primitives a third-party manifest hands the
4544
+ * host; the resolved/wire boundary (`ResolvedPluginWebSession`) re-runs the SAME
4545
+ * check rather than trust it survived resolution (its parse is the surface's
4546
+ * only validation against a skewed daemon). Two copies of the check itself would
4547
+ * be one edit away from disagreeing across those hops.
4548
+ */
4549
+ declare function refineWebSessionScopeConfined(webSession: {
4550
+ readonly cookieDomains: readonly string[];
4551
+ readonly loginUrl: string;
4552
+ readonly probe: {
4553
+ readonly url: string;
4554
+ };
4555
+ }, ctx: z.RefinementCtx): void;
4556
+
4557
+ declare const RegistrableDomain: z.ZodString;
4558
+
4559
+ declare type RegistrableDomain = z.infer<typeof RegistrableDomain>;
4560
+
3674
4561
  /** Applies an RemoveImmutable action to a type. */
3675
4562
  declare function RemoveImmutable<Type extends TSchema>(type: Type, options?: TSchemaOptions): TRemoveImmutable<Type>;
3676
4563
 
@@ -3701,7 +4588,7 @@ declare function RequiredDeferred<Type extends TSchema>(type: Type, options?: TS
3701
4588
  declare type RequiredKeys<Properties extends TProperties, Result extends PropertyKey = keyof Omit<Properties, ReadonlyOptionalKeys<Properties> | ReadonlyKeys<Properties> | OptionalKeys<Properties>>> = Result;
3702
4589
 
3703
4590
  /**
3704
- * The capability⇄service auth binding. A registration carrying this is gated on `serviceId`'s
4591
+ * The capability<->service auth binding. A registration carrying this is gated on `serviceId`'s
3705
4592
  * connectivity; the runtime answers "is this capability authorized?" from this plus vault state.
3706
4593
  */
3707
4594
  declare interface RequiresAuth {
@@ -3709,7 +4596,7 @@ declare interface RequiresAuth {
3709
4596
  /**
3710
4597
  * Optional scheme preference. Absent means the binding is service-level: any injectable scheme
3711
4598
  * 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
4599
+ * it names the scheme resolution tries first - an account connected under a sibling injectable
3713
4600
  * scheme still satisfies the binding and resolves against its own scheme. A non-injectable
3714
4601
  * capability (`external-cli`) MUST name its scheme: it is a scheme-exact island.
3715
4602
  */
@@ -3717,7 +4604,7 @@ declare interface RequiresAuth {
3717
4604
  /**
3718
4605
  * The scopes this capability needs to do its job. Threaded through `resolveToken` into the
3719
4606
  * 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
4607
+ * plugin whose granted scopes don't cover them (`scopesCovered`) - per call, per plugin. Absent
3721
4608
  * means the capability declares no scope requirement (the single-user local path never checks
3722
4609
  * scopes; it is a daemon per-plugin enforcement input). Also read by the scope-aware half of
3723
4610
  * `isBindingSatisfied` when the host supplies a granted-scopes reader.
@@ -3729,11 +4616,11 @@ declare interface RequiresAuth {
3729
4616
  */
3730
4617
  readonly scopeRequirement?: ScopeRequirement;
3731
4618
  /**
3732
- * The service id to NAME in connect guidance when this capability is gated — a host-composed
4619
+ * The service id to NAME in connect guidance when this capability is gated - a host-composed
3733
4620
  * FRONT DOOR whose `service_connect` grants exactly what this binding needs (e.g. Gmail's
3734
4621
  * modules resolve under the shared `google.account` credential but connect via the `gmail`
3735
4622
  * 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
4623
+ * still keys on `(serviceId, schemeId)`. Absent, guidance names `serviceId` - which for a
3737
4624
  * scoped binding on a shared multi-product credential would send the agent to a connect that
3738
4625
  * classifies resolved and no-ops, never granting the missing scopes.
3739
4626
  */
@@ -3741,38 +4628,26 @@ declare interface RequiresAuth {
3741
4628
  }
3742
4629
 
3743
4630
  /**
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.
4631
+ * The credential the host resolved for one call to plugin code that declares `requiresAuth`: a URI
4632
+ * verb (`ctx.auth.credential`), a tool (`ctx.credential`) or a search provider (`ctx.credential`).
4633
+ * Plain data, so it crosses a process boundary with the call; plugin code never asks for one.
3746
4634
  */
3747
- declare interface ResolvedAuth {
4635
+ declare interface ResolvedCredential {
4636
+ /** Bearer for OAuth schemes, apiKey for api-key schemes. Host-resolved and refreshed. */
3748
4637
  readonly accessToken: string;
3749
- readonly account: AuthAccount;
3750
- readonly identityMode: IdentityMode;
4638
+ readonly accountId: string;
3751
4639
  readonly schemeId: string;
3752
4640
  readonly scopes?: readonly string[];
3753
4641
  /**
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.
4642
+ * Every value of a multi-field api-key credential, keyed by the scheme's declared field id
4643
+ * (`accessToken` mirrors the primary field). Absent for single-secret schemes.
3756
4644
  */
3757
4645
  readonly fields?: Readonly<Record<string, string>>;
3758
4646
  }
3759
4647
 
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
4648
  /**
3774
4649
  * Derive a stable provider account id (and optional label) from a freshly connected access token,
3775
- * plus — when known — who the account belongs to.
4650
+ * plus - when known - who the account belongs to.
3776
4651
  *
3777
4652
  * A resolver almost always calls the provider's profile endpoint to derive `accountId` at all, so
3778
4653
  * the identity fields are already in the response: return them rather than flattening the person
@@ -3792,25 +4667,6 @@ declare type ResolvePluginAccounts = (accessToken: string) => Promise<readonly {
3792
4667
  readonly identity?: AccountIdentity;
3793
4668
  }[]>;
3794
4669
 
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
4670
  /** Match remaining characters in the buffer until end. If no characters are in buffer, no match */
3815
4671
  declare function Rest<Input extends string>(input: Input): TRest<Input>;
3816
4672
 
@@ -3824,7 +4680,7 @@ declare function ReturnType_2<Type extends TSchema>(type: Type, options?: TSchem
3824
4680
  declare function ReturnTypeDeferred<Type extends TSchema>(type: Type, options?: TSchemaOptions): TReturnTypeDeferred<Type>;
3825
4681
 
3826
4682
  /** 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"];
4683
+ 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
4684
 
3829
4685
  declare type RunFailureReason = (typeof RUN_FAILURE_REASONS)[number];
3830
4686
 
@@ -4250,7 +5106,7 @@ declare interface SearchParams {
4250
5106
  readonly query: string;
4251
5107
  /**
4252
5108
  * 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,
5109
+ * a time filter only - never as a signal to change topic scope, content domain,
4254
5110
  * or ranking (oh-my-pi's Tavily `topic=news` coupling silently dropped docs).
4255
5111
  * Providers without temporal filtering MUST ignore it silently.
4256
5112
  */
@@ -4264,7 +5120,7 @@ declare interface SearchParams {
4264
5120
  /**
4265
5121
  * A web search backend behind the single `web_search` tool. Directly analogous
4266
5122
  * to a `UriModule` behind `read`/`write`: registered through `ExtensionApi`,
4267
- * merged per session, dispatched by the registry. Stateless and shareable —
5123
+ * merged per session, dispatched by the registry. Stateless and shareable -
4268
5124
  * per-call context (the abort signal and the {@link SearchProviderContext}) is
4269
5125
  * threaded into `search` and `isAvailable`.
4270
5126
  */
@@ -4280,17 +5136,18 @@ declare abstract class SearchProvider {
4280
5136
  */
4281
5137
  abstract readonly providerId: string;
4282
5138
  /**
4283
- * The capability⇄scheme binding when this provider resolves its OWN credential through the
5139
+ * The capability<->scheme binding when this provider resolves its OWN credential through the
4284
5140
  * 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`.
5141
+ * for providers that reuse inference credentials (the builtins) or need no auth. When set, the
5142
+ * host resolves the credential onto {@link SearchProviderContext.credential} before it asks or
5143
+ * calls the provider, and `auth_status` lists the provider as gated-until-connected, parallel to
5144
+ * a tool's or URI module's `requiresAuth`.
4288
5145
  */
4289
5146
  readonly requiresAuth?: RequiresAuth;
4290
5147
  /**
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.
5148
+ * Cheap availability check. The host never asks a provider with `requiresAuth` whose credential
5149
+ * did not resolve: it is unavailable, so the chain skips it - the gating counterpart to a
5150
+ * tool/module's discoverable-but-disallowed state.
4294
5151
  */
4295
5152
  abstract isAvailable(ctx?: SearchProviderContext): Promise<boolean> | boolean;
4296
5153
  /** Run the search. Throw {@link SearchProviderError} on a provider failure. */
@@ -4299,15 +5156,16 @@ declare abstract class SearchProvider {
4299
5156
 
4300
5157
  /**
4301
5158
  * 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.
5159
+ * {@link SearchProvider.search}.
4308
5160
  */
4309
5161
  declare interface SearchProviderContext {
4310
- readonly auth?: ExtensionAuthContext;
5162
+ /**
5163
+ * The credential the host resolved for a provider that declares
5164
+ * {@link SearchProvider.requiresAuth}, for the session-active account. The host neither asks
5165
+ * nor calls such a provider when none resolves. Absent for a provider without `requiresAuth`
5166
+ * (builtins reuse inference credentials through the host model execution capability).
5167
+ */
5168
+ readonly credential?: ResolvedCredential;
4311
5169
  }
4312
5170
 
4313
5171
  /** Registry id of a search provider, e.g. `builtin:anthropic`. Open-ended for extensions. */
@@ -4366,19 +5224,19 @@ declare type SelectorDisposition = {
4366
5224
  * rebuilds it on each `AgentSession.reroot` (`@herd/herd-agent`) (the only event that
4367
5225
  * moves `cwd`), then evaluates every tool's predicate against it exactly once per change.
4368
5226
  *
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
5227
+ * **CLOSED-CONTEXT INVARIANT (load-bearing - do not violate when extending this type).** This object
5228
+ * carries ONLY primary facts that are *independent of tool availability* - `cwd`, `repoRoot`, and the
4371
5229
  * planned `mode`/`platform`. It MUST NEVER carry the tool list, the active/effective set, or anything
4372
5230
  * else derived from availability. If a predicate's input could depend on another predicate's output,
4373
5231
  * evaluation would have to reach a fixpoint and could cycle. Because the context is closed, the kernel
4374
5232
  * evaluates each predicate ONCE per context change (one pass, no fixpoint) and caches the result. Keep
4375
5233
  * every future field in this same closed class so the single-pass guarantee survives.
4376
5234
  *
4377
- * Predicates reading this MUST be **pure and synchronous** — same context in ⇒ same answer out, no I/O,
5235
+ * Predicates reading this MUST be **pure and synchronous** - same context in => same answer out, no I/O,
4378
5236
  * no side effects. The host resolves the VCS facts (`repoRoot`); the predicate only inspects them.
4379
5237
  */
4380
5238
  declare interface SessionContext {
4381
- /** The session's current working directory (the live, re-rootable cwd — not the launch dir). */
5239
+ /** The session's current working directory (the live, re-rootable cwd - not the launch dir). */
4382
5240
  readonly cwd: string;
4383
5241
  /**
4384
5242
  * The enclosing VCS repository root for {@link cwd}, or `null` when `cwd` is not inside a repo.
@@ -4388,6 +5246,10 @@ declare interface SessionContext {
4388
5246
  readonly repoRoot: string | null;
4389
5247
  }
4390
5248
 
5249
+ declare const SignedInStatusCode: z.ZodNumber;
5250
+
5251
+ declare type SignedInStatusCode = z.infer<typeof SignedInStatusCode>;
5252
+
4391
5253
  /** Matches from Start and End capturing everything in-between. Start and End are consumed. */
4392
5254
  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
5255
 
@@ -4500,12 +5362,25 @@ declare const StringPattern = ".*";
4500
5362
  /** Creates a Symbol type. */
4501
5363
  declare function Symbol_2(options?: TSchemaOptions): TSymbol;
4502
5364
 
5365
+ /** Structurally the plugin SDK's `KayAppSessionEventCause`: only the fields the host reads. */
5366
+ declare interface SystemOneCallCause {
5367
+ readonly kind?: "session-event" | "backfill";
5368
+ readonly handler: string;
5369
+ readonly sourceSessionId: string;
5370
+ }
5371
+
4503
5372
  declare interface SystemOneCallOptions {
4504
5373
  readonly signal?: AbortSignal;
4505
5374
  /** Bounds the whole call, retries included. */
4506
5375
  readonly timeoutMs?: number;
4507
5376
  /** What the call is for, carried to the host's usage accounting. Never sent to a provider. */
4508
5377
  readonly purpose?: string;
5378
+ /**
5379
+ * The session event a plugin's call answers, so the host charges the call to that session.
5380
+ * The host checks the plugin is handling that session's event right now. Never sent to a
5381
+ * provider.
5382
+ */
5383
+ readonly cause?: SystemOneCallCause;
4509
5384
  }
4510
5385
 
4511
5386
  declare interface SystemOneClient {
@@ -4959,6 +5834,13 @@ declare type TApplyReadonly<Readonly extends TModifierOperation, Type extends T.
4959
5834
 
4960
5835
  declare type TApplyReverse<Types extends TSchema[], Reversed extends boolean> = Reversed extends true ? TReverse<Types> : Types;
4961
5836
 
5837
+ declare const TargetEntrypoint: z.ZodObject<{
5838
+ module: z.ZodString;
5839
+ export: z.ZodOptional<z.ZodString>;
5840
+ }, z.core.$strict>;
5841
+
5842
+ declare type TargetEntrypoint = z.infer<typeof TargetEntrypoint>;
5843
+
4962
5844
  /** Represents an Array type. */
4963
5845
  declare interface TArray<Type extends TSchema = TSchema> extends TSchema {
4964
5846
  '~kind': 'Array';
@@ -6722,7 +7604,7 @@ declare type TOmitInstantiate<Context extends TProperties, State extends TState,
6722
7604
  * classification (the engine records the rejection on an unexplained failure),
6723
7605
  * and a stored record with one reads as none.
6724
7606
  */
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"];
7607
+ 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
7608
 
6727
7609
  /**
6728
7610
  * ---
@@ -6740,14 +7622,14 @@ declare type ToolActionKind = "read" | "mutate" | "destructive" | "execute";
6740
7622
  * A tool's declarative availability gate. `when` is the authoritative predicate the kernel evaluates
6741
7623
  * against the live {@link SessionContext} to decide whether the tool is offered to the model here;
6742
7624
  * `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
7625
+ * disables (e.g. greyed on the new-session page). The predicate runs in the main process only - a
6744
7626
  * renderer receives just the resolved `available: boolean` plus this `description`.
6745
7627
  *
6746
7628
  * `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.
7629
+ * treated as "unavailable", and surfaced as a session diagnostic - it never crashes the tool sync.
6748
7630
  */
6749
7631
  declare interface ToolAvailability {
6750
- /** Pure, synchronous gate. `true` ⇒ the tool may be offered in this context; `false` ⇒ withheld. */
7632
+ /** Pure, synchronous gate. `true` => the tool may be offered in this context; `false` => withheld. */
6751
7633
  when(ctx: SessionContext): boolean;
6752
7634
  /** Human-readable reason the tool is/isn't available, always shown in UI. */
6753
7635
  description: string;
@@ -6775,6 +7657,7 @@ declare const toolCallFailureCauseSchema: z.ZodObject<{
6775
7657
  "not-found": "not-found";
6776
7658
  permission: "permission";
6777
7659
  unavailable: "unavailable";
7660
+ waiting: "waiting";
6778
7661
  }>;
6779
7662
  retryable: z.ZodBoolean;
6780
7663
  action: z.ZodEnum<{
@@ -6783,6 +7666,7 @@ declare const toolCallFailureCauseSchema: z.ZodObject<{
6783
7666
  retry: "retry";
6784
7667
  "correct-input": "correct-input";
6785
7668
  connect: "connect";
7669
+ wait: "wait";
6786
7670
  }>;
6787
7671
  details: z.ZodOptional<z.ZodObject<{
6788
7672
  moduleId: z.ZodOptional<z.ZodString>;
@@ -6791,6 +7675,7 @@ declare const toolCallFailureCauseSchema: z.ZodObject<{
6791
7675
  uri: z.ZodOptional<z.ZodString>;
6792
7676
  path: z.ZodOptional<z.ZodString>;
6793
7677
  validationField: z.ZodOptional<z.ZodString>;
7678
+ validationRule: z.ZodOptional<z.ZodString>;
6794
7679
  errorName: z.ZodOptional<z.ZodString>;
6795
7680
  errorCode: z.ZodOptional<z.ZodString>;
6796
7681
  providerCode: z.ZodOptional<z.ZodString>;
@@ -6805,14 +7690,14 @@ declare type ToolCallFailureCode = (typeof TOOL_CALL_FAILURE_CODES)[number];
6805
7690
 
6806
7691
  /**
6807
7692
  * 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
7693
+ * real tool, or return a result without calling `next()` to substitute one - the tool then never
6809
7694
  * executes. Every downstream consumer (the model, the session log, result clamps, failure
6810
7695
  * attribution) sees whatever is returned exactly as if the tool produced it. Throwing surfaces to
6811
7696
  * the caller as a tool failure, classified like any other.
6812
7697
  *
6813
7698
  * Interceptors compose as middleware: each receives a `next` that runs the REST of the chain, the
6814
7699
  * 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
7700
+ * demands (zero to short-circuit, once to delegate, more to retry) - the kernel does not police
6816
7701
  * arity.
6817
7702
  */
6818
7703
  declare type ToolCallInterceptor = (call: ToolCallInvocation, next: () => Promise<AgentToolResult<unknown>>) => Promise<AgentToolResult<unknown>>;
@@ -6821,7 +7706,7 @@ declare type ToolCallInterceptor = (call: ToolCallInvocation, next: () => Promis
6821
7706
  declare interface ToolCallInvocation {
6822
7707
  readonly toolCallId: string;
6823
7708
  readonly toolName: string;
6824
- /** The call's arguments, already through `prepareArguments` — the shape `execute` receives. */
7709
+ /** The call's arguments, already through `prepareArguments` - the shape `execute` receives. */
6825
7710
  readonly params: unknown;
6826
7711
  readonly signal: AbortSignal | undefined;
6827
7712
  /**
@@ -6840,7 +7725,7 @@ declare interface ToolCallInvocation {
6840
7725
 
6841
7726
  /**
6842
7727
  * 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
7728
+ * time: identity plus pi's `input` list - the SAME field pi's adapters read when they build the
6844
7729
  * next request, so a capability policy keyed on it agrees with what the provider will accept.
6845
7730
  * Absent when the wrap was built without a resolver; a policy must then treat the capability
6846
7731
  * as unknown, not as absent.
@@ -6875,7 +7760,7 @@ declare type ToolCallPermissionVerdict = {
6875
7760
  * What a tool call ADDRESSES, resolved by the session before execution from facts it already
6876
7761
  * holds for auth scoping: a URI verb's matched module (and the extension that registered it), or
6877
7762
  * 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
7763
+ * modules and tools - the session owns those, no extension does. A host policy that keys on
6879
7764
  * "which integration is this call about" reads this instead of re-parsing `params`.
6880
7765
  */
6881
7766
  declare type ToolCallTarget = {
@@ -6893,15 +7778,15 @@ declare interface ToolContext {
6893
7778
  readonly cwd: string;
6894
7779
  readonly model: Model<any> | undefined;
6895
7780
  /**
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).
7781
+ * The credential the host resolved for this call, for a tool that declares
7782
+ * {@link ToolDefinition.requiresAuth}: the account named by the call's `accountId` argument, else
7783
+ * the session-active one. The host does not call the tool when none resolves. Absent for a tool
7784
+ * without `requiresAuth`, and outside a live session.
6900
7785
  */
6901
- readonly auth?: ExtensionAuthContext;
7786
+ readonly credential?: ResolvedCredential;
6902
7787
  /**
6903
7788
  * 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 —
7789
+ * `nextTurn()` and appears in the Background tasks panel). Not for fire-and-forget side effects -
6905
7790
  * use `ExtensionContext.runEffect`. Absent when the host wires no background-task manager.
6906
7791
  */
6907
7792
  readonly backgroundTasks?: ExtensionBackgroundTasksApi;
@@ -6923,9 +7808,9 @@ declare interface ToolContext {
6923
7808
  /**
6924
7809
  * Re-root the session into `newCwd` mid-run, recomposing the cwd-scoped substrate (builtin tools,
6925
7810
  * 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
7811
+ * there - the conversation continues uninterrupted. This is the agent self-reroot seam: the
6927
7812
  * 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.
7813
+ * `ctx.reroot(worktreePath)` from inside its own `execute`. Safe mid-run - it never gates on idle.
6929
7814
  * Present only with a live session (absent in session-less inventory/standalone contexts, like
6930
7815
  * {@link auth}). `reason` labels the transition for observability (e.g. `"worktree-isolation"`).
6931
7816
  */
@@ -6966,12 +7851,12 @@ declare interface ToolDefinition<TParams extends ToolParametersWire = ToolParame
6966
7851
  executionMode?: ToolExecutionMode;
6967
7852
  /**
6968
7853
  * How long a single execution may legitimately stay silent (#3785).
6969
- * - `"fast"` (the default when omitted): the execution is bounded — if it
7854
+ * - `"fast"` (the default when omitted): the execution is bounded - if it
6970
7855
  * neither settles nor loses the abort race within the fast-tool deadline
6971
7856
  * (120s), the kernel fails the call with a timeout error the model can
6972
7857
  * react to. Right for local reads/writes and short network calls, which
6973
7858
  * finish in seconds or are stuck.
6974
- * - `"long"`: no deadline — a build under a shell, a parent parked on a
7859
+ * - `"long"`: no deadline - a build under a shell, a parent parked on a
6975
7860
  * child task. The abort race still applies: a user Stop always wins,
6976
7861
  * whether or not the tool honors its `signal`.
6977
7862
  * Declare `"long"` only when unbounded silent execution is genuinely part
@@ -6988,29 +7873,29 @@ declare interface ToolDefinition<TParams extends ToolParametersWire = ToolParame
6988
7873
  */
6989
7874
  replaySafe?: true;
6990
7875
  /**
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).
7876
+ * Optional capability<->scheme binding. When present this tool is auth-gated: discoverable when
7877
+ * unauthed, callable once the binding is satisfied, and the host resolves its credential onto
7878
+ * {@link ToolContext.credential} before each call. The SDK's `withResolvedAuth` stamps this; absent
7879
+ * means an ordinary always-on tool.
6995
7880
  */
6996
7881
  requiresAuth?: RequiresAuth;
6997
7882
  /**
6998
7883
  * Optional declarative availability gate ({@link ToolAvailability}). When present, the kernel
6999
7884
  * evaluates `availability.when` against the live {@link SessionContext} once per context change
7000
7885
  * (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
7886
+ * returns `false`. Backward-compatible: **absent => always available**. Orthogonal to
7887
+ * mode-disallowed sets and to auth/permission gates - availability answers only "does this tool
7003
7888
  * apply in this context", and composes with the others at the same chokepoint.
7004
7889
  */
7005
7890
  availability?: ToolAvailability;
7006
7891
  /**
7007
7892
  * Optional declarative permission classification ({@link ToolPermissionHint}). Consulted by
7008
7893
  * 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`),
7894
+ * tools), letting a tool relax the fail-closed `execute` fallback (a pure staging tool -> `read`),
7010
7895
  * declare its provider `scheme`, derive the `resources` a call touches (a browser
7011
7896
  * navigation's target origin) so per-provider and per-origin rules can gate it.
7012
7897
  * Because it lives on the definition it is
7013
- * provenance-bound. Backward-compatible: **absent ⇒ the classifier's default (`execute`, no
7898
+ * provenance-bound. Backward-compatible: **absent => the classifier's default (`execute`, no
7014
7899
  * scheme)**.
7015
7900
  */
7016
7901
  permission?: ToolPermissionHint;
@@ -7064,7 +7949,7 @@ declare type ToolParametersWire = unknown;
7064
7949
  * definition (the same shape of author-declared, kernel-read fact as `requiresAuth` and
7065
7950
  * `availability`). Consulted ONLY when the classifier does not otherwise recognize the tool
7066
7951
  * (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
7952
+ * `execute` fallback (e.g. a pure staging tool -> `read`) or scope itself for per-provider and
7068
7953
  * per-origin rules. Because it lives on the registered definition, it is provenance-bound: a
7069
7954
  * tool named the same as another carries its OWN hint, never inherits a first-party one.
7070
7955
  *
@@ -7073,7 +7958,7 @@ declare type ToolParametersWire = unknown;
7073
7958
  * touch protected config must NOT declare `read`. Bonsai's embedded-experience tools never touch
7074
7959
  * `configDir`, so this is safe for them. `read` is also what every capability surface reports as
7075
7960
  * "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
7961
+ * tool DOES - never a way to buy silence. A tool that should raise no ask on its own account
7077
7962
  * declares {@link ToolPermissionHint.quiet} and keeps its honest `kind`.
7078
7963
  */
7079
7964
  declare interface ToolPermissionHint {
@@ -7090,8 +7975,8 @@ declare interface ToolPermissionHint {
7090
7975
  readonly ruleKey?: string;
7091
7976
  /**
7092
7977
  * 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 —
7978
+ * A UX window declaration, not authority: it cannot loosen or tighten what is asked -
7979
+ * kind/ruleKey/scheme/rules/mode all decide that exactly as they would without it -
7095
7980
  * only how long the prompt waits before it lapses unanswered (`expired`, which the gate
7096
7981
  * already treats as `unanswered`, never `denied`). Bounded use case: a suggestion-shaped
7097
7982
  * tool whose silence means "continue" (e.g. `suggest_compaction`'s 15s ask, chosen to
@@ -7102,26 +7987,26 @@ declare interface ToolPermissionHint {
7102
7987
  /**
7103
7988
  * Derives the resources a call touches from its validated input (e.g. the target origin of
7104
7989
  * 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).
7990
+ * falls back to the plain classification (fail-closed - never crashes the gate).
7106
7991
  */
7107
7992
  readonly deriveResources?: (input: Record<string, unknown>) => readonly string[];
7108
7993
  /**
7109
7994
  * Derives a call's classification (`kind`/`ruleKey`/`scheme`) from its validated input, where
7110
7995
  * 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
7996
+ * {@link deriveResources} (input -> resources) to the rule-matching triple, and of the kernel's
7112
7997
  * built-in `deriveBashRuleKey` (a bash call's argv sharpens its key) to an author-declared tool.
7113
7998
  *
7114
7999
  * Returns a partial: each present field OVERRIDES the static hint for this call; each absent
7115
8000
  * field (or a wholly `undefined` return) falls back to the static hint, then the fail-closed
7116
8001
  * 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).
8002
+ * static hint (fail-closed - never crashes the gate).
7118
8003
  *
7119
8004
  * It grants no authority the static fields don't already grant: it computes the same
7120
8005
  * kind/ruleKey/scheme the tool could hard-code, per call rather than once. `kind` here matters
7121
8006
  * because the same tool can read on one input and execute on another, which a static `kind`
7122
8007
  * cannot express.
7123
8008
  *
7124
- * Exemplar — Bonsai's `mcp` proxy tool, one tool spanning every server: `search`/`describe` derive
8009
+ * Exemplar - Bonsai's `mcp` proxy tool, one tool spanning every server: `search`/`describe` derive
7125
8010
  * `{ kind: "read" }` (metadata lookups), while `call` derives
7126
8011
  * `{ kind: "execute", ruleKey: "mcp:<server>::<tool>", scheme: "<server>" }`, so a
7127
8012
  * `{ scheme: "<server>" }` rule gates a whole server and a `{ ruleKey: "mcp:<server>::<tool>" }`
@@ -7138,7 +8023,7 @@ declare interface ToolPermissionHint {
7138
8023
  * deriver is a pure function of its own input and cannot see the target's hint, its service
7139
8024
  * scheme, or the kernel's own recognition of names like `bash` and the URI verbs. Resolution
7140
8025
  * 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.
8026
+ * rules its direct form would - same kind, same ruleKey, same scheme.
7142
8027
  *
7143
8028
  * A hop ADDS the target's rules; it never SUBTRACTS the carrier's. The carrier's own
7144
8029
  * classification rides along (`ToolActionPreflight.carrier`) and the controller takes the
@@ -7148,22 +8033,22 @@ declare interface ToolPermissionHint {
7148
8033
  * alone could not.
7149
8034
  *
7150
8035
  * 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
8036
+ * verbs, the read tools) - the same invariant that governs the rest of this hint. A proxy may
7152
8037
  * still freely TARGET one of them.
7153
8038
  *
7154
8039
  * Pure and total. EVERY abnormal outcome falls back to the carrier's own classification, never
7155
8040
  * to an intermediate link: a throw, a malformed return, a blank target name, a non-object input,
7156
8041
  * 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
8042
+ * bypass - a crafted 5-link chain could present hop 4's benign classification for a call whose
7158
8043
  * real target is `bash rm -rf /`.) Only a chain terminating normally, at a tool declaring no
7159
8044
  * further proxy, hops at all.
7160
8045
  *
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
8046
+ * "Fail-closed" here means the carrier's own classification applies - the identity the call
8047
+ * really had - not that the result is always stricter than the target's. How much gate is left
7163
8048
  * on such a call is exactly how much the CARRIER carries: an `execute` carrier is stricter than
7164
8049
  * a `read` target and equal to a `mutate`/`destructive` one outside allow mode, while a
7165
8050
  * {@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
8051
+ * fallback leaves nothing but the carrier's rules and the protected-path deny - and the carrier
7167
8052
  * names no path, so that deny has nothing to fire on. This is why a carrier must refuse a target
7168
8053
  * that is itself a carrier: no chain long or circular enough to reach the fallback can be built,
7169
8054
  * which is the only thing that keeps the fallback from being toothless. Fail-closed is a
@@ -7174,24 +8059,24 @@ declare interface ToolPermissionHint {
7174
8059
  * Declares that this tool's identity contributes its RULES to the gate but not its MODE
7175
8060
  * DEFAULT. A rule about it still applies in full (a deny denies, an ask asks, an allow
7176
8061
  * 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".
8062
+ * MODE would reach from `kind` alone - default/ask mode's "anything but a read asks".
7178
8063
  *
7179
8064
  * It is a statement about this identity's CONTRIBUTION, never about what the call does.
7180
8065
  * `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
8066
+ * tool declares it exactly as loudly as a noisy one - which is the whole point: buying
7182
8067
  * silence with `kind: "read"` instead moves the call below the protected-path hard-deny and
7183
8068
  * reports the tool as read-only on every capability surface.
7184
8069
  *
7185
8070
  * It exists for a PROXY CARRIER ({@link deriveProxiedCall}), and is honest only for a tool
7186
8071
  * whose only effect is invoking the target the gate is already classifying beside it: the
7187
8072
  * 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
8073
+ * call is gated exactly like the direct one - no more, and no ask the direct call would never
7189
8074
  * have raised. Bonsai's `call_tool` declares `{ kind: "execute", quiet: true }` (owner ruling,
7190
8075
  * 2026-08-18): honest about everything it can reach, silent on its own account.
7191
8076
  *
7192
8077
  * Quiet is not ungated, but it IS nearly all the gate a lone carrier has. On a call that
7193
8078
  * 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
8079
+ * carrier leaves only its own rules - see {@link deriveProxiedCall}'s fail-closed paragraph
7195
8080
  * for why a carrier must therefore refuse a carrier as a target.
7196
8081
  */
7197
8082
  readonly quiet?: boolean;
@@ -8394,32 +9279,43 @@ declare interface UriAgentHint {
8394
9279
  readonly triggers?: UriHintTriggers;
8395
9280
  /**
8396
9281
  * 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
9282
+ * deeper workflow judgment - the name a `read skill://<name>` dispatch resolves, not a display
8398
9283
  * title or a path. Point here only at skills that actually exist in the composed skill roster;
8399
9284
  * a dangling name sends the agent to a `skill://` read for nothing.
8400
9285
  */
8401
9286
  readonly skills?: readonly string[];
8402
9287
  }
8403
9288
 
8404
- declare type UriAuthActionClassification = {
8405
- readonly kind: "action";
8406
- readonly actionId: string;
8407
- } | {
8408
- readonly kind: "public";
8409
- };
8410
-
8411
9289
  /**
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.
9290
+ * Finite authorization actions plus the rules that select one before dispatch. A call no rule
9291
+ * matches, or a rule naming an action that is not in `actions`, fails closed in the host.
8414
9292
  */
8415
9293
  declare interface UriAuthActionPolicy {
8416
9294
  readonly actions: Readonly<Record<string, ScopeRequirement>>;
8417
- classifyOperation(request: {
8418
- readonly operation: UriOperation;
8419
- readonly uri: string;
8420
- }): UriAuthActionClassification | undefined;
9295
+ /** Absent matches no call, so every call fails closed. */
9296
+ readonly rules?: readonly UriAuthActionRule[];
8421
9297
  }
8422
9298
 
9299
+ /**
9300
+ * One row of a module's sign-in action rules: which calls need which action. A row matches a call
9301
+ * when every condition it names holds; a condition it leaves out matches anything. The first
9302
+ * matching row decides, so a row with no conditions at the end means "everything else".
9303
+ */
9304
+ declare type UriAuthActionRule = {
9305
+ /** The verbs this row covers. */
9306
+ readonly operations?: readonly UriOperation[];
9307
+ /** The claim `route` ids this row covers, matched against the call's address. */
9308
+ readonly routes?: readonly string[];
9309
+ /** Query parameters the address must carry with exactly these values (`{ permanent: "true" }`). */
9310
+ readonly query?: Readonly<Record<string, string>>;
9311
+ } & ({
9312
+ /** The {@link UriAuthActionPolicy.actions} entry the call needs. */
9313
+ readonly action: string;
9314
+ } | {
9315
+ /** The call needs no credential. */
9316
+ readonly public: true;
9317
+ });
9318
+
8423
9319
  declare interface UriAuthRef {
8424
9320
  readonly serviceId: string;
8425
9321
  readonly schemeId: string;
@@ -8441,7 +9337,7 @@ declare interface UriAuthRequirement {
8441
9337
  */
8442
9338
  readonly requiredScopes?: readonly string[];
8443
9339
  /**
8444
- * The service id connect guidance should NAME when this module is gated — a host-composed
9340
+ * The service id connect guidance should NAME when this module is gated - a host-composed
8445
9341
  * front door whose connect grants exactly this module's scopes (e.g. `gmail` expanding onto
8446
9342
  * the shared `google.account` credential). Guidance only; resolution keys on
8447
9343
  * `(serviceId, schemeId)`. Absent, guidance names `serviceId`.
@@ -8487,6 +9383,21 @@ declare type UriCachePolicyResolver = (operation: UriOperation, cacheUri: string
8487
9383
 
8488
9384
  declare type UriClaim = UriSchemeClaim | UriPatternClaim | UrlPatternClaim;
8489
9385
 
9386
+ /**
9387
+ * A URL with `:name` placeholders for the claim's captures. Each capture is escaped for where it
9388
+ * lands: lowercased in the host, one path segment (a `:name*` capture keeps its `/`), or a query
9389
+ * or fragment value. `{transform(...)}` runs one named, built-in transform over the text inside
9390
+ * it, captures pasted in unescaped, and escapes the output for where it lands:
9391
+ *
9392
+ * - `base64`: unpadded base64 of the UTF-8 text (`{base64(:id :calendar)}`).
9393
+ * - `slackTimestamp`: a Slack archive message token as the API's timestamp
9394
+ * (`p1783350000000100` becomes `1783350000.000100`).
9395
+ *
9396
+ * A transform that cannot read its input fills nothing. A claim whose `normalizeTo` fills nothing
9397
+ * does not match; a `sourceUrl` that fills nothing paints no link.
9398
+ */
9399
+ declare type UriClaimTemplate = string;
9400
+
8490
9401
  declare interface UriCommonFlow {
8491
9402
  readonly label: string;
8492
9403
  readonly when?: string;
@@ -8536,41 +9447,28 @@ declare interface UriExample {
8536
9447
  readonly operation?: UriOperation;
8537
9448
  }
8538
9449
 
8539
- declare interface UriExtensionModule extends Omit<UriModule, "verbs"> {
9450
+ /**
9451
+ * A plugin's URI module. Its claims route, spell and describe its addresses, so it has no
9452
+ * `matches`, `normalize` or `describe`. It also has no `fallback` (a plugin's addresses are always
9453
+ * claimed) and no `locate` (a plugin has no file on disk for the daemon to open).
9454
+ */
9455
+ declare interface UriExtensionModule extends Omit<UriModule, "verbs" | "normalize" | "matches" | "describe" | "fallback" | "locate"> {
8540
9456
  readonly verbs: UriVerbSupport;
8541
9457
  /** Human-facing per-verb effect docs; see {@link UriVerbDocs}. */
8542
9458
  readonly verbDocs?: UriVerbDocs;
8543
9459
  /**
8544
9460
  * 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
9461
+ * compiles into a defaults-level ask rule - the user is prompted by default but can
8546
9462
  * override with an explicit allow. Undeclared = `"allow"` (fail-open).
8547
9463
  * See {@link UriPermissionDefaults}.
8548
9464
  */
8549
9465
  readonly permissionDefaults?: UriPermissionDefaults;
8550
9466
  /** Requires a resolved URI connection before any verb handler runs. */
8551
9467
  readonly requiresConnection?: boolean;
8552
- /** Pure route-aware permission facts; see {@link UriExtensionOperationPermissionPolicy}. */
8553
- readonly permission?: UriExtensionOperationPermissionPolicy;
8554
9468
  /** False rejects generic write-tool `source` copies before source fetch. */
8555
9469
  readonly acceptsSourceCopy?: boolean;
8556
9470
  }
8557
9471
 
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
9472
  declare interface UriExternalCliRunner {
8575
9473
  readonly run: (request: UriExternalCliRunRequest) => Promise<UriExternalCliRunResult>;
8576
9474
  }
@@ -8605,6 +9503,8 @@ declare interface UriHelp {
8605
9503
  * than text the module's author wrote. Absent: the module's own text.
8606
9504
  */
8607
9505
  readonly thirdPartyContent?: true;
9506
+ /** The module's own markdown for this result, shown instead of the host's default rendering. */
9507
+ readonly markdown?: string;
8608
9508
  }
8609
9509
 
8610
9510
  declare interface UriHintTriggers {
@@ -8644,6 +9544,12 @@ declare interface UriListResult {
8644
9544
  readonly items: readonly UriListItem[];
8645
9545
  readonly nextCursor?: string;
8646
9546
  readonly metadata?: UriListMetadata;
9547
+ /** How long the host may cache this result; see {@link UriCachePolicy.ttlMs}. */
9548
+ readonly cachePolicy?: Pick<UriCachePolicy, "ttlMs">;
9549
+ /** The module's own markdown for this result, shown instead of the host's default rendering. */
9550
+ readonly markdown?: string;
9551
+ /** The touched resource's name for the transcript row, as on `RawResource.label`. */
9552
+ readonly label?: UriResourceLabel;
8647
9553
  }
8648
9554
 
8649
9555
  declare type UriLocateContext = Pick<UriResolveContext, "cwd" | "signal" | "connection" | "sessionId" | "paths"> & {
@@ -8661,31 +9567,13 @@ declare type UriLocateResult = {
8661
9567
  readonly kind: "not-disk-backed";
8662
9568
  };
8663
9569
 
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
9570
  declare interface UriModule {
8683
9571
  readonly id: string;
8684
9572
  /**
8685
9573
  * Explicit human-readable name for catalog/display purposes (the plugin
8686
9574
  * 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
9575
+ * hand since there is no automatic manifest->runtime-module composition
9576
+ * step). Falls back to a mechanical id-derived title when absent - see
8689
9577
  * `uriCatalogTitle`.
8690
9578
  */
8691
9579
  readonly title?: string;
@@ -8705,14 +9593,26 @@ declare interface UriModule {
8705
9593
  * calls `parseLineSelector` itself and reports what it did through `RawResource.selector`.
8706
9594
  */
8707
9595
  readonly ownsLineSelectors?: boolean;
9596
+ /** Opt in to host-resolved supplementary sources; absent means unsupported. */
9597
+ readonly acceptsWriteSources?: boolean;
9598
+ /**
9599
+ * Opt in to raw bytes as `write`'s content; absent means text and JSON only. The `write`
9600
+ * tool's `source` copy leg is the only producer: a provider's binary resource (an attachment,
9601
+ * an image, a PDF) reaches a module that declares this as a `Uint8Array`, never decoded into
9602
+ * the model's context, and a binary copy to any other module is refused before it runs.
9603
+ */
9604
+ readonly acceptsBytes?: boolean;
8708
9605
  readonly claims?: readonly UriClaim[];
9606
+ /**
9607
+ * The routes the module's scheme-root `help` lists, as data, so a host can list them without
9608
+ * calling `help`.
9609
+ */
9610
+ readonly templates?: readonly UriTemplate[];
8709
9611
  /** Migration-only prompt summary until structured help/catalog coverage replaces prose. */
8710
9612
  readonly description?: string;
8711
9613
  readonly agentHint?: UriAgentHint;
8712
9614
  readonly requiresAuth?: UriAuthRequirement;
8713
9615
  readonly authActions?: UriAuthActionPolicy;
8714
- /** Pure route/payload validation applied before a consequential permission ask. */
8715
- readonly preflight?: UriOperationPreflightPolicy;
8716
9616
  /**
8717
9617
  * Structured facts for approval surfaces, or `undefined` when unavailable.
8718
9618
  * A one-line description must name the whole consequence; `preview` instead
@@ -8722,120 +9622,46 @@ declare interface UriModule {
8722
9622
  * of covered payload fields and query parameters. Unknown fields cost the summary.
8723
9623
  * The host owns framing, the whole-quote fit (no elision) and display-safety admission.
8724
9624
  *
8725
- * Unlike preflight, this hook is async and may do I/O. Resolve names through `ctx`,
9625
+ * This hook is async and may do I/O. Resolve names through `ctx`,
8726
9626
  * the same dispatch context used by execution, never an ambient or cached account. A name
8727
9627
  * the module resolved under that account may be cached for a bounded time, keyed by that
8728
9628
  * 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.
9629
+ * The host calls it once before raising the ask and waits for the answer, with no deadline;
9630
+ * `ctx.signal` fires when the host stops waiting (the session closes, or a caller gives up),
9631
+ * and a late answer is discarded. A throw yields no
9632
+ * summary and a module-attributed diagnostic; declining costs a click into the app, never an
9633
+ * under-described approval.
8744
9634
  *
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
9635
+ * "Allow with edits": a field marked `editable` may be rewritten by the person on the card.
9636
+ * The host rebuilds the call itself (see the field's `editable`), then holds the rebuilt call to
9637
+ * what the person was shown, in two steps, and runs it only if both pass. It classifies the
9638
+ * rebuilt call again and requires the same tool, rule key, kind, resources, scheme and target
9639
+ * address. It then calls this hook on the rebuilt call and requires that preview to read exactly
9640
+ * as the one the person saw, apart from the values of the fields they edited: the same title,
9641
+ * the same fields in the same order, each with the same label, key, `editable` marker and
8764
9642
  * `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
9643
+ * with nothing done: a mismatch, and also an answer of `undefined` or a throw. The `description`
9644
+ * line is not compared. So put whatever says where a call goes, or what it does beyond its
8771
9645
  * editable fields, in the preview's `title` or a field, never only in its `description`, and
8772
9646
  * keep recipients, destinations and thread bindings out of `editable` fields.
8773
9647
  */
8774
- amendOperation?(request: {
9648
+ describeOperation?(request: {
8775
9649
  readonly operation: UriOperation;
8776
9650
  readonly uri: string;
8777
9651
  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>;
9652
+ }, ctx: UriResolveContext): Promise<UriOperationDescription | undefined>;
8835
9653
  readonly getCachePolicy?: UriCachePolicyResolver;
8836
- /** Retained matching escape hatch; future dispatch should prefer compiled claims. */
8837
- matches(input: string, ctx: UriResolveContext): boolean;
9654
+ /**
9655
+ * Code matching, for the host's own modules whose addresses no claim can describe (a bare file
9656
+ * path). A plugin module has none; it routes through its `claims` alone.
9657
+ */
9658
+ matches?(input: string, ctx: UriResolveContext): boolean;
9659
+ /** The official spelling, for the host's own modules; a plugin's comes from its claims' `normalizeTo`. */
8838
9660
  normalize?(input: string, ctx: UriResolveContext): UriNormalizeResult | undefined;
9661
+ /**
9662
+ * The pill's title and link, for the host's own modules only; a plugin module has none, and its
9663
+ * pill comes from its claims' `title` and `sourceUrl`.
9664
+ */
8839
9665
  describe?(input: string, ctx: UriDescribeContext): UriResourceDescription | undefined;
8840
9666
  locate?(input: string, ctx: UriLocateContext): Promise<UriLocateResult>;
8841
9667
  /**
@@ -8848,33 +9674,21 @@ declare interface UriModule {
8848
9674
  read(input: string, ctx: UriResolveContext): Promise<RawResource>;
8849
9675
  /** Observes only; see {@link read}. */
8850
9676
  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.
9677
+ /**
9678
+ * `sources` is empty for ordinary writes; populated only with `acceptsWriteSources: true`.
9679
+ * `content` is a `Uint8Array` only with `acceptsBytes: true`, and then `ctx.writeSource`
9680
+ * names and types it. It is `undefined` when the call carried neither `content` nor `source`.
9681
+ * The module decides what that means: a route that takes no body (a folder
9682
+ * create) proceeds, and a route that needs one refuses with an error naming
9683
+ * what is missing. Never treat it as empty content.
8869
9684
  */
8870
- writeBytes?(input: string, bytes: Uint8Array, source: UriSourceDescriptor, ctx: UriResolveContext): Promise<UriMutationResult | void>;
9685
+ write?(input: string, content: UriPayload | Uint8Array | undefined, sources: readonly UriWriteSource[], ctx: UriResolveContext): Promise<UriMutationResult | void>;
8871
9686
  edit?(input: string, patch: UriPayload, ctx: UriResolveContext): Promise<UriMutationResult>;
8872
9687
  /** Observes only; see {@link read}. */
8873
9688
  list?(input: string, params: UriListParams, ctx: UriResolveContext): Promise<UriListResult>;
8874
9689
  /** Observes only; see {@link read}. */
8875
9690
  search?(input: string, params: UriSearchParams, ctx: UriResolveContext): Promise<UriSearchResult>;
8876
9691
  delete?(input: string, ctx: UriResolveContext): Promise<UriMutationResult>;
8877
- toMarkdown?(input: UriMarkdownRenderInput, ctx: UriResolveContext): string | undefined;
8878
9692
  }
8879
9693
 
8880
9694
  /** Authoritative lifecycle facts for a successful record-like URI mutation. */
@@ -8900,6 +9714,12 @@ declare interface UriMutationResult {
8900
9714
  readonly receipt?: UriMutationReceipt;
8901
9715
  /** Opaque source-native revision after this successful mutation. */
8902
9716
  readonly revision?: string;
9717
+ /** Cached reads this mutation made stale; see {@link UriCachePolicy.invalidate}. */
9718
+ readonly cachePolicy?: Pick<UriCachePolicy, "invalidate">;
9719
+ /** The module's own markdown for this result, shown instead of the host's default rendering. */
9720
+ readonly markdown?: string;
9721
+ /** The touched resource's name for the transcript row, as on `RawResource.label`. */
9722
+ readonly label?: UriResourceLabel;
8903
9723
  }
8904
9724
 
8905
9725
  declare interface UriNodeCapability {
@@ -8920,11 +9740,6 @@ declare interface UriNormalizeResult {
8920
9740
 
8921
9741
  declare type UriOperation = "help" | "read" | "write" | "edit" | "list" | "search" | "delete";
8922
9742
 
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
9743
  declare interface UriOperationDescription {
8929
9744
  /** Static verb phrase authored by the module. Never interpolated from input. */
8930
9745
  readonly action: string;
@@ -8947,9 +9762,11 @@ declare interface UriOperationDescription {
8947
9762
  */
8948
9763
  readonly key?: string;
8949
9764
  /**
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.
9765
+ * `true` offers this field to the person for editing on the approval card. The host applies
9766
+ * an edit itself: it copies the payload and replaces the value of the top-level string
9767
+ * property named `key`. So offer a field only when the payload is an object, `key` is one
9768
+ * of its string properties, and the module would send any value within `maxLength` as it
9769
+ * sends the model's; the host drops the offer otherwise. Absent means read-only.
8953
9770
  */
8954
9771
  readonly editable?: true;
8955
9772
  /** Longest edited value, in UTF-16 code units. Meaningful only with `editable`. */
@@ -8967,56 +9784,6 @@ declare interface UriOperationDescription {
8967
9784
  };
8968
9785
  }
8969
9786
 
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
9787
  declare interface UriParameter {
9021
9788
  readonly name: string;
9022
9789
  readonly in: "uri" | "query" | "selector";
@@ -9026,29 +9793,60 @@ declare interface UriParameter {
9026
9793
  readonly examples?: readonly unknown[];
9027
9794
  }
9028
9795
 
9796
+ /**
9797
+ * A claim on a module's own custom-scheme addresses. The pattern matches with or without an
9798
+ * account prefix: `gcal://@work/events/x` is matched as `gcal://events/x`, and the prefix is put
9799
+ * back on the official spelling, so one claim covers both spellings.
9800
+ */
9029
9801
  declare interface UriPatternClaim {
9030
9802
  readonly kind: "uriPattern";
9031
9803
  readonly pattern: UriPatternInit;
9032
9804
  /**
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
9805
+ * Canonical spelling rebuilt from the captures (see {@link UriClaimTemplate}). When the template
9806
+ * keeps the input's scheme (the module's own addressing), the input's query string is
9035
9807
  * carried over verbatim so a route flag such as `?permanent=true` survives
9036
9808
  * canonicalization (kay#7859); a template that re-targets to another scheme
9037
9809
  * (a projection to a web link) drops it, as does a pattern that consumed the
9038
9810
  * query itself or a template that spells its own.
9039
9811
  */
9040
- readonly normalizeTo?: string;
9812
+ readonly normalizeTo?: UriClaimTemplate;
9813
+ /**
9814
+ * Names the route this claim matches, so data keyed by route (a module's sign-in action rules)
9815
+ * can say which addresses it means. Several claims may name the same route.
9816
+ */
9817
+ readonly route?: string;
9818
+ /**
9819
+ * The web page a matched address opens, filled from the captures (see
9820
+ * {@link UriClaimTemplate}). The host matches an address's official spelling and paints its
9821
+ * reference with the source URL of the first matching claim that declares one.
9822
+ */
9823
+ readonly sourceUrl?: UriClaimTemplate;
9824
+ /**
9825
+ * The name a matched address's reference shows: literal text, with each `:name` replaced by
9826
+ * that capture as decoded (`"Flow :flow"`, or a fixed `"BFS"`). The first matching claim that
9827
+ * declares one names the address.
9828
+ */
9829
+ readonly title?: string;
9041
9830
  readonly examples?: readonly string[];
9042
9831
  }
9043
9832
 
9044
9833
  declare interface UriPatternInit {
9045
9834
  readonly protocol?: string;
9835
+ /**
9836
+ * Dot-delimited host pattern. A pattern that is one capture (`:owner`) takes the whole host,
9837
+ * dots included, as a `github://some.org/repo` owner must.
9838
+ */
9046
9839
  readonly hostname?: string;
9047
9840
  /**
9048
9841
  * Slash-delimited path pattern. A final capture may use `:name*` to consume
9049
- * the remaining path segments, preserving `/` in the captured value.
9842
+ * the remaining path segments, preserving `/` in the captured value. Unless the pattern itself
9843
+ * ends in `/`, an input path ending in one `/` matches as if the slash were absent, so
9844
+ * `threads/t1/` is `threads/t1` (captures and official spelling included) and a lone `/` after a
9845
+ * host is the empty path; a doubled `//` is never dropped, nor the root path of an address with
9846
+ * no host (`bfs:///`).
9050
9847
  */
9051
9848
  readonly pathname?: string;
9849
+ /** `&`-delimited query pattern; `""` matches only an address with no query. */
9052
9850
  readonly search?: string;
9053
9851
  }
9054
9852
 
@@ -9067,7 +9865,7 @@ declare interface UriPayloadContract {
9067
9865
 
9068
9866
  /**
9069
9867
  * The three-state permission vocabulary a plugin declares for its default permissions.
9070
- * Maps to `PermissionRule.effect`: `allow` → allow, `ask` → ask, `never` → deny.
9868
+ * Maps to `PermissionRule.effect`: `allow` -> allow, `ask` -> ask, `never` -> deny.
9071
9869
  */
9072
9870
  declare type UriPermissionDefault = "allow" | "ask" | "never";
9073
9871
 
@@ -9085,19 +9883,6 @@ declare interface UriPermissionDefaults {
9085
9883
  readonly delete?: UriPermissionDefault;
9086
9884
  }
9087
9885
 
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
9886
  declare interface UriResolveContext {
9102
9887
  readonly cwd: string;
9103
9888
  readonly signal?: AbortSignal;
@@ -9106,8 +9891,8 @@ declare interface UriResolveContext {
9106
9891
  readonly auth?: UriResolvedAuth;
9107
9892
  /**
9108
9893
  * 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.
9894
+ * It never originates from anything the caller can influence - no verb argument, no write
9895
+ * body - so a module may treat it as trustworthy caller identity and stamp it onto writes.
9111
9896
  *
9112
9897
  * Absent when no session owns the dispatch: registry matching and permission pre-checks
9113
9898
  * resolve a URI outside any verb call.
@@ -9115,14 +9900,14 @@ declare interface UriResolveContext {
9115
9900
  readonly sessionId?: string;
9116
9901
  /**
9117
9902
  * 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
9903
+ * {@link UriResolveContext.sessionId} and just as un-influenceable by the caller - so a module
9119
9904
  * writing to a store it owns can record the actor without the agent being able to choose one.
9120
9905
  *
9121
9906
  * Free text minted by the host's own runtime (Bonsai's form is `user`, `subagent`,
9122
9907
  * `system/<surface>`, `followup/<surface>`) rather than a closed union: the surface half is the
9123
9908
  * host's vocabulary, and a second enumeration of it here would drift from the first.
9124
9909
  *
9125
- * Absent whenever the host has nothing to say — no session owns the dispatch, or the session
9910
+ * Absent whenever the host has nothing to say - no session owns the dispatch, or the session
9126
9911
  * predates the host recording an origin. A module must then omit the actor rather than assume
9127
9912
  * one; an invented actor written into a durable artifact can never be walked back.
9128
9913
  */
@@ -9138,7 +9923,7 @@ declare interface UriResolveContext {
9138
9923
  readonly origin: "model" | "host";
9139
9924
  /**
9140
9925
  * 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
9926
+ * frame carried one. Absent means the frame carried none - never a reason to guess one: a
9142
9927
  * module recording this onto a durable artifact must treat absence as unresolved provenance,
9143
9928
  * not as "no run was active".
9144
9929
  */
@@ -9147,39 +9932,33 @@ declare interface UriResolveContext {
9147
9932
  readonly toolName?: string;
9148
9933
  };
9149
9934
  /**
9150
- * An OPAQUE, host-captured context for the mutation this dispatch is about to make —
9935
+ * An OPAQUE, host-captured context for the mutation this dispatch is about to make -
9151
9936
  * the artifact-provenance counterpart of `sessionId`/`actor`, and stamped at the same
9152
9937
  * un-influenceable dispatch site.
9153
9938
  *
9154
9939
  * The host composes it from facts a caller cannot supply: which session is acting, the
9155
9940
  * 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
9941
+ * session came to exist. The kernel treats it as an uninterpreted JSON value - it never
9157
9942
  * 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
9943
+ * dispatch boundary - so this layer stays ignorant of any one product's provenance
9159
9944
  * semantics. A module that owns a durable artifact carries it to its store and validates
9160
9945
  * it against its OWN schema at the point it persists, where the target revision is known.
9161
9946
  *
9162
9947
  * 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
9948
+ * capture resolver, or a capture that failed) - never a claim that there was nothing to
9164
9949
  * capture. Present only on mutating dispatches (`write`/`edit`/`delete`); reads never pay
9165
9950
  * for it.
9166
9951
  */
9167
9952
  readonly captureContext?: Readonly<Record<string, unknown>>;
9168
9953
  /**
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 —
9954
+ * Host-provided writable data root for the OWNING extension of the module handling this call -
9176
9955
  * structurally identical to the per-extension `paths` a tool/handler context already receives, so
9177
9956
  * a module authored against both surfaces reads `ctx.paths?.dataDir` uniformly. Lets a URI module
9178
9957
  * own durable local state (a JSON store, a small file tree) instead of the host constructing and
9179
9958
  * injecting it. Host-populated after dispatch selects a module and keyed to that module's owner;
9180
9959
  * always absent during matching, and absent for modules with no owning extension (builtins) or a
9181
9960
  * 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
9961
+ * a URI module and does NOT pre-create the directory - create it on first write and tolerate its
9183
9962
  * absence on read.
9184
9963
  */
9185
9964
  readonly paths?: {
@@ -9194,6 +9973,15 @@ declare interface UriResolveContext {
9194
9973
  * proceeds as before.
9195
9974
  */
9196
9975
  readonly expectedRevision?: string;
9976
+ /**
9977
+ * The revision this session already holds for the address a `read` is about to fetch: the
9978
+ * `RawResourceMetadata.revision` its last read reported. Set only on a repeat `read`, never
9979
+ * on a mutation (`expectedRevision` is the mutation witness). A module with a cheap
9980
+ * current-revision check (a metadata request, a `stat`) may compare first and, when it has
9981
+ * verified the two are equal, answer {@link RawResource.unchanged} with no body. A module
9982
+ * with no such check ignores it and reads in full, which is always correct.
9983
+ */
9984
+ readonly knownRevision?: string;
9197
9985
  /** Host marks a mediated session that enforces its private revision witness ledger. */
9198
9986
  readonly requiresRevisionWitness?: boolean;
9199
9987
  /** Host-derived descriptor for a top-level copied resource; absent for inline writes. */
@@ -9203,26 +9991,13 @@ declare interface UriResolveContext {
9203
9991
  declare interface UriResolvedAuth {
9204
9992
  readonly connection?: UriConnection;
9205
9993
  /** Present only for verb handlers of a module declaring `requiresAuth`; absent during matching. */
9206
- readonly credential?: UriResolvedCredential;
9994
+ readonly credential?: ResolvedCredential;
9207
9995
  /** Present for handlers gated by a non-injectable external-CLI readiness scheme. */
9208
9996
  readonly externalCli?: UriExternalCliRunner;
9209
9997
  /** Opaque host metadata; not a credential channel. */
9210
9998
  readonly metadata?: Record<string, unknown>;
9211
9999
  }
9212
10000
 
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
10001
  declare interface UriResourceDescription {
9227
10002
  readonly uri: string;
9228
10003
  /** Absent when the caller should retain its existing label. */
@@ -9242,12 +10017,9 @@ declare interface UriResourceLabel {
9242
10017
  readonly section?: string;
9243
10018
  }
9244
10019
 
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
- }
10020
+ declare const UriScheme: z.ZodString;
10021
+
10022
+ declare type UriScheme = z.infer<typeof UriScheme>;
9251
10023
 
9252
10024
  declare interface UriSchemeClaim {
9253
10025
  readonly kind: "scheme";
@@ -9282,15 +10054,21 @@ declare interface UriSearchResult {
9282
10054
  * so the caller knows the matches may be partial.
9283
10055
  */
9284
10056
  readonly metadata?: UriListMetadata;
10057
+ /** How long the host may cache this result; see {@link UriCachePolicy.ttlMs}. */
10058
+ readonly cachePolicy?: Pick<UriCachePolicy, "ttlMs">;
10059
+ /** The module's own markdown for this result, shown instead of the host's default rendering. */
10060
+ readonly markdown?: string;
10061
+ /** The touched resource's name for the transcript row, as on `RawResource.label`. */
10062
+ readonly label?: UriResourceLabel;
9285
10063
  }
9286
10064
 
9287
10065
  /**
9288
10066
  * What a copied resource IS, minus its bytes: the identity half of a mediated
9289
10067
  * source. The host derives it once (filename inferred from the source address,
9290
10068
  * `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.
10069
+ * delivers it inside each `UriWriteSource`, and on `ctx.writeSource` for a
10070
+ * `source` copy - so a remote sink can name and type what it stores without
10071
+ * re-deriving either from raw bytes.
9294
10072
  */
9295
10073
  declare interface UriSourceDescriptor {
9296
10074
  readonly uri: string;
@@ -9305,11 +10083,11 @@ declare interface UriTemplate {
9305
10083
  /**
9306
10084
  * Per-operation effect docs for this route, keyed by operation. A route
9307
10085
  * whose shared {@link description} spans several verbs ("Read, edit, or
9308
- * trash a thread") is ambiguous for any surface that groups by verb — a
10086
+ * trash a thread") is ambiguous for any surface that groups by verb - a
9309
10087
  * permission view must not describe `read` with a sentence that mentions
9310
10088
  * trashing. Each entry says what THAT operation concretely does at THIS
9311
10089
  * route; operations without an entry fall back to the shared description.
9312
- * Additive and optional — existing templates are unaffected.
10090
+ * Additive and optional - existing templates are unaffected.
9313
10091
  */
9314
10092
  readonly operationDocs?: Partial<Record<UriOperation, string>>;
9315
10093
  /**
@@ -9349,13 +10127,13 @@ declare interface UriValidationMessage {
9349
10127
 
9350
10128
  /**
9351
10129
  * Author-declared, human-facing documentation of what each supported verb
9352
- * CONCRETELY does against the provider — the capability half of a module's
10130
+ * CONCRETELY does against the provider - the capability half of a module's
9353
10131
  * boolean verb-support declaration. Written for a permission/capability
9354
10132
  * surface ("what am I allowing?"), not for the model: one plain sentence per
9355
10133
  * verb naming the real-world effect ("Sends mail, creates/updates drafts,
9356
10134
  * applies label changes"), never wire mechanics or URI shapes. Verbs the
9357
10135
  * 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
10136
+ * entry simply renders undocumented (fail-open metadata) - but for a plugin's
9359
10137
  * `kay.agent` URI modules, `auditPluginDisclosure` (`@bonsai/plugin-sdk/
9360
10138
  * disclosure`) enforces coverage of every supported verb at authoring/load
9361
10139
  * time.
@@ -9363,7 +10141,7 @@ declare interface UriValidationMessage {
9363
10141
  * Lives here in the protocol layer (beside {@link UriTemplate.operationDocs},
9364
10142
  * its per-route sibling) so both `@herd/uri-extensions`' `UriExtensionModule`
9365
10143
  * and herd-agent's kernel `UriModule` can carry it without inverting the
9366
- * `@herd/uri` ← `@herd/uri-extensions` dependency.
10144
+ * `@herd/uri` <- `@herd/uri-extensions` dependency.
9367
10145
  */
9368
10146
  declare interface UriVerbDocs {
9369
10147
  /** What a `read` of this address space returns (also covers `help`). */
@@ -9386,6 +10164,7 @@ declare interface UriVerbSupport {
9386
10164
  readonly editable: boolean;
9387
10165
  readonly deletable?: boolean;
9388
10166
  readonly listable: boolean;
10167
+ readonly searchable?: boolean;
9389
10168
  }
9390
10169
 
9391
10170
  /** A mediated resource supplied beside structured write content without entering model context. */
@@ -9393,10 +10172,15 @@ declare interface UriWriteSource extends UriSourceDescriptor {
9393
10172
  readonly content: string | Uint8Array;
9394
10173
  }
9395
10174
 
10175
+ /** {@link hostWithinDomain} over a URL's hostname; an unparseable URL is never within. */
10176
+ declare function urlHostWithinDomain(url: string, domain: string): boolean;
10177
+
10178
+ /** A claim on a public web link (`http` or `https`), usually folded onto the module's own scheme. */
9396
10179
  declare interface UrlPatternClaim {
9397
10180
  readonly kind: "urlPattern";
9398
10181
  readonly pattern: UriPatternInit;
9399
- readonly normalizeTo?: string;
10182
+ readonly normalizeTo?: UriClaimTemplate;
10183
+ readonly route?: string;
9400
10184
  readonly examples?: readonly string[];
9401
10185
  }
9402
10186
 
@@ -9443,6 +10227,66 @@ declare interface ValidateApiKeyCredentialResult {
9443
10227
 
9444
10228
  declare type ValidatePluginApiKeyCredential = ValidateApiKeyCredential;
9445
10229
 
10230
+ /**
10231
+ * ---
10232
+ * responsibility: The one interface through which a vendored-MCP plugin's code calls a tool on its
10233
+ * own vendored MCP server, so every read-through plugin and the daemon that implements the port
10234
+ * agree on the same shape by construction.
10235
+ * owns:
10236
+ * - {@link VendoredMcpPort} - what a plugin's URI module and tools call.
10237
+ * - {@link VendoredMcpOutcome} - the typed result a plugin decodes, including every failure it
10238
+ * must surface to the agent rather than swallow.
10239
+ * does_not_own:
10240
+ * - The implementation. A plugin never dials MCP itself: it takes the port from
10241
+ * `api.vendoredMcp.port(server)` (`@bonsai/plugin-sdk/kay-agent`), which the daemon binds to the
10242
+ * plugin's own hosted server over its MCP host. Plugin source stays host-free, and a plugin
10243
+ * under test substitutes a fake port.
10244
+ * - Which tool names exist. `TTool` is the plugin's own union of the vendor tools it calls; the
10245
+ * port is generic so a typo in a tool name fails typecheck inside the plugin.
10246
+ * ---
10247
+ */
10248
+ /**
10249
+ * Why a call failed, as the plugin must tell the agent. `needs-auth` carries the exact service to
10250
+ * reconnect (`serviceId`) so a plugin can name it in its error instead of returning an empty
10251
+ * result; today's plugins name their statically declared `requiresAuth` service instead, which is
10252
+ * the same service for a single-service plugin. `unknown-server` and `unknown-tool` are wiring
10253
+ * faults (a server Bonsai does not host for this plugin, or a tool name the server does not have)
10254
+ * and stay loud for the same reason: an unreachable host must never decode as "no data".
10255
+ */
10256
+ declare type VendoredMcpFailureReason = "needs-auth" | "unknown-server" | "unknown-tool" | "failed";
10257
+
10258
+ declare type VendoredMcpOutcome =
10259
+ /**
10260
+ * `value` is the tool's `structuredContent` when it sent one, else its text content parsed as
10261
+ * JSON, else that text as a string: a vendor tool that answers in prose (a transcript, a
10262
+ * markdown summary) is a result, not a failure. A route that needs a record rejects a string
10263
+ * itself, with its own message.
10264
+ */
10265
+ {
10266
+ readonly ok: true;
10267
+ readonly value: unknown;
10268
+ } | {
10269
+ readonly ok: false;
10270
+ readonly reason: VendoredMcpFailureReason;
10271
+ readonly message: string;
10272
+ /** Exact reconnect target supplied by the MCP host for an auth failure. */
10273
+ readonly serviceId?: string;
10274
+ };
10275
+
10276
+ /**
10277
+ * One vendored MCP server, as the plugin that declares it calls it. `invoke` calls any tool the
10278
+ * server has, whether it reads or writes. The port checks no permission of its own: a call is as
10279
+ * permitted as the plugin tool or URI verb that makes it.
10280
+ */
10281
+ declare interface VendoredMcpPort<TTool extends string = string> {
10282
+ invoke(input: {
10283
+ /** The session's working directory; the host resolves the server binding per cwd. */
10284
+ readonly cwd: string;
10285
+ readonly tool: TTool;
10286
+ readonly args: Readonly<Record<string, unknown>>;
10287
+ }): Promise<VendoredMcpOutcome>;
10288
+ }
10289
+
9446
10290
  /** Creates a Void type. */
9447
10291
  declare function Void(options?: TSchemaOptions): TVoid;
9448
10292
 
@@ -9477,8 +10321,8 @@ declare const WhiteboardEntryInfo: z.ZodObject<{
9477
10321
  authorSessionId: z.ZodNullable<z.ZodString>;
9478
10322
  }, z.core.$strict>>;
9479
10323
  state: z.ZodEnum<{
9480
- available: "available";
9481
10324
  unavailable: "unavailable";
10325
+ available: "available";
9482
10326
  "unsupported-version": "unsupported-version";
9483
10327
  invalid: "invalid";
9484
10328
  }>;
@@ -9499,8 +10343,8 @@ declare const WhiteboardEntryInspection: z.ZodObject<{
9499
10343
  authorSessionId: z.ZodNullable<z.ZodString>;
9500
10344
  }, z.core.$strict>>;
9501
10345
  state: z.ZodEnum<{
9502
- available: "available";
9503
10346
  unavailable: "unavailable";
10347
+ available: "available";
9504
10348
  "unsupported-version": "unsupported-version";
9505
10349
  invalid: "invalid";
9506
10350
  }>;