@yaklabs-ai/plugin-sdk 0.12.5 → 0.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # @yaklabs-ai/plugin-sdk
2
2
 
3
- The SDK for authoring a **Kay directory plugin** — a plugin you write in a
4
- repository and add to Kay through an approved plugin repository source.
3
+ The SDK for authoring a **Bonsai directory plugin** — a plugin you write in a
4
+ repository and add to Bonsai through an approved plugin repository source.
5
5
 
6
6
  Published to the public npm registry under the MIT licence.
7
7
 
@@ -13,13 +13,13 @@ it under the name your plugin code actually imports:
13
13
  ```json
14
14
  {
15
15
  "dependencies": {
16
- "@kay/plugin-sdk": "npm:@yaklabs-ai/plugin-sdk@^0.12.5"
16
+ "@kay/plugin-sdk": "npm:@yaklabs-ai/plugin-sdk@^0.13.1"
17
17
  }
18
18
  }
19
19
  ```
20
20
 
21
21
  **The alias is not cosmetic.** The package is published as
22
- `@yaklabs-ai/plugin-sdk`, but Kay's daemon resolves the specifier
22
+ `@yaklabs-ai/plugin-sdk`, but Bonsai's daemon resolves the specifier
23
23
  `@kay/plugin-sdk` when it loads your entrypoint, so that is the name your source
24
24
  must import. The alias installs the published package at
25
25
  `node_modules/@kay/plugin-sdk`, satisfying both.
@@ -79,22 +79,22 @@ export default activate;
79
79
 
80
80
  ## Entrypoints
81
81
 
82
- | Import | What it gives you |
83
- | ----------------------------------- | -------------------------------------------------------------------------- |
84
- | `@kay/plugin-sdk/kay-agent` | Tools, URI modules, search providers, auth — the per-session agent surface |
85
- | `@kay/plugin-sdk/kay-app` | App-lifetime surface: storage, endpoints, app-scoped URI modules |
86
- | `@kay/plugin-sdk/kay-agent/testing` | `invokeToolForTest`, `fauxAuthContext` — for your own `node --test` runs |
87
- | `@kay/plugin-sdk/disclosure` | The conformance audit Kay gates your plugin with — run it before you ship |
82
+ | Import | What it gives you |
83
+ | ----------------------------------- | ---------------------------------------------------------------------------- |
84
+ | `@kay/plugin-sdk/kay-agent` | Tools, URI modules, search providers, auth — the per-session agent surface |
85
+ | `@kay/plugin-sdk/kay-app` | App-lifetime surface: storage, endpoints, app-scoped URI modules |
86
+ | `@kay/plugin-sdk/kay-agent/testing` | `invokeToolForTest`, `fauxAuthContext` — for your own `node --test` runs |
87
+ | `@kay/plugin-sdk/disclosure` | The conformance audit Bonsai gates your plugin with — run it before you ship |
88
88
 
89
- `kay-agent/host` is intentionally not published; it is Kay's own loading
89
+ `kay-agent/host` is intentionally not published; it is Bonsai's own loading
90
90
  machinery, not an authoring API.
91
91
 
92
92
  ## Check yourself against the gate that can reject you
93
93
 
94
- Kay runs `auditPluginDisclosure` against your `kay.agent` target **at load,
94
+ Bonsai runs `auditPluginDisclosure` against your `kay.agent` target **at load,
95
95
  fail-closed**: one violation and the whole target fails — no tools, no URI
96
96
  modules, no skills. It is the same function this entrypoint exports, so the
97
- run below is the run Kay does.
97
+ run below is the run Bonsai does.
98
98
 
