@yaklabs-ai/plugin-sdk 0.54.1 → 0.55.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.
package/README.md CHANGED
@@ -13,7 +13,7 @@ it under the name your plugin code actually imports:
13
13
  ```json
14
14
  {
15
15
  "dependencies": {
16
- "@bonsai/plugin-sdk": "npm:@yaklabs-ai/plugin-sdk@^0.54.1"
16
+ "@bonsai/plugin-sdk": "npm:@yaklabs-ai/plugin-sdk@^0.55.0"
17
17
  }
18
18
  }
19
19
  ```
@@ -62,7 +62,7 @@ Every tool the entrypoint registers is declared in the manifest too:
62
62
  "description": "Greets people by name.",
63
63
  "version": "0.1.0",
64
64
  "namespace": "widgets",
65
- "compatibility": { "sdk": "^0.54.1" },
65
+ "compatibility": { "sdk": "^0.55.0" },
66
66
  "targets": {
67
67
  "kay": {
68
68
  "agent": {
@@ -153,7 +153,7 @@ naming both versions, instead of loading it and finding no `activate`.
153
153
  | `@bonsai/plugin-sdk/system-one` | The types and error class for `api.systemOne` / `app.systemOne`, from 0.13.8 |
154
154
  | `@bonsai/plugin-sdk/zod` | `z` (the host's in the daemon), on an entry with no other import, from 0.13.9 |
155
155
 
156
- A plugin that imports these subpaths declares the current supported SDK range (`^0.54.1`).
156
+ A plugin that imports these subpaths declares the current supported SDK range (`^0.55.0`).
157
157
  The rows record when each subpath appeared, not retained runtime compatibility. `vendored-mcp` builds the plugin's URI module (its link cards and help); hosting the
158
158
  vendor's MCP server itself is a separate host decision: Bonsai hosts it once a person approves
159
159
  the plugin's `mcp.host-server` grant for that server's name and URL. A server that needs an OAuth
@@ -161,7 +161,7 @@ sign-in names one of the plugin's auth services in `authService`; that service m
161
161
  one `delegated` scheme, which Bonsai uses to sign the person in to the server. Bonsai declares that
162
162
  scheme at runtime; the plugin's code must not declare it again. From 0.16.2 a plugin's code can also
163
163
  call the server's tools through `api.vendoredMcp.port(<server>)`, typed by this subpath's
164
- `VendoredMcpPort` and `VendoredMcpOutcome` (declare `^0.54.1`).
164
+ `VendoredMcpPort` and `VendoredMcpOutcome` (declare `^0.55.0`).
165
165
 
166
166
  From 0.13.10 the manifest schema carries two facts Bonsai reads for its own plugins only; another
167
167
  plugin's are ignored, with a load diagnostic. An `external-cli` auth scheme's `cli` says how Bonsai
@@ -243,7 +243,7 @@ Provider schemas remain authoritative; declaring a dependency grants no authorit
243
243
  through `app.uri`, parse returned Markdown in the consuming plugin, and retain the read's
244
244
  revision (or `contentDigest` from `/uri`) for edits. The old `memory.read` and `memory.write`
245
245
  grants do not authorize the replacement scheme-and-verb calls. Contracts before 0.49.0 are
246
- refused; migrate and declare `^0.54.1`. The current host also serves the 0.49.0 resource contract.
246
+ refused; migrate and declare `^0.55.0`. The current host also serves the 0.49.0 resource contract.
247
247
 
248
248
  ## What this package is, and is not, at runtime
249
249
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "package": "@yaklabs-ai/plugin-sdk",
4
- "version": "0.54.1",
4
+ "version": "0.55.0",
5
5
  "target": "npm",
6
6
  "firstPartyLicense": "MIT",
7
7
  "bundled": [
@@ -3873,14 +3873,20 @@ declare interface RunFailureObservedSessionEvent {
3873
3873
  readonly failure?: RunFailureOccurrence;
3874
3874
  }
3875
3875
 
3876
- declare type RunFailureOccurrence = FailureOccurrence<RunFailureCause>;
3876
+ /**
3877
+ * One run failure record. `Code` and `Domain` default to `string`, as every
3878
+ * reader takes them; a producer passes `RunFailureCode` and
3879
+ * `RunFailureDomain` on the record it makes, so an undeclared code fails to
3880
+ * compile.
3881
+ */
3882
+ declare type RunFailureOccurrence<Code extends string = string, Domain extends string = string> = FailureOccurrence<RunFailureCause, Code, Domain>;
3877
3883
 
3878
3884
  declare type RunFailureReason = (typeof RUN_FAILURE_REASONS)[number];
3879
3885
 
3880
3886
  declare type RunFailureRecord = z.infer<typeof runFailureRecordSchema>;
3881
3887
 
3882
3888
  declare const runFailureRecordSchema: z.ZodObject<{
3883
- failure: z.ZodType<RunFailureOccurrence, unknown, z.core.$ZodTypeInternals<RunFailureOccurrence, unknown>>;
3889
+ failure: z.ZodType<RunFailureOccurrence<string, string>, unknown, z.core.$ZodTypeInternals<RunFailureOccurrence<string, string>, unknown>>;
3884
3890
  errorText: z.ZodString;
3885
3891
  }, z.core.$strict>;
3886
3892
 
@@ -3898,8 +3904,8 @@ declare const runRecoveryOutcomeSchema: z.ZodObject<{
3898
3904
  id: z.ZodString;
3899
3905
  }, z.core.$strict>;
3900
3906
  action: z.ZodEnum<{
3901
- compact: "compact";
3902
3907
  retry: "retry";
3908
+ compact: "compact";
3903
3909
  switch_model: "switch_model";
3904
3910
  switch_account: "switch_account";
3905
3911
  }>;
@@ -221,6 +221,26 @@ var coverageSchema = z2.strictObject({
221
221
  knownOmittedCount: z2.number().int().nonnegative().optional()
222
222
  }).refine((value) => value.status !== "complete" || !value.knownOmittedCount);
223
223
 
224
+ // packages/herd-transcript/dist/is-object.js
225
+ function isObject(value) {
226
+ return typeof value === "object" && value !== null && !Array.isArray(value);
227
+ }
228
+
229
+ // packages/herd-transcript/dist/subscription-failure.js
230
+ var SUBSCRIPTION_FAILURE_KEYS = [
231
+ "subscription.expired",
232
+ "subscription.access-denied",
233
+ "subscription.client-refused",
234
+ "subscription.scope-refused",
235
+ "subscription.renewal-failed",
236
+ "subscription.unreachable",
237
+ "subscription.incomplete",
238
+ "subscription.temporary",
239
+ // Picked by a screen that knows a renewal failed but holds no record.
240
+ "subscription.no-record",
241
+ "subscription.unclassified"
242
+ ];
243
+
224
244
  // packages/herd-transcript/dist/run-failure.js
225
245
  var RUN_FAILURE_DIAGNOSTIC_TYPE = "herd.run.failure";
226
246
  var RUN_FAILURE_CUSTOM_TYPE = "run_failure";
@@ -243,8 +263,27 @@ var runFailureCauseSchema = z3.strictObject({
243
263
  authSource: z3.enum(["oauth", "api-key"]).optional(),
244
264
  credentialRef: z3.string().optional()
245
265
  });
266
+ var RUN_FAILURE_DOMAINS = ["model", "runtime"];
267
+ var RUN_FAILURE_ORGANIZATION_CODES = [
268
+ "model.organization_unavailable.changed",
269
+ "model.organization_unavailable.unreachable",
270
+ "model.organization_unavailable.connecting",
271
+ "model.organization_unavailable.not_ready"
272
+ ];
273
+ var RUN_FAILURE_SIGN_IN_REASONS = [
274
+ "auth",
275
+ "network",
276
+ "server_error"
277
+ ];
278
+ var RUN_FAILURE_CODES = [
279
+ ...RUN_FAILURE_REASONS.map((reason) => `model.${reason}`),
280
+ "runtime.interrupted",
281
+ "runtime.final_output_missing",
282
+ ...RUN_FAILURE_ORGANIZATION_CODES,
283
+ ...RUN_FAILURE_SIGN_IN_REASONS.flatMap((reason) => SUBSCRIPTION_FAILURE_KEYS.map((key) => `model.${reason}.${key}`))
284
+ ];
246
285
  var runFailureSchema = failureSchema(runFailureCauseSchema).extend({
247
- domain: z3.enum(["model", "runtime"])
286
+ domain: z3.enum(RUN_FAILURE_DOMAINS)
248
287
  });
249
288
  var runFailureRecordSchema = z3.strictObject({
250
289
  failure: runFailureSchema.refine((failure) => readFailure(runFailureSchema, failure).status === "valid", "invalid or oversized model failure occurrence"),
@@ -631,13 +670,6 @@ function parsePresentationAnchor(value) {
631
670
 
632
671
  // packages/herd-transcript/dist/tool-call-failure.js
633
672
  import { z as z5 } from "zod";
634
-
635
- // packages/herd-transcript/dist/is-object.js
636
- function isObject(value) {
637
- return typeof value === "object" && value !== null && !Array.isArray(value);
638
- }
639
-
640
- // packages/herd-transcript/dist/tool-call-failure.js
641
673
  var TOOL_CALL_FAILURE_DETAILS_KEY = "toolCallFailure";
642
674
  var TOOL_CALL_FAILURE_DOMAIN = "tool-call";
643
675
  var TOOL_CALL_FAILURE_CATEGORIES = [
@@ -2668,8 +2668,16 @@ declare const KAY_PLUGIN_PROTOCOL_VERSION: "kay.plugin.v1";
2668
2668
  *
2669
2669
  * 0.54.1 adds manifest-declared plugin workers on both backend targets and exposes the canonical
2670
2670
  * transcript placement and answer-stretch rules through the SDK.
2671
+ *
2672
+ * 0.54.2 lets a signal list the files that came with it (epic #15668). `PluginSignal` gains an
2673
+ * optional `attachments`: each file's name, media type, size and the address its bytes are read
2674
+ * from, never the bytes. Gmail lists a mail's attachments there, so Flows can file them. Additive:
2675
+ * a plugin that publishes attachments declares ^0.54.2.
2676
+ *
2677
+ * 0.55.0 carries managed MCP calls over resource writes in both daemon and plugin-host code.
2678
+ * Existing agent and background MCP call shapes remain supported.
2671
2679
  */
2672
- declare const KAY_PLUGIN_SDK_VERSION: "0.54.1";
2680
+ declare const KAY_PLUGIN_SDK_VERSION: "0.55.0";
2673
2681
 
2674
2682
  declare const KAY_PLUGIN_TARGET_NAMES: readonly ["kay.agent", "kay.app", "kay.ui"];
2675
2683
 
@@ -3127,13 +3135,8 @@ declare interface KayAgentVendoredMcp {
3127
3135
  * The port to `server`, a name this plugin declares in its `kay.app` target's
3128
3136
  * `contributes.mcpServers`. Throws when the plugin declares no server of that name. A declared
3129
3137
  * server that Bonsai does not host for this plugin still returns a port; each call then answers
3130
- * `unknown-server`. Its message names the plugin that holds the name, or else lists the possible
3131
- * reasons (no approved `mcp.host-server` grant, a sign-in the server cannot use, or a name that
3132
- * a plugin Bonsai ships or another installed plugin also claims) and points at the plugin's load
3133
- * diagnostic.
3134
- *
3135
- * A port reaches this plugin's own server only. To use another plugin's data, declare that
3136
- * plugin in the manifest's `dependencies.plugins`; a later SDK surface reaches it.
3138
+ * `unknown-server`. Each invocation uses the resource caller's exact `kay.agent` `uri.call`
3139
+ * grant for `mcp` / `write`; MCP checks the host-stamped caller against server ownership.
3137
3140
  */
3138
3141
  port<TTool extends string = string>(server: string): VendoredMcpPort<TTool>;
3139
3142
  }
@@ -10634,7 +10637,7 @@ declare interface Usage {
10634
10637
  /**
10635
10638
  * ---
10636
10639
  * responsibility: The one interface through which a vendored-MCP plugin's code calls a tool on its
10637
- * own vendored MCP server, so every read-through plugin and the daemon that implements the port
10640
+ * own vendored MCP server, so every read-through plugin and the SDK that implements the port
10638
10641
  * agree on the same shape by construction.
10639
10642
  * owns:
10640
10643
  * - {@link VendoredMcpPort} - what a plugin's URI module and tools call.
@@ -10642,8 +10645,8 @@ declare interface Usage {
10642
10645
  * must surface to the agent rather than swallow.
10643
10646
  * does_not_own:
10644
10647
  * - The implementation. A plugin never dials MCP itself: it takes the port from
10645
- * `api.vendoredMcp.port(server)` (`@bonsai/plugin-sdk/kay-agent`), which the daemon binds to the
10646
- * plugin's own hosted server over its MCP host. Plugin source stays host-free, and a plugin
10648
+ * `api.vendoredMcp.port(server)` (`@bonsai/plugin-sdk/kay-agent`), which the SDK binds through
10649
+ * resource calls to the plugin's own hosted server. Plugin source stays host-free, and a plugin
10647
10650
  * under test substitutes a fake port.
10648
10651
  * - Which tool names exist. `TTool` is the plugin's own union of the vendor tools it calls; the
10649
10652
  * port is generic so a typo in a tool name fails typecheck inside the plugin.
@@ -10679,8 +10682,8 @@ declare type VendoredMcpOutcome =
10679
10682
 
10680
10683
  /**
10681
10684
  * One vendored MCP server, as the plugin that declares it calls it. `invoke` calls any tool the
10682
- * server has, whether it reads or writes. The port checks no permission of its own: a call is as
10683
- * permitted as the plugin tool or URI verb that makes it.
10685
+ * server has, whether it reads or writes. The resource caller checks the target's exact
10686
+ * `uri.call` grant, and MCP checks the host-stamped caller against server ownership.
10684
10687
  */
10685
10688
  declare interface VendoredMcpPort<TTool extends string = string> {
10686
10689
  invoke(input: {
@@ -2722,8 +2722,16 @@ declare const KAY_PLUGIN_PROTOCOL_VERSION: "kay.plugin.v1";
2722
2722
  *
2723
2723
  * 0.54.1 adds manifest-declared plugin workers on both backend targets and exposes the canonical
2724
2724
  * transcript placement and answer-stretch rules through the SDK.
2725
+ *
2726
+ * 0.54.2 lets a signal list the files that came with it (epic #15668). `PluginSignal` gains an
2727
+ * optional `attachments`: each file's name, media type, size and the address its bytes are read
2728
+ * from, never the bytes. Gmail lists a mail's attachments there, so Flows can file them. Additive:
2729
+ * a plugin that publishes attachments declares ^0.54.2.
2730
+ *
2731
+ * 0.55.0 carries managed MCP calls over resource writes in both daemon and plugin-host code.
2732
+ * Existing agent and background MCP call shapes remain supported.
2725
2733
  */
2726
- declare const KAY_PLUGIN_SDK_VERSION: "0.54.1";
2734
+ declare const KAY_PLUGIN_SDK_VERSION: "0.55.0";
2727
2735
 
2728
2736
  declare const KAY_PLUGIN_TARGET_NAMES: readonly ["kay.agent", "kay.app", "kay.ui"];
2729
2737
 
@@ -3178,13 +3186,8 @@ declare interface KayAgentVendoredMcp {
3178
3186
  * The port to `server`, a name this plugin declares in its `kay.app` target's
3179
3187
  * `contributes.mcpServers`. Throws when the plugin declares no server of that name. A declared
3180
3188
  * server that Bonsai does not host for this plugin still returns a port; each call then answers
3181
- * `unknown-server`. Its message names the plugin that holds the name, or else lists the possible
3182
- * reasons (no approved `mcp.host-server` grant, a sign-in the server cannot use, or a name that
3183
- * a plugin Bonsai ships or another installed plugin also claims) and points at the plugin's load
3184
- * diagnostic.
3185
- *
3186
- * A port reaches this plugin's own server only. To use another plugin's data, declare that
3187
- * plugin in the manifest's `dependencies.plugins`; a later SDK surface reaches it.
3189
+ * `unknown-server`. Each invocation uses the resource caller's exact `kay.agent` `uri.call`
3190
+ * grant for `mcp` / `write`; MCP checks the host-stamped caller against server ownership.
3188
3191
  */
3189
3192
  port<TTool extends string = string>(server: string): VendoredMcpPort<TTool>;
3190
3193
  }
@@ -5150,6 +5153,7 @@ declare const ResolvedPluginContractV1: z.ZodObject<{
5150
5153
  eventSubscription: "eventSubscription";
5151
5154
  appDataSource: "appDataSource";
5152
5155
  whiteboardType: "whiteboardType";
5156
+ workingDocumentProvider: "workingDocumentProvider";
5153
5157
  approvalType: "approvalType";
5154
5158
  workflow: "workflow";
5155
5159
  contextContributor: "contextContributor";
@@ -6209,6 +6213,24 @@ declare const ResolvedPluginContractV1: z.ZodObject<{
6209
6213
  handover: z.ZodOptional<z.ZodLiteral<true>>;
6210
6214
  globalId: z.ZodString;
6211
6215
  }, z.core.$strict>>>;
6216
+ kayAppWorkingDocumentProviders: z.ZodOptional<z.ZodArray<z.ZodObject<{
6217
+ localId: z.ZodString;
6218
+ globalId: z.ZodString;
6219
+ displayName: z.ZodString;
6220
+ schemes: z.ZodArray<z.ZodString>;
6221
+ hosts: z.ZodArray<z.ZodString>;
6222
+ authServiceId: z.ZodOptional<z.ZodString>;
6223
+ authSchemeId: z.ZodOptional<z.ZodString>;
6224
+ scopeRequirement: z.ZodOptional<z.ZodObject<{
6225
+ purpose: z.ZodString;
6226
+ alternatives: z.ZodArray<z.ZodObject<{
6227
+ allOf: z.ZodArray<z.ZodObject<{
6228
+ scope: z.ZodString;
6229
+ reason: z.ZodExactOptional<z.ZodString>;
6230
+ }, z.core.$strict>>;
6231
+ }, z.core.$strict>>;
6232
+ }, z.core.$strict>>;
6233
+ }, z.core.$strict>>>;
6212
6234
  kayAppMcpServers: z.ZodOptional<z.ZodArray<z.ZodObject<{
6213
6235
  name: z.ZodString;
6214
6236
  pluginId: z.ZodString;
@@ -12020,7 +12042,7 @@ declare interface Usage {
12020
12042
  /**
12021
12043
  * ---
12022
12044
  * responsibility: The one interface through which a vendored-MCP plugin's code calls a tool on its
12023
- * own vendored MCP server, so every read-through plugin and the daemon that implements the port
12045
+ * own vendored MCP server, so every read-through plugin and the SDK that implements the port
12024
12046
  * agree on the same shape by construction.
12025
12047
  * owns:
12026
12048
  * - {@link VendoredMcpPort} - what a plugin's URI module and tools call.
@@ -12028,8 +12050,8 @@ declare interface Usage {
12028
12050
  * must surface to the agent rather than swallow.
12029
12051
  * does_not_own:
12030
12052
  * - The implementation. A plugin never dials MCP itself: it takes the port from
12031
- * `api.vendoredMcp.port(server)` (`@bonsai/plugin-sdk/kay-agent`), which the daemon binds to the
12032
- * plugin's own hosted server over its MCP host. Plugin source stays host-free, and a plugin
12053
+ * `api.vendoredMcp.port(server)` (`@bonsai/plugin-sdk/kay-agent`), which the SDK binds through
12054
+ * resource calls to the plugin's own hosted server. Plugin source stays host-free, and a plugin
12033
12055
  * under test substitutes a fake port.
12034
12056
  * - Which tool names exist. `TTool` is the plugin's own union of the vendor tools it calls; the
12035
12057
  * port is generic so a typo in a tool name fails typecheck inside the plugin.
@@ -12065,8 +12087,8 @@ declare type VendoredMcpOutcome =
12065
12087
 
12066
12088
  /**
12067
12089
  * One vendored MCP server, as the plugin that declares it calls it. `invoke` calls any tool the
12068
- * server has, whether it reads or writes. The port checks no permission of its own: a call is as
12069
- * permitted as the plugin tool or URI verb that makes it.
12090
+ * server has, whether it reads or writes. The resource caller checks the target's exact
12091
+ * `uri.call` grant, and MCP checks the host-stamped caller against server ownership.
12070
12092
  */
12071
12093
  declare interface VendoredMcpPort<TTool extends string = string> {
12072
12094
  invoke(input: {
@@ -3076,8 +3076,16 @@ declare const KAY_PLUGIN_PROTOCOL_VERSION: "kay.plugin.v1";
3076
3076
  *
3077
3077
  * 0.54.1 adds manifest-declared plugin workers on both backend targets and exposes the canonical
3078
3078
  * transcript placement and answer-stretch rules through the SDK.
3079
+ *
3080
+ * 0.54.2 lets a signal list the files that came with it (epic #15668). `PluginSignal` gains an
3081
+ * optional `attachments`: each file's name, media type, size and the address its bytes are read
3082
+ * from, never the bytes. Gmail lists a mail's attachments there, so Flows can file them. Additive:
3083
+ * a plugin that publishes attachments declares ^0.54.2.
3084
+ *
3085
+ * 0.55.0 carries managed MCP calls over resource writes in both daemon and plugin-host code.
3086
+ * Existing agent and background MCP call shapes remain supported.
3079
3087
  */
3080
- declare const KAY_PLUGIN_SDK_VERSION: "0.54.1";
3088
+ declare const KAY_PLUGIN_SDK_VERSION: "0.55.0";
3081
3089
 
3082
3090
  declare const KAY_PLUGIN_TARGET_NAMES: readonly ["kay.agent", "kay.app", "kay.ui"];
3083
3091
 
@@ -3575,13 +3583,8 @@ export declare interface KayAgentVendoredMcp {
3575
3583
  * The port to `server`, a name this plugin declares in its `kay.app` target's
3576
3584
  * `contributes.mcpServers`. Throws when the plugin declares no server of that name. A declared
3577
3585
  * server that Bonsai does not host for this plugin still returns a port; each call then answers
3578
- * `unknown-server`. Its message names the plugin that holds the name, or else lists the possible
3579
- * reasons (no approved `mcp.host-server` grant, a sign-in the server cannot use, or a name that
3580
- * a plugin Bonsai ships or another installed plugin also claims) and points at the plugin's load
3581
- * diagnostic.
3582
- *
3583
- * A port reaches this plugin's own server only. To use another plugin's data, declare that
3584
- * plugin in the manifest's `dependencies.plugins`; a later SDK surface reaches it.
3586
+ * `unknown-server`. Each invocation uses the resource caller's exact `kay.agent` `uri.call`
3587
+ * grant for `mcp` / `write`; MCP checks the host-stamped caller against server ownership.
3585
3588
  */
3586
3589
  port<TTool extends string = string>(server: string): VendoredMcpPort<TTool>;
3587
3590
  }
@@ -11847,6 +11850,7 @@ export declare class UriProviderError extends UriError {
11847
11850
  readonly uri: string;
11848
11851
  readonly status?: number;
11849
11852
  readonly providerCode?: string;
11853
+ readonly serviceId?: string;
11850
11854
  readonly retryable?: boolean;
11851
11855
  /** Provider-directed delay before retrying, in milliseconds. */
11852
11856
  readonly retryAfterMs?: number;
@@ -11864,6 +11868,8 @@ export declare interface UriProviderErrorOptions {
11864
11868
  readonly message: string;
11865
11869
  readonly status?: number;
11866
11870
  readonly providerCode?: string;
11871
+ /** Service to reconnect when the provider requires sign-in. */
11872
+ readonly serviceId?: string;
11867
11873
  readonly retryable?: boolean;
11868
11874
  /** Provider-directed delay before retrying, in milliseconds. */
11869
11875
  readonly retryAfterMs?: number;
@@ -12335,7 +12341,7 @@ declare interface Usage {
12335
12341
  /**
12336
12342
  * ---
12337
12343
  * responsibility: The one interface through which a vendored-MCP plugin's code calls a tool on its
12338
- * own vendored MCP server, so every read-through plugin and the daemon that implements the port
12344
+ * own vendored MCP server, so every read-through plugin and the SDK that implements the port
12339
12345
  * agree on the same shape by construction.
12340
12346
  * owns:
12341
12347
  * - {@link VendoredMcpPort} - what a plugin's URI module and tools call.
@@ -12343,8 +12349,8 @@ declare interface Usage {
12343
12349
  * must surface to the agent rather than swallow.
12344
12350
  * does_not_own:
12345
12351
  * - The implementation. A plugin never dials MCP itself: it takes the port from
12346
- * `api.vendoredMcp.port(server)` (`@bonsai/plugin-sdk/kay-agent`), which the daemon binds to the
12347
- * plugin's own hosted server over its MCP host. Plugin source stays host-free, and a plugin
12352
+ * `api.vendoredMcp.port(server)` (`@bonsai/plugin-sdk/kay-agent`), which the SDK binds through
12353
+ * resource calls to the plugin's own hosted server. Plugin source stays host-free, and a plugin
12348
12354
  * under test substitutes a fake port.
12349
12355
  * - Which tool names exist. `TTool` is the plugin's own union of the vendor tools it calls; the
12350
12356
  * port is generic so a typo in a tool name fails typecheck inside the plugin.
@@ -12380,8 +12386,8 @@ declare type VendoredMcpOutcome =
12380
12386
 
12381
12387
  /**
12382
12388
  * One vendored MCP server, as the plugin that declares it calls it. `invoke` calls any tool the
12383
- * server has, whether it reads or writes. The port checks no permission of its own: a call is as
12384
- * permitted as the plugin tool or URI verb that makes it.
12389
+ * server has, whether it reads or writes. The resource caller checks the target's exact
12390
+ * `uri.call` grant, and MCP checks the host-stamped caller against server ownership.
12385
12391
  */
12386
12392
  declare interface VendoredMcpPort<TTool extends string = string> {
12387
12393
  invoke(input: {
package/dist/kay-agent.js CHANGED
@@ -422,6 +422,7 @@ var UriProviderError = class extends UriError {
422
422
  uri;
423
423
  status;
424
424
  providerCode;
425
+ serviceId;
425
426
  retryable;
426
427
  /** Provider-directed delay before retrying, in milliseconds. */
427
428
  retryAfterMs;
@@ -439,6 +440,8 @@ var UriProviderError = class extends UriError {
439
440
  this.status = options.status;
440
441
  if (options.providerCode !== void 0)
441
442
  this.providerCode = options.providerCode;
443
+ if (options.serviceId !== void 0)
444
+ this.serviceId = options.serviceId;
442
445
  if (options.retryable !== void 0)
443
446
  this.retryable = options.retryable;
444
447
  if (options.retryAfterMs !== void 0) {
@@ -3404,14 +3404,20 @@ declare const runFailureCauseSchema: z.ZodObject<{
3404
3404
  credentialRef: z.ZodOptional<z.ZodString>;
3405
3405
  }, z.core.$strict>;
3406
3406
 
3407
- declare type RunFailureOccurrence = FailureOccurrence<RunFailureCause>;
3407
+ /**
3408
+ * One run failure record. `Code` and `Domain` default to `string`, as every
3409
+ * reader takes them; a producer passes `RunFailureCode` and
3410
+ * `RunFailureDomain` on the record it makes, so an undeclared code fails to
3411
+ * compile.
3412
+ */
3413
+ declare type RunFailureOccurrence<Code extends string = string, Domain extends string = string> = FailureOccurrence<RunFailureCause, Code, Domain>;
3408
3414
 
3409
3415
  declare type RunFailureReason = (typeof RUN_FAILURE_REASONS)[number];
3410
3416
 
3411
3417
  declare type RunFailureRecord = z.infer<typeof runFailureRecordSchema>;
3412
3418
 
3413
3419
  declare const runFailureRecordSchema: z.ZodObject<{
3414
- failure: z.ZodType<RunFailureOccurrence, unknown, z.core.$ZodTypeInternals<RunFailureOccurrence, unknown>>;
3420
+ failure: z.ZodType<RunFailureOccurrence<string, string>, unknown, z.core.$ZodTypeInternals<RunFailureOccurrence<string, string>, unknown>>;
3415
3421
  errorText: z.ZodString;
3416
3422
  }, z.core.$strict>;
3417
3423
 
package/dist/kay-app.d.ts CHANGED
@@ -324,7 +324,7 @@ declare const AgentRuntimeAutomationRunDetail: z.ZodObject<{
324
324
  retryable: z.ZodBoolean;
325
325
  retryAfterMs: z.ZodOptional<z.ZodNumber>;
326
326
  }, z.core.$strict>>;
327
- failure: z.ZodOptional<z.ZodType<RunFailureOccurrence, unknown, z.core.$ZodTypeInternals<RunFailureOccurrence, unknown>>>;
327
+ failure: z.ZodOptional<z.ZodType<RunFailureOccurrence<string, string>, unknown, z.core.$ZodTypeInternals<RunFailureOccurrence<string, string>, unknown>>>;
328
328
  sessionId: z.ZodOptional<z.ZodString>;
329
329
  retryAtMs: z.ZodOptional<z.ZodNumber>;
330
330
  tries: z.ZodOptional<z.ZodNumber>;
@@ -4304,8 +4304,16 @@ declare const KAY_PLUGIN_PROTOCOL_VERSION: "kay.plugin.v1";
4304
4304
  *
4305
4305
  * 0.54.1 adds manifest-declared plugin workers on both backend targets and exposes the canonical
4306
4306
  * transcript placement and answer-stretch rules through the SDK.
4307
+ *
4308
+ * 0.54.2 lets a signal list the files that came with it (epic #15668). `PluginSignal` gains an
4309
+ * optional `attachments`: each file's name, media type, size and the address its bytes are read
4310
+ * from, never the bytes. Gmail lists a mail's attachments there, so Flows can file them. Additive:
4311
+ * a plugin that publishes attachments declares ^0.54.2.
4312
+ *
4313
+ * 0.55.0 carries managed MCP calls over resource writes in both daemon and plugin-host code.
4314
+ * Existing agent and background MCP call shapes remain supported.
4307
4315
  */
4308
- declare const KAY_PLUGIN_SDK_VERSION: "0.54.1";
4316
+ declare const KAY_PLUGIN_SDK_VERSION: "0.55.0";
4309
4317
 
4310
4318
  declare const KAY_PLUGIN_TARGET_NAMES: readonly ["kay.agent", "kay.app", "kay.ui"];
4311
4319
 
@@ -6604,6 +6612,11 @@ export declare interface KayAppPluginApi {
6604
6612
  readonly resourcePicks?: {
6605
6613
  registerCompletion(moduleId: string, handler: PluginResourcePickHandler): void;
6606
6614
  };
6615
+ /**
6616
+ * Working-document providers. Present when the manifest declares `workingDocumentProviders`;
6617
+ * each registration must name one of them.
6618
+ */
6619
+ readonly workingDocuments?: KayAppWorkingDocuments;
6607
6620
  readonly mcp: KayAppMcp;
6608
6621
  /** This plugin's canonical id (already validated by the host registry). */
6609
6622
  readonly id: string;
@@ -9882,6 +9895,73 @@ export declare type KayAppWorkflowTimeTrigger = Extract<KayAppAutomationConditio
9882
9895
  */
9883
9896
  export declare type KayAppWorkflowTrigger = KayAppWorkflowTimeTrigger | KayAppAutomationPluginEventCondition | KayAppWorkflowSignalsTrigger;
9884
9897
 
9898
+ /** One binary asset pulled with a document (an image, an embed). */
9899
+ export declare interface KayAppWorkingDocumentAsset {
9900
+ /** A file-safe suggested name; the daemon dedupes it. */
9901
+ readonly suggestedName: string;
9902
+ readonly bytes: Uint8Array;
9903
+ readonly mime: string;
9904
+ /** Where it was downloaded from, so a push can put the upstream URL back. */
9905
+ readonly sourceUrl?: string;
9906
+ }
9907
+
9908
+ /**
9909
+ * What one working-document call receives. The daemon owns the local copy, the base text and the
9910
+ * revision guard; a provider only talks to its document source. `auth` is the token the daemon
9911
+ * resolved from the provider's declared `authService`, absent for a provider that declares none.
9912
+ */
9913
+ export declare interface KayAppWorkingDocumentContext {
9914
+ /** The source the person named, as the daemon parsed it: a web link or a URI. */
9915
+ readonly sourceUrl: string;
9916
+ readonly auth?: {
9917
+ readonly accessToken: string;
9918
+ readonly accountId?: string;
9919
+ };
9920
+ readonly signal: AbortSignal;
9921
+ }
9922
+
9923
+ /**
9924
+ * A document source's code, registered for a `workingDocumentProviders` entry the manifest
9925
+ * declares. Which URLs it serves, its display name and its sign-in are manifest data.
9926
+ */
9927
+ export declare interface KayAppWorkingDocumentProvider {
9928
+ pull(ctx: KayAppWorkingDocumentContext): Promise<KayAppWorkingDocumentPull>;
9929
+ push(ctx: KayAppWorkingDocumentContext, input: KayAppWorkingDocumentPushInput): Promise<KayAppWorkingDocumentPushResult>;
9930
+ /** The live upstream revision, which the daemon compares before a push. */
9931
+ checkRevision(ctx: KayAppWorkingDocumentContext): Promise<string>;
9932
+ }
9933
+
9934
+ /** What a pull returns: the document as Markdown, its title, its upstream revision and assets. */
9935
+ export declare interface KayAppWorkingDocumentPull {
9936
+ readonly markdown: string;
9937
+ readonly title: string;
9938
+ readonly revision: string;
9939
+ readonly assets: readonly KayAppWorkingDocumentAsset[];
9940
+ /** Notes for the person who pulled it (blocks that did not convert, rewrites). */
9941
+ readonly notes: readonly string[];
9942
+ }
9943
+
9944
+ /** What a push receives: the text the copy was pulled at and the edited text to write upstream. */
9945
+ export declare interface KayAppWorkingDocumentPushInput {
9946
+ readonly baseText: string;
9947
+ readonly nextText: string;
9948
+ /** The person forced past a changed upstream: overwrite the whole body. */
9949
+ readonly force?: boolean;
9950
+ /** A snippet naming the block to scroll to afterwards; return `anchor` if you can resolve it. */
9951
+ readonly anchorHint?: string;
9952
+ }
9953
+
9954
+ export declare interface KayAppWorkingDocumentPushResult {
9955
+ readonly revision: string;
9956
+ readonly warnings: readonly string[];
9957
+ readonly anchor?: string;
9958
+ }
9959
+
9960
+ export declare interface KayAppWorkingDocuments {
9961
+ /** Register the code for the declared provider `localId`, once, during activation. */
9962
+ registerProvider(localId: string, provider: KayAppWorkingDocumentProvider): void;
9963
+ }
9964
+
9885
9965
  /**
9886
9966
  * Read/arrange access to the live desktop workspace (tabs, panes, the shell chrome). Always
9887
9967
  * present. `snapshot` is ungated; `execute` is checked per call against the `workspace.command`
@@ -11363,6 +11443,15 @@ declare function PickDeferred<Type extends TSchema, Indexer extends TSchema>(typ
11363
11443
  */
11364
11444
  export declare const PLUGIN_EVENT_REFUSED_ERROR_NAME = "PluginEventRefusedError";
11365
11445
 
11446
+ /** An attachment's file name, at most this many characters. */
11447
+ export declare const PLUGIN_SIGNAL_ATTACHMENT_NAME_MAX = 255;
11448
+
11449
+ /** An attachment's media type (`application/pdf`), at most this many characters. */
11450
+ export declare const PLUGIN_SIGNAL_ATTACHMENT_TYPE_MAX = 127;
11451
+
11452
+ /** At most this many entries in a signal's `attachments`; a source lists the first ones. */
11453
+ export declare const PLUGIN_SIGNAL_ATTACHMENTS_MAX = 10;
11454
+
11366
11455
  /** The `kind` values of `PluginSignal` v1. `status` is system state that clears itself. */
11367
11456
  export declare const PLUGIN_SIGNAL_KINDS: readonly ["message", "request", "mention", "invite", "status"];
11368
11457
 
@@ -11561,6 +11650,12 @@ export declare const PluginSignal: z.ZodObject<{
11561
11650
  open: "open";
11562
11651
  resolved: "resolved";
11563
11652
  }>>;
11653
+ attachments: z.ZodOptional<z.ZodArray<z.ZodObject<{
11654
+ name: z.ZodString;
11655
+ type: z.ZodString;
11656
+ size: z.ZodNumber;
11657
+ uri: z.ZodString;
11658
+ }, z.core.$strict>>>;
11564
11659
  }, z.core.$strict>;
11565
11660
 
11566
11661
  export declare type PluginSignal = z.infer<typeof PluginSignal>;
@@ -11599,6 +11694,12 @@ export declare const PluginSignalPublish: z.ZodObject<{
11599
11694
  open: "open";
11600
11695
  resolved: "resolved";
11601
11696
  }>>;
11697
+ attachments: z.ZodOptional<z.ZodArray<z.ZodObject<{
11698
+ name: z.ZodString;
11699
+ type: z.ZodString;
11700
+ size: z.ZodNumber;
11701
+ uri: z.ZodString;
11702
+ }, z.core.$strict>>>;
11602
11703
  }, z.core.$strict>;
11603
11704
  related: z.ZodObject<{
11604
11705
  thread: z.ZodString;
@@ -12270,14 +12371,20 @@ declare const runFailureCauseSchema: z.ZodObject<{
12270
12371
  credentialRef: z.ZodOptional<z.ZodString>;
12271
12372
  }, z.core.$strict>;
12272
12373
 
12273
- declare type RunFailureOccurrence = FailureOccurrence<RunFailureCause>;
12374
+ /**
12375
+ * One run failure record. `Code` and `Domain` default to `string`, as every
12376
+ * reader takes them; a producer passes `RunFailureCode` and
12377
+ * `RunFailureDomain` on the record it makes, so an undeclared code fails to
12378
+ * compile.
12379
+ */
12380
+ declare type RunFailureOccurrence<Code extends string = string, Domain extends string = string> = FailureOccurrence<RunFailureCause, Code, Domain>;
12274
12381
 
12275
12382
  declare type RunFailureReason = (typeof RUN_FAILURE_REASONS)[number];
12276
12383
 
12277
12384
  declare type RunFailureRecord = z.infer<typeof runFailureRecordSchema>;
12278
12385
 
12279
12386
  declare const runFailureRecordSchema: z.ZodObject<{
12280
- failure: z.ZodType<RunFailureOccurrence, unknown, z.core.$ZodTypeInternals<RunFailureOccurrence, unknown>>;
12387
+ failure: z.ZodType<RunFailureOccurrence<string, string>, unknown, z.core.$ZodTypeInternals<RunFailureOccurrence<string, string>, unknown>>;
12281
12388
  errorText: z.ZodString;
12282
12389
  }, z.core.$strict>;
12283
12390
 
@@ -18166,6 +18273,7 @@ export declare class UriProviderError extends UriError {
18166
18273
  readonly uri: string;
18167
18274
  readonly status?: number;
18168
18275
  readonly providerCode?: string;
18276
+ readonly serviceId?: string;
18169
18277
  readonly retryable?: boolean;
18170
18278
  /** Provider-directed delay before retrying, in milliseconds. */
18171
18279
  readonly retryAfterMs?: number;
@@ -18183,6 +18291,8 @@ export declare interface UriProviderErrorOptions {
18183
18291
  readonly message: string;
18184
18292
  readonly status?: number;
18185
18293
  readonly providerCode?: string;
18294
+ /** Service to reconnect when the provider requires sign-in. */
18295
+ readonly serviceId?: string;
18186
18296
  readonly retryable?: boolean;
18187
18297
  /** Provider-directed delay before retrying, in milliseconds. */
18188
18298
  readonly retryAfterMs?: number;
@@ -18695,7 +18805,7 @@ declare type UserShellCommandMessage = CustomMessage<UserShellCommandDetails> &
18695
18805
  /**
18696
18806
  * ---
18697
18807
  * responsibility: The one interface through which a vendored-MCP plugin's code calls a tool on its
18698
- * own vendored MCP server, so every read-through plugin and the daemon that implements the port
18808
+ * own vendored MCP server, so every read-through plugin and the SDK that implements the port
18699
18809
  * agree on the same shape by construction.
18700
18810
  * owns:
18701
18811
  * - {@link VendoredMcpPort} - what a plugin's URI module and tools call.
@@ -18703,8 +18813,8 @@ declare type UserShellCommandMessage = CustomMessage<UserShellCommandDetails> &
18703
18813
  * must surface to the agent rather than swallow.
18704
18814
  * does_not_own:
18705
18815
  * - The implementation. A plugin never dials MCP itself: it takes the port from
18706
- * `api.vendoredMcp.port(server)` (`@bonsai/plugin-sdk/kay-agent`), which the daemon binds to the
18707
- * plugin's own hosted server over its MCP host. Plugin source stays host-free, and a plugin
18816
+ * `api.vendoredMcp.port(server)` (`@bonsai/plugin-sdk/kay-agent`), which the SDK binds through
18817
+ * resource calls to the plugin's own hosted server. Plugin source stays host-free, and a plugin
18708
18818
  * under test substitutes a fake port.
18709
18819
  * - Which tool names exist. `TTool` is the plugin's own union of the vendor tools it calls; the
18710
18820
  * port is generic so a typo in a tool name fails typecheck inside the plugin.
package/dist/kay-app.js CHANGED
@@ -55,6 +55,7 @@ var UriProviderError = class extends UriError {
55
55
  uri;
56
56
  status;
57
57
  providerCode;
58
+ serviceId;
58
59
  retryable;
59
60
  /** Provider-directed delay before retrying, in milliseconds. */
60
61
  retryAfterMs;
@@ -72,6 +73,8 @@ var UriProviderError = class extends UriError {
72
73
  this.status = options.status;
73
74
  if (options.providerCode !== void 0)
74
75
  this.providerCode = options.providerCode;
76
+ if (options.serviceId !== void 0)
77
+ this.serviceId = options.serviceId;
75
78
  if (options.retryable !== void 0)
76
79
  this.retryable = options.retryable;
77
80
  if (options.retryAfterMs !== void 0) {
@@ -370,6 +373,9 @@ var PLUGIN_SIGNAL_TITLE_MAX = 300;
370
373
  var PLUGIN_SIGNAL_SNIPPET_MAX = 500;
371
374
  var PLUGIN_SIGNAL_LABELS_MAX = 20;
372
375
  var PLUGIN_SIGNAL_LABEL_MAX = 64;
376
+ var PLUGIN_SIGNAL_ATTACHMENTS_MAX = 10;
377
+ var PLUGIN_SIGNAL_ATTACHMENT_NAME_MAX = 255;
378
+ var PLUGIN_SIGNAL_ATTACHMENT_TYPE_MAX = 127;
373
379
  var PLUGIN_SIGNAL_RELATED_KEYS = [
374
380
  "thread",
375
381
  "from",
@@ -1184,6 +1190,9 @@ export {
1184
1190
  KayAppWorkflowFailure,
1185
1191
  MAX_CHOICE_LABELS,
1186
1192
  PLUGIN_EVENT_REFUSED_ERROR_NAME,
1193
+ PLUGIN_SIGNAL_ATTACHMENTS_MAX,
1194
+ PLUGIN_SIGNAL_ATTACHMENT_NAME_MAX,
1195
+ PLUGIN_SIGNAL_ATTACHMENT_TYPE_MAX,
1187
1196
  PLUGIN_SIGNAL_KINDS,
1188
1197
  PLUGIN_SIGNAL_LABELS_MAX,
1189
1198
  PLUGIN_SIGNAL_LABEL_MAX,
@@ -1,3 +1,9 @@
1
+ export declare function createKayAgentVendoredMcp(pluginId: string, servers: readonly string[], uri: PluginUriCaller): KayAgentVendoredMcp;
2
+
3
+ export declare function createKayAppMcp(pluginId: string, servers: readonly string[], uri: PluginUriCaller): KayAppMcp;
4
+
5
+ export declare function createVendoredMcpPort(server: string, uri: PluginUriCaller): VendoredMcpPort;
6
+
1
7
  /**
2
8
  * The URI module for one vendored-MCP integration plugin.
3
9
  *
@@ -44,6 +50,35 @@ declare type JsonValue = JsonPrimitive | {
44
50
  readonly [key: string]: JsonValue;
45
51
  } | readonly JsonValue[];
46
52
 
53
+ /**
54
+ * Calls tools on the plugin's own vendored MCP servers. See {@link KayAgentPluginApi.vendoredMcp}.
55
+ */
56
+ declare interface KayAgentVendoredMcp {
57
+ /**
58
+ * The port to `server`, a name this plugin declares in its `kay.app` target's
59
+ * `contributes.mcpServers`. Throws when the plugin declares no server of that name. A declared
60
+ * server that Bonsai does not host for this plugin still returns a port; each call then answers
61
+ * `unknown-server`. Each invocation uses the resource caller's exact `kay.agent` `uri.call`
62
+ * grant for `mcp` / `write`; MCP checks the host-stamped caller against server ownership.
63
+ */
64
+ port<TTool extends string = string>(server: string): VendoredMcpPort<TTool>;
65
+ }
66
+
67
+ /** Calls tools on this plugin's manifest-declared MCP servers through Bonsai's managed connection. */
68
+ declare interface KayAppMcp {
69
+ /**
70
+ * Uses the current app-wide server set, without a session or working directory.
71
+ * Refuses undeclared or no-longer-hosted servers. Sign-in and tool failures return typed outcomes;
72
+ * credentials remain with the host. Like the agent-side vendored MCP API, calls can read or write
73
+ * and do not use the agent's per-tool approval path.
74
+ */
75
+ callTool(input: {
76
+ readonly server: string;
77
+ readonly tool: string;
78
+ readonly args: Readonly<Record<string, unknown>>;
79
+ }): Promise<VendoredMcpOutcome>;
80
+ }
81
+
47
82
  declare interface LineRange {
48
83
  readonly start: number;
49
84
  readonly end: number;
@@ -127,6 +162,38 @@ declare interface ObservationMaterial {
127
162
  readonly retainForMs?: number;
128
163
  }
129
164
 
165
+ /**
166
+ * Sessionless resource calls. Each call checks the calling target's exact scheme and verb grant.
167
+ * Results retain provider bytes, metadata and revisions; provider failures retain their URI tags.
168
+ * A cancellation or lost response does not prove a mutation was not applied.
169
+ */
170
+ declare interface PluginUriCaller {
171
+ read(input: string, options?: PluginUriReadOptions): Promise<RawResource>;
172
+ list(input: string, params?: UriListParams, options?: PluginUriCallOptions): Promise<UriListResult>;
173
+ search(input: string, params: UriSearchParams, options?: PluginUriCallOptions): Promise<UriSearchResult>;
174
+ write(input: string, content?: UriPayload | Uint8Array, options?: PluginUriWriteOptions): Promise<UriMutationResult | void>;
175
+ edit(input: string, payload: UriPayload, options?: PluginUriMutationOptions): Promise<UriMutationResult>;
176
+ delete(input: string, options?: PluginUriMutationOptions): Promise<UriMutationResult>;
177
+ }
178
+
179
+ declare interface PluginUriCallOptions {
180
+ readonly cwd?: string;
181
+ readonly signal?: AbortSignal;
182
+ }
183
+
184
+ declare interface PluginUriMutationOptions extends PluginUriCallOptions {
185
+ readonly expectedRevision?: string;
186
+ }
187
+
188
+ declare interface PluginUriReadOptions extends PluginUriCallOptions {
189
+ readonly knownRevision?: string;
190
+ }
191
+
192
+ declare interface PluginUriWriteOptions extends PluginUriMutationOptions {
193
+ readonly sources?: readonly UriWriteSource[];
194
+ readonly writeSource?: UriSourceDescriptor;
195
+ }
196
+
130
197
  declare interface RawResource {
131
198
  readonly uri: string;
132
199
  readonly content: string | Uint8Array;
@@ -1246,7 +1313,7 @@ declare type VendoredMcpFactClaim = {
1246
1313
  /**
1247
1314
  * ---
1248
1315
  * responsibility: The one interface through which a vendored-MCP plugin's code calls a tool on its
1249
- * own vendored MCP server, so every read-through plugin and the daemon that implements the port
1316
+ * own vendored MCP server, so every read-through plugin and the SDK that implements the port
1250
1317
  * agree on the same shape by construction.
1251
1318
  * owns:
1252
1319
  * - {@link VendoredMcpPort} - what a plugin's URI module and tools call.
@@ -1254,8 +1321,8 @@ declare type VendoredMcpFactClaim = {
1254
1321
  * must surface to the agent rather than swallow.
1255
1322
  * does_not_own:
1256
1323
  * - The implementation. A plugin never dials MCP itself: it takes the port from
1257
- * `api.vendoredMcp.port(server)` (`@bonsai/plugin-sdk/kay-agent`), which the daemon binds to the
1258
- * plugin's own hosted server over its MCP host. Plugin source stays host-free, and a plugin
1324
+ * `api.vendoredMcp.port(server)` (`@bonsai/plugin-sdk/kay-agent`), which the SDK binds through
1325
+ * resource calls to the plugin's own hosted server. Plugin source stays host-free, and a plugin
1259
1326
  * under test substitutes a fake port.
1260
1327
  * - Which tool names exist. `TTool` is the plugin's own union of the vendor tools it calls; the
1261
1328
  * port is generic so a typo in a tool name fails typecheck inside the plugin.
@@ -1291,8 +1358,8 @@ export declare type VendoredMcpOutcome =
1291
1358
 
1292
1359
  /**
1293
1360
  * One vendored MCP server, as the plugin that declares it calls it. `invoke` calls any tool the
1294
- * server has, whether it reads or writes. The port checks no permission of its own: a call is as
1295
- * permitted as the plugin tool or URI verb that makes it.
1361
+ * server has, whether it reads or writes. The resource caller checks the target's exact
1362
+ * `uri.call` grant, and MCP checks the host-stamped caller against server ownership.
1296
1363
  */
1297
1364
  export declare interface VendoredMcpPort<TTool extends string = string> {
1298
1365
  invoke(input: {
@@ -480,6 +480,30 @@ var UriPayloadContractSchema = z2.strictObject({
480
480
  examples: z2.array(z2.unknown()).optional()
481
481
  });
482
482
 
483
+ // packages/herd-uri/dist/payloads.js
484
+ function isJsonPrimitive(value) {
485
+ return value === null || typeof value === "string" || typeof value === "boolean" || typeof value === "number" && Number.isFinite(value);
486
+ }
487
+ function isJsonValue(value) {
488
+ return isJsonValueInner(value, /* @__PURE__ */ new WeakSet());
489
+ }
490
+ function isJsonValueInner(value, seen) {
491
+ if (isJsonPrimitive(value))
492
+ return true;
493
+ if (typeof value !== "object" || value === null)
494
+ return false;
495
+ if (seen.has(value))
496
+ return false;
497
+ seen.add(value);
498
+ if (Array.isArray(value)) {
499
+ return value.every((item) => isJsonValueInner(item, seen));
500
+ }
501
+ const prototype = Object.getPrototypeOf(value);
502
+ if (prototype !== Object.prototype && prototype !== null)
503
+ return false;
504
+ return Object.values(value).every((item) => isJsonValueInner(item, seen));
505
+ }
506
+
483
507
  // packages/herd-uri/dist/text-edit.js
484
508
  import { z as z3 } from "zod";
485
509
  var findReplaceArm = z3.strictObject({
@@ -664,7 +688,70 @@ function createVendoredMcpUriModule(descriptor) {
664
688
  }
665
689
  };
666
690
  }
691
+
692
+ // packages/plugin-sdk/dist/vendored-mcp.js
693
+ function createVendoredMcpPort(server, uri) {
694
+ return {
695
+ async invoke({ cwd, tool, args }) {
696
+ try {
697
+ if (!isJsonValue(args)) {
698
+ return {
699
+ ok: false,
700
+ reason: "failed",
701
+ message: "MCP tool arguments must be JSON-compatible."
702
+ };
703
+ }
704
+ const result = await uri.write(`mcp://${encodeURIComponent(server)}/tools/${encodeURIComponent(tool)}`, args, { cwd });
705
+ if (result === void 0 || result.resource === void 0) {
706
+ return {
707
+ ok: false,
708
+ reason: "failed",
709
+ message: `MCP server '${server}' returned no tool result.`
710
+ };
711
+ }
712
+ return { ok: true, value: result.resource };
713
+ } catch (error) {
714
+ const failure = error;
715
+ const code = failure?.providerCode;
716
+ const reason = failure?._tag === "UriProviderError" && failure.moduleId === "mcp.servers" && (code === "needs-auth" || code === "unknown-server" || code === "unknown-tool" || code === "failed") ? code : "failed";
717
+ return {
718
+ ok: false,
719
+ reason,
720
+ message: error instanceof Error ? error.message : String(error),
721
+ ...typeof failure?.serviceId === "string" ? { serviceId: failure.serviceId } : {}
722
+ };
723
+ }
724
+ }
725
+ };
726
+ }
727
+ function createKayAgentVendoredMcp(pluginId, servers, uri) {
728
+ return {
729
+ port(server) {
730
+ if (!servers.includes(server)) {
731
+ throw new Error(`Plugin '${pluginId}' declares no MCP server '${server}' in its kay.app target's contributes.mcpServers, so api.vendoredMcp has no port to it.`);
732
+ }
733
+ return createVendoredMcpPort(server, uri);
734
+ }
735
+ };
736
+ }
737
+ function createKayAppMcp(pluginId, servers, uri) {
738
+ return {
739
+ async callTool({ server, tool, args }) {
740
+ if (!servers.includes(server)) {
741
+ return {
742
+ ok: false,
743
+ reason: "unknown-server",
744
+ message: `Plugin '${pluginId}' does not declare MCP server '${server}'.`
745
+ };
746
+ }
747
+ return createVendoredMcpPort(server, uri).invoke({ cwd: "", tool, args });
748
+ }
749
+ };
750
+ }
667
751
  export {
752
+ createKayAgentVendoredMcp,
753
+ createKayAppMcp,
754
+ createVendoredMcpPort,
668
755
  createVendoredMcpUriModule,
669
756
  vendoredMcpUriModuleFacts
670
757
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yaklabs-ai/plugin-sdk",
3
- "version": "0.54.1",
3
+ "version": "0.55.0",
4
4
  "description": "The Kay plugin SDK — types and helpers for authoring a Kay directory plugin.",
5
5
  "author": "YakLabs",
6
6
  "type": "module",