99
99
  ```ts
100
100
  import {
@@ -128,7 +128,7 @@ Pass the loader's inputs, not a subset — the audit judges what it is given:
128
128
  - `commandBoundSkillIds` — the `localId` of every skill bound to a declared
129
129
  command (`contributes.commands[].handler.kind === "skill"`). Omit it when
130
130
  you have such commands and you will see `skill-link-missing-scheme`
131
- violations Kay itself would not report.
131
+ violations Bonsai itself would not report.
132
132
 
133
133
  `auditSkillCodegenPairs({ declaredSkills, exportedSkills, packageDir })` is the
134
134
  companion check for a bundled-skill plugin. It compares the decoded YAML
@@ -138,7 +138,7 @@ Both return violations rather than asserting, so any runner works. Both read fro
138
138
  and call your own `help()`, so run them in Node, and keep `activate` free of
139
139
  side effects: the probe calls it.
140
140
 
141
- `PLUGIN_DISCLOSURE_CONTRACT` names the floor this SDK enforces. Kay's load
141
+ `PLUGIN_DISCLOSURE_CONTRACT` names the floor this SDK enforces. Bonsai's load
142
142
  failure names the floor **it** enforces. Different numbers mean the rules
143
143
  moved, not your code — update this package and re-run.
144
144
 
@@ -149,47 +149,51 @@ someone remembering to cut one. If the numbers differ, a newer version exists.
149
149
  ## What this package is, and is not, at runtime
150
150
 
151
151
  Installing this SDK gives you **types, editor completion, and a runtime you can
152
- execute in your own tests**. It is not what runs inside Kay.
152
+ execute in your own tests**. It is not what runs inside Bonsai.
153
153
 
154
154
  When the daemon loads your plugin, chord bundles its declared target and keeps
155
155
  `@kay/plugin-sdk/*` as a host external. Those imports resolve to the SDK
156
- **bundled with the Kay app**. The host's copy always wins. That is deliberate —
156
+ **bundled with the Bonsai app**. The host's copy always wins. That is deliberate —
157
157
  one implementation, the one the daemon is built against — but it has a
158
158
  consequence worth stating plainly:
159
159
 
160
- > If your installed SDK is newer than your Kay app, code that typechecks against
160
+ > If your installed SDK is newer than your Bonsai app, code that typechecks against
161
161
  > a newly-added API can still fail at load time, because the running host does
162
162
  > not have it.
163
163
 
164
164
  Importing an entire subpath the host does not know about fails loudly and
165
165
  specifically (a `target-load` diagnostic naming the subpath, shown on the
166
- Plugins page). A _symbol_ that exists here but not in the host's older copy is
167
- not caught that way. Keep the SDK version in step with the Kay app you run.
166
+ App Store on the app's detail page). A _symbol_ that exists here but not in the host's older copy is
167
+ not caught that way. Keep the SDK version in step with the Bonsai app you run.
168
168
 
169
169
  The same skew applies to the conformance floor, and in the direction that
170
- bites: if your Kay app is **newer** than this package, it can enforce a rule
170
+ bites: if your Bonsai app is **newer** than this package, it can enforce a rule
171
171
  your copy of the audit does not know about, so a green local run is not a
172
172
  promise the host will load you. That is why the load failure names its
173
173
  contract number — compare it against `PLUGIN_DISCLOSURE_CONTRACT` here, and if
174
174
  they differ, update before you debug your code.
175
175
 
176
- ## Install the plugin in Kay
176
+ ## Install the plugin in Bonsai
177
177
 
178
178
  The fastest route is the local plugin root. Put the plugin directory in
179
- `~/.kay/plugins/<your-plugin>/` and turn it on in **Settings → Plugins**. That
180
- root is already consented, so there is no repository to stage and no Trust step,
181
- and your edits load without restarting Kay.
179
+ `~/.kay/plugins/<your-plugin>/` and enable it in **App Store → Installed**.
180
+ That root is already consented, so there is no repository to stage and no Trust
181
+ step. Commit your edits to load them without restarting Bonsai; uncommitted edits
182
+ never execute.
182
183
 
183
184
  To share a plugin with someone who does not have your files, commit it to a Git
184
- repository and have them add it under **Settings → Plugins → Plugin sources →
185
- Add repository source**. They stage the source, Trust it, and enable the plugin;
186
- Trust and activation are separate gates.
185
+ repository and have them add it under **App Store → Sources → Add source**.
186
+ They stage and Trust the source, then add the plugin: **Get** in
187
+ **App Store → Browse** for a marketplace catalog, or **Turn on** on the
188
+ source's page under **App Store → Sources** for a plain Git repository.
189
+ Either action installs and enables that plugin; source Trust does not install
190
+ every item it contains.
187
191
 
188
192
  A plugin executes as a trusted in-process Node module with the daemon's own
189
193
  authority. **The SDK does not provide a sandbox.** Putting code in the local
190
- plugin root is itself the decision to run it, with no prompt in between — so
191
- treat that directory exactly as carefully as you would Trust an unfamiliar
192
- repository.
194
+ plugin root makes it discoverable without a source Trust prompt; enabling its install
195
+ row is still required. Treat that directory exactly as carefully as you would
196
+ Trust an unfamiliar repository.
193
197
 
194
198
  ## Requirements
195
199
 
@@ -200,8 +204,8 @@ checkout script is not the standalone path. The SDK's `kay-app` declarations
200
204
  use `node:sqlite`, which TypeScript cannot resolve without the Node 24
201
205
  typings.
202
206
 
203
- When Kay loads your plugin, chord bundles the declared entrypoint and its
207
+ When Bonsai loads your plugin, chord bundles the declared entrypoint and its
204
208
  relative imports. Multi-file entrypoints are supported, and TypeScript syntax
205
209
  that native stripping cannot execute, such as enums, namespaces, constructor
206
210
  parameter properties, and decorators, is compiled into the bundle. The host
207
- SDK remains external and is supplied by the running Kay app.
211
+ SDK remains external and is supplied by the running Bonsai app.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "package": "@yaklabs-ai/plugin-sdk",
4
- "version": "0.12.5",
4
+ "version": "0.13.1",
5
5
  "target": "npm",
6
6
  "firstPartyLicense": "MIT",
7
7
  "bundled": [
@@ -20,7 +20,7 @@ declare interface AccountIdentity {
20
20
  readonly displayName?: string;
21
21
  /** Absolute URL of the account's avatar. */
22
22
  readonly avatarUrl?: string;
23
- /** Issuer-asserted beta-tester flag; only Kay's product-account service sets it. */
23
+ /** Issuer-asserted beta-tester flag; only Bonsai's product-account service sets it. */
24
24
  readonly betaTester?: boolean;
25
25
  readonly activeOrganizationId?: string;
26
26
  readonly organizationSelectionState?: "selected" | "unresolved";
@@ -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 Kay'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;
@@ -164,7 +164,7 @@ declare interface ApiKeyAuthSchemeField {
164
164
  readonly label: string;
165
165
  }
166
166
 
167
- /** Provider-owned instructions for creating a credential outside Kay. */
167
+ /** Provider-owned instructions for creating a credential outside Bonsai. */
168
168
  declare interface ApiKeyAuthSchemeSetup {
169
169
  readonly instructions: string;
170
170
  /** Absolute HTTPS page where the user can create or manage the credential. */
@@ -755,7 +755,7 @@ declare interface InteractionCandidate {
755
755
  declare type InteractionCandidateKind = "click" | "fill" | "select" | "scroll" | "wait" | "activate" | "press";
756
756
 
757
757
  declare interface InteractionDriver {
758
- /** Unique in the session. `builtin:` is reserved for Kay's own drivers. */
758
+ /** Unique in the session. `builtin:` is reserved for Bonsai's own drivers. */
759
759
  readonly id: string;
760
760
  /** Human-readable name the agent sees beside the driver's targets. */
761
761
  readonly label: string;
@@ -778,7 +778,7 @@ declare interface InteractionDriver {
778
778
  }
779
779
 
780
780
  /**
781
- * The Interaction driver contract: what a plugin implements to let Kay's agent drive something
781
+ * The Interaction driver contract: what a plugin implements to let Bonsai's agent drive something
782
782
  * on the user's machine (a browser, an app, a remote screen) through the harness-owned
783
783
  * Interaction engine.
784
784
  *
@@ -791,7 +791,7 @@ declare interface InteractionDriver {
791
791
  * every tool work on every driver.
792
792
  *
793
793
  * Shape mirrors `SearchProvider` behind `web_search`: one tool set owned by the harness, many
794
- * backends registered per session, the `builtin:` id namespace reserved for Kay's own.
794
+ * backends registered per session, the `builtin:` id namespace reserved for Bonsai's own.
795
795
  */
796
796
  /** "Browser Interaction" drives web pages; "Computer Use Interaction" drives native apps. */
797
797
  declare type InteractionDriverKind = "browser" | "computer-use";
@@ -850,7 +850,7 @@ declare interface InteractionRegion {
850
850
  }
851
851
 
852
852
  /**
853
- * One surface Kay ships, in the driver contract's own shape, so a plugin's driver is the surface
853
+ * One surface Bonsai ships, in the driver contract's own shape, so a plugin's driver is the surface
854
854
  * plus an identity: `{ id, label, kind, ...api.interaction.desktop }`. Each call is checked
855
855
  * against the plugin's `interaction.surface {surface}` grant, and then against the session's
856
856
  * permissions exactly as the agent's `interaction_*` tools are: an act is judged as the plugin
@@ -972,7 +972,7 @@ declare interface JevScoreQuestion {
972
972
  * Those are host-side (`@kay/agent-runtime`'s `kay/jev/`).
973
973
  * ---
974
974
  *
975
- * The vocabulary is Kay's own (SDK 0.12.0). Before it, `use` received TypeSafe's `TypeSafeClient`
975
+ * The vocabulary is Bonsai's own (SDK 0.12.0). Before it, `use` received TypeSafe's `TypeSafeClient`
976
976
  * and plugins coded against a vendor's types. It follows pi's classifier contract, which the host
977
977
  * runs Jev through: a yes/no question is `bool` and answers a `probability`, every instruction and
978
978
  * criterion is text, and the state is a JSON object.
@@ -1004,7 +1004,7 @@ declare const JsonValue_2: z.ZodType<JsonValue_2>;
1004
1004
 
1005
1005
  declare type JsonValue_3 = null | boolean | number | string | readonly JsonValue_3[] | JsonObject;
1006
1006
 
1007
- /** A plugin's Kay agent activation — receives only the brokered {@link KayAgentPluginApi}. */
1007
+ /** A plugin's Bonsai agent activation — receives only the brokered {@link KayAgentPluginApi}. */
1008
1008
  declare type KayAgentActivate = (api: KayAgentPluginApi) => void;
1009
1009
 
1010
1010
  /**
@@ -1024,52 +1024,14 @@ declare interface KayAgentEvents {
1024
1024
  publish(localId: string, payload: unknown): void;
1025
1025
  }
1026
1026
 
1027
- /**
1028
- * Claim-checked filesystem operations. Paths are root-relative; canonical host paths never cross
1029
- * the seam. Writes are atomic: readers see the old file or the new one, never a partial write. A
1030
- * replace keeps the file's mode but makes a new file, so owner and extended attributes are not
1031
- * carried over. On a volume without hard links an exclusive create can briefly show an empty file.
1032
- */
1033
- declare interface KayAgentFilesystem {
1034
- readText(location: KayAgentFilesystemLocation): Promise<string>;
1035
- writeText(location: KayAgentFilesystemLocation, content: string, options?: KayAgentFilesystemWriteOptions): Promise<void>;
1036
- /** The whole file, or the requested window of it. */
1037
- readBytes(location: KayAgentFilesystemLocation, range?: KayAgentFilesystemByteRange): Promise<Uint8Array>;
1038
- writeBytes(location: KayAgentFilesystemLocation, bytes: Uint8Array, options?: KayAgentFilesystemWriteOptions): Promise<void>;
1039
- mkdir(location: KayAgentFilesystemLocation): Promise<void>;
1040
- readDir(location: KayAgentFilesystemLocation): Promise<readonly KayAgentFilesystemEntry[]>;
1041
- stat(location: KayAgentFilesystemLocation): Promise<KayAgentFilesystemStat>;
1042
- rename(from: KayAgentFilesystemLocation, to: KayAgentFilesystemLocation): Promise<void>;
1043
- /** Returns the canonical path relative to the selected grant root. */
1044
- realpath(location: KayAgentFilesystemLocation): Promise<string>;
1045
- }
1046
-
1047
- /** A byte window of a file; `maxBytes` is capped by the host. */
1048
- declare interface KayAgentFilesystemByteRange {
1049
- readonly offset: number;
1050
- readonly maxBytes: number;
1051
- }
1052
-
1053
- declare interface KayAgentFilesystemEntry {
1054
- readonly name: string;
1055
- readonly kind: "file" | "directory" | "symlink" | "other";
1056
- }
1057
-
1058
- declare interface KayAgentFilesystemLocation {
1059
- /** Exact `root` selector the host bound for this target, plus a path relative to it. */
1060
- readonly root: string;
1061
- readonly path: string;
1062
- }
1063
-
1064
- declare interface KayAgentFilesystemStat {
1065
- readonly kind: "file" | "directory" | "other";
1066
- readonly size: number;
1067
- readonly modifiedAtMs: number;
1068
- }
1069
-
1070
- declare interface KayAgentFilesystemWriteOptions {
1071
- /** Fail with `already_exists` instead of replacing a file already at the location. */
1072
- readonly exclusive?: boolean;
1027
+ /** This plugin's folder in the session family's shared Whiteboard. */
1028
+ declare interface KayAgentFamilyFolder {
1029
+ /**
1030
+ * The folder's absolute path, created on first use. The plugin that owns the Whiteboard gets
1031
+ * the whole family folder; every other plugin gets its own `plugins/<plugin-id>/` inside it.
1032
+ * Undefined outside a session.
1033
+ */
1034
+ path(): Promise<string | undefined>;
1073
1035
  }
1074
1036
 
1075
1037
  declare interface KayAgentInteractionSurfaces {
@@ -1081,7 +1043,7 @@ declare interface KayAgentInteractionSurfaces {
1081
1043
  readonly browserPane: InteractionSurface;
1082
1044
  /**
1083
1045
  * The user's desktop through the platform's accessibility APIs: native apps as targets
1084
- * (`app://<bundle id>`), their windows' controls as elements. On macOS Kay drives it with its
1046
+ * (`app://<bundle id>`), their windows' controls as elements. On macOS Bonsai drives it with its
1085
1047
  * bundled accessibility worker under the app's Accessibility permission; on a platform with no
1086
1048
  * backend it reports itself unavailable.
1087
1049
  */
@@ -1211,11 +1173,8 @@ declare interface KayAgentPluginApi {
1211
1173
  /** Register a whole URI module (a scheme handler that owns its address space, e.g. `drive://`). */
1212
1174
  registerModule(module: UriExtensionModule): void;
1213
1175
  };
1214
- /**
1215
- * Always present; each call is checked against `filesystem.read {root}` / `filesystem.write
1216
- * {root}` for the root it touches, and against the host's binding for that root.
1217
- */
1218
- readonly filesystem: KayAgentFilesystem;
1176
+ /** This plugin's folder in the session family's shared Whiteboard, created on first use. Undefined outside a session. */
1177
+ readonly familyFolder: KayAgentFamilyFolder;
1219
1178
  /** Absent unless the `knowledgeSearch` grant is requested and effectively granted. */
1220
1179
  readonly knowledgeSearch?: KayAgentKnowledgeSearch;
1221
1180
  /** Absent unless the `knownResources` grant is requested and effectively granted. */
@@ -1241,7 +1200,7 @@ declare interface KayAgentPluginApi {
1241
1200
  */
1242
1201
  readonly process: KayAgentProcess;
1243
1202
  /**
1244
- * Always present; the interaction surfaces Kay ships (its browser pane, and the desktop through
1203
+ * Always present; the interaction surfaces Bonsai ships (its browser pane, and the desktop through
1245
1204
  * the platform accessibility APIs), each call checked against `interaction.surface {surface}`
1246
1205
  * and the session's permissions (see {@link InteractionSurface}).
1247
1206
  * Wrap one in an {@link InteractionDriver} and register it with
@@ -1268,9 +1227,8 @@ declare interface KayAgentPluginApi {
1268
1227
  readonly time: KayAppTime;
1269
1228
  /**
1270
1229
  * File I/O in user folders, run by the host on its own worker threads under a deadline and
1271
- * answered as a typed result. Always present and ungated: unlike {@link filesystem} it takes
1272
- * absolute paths and checks no grant, because it adds no authority a plugin's `node:fs` does not
1273
- * already have. What it adds is that a slow or unresponsive folder never blocks the daemon. See
1230
+ * answered as a typed result. Always present and ungated: it takes absolute paths and checks no
1231
+ * grant, because it adds no authority a plugin's `node:fs` does not already have. What it adds is that a slow or unresponsive folder never blocks the daemon. See
1274
1232
  * {@link KayAppUserFiles}.
1275
1233
  */
1276
1234
  readonly userFiles: KayAppUserFiles;
@@ -1841,7 +1799,7 @@ declare type ModelPromptCache = Partial<Record<Exclude<CacheRetention, "none">,
1841
1799
  declare type ModelThinkingLevel = "off" | ThinkingLevel;
1842
1800
 
1843
1801
  /**
1844
- * Who may supply the OAuth client for a scheme: Kay's embedded client, or one the user pastes in
1802
+ * Who may supply the OAuth client for a scheme: Bonsai's embedded client, or one the user pastes in
1845
1803
  * (BYOOC). An UNORDERED SET — list order carries no meaning (nothing reads it as a preference).
1846
1804
  */
1847
1805
  declare type OAuthClientSupplier = "kay" | "user";
@@ -1854,7 +1812,7 @@ declare interface OAuthRedirectResult {
1854
1812
 
1855
1813
  /**
1856
1814
  * The two client-supply axes, structured so the illegal cell is unrepresentable: a `confidential`
1857
- * exchange's secret may only ever be Kay's (held by the host's broker, never pasted by a
1815
+ * exchange's secret may only ever be Bonsai's (held by the host's broker, never pasted by a
1858
1816
  * customer), so `confidential` types `clientSuppliers` as `readonly ["kay"]` — `confidential ×
1859
1817
  * user` is a compile-time impossibility in v1, not a runtime refusal. `pkce`'s `clientSuppliers`
1860
1818
  * is a non-empty set; {@link createOAuthPkceAuthScheme} dedupes it and refuses empty.
@@ -2037,7 +1995,7 @@ declare interface OpenRouterRouting {
2037
1995
  * {@link PLUGIN_DISCLOSURE_RULES} changes, and never for anything else.
2038
1996
  *
2039
1997
  * Two readers, and the second is why a number beats a changelog entry. A plugin author compares it
2040
- * against the Kay build they target. The daemon's loader NAMES it in the
1998
+ * against the Bonsai build they target. The daemon's loader NAMES it in the
2041
1999
  * `disclosure-conformance-failed` diagnostic, so a plugin that passed its own suite against an
2042
2000
  * older SDK and then failed at load says "this host enforces contract N, you audited against
2043
2001
  * something older" instead of leaving the author to guess whether their code or their SDK moved.
@@ -2126,7 +2084,7 @@ declare interface PluginDelegatedScheme {
2126
2084
  readonly connect: (ctx: PluginDelegatedConnectContext) => Promise<PluginDelegatedConnectResult>;
2127
2085
  /**
2128
2086
  * Mint fresh material from the current (expiring) material, including the `data` the last
2129
- * connect or refresh returned. Kay calls this only inside its credential authority, which holds
2087
+ * connect or refresh returned. Bonsai calls this only inside its credential authority, which holds
2130
2088
  * the refresh lease and saves the result; this hook performs the exchange and nothing else.
2131
2089
  * Returned `data` replaces the stored `data`; omitted `data` keeps it. Returned `expiresAt`
2132
2090
  * replaces the stored expiry; omitted `expiresAt` means the new material does not expire.
@@ -2203,7 +2161,7 @@ declare type PluginOAuthPkceScheme = PluginOAuthSchemeClientAxes & {
2203
2161
  readonly summary?: string;
2204
2162
  readonly authorizationUrl: string;
2205
2163
  readonly tokenUrl: string;
2206
- /** The Kay-supplied embedded client id; omit for a `confidential` scheme or a `pkce` scheme
2164
+ /** The Bonsai-supplied embedded client id; omit for a `confidential` scheme or a `pkce` scheme
2207
2165
  * the host ships no client for. */
2208
2166
  readonly clientId?: string;
2209
2167
  /**
@@ -2833,6 +2791,14 @@ declare interface ToolDefinition<TParams extends ToolParametersWire = ToolParame
2833
2791
  * explicit Stop.
2834
2792
  */
2835
2793
  durationClass?: "fast" | "long";
2794
+ /**
2795
+ * Set only for a tool whose rerun with the same arguments is harmless, such
2796
+ * as one keyed on its own call: after a crash cuts a call off, a pi-durable
2797
+ * session may rerun it with its recorded arguments (without `before_tool`)
2798
+ * instead of ending it as an interrupted error. Absent: never rerun. Ignored
2799
+ * on pi 0.99.1.
2800
+ */
2801
+ replaySafe?: true;
2836
2802
  /**
2837
2803
  * Optional capability⇄scheme binding. When present this tool is auth-gated: discoverable when
2838
2804
  * unauthed, callable once the binding is satisfied. The SDK's {@link withResolvedAuth}
@@ -2916,9 +2882,9 @@ declare type ToolParametersWire = unknown;
2916
2882
  *
2917
2883
  * The host is trusted to supply honest values; a declared `kind` of `read` moves a call below
2918
2884
  * the protected-path hard-deny (which only fires for non-read kinds), so a tool that could
2919
- * touch protected config must NOT declare `read`. Kay's embedded-experience tools never touch
2885
+ * touch protected config must NOT declare `read`. Bonsai's embedded-experience tools never touch
2920
2886
  * `configDir`, so this is safe for them. `read` is also what every capability surface reports as
2921
- * "read-only" (Kay's Permissions page groups and defaults on it), so it is a claim about what the
2887
+ * "read-only" (Bonsai's Permissions page groups and defaults on it), so it is a claim about what the
2922
2888
  * tool DOES — never a way to buy silence. A tool that should raise no ask on its own account
2923
2889
  * declares {@link ToolPermissionHint.quiet} and keeps its honest `kind`.
2924
2890
  */
@@ -2959,7 +2925,7 @@ declare interface ToolPermissionHint {
2959
2925
  * because the same tool can read on one input and execute on another, which a static `kind`
2960
2926
  * cannot express.
2961
2927
  *
2962
- * Exemplar — Kay's `mcp` proxy tool, one tool spanning every server: `search`/`describe` derive
2928
+ * Exemplar — Bonsai's `mcp` proxy tool, one tool spanning every server: `search`/`describe` derive
2963
2929
  * `{ kind: "read" }` (metadata lookups), while `call` derives
2964
2930
  * `{ kind: "execute", ruleKey: "mcp:<server>::<tool>", scheme: "<server>" }`, so a
2965
2931
  * `{ scheme: "<server>" }` rule gates a whole server and a `{ ruleKey: "mcp:<server>::<tool>" }`
@@ -3000,7 +2966,7 @@ declare interface ToolPermissionHint {
3000
2966
  * really had — not that the result is always stricter than the target's. How much gate is left
3001
2967
  * on such a call is exactly how much the CARRIER carries: an `execute` carrier is stricter than
3002
2968
  * a `read` target and equal to a `mutate`/`destructive` one outside allow mode, while a
3003
- * {@link quiet} carrier (Kay's own `call_tool`) contributes no mode default at all, so the
2969
+ * {@link quiet} carrier (Bonsai's own `call_tool`) contributes no mode default at all, so the
3004
2970
  * fallback leaves nothing but the carrier's rules and the protected-path deny — and the carrier
3005
2971
  * names no path, so that deny has nothing to fire on. This is why a carrier must refuse a target
3006
2972
  * that is itself a carrier: no chain long or circular enough to reach the fallback can be built,
@@ -3024,7 +2990,7 @@ declare interface ToolPermissionHint {
3024
2990
  * whose only effect is invoking the target the gate is already classifying beside it: the
3025
2991
  * target's kind, rules, resources and the protected-path deny all still decide, so a proxied
3026
2992
  * call is gated exactly like the direct one — no more, and no ask the direct call would never
3027
- * have raised. Kay's `call_tool` declares `{ kind: "execute", quiet: true }` (owner ruling,
2993
+ * have raised. Bonsai's `call_tool` declares `{ kind: "execute", quiet: true }` (owner ruling,
3028
2994
  * 2026-08-18): honest about everything it can reach, silent on its own account.
3029
2995
  *
3030
2996
  * Quiet is not ungated, but it IS nearly all the gate a lone carrier has. On a call that
@@ -3214,13 +3180,19 @@ declare interface UriExtensionModule extends Omit<UriModule, "verbs"> {
3214
3180
  readonly permissionDefaults?: UriPermissionDefaults;
3215
3181
  /** Requires a resolved URI connection before any verb handler runs. */
3216
3182
  readonly requiresConnection?: boolean;
3217
- /** Pure route-aware permission facts; may only strengthen the verb default. */
3183
+ /** Pure route-aware permission facts; see {@link UriExtensionOperationPermissionPolicy}. */
3218
3184
  readonly permission?: UriExtensionOperationPermissionPolicy;
3219
3185
  /** False rejects generic write-tool `source` copies before source fetch. */
3220
3186
  readonly acceptsSourceCopy?: boolean;
3221
3187
  }
3222
3188
 
3223
3189
  declare interface UriExtensionOperationPermissionPolicy {
3190
+ /**
3191
+ * Classify one canonical route, purely. `write`/`edit`/`delete` may only be made stricter
3192
+ * than the verb default; `help`/`read`/`list`/`search` must stay `"read"` (a `ruleKey` is
3193
+ * allowed), because the kernel reruns a crash-interrupted read without asking again. Any
3194
+ * other answer, or a throw, fails the call closed.
3195
+ */
3224
3196
  classifyOperation(request: {
3225
3197
  readonly operation: UriOperation;
3226
3198
  readonly uri: string;
@@ -3448,7 +3420,15 @@ declare interface UriModule {
3448
3420
  normalize?(input: string, ctx: UriResolveContext): UriNormalizeResult | undefined;
3449
3421
  describe?(input: string, ctx: UriDescribeContext): UriResourceDescription | undefined;
3450
3422
  locate?(input: string, ctx: UriLocateContext): Promise<UriLocateResult>;
3423
+ /**
3424
+ * `read`, `help`, `list` and `search` observe; they never change the resource or anything
3425
+ * outside the host. Repeating one with the same input must be harmless: the kernel reruns a
3426
+ * call of these verbs that a crash cut off, with the same input, without asking the user
3427
+ * again. Idempotent bookkeeping (a cache fill, an index of what was read, a usage count) is
3428
+ * fine; a send, a mark-as-read or a create is not, and belongs on a mutating verb.
3429
+ */
3451
3430
  read(input: string, ctx: UriResolveContext): Promise<RawResource>;
3431
+ /** Observes only; see {@link read}. */
3452
3432
  help?(input: string, ctx: UriResolveContext): Promise<UriHelp>;
3453
3433
  write?(input: string, content: UriPayload, ctx: UriResolveContext): Promise<UriMutationResult | void>;
3454
3434
  /** Opt-in structured write with one or more host-resolved resources delivered beside the content. */
@@ -3471,7 +3451,9 @@ declare interface UriModule {
3471
3451
  */
3472
3452
  writeBytes?(input: string, bytes: Uint8Array, source: UriSourceDescriptor, ctx: UriResolveContext): Promise<UriMutationResult | void>;
3473
3453
  edit?(input: string, patch: UriPayload, ctx: UriResolveContext): Promise<UriMutationResult>;
3454
+ /** Observes only; see {@link read}. */
3474
3455
  list?(input: string, params: UriListParams, ctx: UriResolveContext): Promise<UriListResult>;
3456
+ /** Observes only; see {@link read}. */
3475
3457
  search?(input: string, params: UriSearchParams, ctx: UriResolveContext): Promise<UriSearchResult>;
3476
3458
  delete?(input: string, ctx: UriResolveContext): Promise<UriMutationResult>;
3477
3459
  toMarkdown?(input: UriMarkdownRenderInput, ctx: UriResolveContext): string | undefined;
@@ -3689,7 +3671,7 @@ declare interface UriResolveContext {
3689
3671
  * {@link UriResolveContext.sessionId} and just as un-influenceable by the caller — so a module
3690
3672
  * writing to a store it owns can record the actor without the agent being able to choose one.
3691
3673
  *
3692
- * Free text minted by the host's own runtime (Kay's form is `user`, `subagent`,
3674
+ * Free text minted by the host's own runtime (Bonsai's form is `user`, `subagent`,
3693
3675
  * `system/<surface>`, `followup/<surface>`) rather than a closed union: the surface half is the
3694
3676
  * host's vocabulary, and a second enumeration of it here would drift from the first.
3695
3677
  *
@@ -765,17 +765,6 @@ function unavailableInteractionSurfaces(message) {
765
765
  // packages/plugin-sdk/dist/disclosure/probe-kay-agent-capabilities.js
766
766
  var inertResult = () => new Promise(() => {
767
767
  });
768
- var INERT_FILESYSTEM = {
769
- readText: inertResult,
770
- writeText: inertResult,
771
- readBytes: inertResult,
772
- writeBytes: inertResult,
773
- mkdir: inertResult,
774
- readDir: inertResult,
775
- stat: inertResult,
776
- rename: inertResult,
777
- realpath: inertResult
778
- };
779
768
  var INERT_NETWORK = {
780
769
  fetch: inertResult
781
770
  };
@@ -807,7 +796,6 @@ var INERT_WHITEBOARD = {
807
796
  var INERT_WITH_JEV = async (_use, fallback) => fallback();
808
797
  function createKayAgentProbeCapabilityHandles() {
809
798
  return {
810
- filesystem: INERT_FILESYSTEM,
811
799
  network: INERT_NETWORK,
812
800
  webSession: INERT_WEB_SESSION,
813
801
  process: INERT_PROCESS,
@@ -825,7 +813,7 @@ function createKayAgentProbeCapabilityHandles() {
825
813
  time: {
826
814
  snapshot: () => ({ nowMs: 0, zone: "UTC", zoneSource: "utc-fallback" })
827
815
  },
828
- // Inert like the filesystem broker: a probe must never touch the user's disk.
816
+ // Inert like the process broker: a probe must never touch the user's disk.
829
817
  userFiles: {
830
818
  stat: inertResult,
831
819
  readText: inertResult,
@@ -835,7 +823,9 @@ function createKayAgentProbeCapabilityHandles() {
835
823
  writeText: inertResult,
836
824
  mkdir: inertResult,
837
825
  rename: inertResult
838
- }
826
+ },
827
+ // A probe runs outside any session, and must not create a folder on the user's disk.
828
+ familyFolder: { path: async () => void 0 }
839
829
  };
840
830
  }
841
831