@ryuhq/sdk 0.2.0 → 0.2.2

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.
Files changed (66) hide show
  1. package/README.md +51 -2
  2. package/dist/action.cjs +839 -0
  3. package/dist/action.d.cts +88 -0
  4. package/dist/action.d.ts +88 -0
  5. package/dist/action.js +8 -0
  6. package/dist/agent-plugin.d.cts +1 -1
  7. package/dist/agent-plugin.d.ts +1 -1
  8. package/dist/agent.cjs +7 -0
  9. package/dist/agent.d.cts +1 -1
  10. package/dist/agent.d.ts +1 -1
  11. package/dist/agent.js +2 -2
  12. package/dist/{app-DNaGmLVf.d.cts → app-B0Z9Ew_R.d.cts} +18 -6
  13. package/dist/{app-Bkw7LlCK.d.ts → app-C-BDJwfG.d.ts} +18 -6
  14. package/dist/builder.cjs +110 -11
  15. package/dist/builder.d.cts +7 -2
  16. package/dist/builder.d.ts +7 -2
  17. package/dist/builder.js +4 -3
  18. package/dist/chunk-4TPUZDTI.js +94 -0
  19. package/dist/{chunk-T5676WL2.js → chunk-BC3A7HMO.js} +1 -77
  20. package/dist/{chunk-IOLP5FFE.js → chunk-HLKJZAFK.js} +9 -2
  21. package/dist/{chunk-IEUQ3CDG.js → chunk-NZKVOSC2.js} +71 -5
  22. package/dist/chunk-QYFUNJOH.js +83 -0
  23. package/dist/{chunk-W3KPP4WN.js → chunk-SN2QBJUF.js} +20 -7
  24. package/dist/{chunk-ULSVL7EC.js → chunk-Z57QDDJR.js} +1 -1
  25. package/dist/{chunk-A3RGEPDG.js → chunk-ZTJWBRUL.js} +32 -0
  26. package/dist/cli.cjs +93 -8
  27. package/dist/cli.js +33 -8
  28. package/dist/index.cjs +297 -87
  29. package/dist/index.d.cts +10 -9
  30. package/dist/index.d.ts +10 -9
  31. package/dist/index.js +34 -19
  32. package/dist/manifest.cjs +76 -6
  33. package/dist/manifest.d.cts +157 -5
  34. package/dist/manifest.d.ts +157 -5
  35. package/dist/manifest.js +9 -1
  36. package/dist/mcp/server.d.cts +2 -1
  37. package/dist/mcp/server.d.ts +2 -1
  38. package/dist/runnable.cjs +279 -71
  39. package/dist/runnable.d.cts +6 -3
  40. package/dist/runnable.d.ts +6 -3
  41. package/dist/runnable.js +11 -5
  42. package/dist/{tool-DSx2bFx8.d.ts → tool-AjkdFvhE.d.ts} +54 -4
  43. package/dist/{tool-u-VR0fLF.d.cts → tool-CgzW92O_.d.cts} +54 -4
  44. package/package.json +12 -3
  45. package/src/agent-plugin.ts +1 -1
  46. package/src/builder.ts +9 -0
  47. package/src/cli-security.test.ts +109 -0
  48. package/src/cli.ts +43 -10
  49. package/src/contracts-lockstep.test.ts +16 -2
  50. package/src/exports-lockstep.test.ts +1 -0
  51. package/src/generated/plugin-manifest.ts +82 -5
  52. package/src/index.ts +18 -0
  53. package/src/manifest.fixtures.test.ts +9 -3
  54. package/src/manifest.test.ts +69 -0
  55. package/src/manifest.ts +137 -18
  56. package/src/mcp/server.ts +2 -1
  57. package/src/runnable/action.test.ts +128 -0
  58. package/src/runnable/action.ts +202 -0
  59. package/src/runnable/app.ts +45 -12
  60. package/src/runnable/index.ts +18 -3
  61. package/src/runnable/primitives.test.ts +34 -0
  62. package/src/runnable/primitives.ts +59 -0
  63. package/src/runnable/runnable-types.ts +3 -0
  64. package/src/runnable/tool.ts +35 -4
  65. package/src/runnable/turn-hook.ts +4 -2
  66. package/src/slash-command.test.ts +69 -0
@@ -0,0 +1,88 @@
1
+ import { Surface, PluginManifest } from './manifest.cjs';
2
+ import { SdkRunnable } from './mcp/server.cjs';
3
+ import { k as RunnableContext, o as ToolSchema, T as ToolRunnable } from './tool-CgzW92O_.cjs';
4
+ import 'zod';
5
+ import './mcp/client.cjs';
6
+ import './client-D5U6ssPc.cjs';
7
+
8
+ /**
9
+ * Canonical Ryu Action authoring API.
10
+ *
11
+ * An Action is the semantic contract for a business operation: one description,
12
+ * input schema, output schema, implementation, and effect declaration. It lowers
13
+ * to Ryu's existing governed `inline_deno` Tool backend, so this adds a coherent
14
+ * authoring seam without introducing a second Core execution runtime.
15
+ */
16
+
17
+ /** The two effects Core can enforce from tool annotations. */
18
+ type ActionEffect = "mutate" | "read";
19
+ /** MCP-compatible effect hints plus any future provider-neutral boolean hints. */
20
+ interface ActionAnnotations {
21
+ destructiveHint?: boolean;
22
+ idempotentHint?: boolean;
23
+ openWorldHint?: boolean;
24
+ readOnlyHint?: boolean;
25
+ [key: string]: boolean | undefined;
26
+ }
27
+ /** Options for defining a canonical Action. */
28
+ interface ActionOptions<TInput extends Record<string, unknown>, TOutput> {
29
+ /** Additional MCP-compatible effect hints. */
30
+ annotations?: ActionAnnotations;
31
+ /** Description the agent reads when deciding whether to call the action. */
32
+ description: string;
33
+ /** Explicit effect used by read-only and approval enforcement. */
34
+ effect: ActionEffect;
35
+ /** Stable action id, also used as the generated Core tool slug. */
36
+ id: string;
37
+ /** Human-readable action name. */
38
+ name: string;
39
+ /** Require human approval even when global smart mode would not classify it. */
40
+ needsApproval?: boolean;
41
+ /** JSON Schema for the structured action result. */
42
+ outputSchema?: Record<string, unknown>;
43
+ /** The one implementation used by local callers and the packaged tool body. */
44
+ run(input: TInput, ctx: RunnableContext): Promise<TOutput>;
45
+ /** JSON Schema for all action inputs. */
46
+ schema: ToolSchema;
47
+ }
48
+ /** Options for lowering one Action into a standalone Ryu plugin manifest. */
49
+ interface ActionManifestOptions {
50
+ /** Plugin activation events; defaults to eager activation. */
51
+ activationEvents?: readonly string[];
52
+ /** Extra Gateway grants required by the action body. */
53
+ grants?: readonly string[];
54
+ /** Reverse-domain plugin id (for example `com.acme.support`). */
55
+ id: string;
56
+ /** Display name; defaults to the Action name. */
57
+ name?: string;
58
+ /** Host surfaces this plugin targets. */
59
+ targets?: readonly Surface[];
60
+ /** Plugin semver. */
61
+ version: string;
62
+ }
63
+ /** A ToolRunnable with Action semantics and manifest/MCP adapters. */
64
+ interface ActionRunnable<TInput extends Record<string, unknown> = Record<string, unknown>, TOutput = unknown> extends ToolRunnable<TInput, TOutput> {
65
+ /** Discriminates this semantic contract from an ordinary ToolRunnable. */
66
+ readonly action: true;
67
+ /** Effect hints lowered into Core's existing tool metadata. */
68
+ readonly annotations: ActionAnnotations;
69
+ /** Required action description. */
70
+ readonly description: string;
71
+ /** Declared effect. */
72
+ readonly effect: ActionEffect;
73
+ /** Whether Core must queue approval before execution. */
74
+ readonly needsApproval: boolean;
75
+ /** Structured result schema, when the action returns one. */
76
+ readonly outputSchema?: Record<string, unknown>;
77
+ /** Lower this Action to a validated, installable plugin manifest. */
78
+ toManifest(options: ActionManifestOptions): PluginManifest;
79
+ /** Adapt this Action to the SDK MCP server using the same implementation. */
80
+ toMcpTool(context: RunnableContext): SdkRunnable;
81
+ }
82
+ /**
83
+ * Define one business operation that can be run locally, packaged as a Core
84
+ * tool, or registered on an SDK MCP server without rewriting its implementation.
85
+ */
86
+ declare function defineAction<TInput extends Record<string, unknown> = Record<string, unknown>, TOutput = unknown>(options: ActionOptions<TInput, TOutput>): ActionRunnable<TInput, TOutput>;
87
+
88
+ export { type ActionAnnotations, type ActionEffect, type ActionManifestOptions, type ActionOptions, type ActionRunnable, defineAction };
@@ -0,0 +1,88 @@
1
+ import { Surface, PluginManifest } from './manifest.js';
2
+ import { SdkRunnable } from './mcp/server.js';
3
+ import { k as RunnableContext, o as ToolSchema, T as ToolRunnable } from './tool-AjkdFvhE.js';
4
+ import 'zod';
5
+ import './mcp/client.js';
6
+ import './client-D5U6ssPc.js';
7
+
8
+ /**
9
+ * Canonical Ryu Action authoring API.
10
+ *
11
+ * An Action is the semantic contract for a business operation: one description,
12
+ * input schema, output schema, implementation, and effect declaration. It lowers
13
+ * to Ryu's existing governed `inline_deno` Tool backend, so this adds a coherent
14
+ * authoring seam without introducing a second Core execution runtime.
15
+ */
16
+
17
+ /** The two effects Core can enforce from tool annotations. */
18
+ type ActionEffect = "mutate" | "read";
19
+ /** MCP-compatible effect hints plus any future provider-neutral boolean hints. */
20
+ interface ActionAnnotations {
21
+ destructiveHint?: boolean;
22
+ idempotentHint?: boolean;
23
+ openWorldHint?: boolean;
24
+ readOnlyHint?: boolean;
25
+ [key: string]: boolean | undefined;
26
+ }
27
+ /** Options for defining a canonical Action. */
28
+ interface ActionOptions<TInput extends Record<string, unknown>, TOutput> {
29
+ /** Additional MCP-compatible effect hints. */
30
+ annotations?: ActionAnnotations;
31
+ /** Description the agent reads when deciding whether to call the action. */
32
+ description: string;
33
+ /** Explicit effect used by read-only and approval enforcement. */
34
+ effect: ActionEffect;
35
+ /** Stable action id, also used as the generated Core tool slug. */
36
+ id: string;
37
+ /** Human-readable action name. */
38
+ name: string;
39
+ /** Require human approval even when global smart mode would not classify it. */
40
+ needsApproval?: boolean;
41
+ /** JSON Schema for the structured action result. */
42
+ outputSchema?: Record<string, unknown>;
43
+ /** The one implementation used by local callers and the packaged tool body. */
44
+ run(input: TInput, ctx: RunnableContext): Promise<TOutput>;
45
+ /** JSON Schema for all action inputs. */
46
+ schema: ToolSchema;
47
+ }
48
+ /** Options for lowering one Action into a standalone Ryu plugin manifest. */
49
+ interface ActionManifestOptions {
50
+ /** Plugin activation events; defaults to eager activation. */
51
+ activationEvents?: readonly string[];
52
+ /** Extra Gateway grants required by the action body. */
53
+ grants?: readonly string[];
54
+ /** Reverse-domain plugin id (for example `com.acme.support`). */
55
+ id: string;
56
+ /** Display name; defaults to the Action name. */
57
+ name?: string;
58
+ /** Host surfaces this plugin targets. */
59
+ targets?: readonly Surface[];
60
+ /** Plugin semver. */
61
+ version: string;
62
+ }
63
+ /** A ToolRunnable with Action semantics and manifest/MCP adapters. */
64
+ interface ActionRunnable<TInput extends Record<string, unknown> = Record<string, unknown>, TOutput = unknown> extends ToolRunnable<TInput, TOutput> {
65
+ /** Discriminates this semantic contract from an ordinary ToolRunnable. */
66
+ readonly action: true;
67
+ /** Effect hints lowered into Core's existing tool metadata. */
68
+ readonly annotations: ActionAnnotations;
69
+ /** Required action description. */
70
+ readonly description: string;
71
+ /** Declared effect. */
72
+ readonly effect: ActionEffect;
73
+ /** Whether Core must queue approval before execution. */
74
+ readonly needsApproval: boolean;
75
+ /** Structured result schema, when the action returns one. */
76
+ readonly outputSchema?: Record<string, unknown>;
77
+ /** Lower this Action to a validated, installable plugin manifest. */
78
+ toManifest(options: ActionManifestOptions): PluginManifest;
79
+ /** Adapt this Action to the SDK MCP server using the same implementation. */
80
+ toMcpTool(context: RunnableContext): SdkRunnable;
81
+ }
82
+ /**
83
+ * Define one business operation that can be run locally, packaged as a Core
84
+ * tool, or registered on an SDK MCP server without rewriting its implementation.
85
+ */
86
+ declare function defineAction<TInput extends Record<string, unknown> = Record<string, unknown>, TOutput = unknown>(options: ActionOptions<TInput, TOutput>): ActionRunnable<TInput, TOutput>;
87
+
88
+ export { type ActionAnnotations, type ActionEffect, type ActionManifestOptions, type ActionOptions, type ActionRunnable, defineAction };
package/dist/action.js ADDED
@@ -0,0 +1,8 @@
1
+ import {
2
+ defineAction
3
+ } from "./chunk-4TPUZDTI.js";
4
+ import "./chunk-QYFUNJOH.js";
5
+ import "./chunk-NZKVOSC2.js";
6
+ export {
7
+ defineAction
8
+ };
@@ -29,7 +29,7 @@
29
29
  * is deliberately NOT a copy of the whole native manifest — that would be a second
30
30
  * source of truth with a stale-copy failure mode.
31
31
  *
32
- * Both `plugins-store/*` and `apps-store/*` use this one `PluginManifest` shape, so
32
+ * Both `plugins-store/{plugins,lsp,external_plugins}/*` and `apps-store/*` use this one `PluginManifest` shape, so
33
33
  * one converter covers both stores.
34
34
  */
35
35
  /** Agent Plugins spec version this module targets. */
@@ -29,7 +29,7 @@
29
29
  * is deliberately NOT a copy of the whole native manifest — that would be a second
30
30
  * source of truth with a stale-copy failure mode.
31
31
  *
32
- * Both `plugins-store/*` and `apps-store/*` use this one `PluginManifest` shape, so
32
+ * Both `plugins-store/{plugins,lsp,external_plugins}/*` and `apps-store/*` use this one `PluginManifest` shape, so
33
33
  * one converter covers both stores.
34
34
  */
35
35
  /** Agent Plugins spec version this module targets. */
package/dist/agent.cjs CHANGED
@@ -282,6 +282,13 @@ function createPrimitives(transport) {
282
282
  process_id: input.processId
283
283
  })
284
284
  },
285
+ storage: {
286
+ get: (input) => transport.bridge("storage.get", input),
287
+ set: (input) => transport.bridge("storage.set", input).then(() => void 0),
288
+ delete: (input) => transport.bridge("storage.delete", input).then(() => void 0),
289
+ keys: (input) => transport.bridge("storage.keys", input ?? {}).then((value) => Array.isArray(value) ? value : []),
290
+ compareAndSet: (input) => transport.bridge("storage.compareAndSet", input)
291
+ },
285
292
  rag: {
286
293
  retrieve: (input) => brokerCall(transport, "rag", "retrieve", input),
287
294
  embed: (input) => brokerCall(transport, "rag", "embed", input),
package/dist/agent.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { T as ToolRunnable, j as RunnableContext } from './tool-u-VR0fLF.cjs';
1
+ import { T as ToolRunnable, k as RunnableContext } from './tool-CgzW92O_.cjs';
2
2
  import './manifest.cjs';
3
3
  import 'zod';
4
4
  import './client-D5U6ssPc.cjs';
package/dist/agent.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { T as ToolRunnable, j as RunnableContext } from './tool-DSx2bFx8.js';
1
+ import { T as ToolRunnable, k as RunnableContext } from './tool-AjkdFvhE.js';
2
2
  import './manifest.js';
3
3
  import 'zod';
4
4
  import './client-D5U6ssPc.js';
package/dist/agent.js CHANGED
@@ -8,8 +8,8 @@ import {
8
8
  resolveToolDefs,
9
9
  runAgentLoop,
10
10
  ryuTool
11
- } from "./chunk-ULSVL7EC.js";
12
- import "./chunk-A3RGEPDG.js";
11
+ } from "./chunk-Z57QDDJR.js";
12
+ import "./chunk-ZTJWBRUL.js";
13
13
  import "./chunk-HACAGK65.js";
14
14
  import "./chunk-IKEDLLFY.js";
15
15
  export {
@@ -1,4 +1,5 @@
1
- import { Requires, Surface, PluginManifest } from './manifest.cjs';
1
+ import { Requires, SlashCommandContribution, Surface, PluginManifest } from './manifest.cjs';
2
+ import { T as ToolRunnable } from './tool-CgzW92O_.cjs';
2
3
 
3
4
  /**
4
5
  * Ryu App authoring factory — `defineApp`.
@@ -16,11 +17,13 @@ import { Requires, Surface, PluginManifest } from './manifest.cjs';
16
17
  * - A **companion** tool (`accessible:true`) is a call target a mounted widget
17
18
  * may invoke: it carries `widget_accessible:true` and gets no widget template.
18
19
  *
19
- * v1 boundary: this is **declarative pass-through only** there is no `run`
20
- * handler. Third-party tool code execution needs the plugin runtime (out of
21
- * scope); the widget renders from `window.openai.toolInput`/`toolOutput` and Core
22
- * echoes the validated arguments as `structuredContent`. `ryu pack` bundles the
23
- * `uiEntry` source into the manifest's `ui_code`.
20
+ * A tool may also attach a `ToolRunnable`. When present, its self-contained run
21
+ * body is emitted as Core's grant-gated `inline_deno` backend, so the widget and
22
+ * the behavior ship in one bundle. Without a runnable, the tool remains the
23
+ * declarative pass-through form: the widget renders from
24
+ * `window.openai.toolInput`/`toolOutput` and Core echoes the validated arguments
25
+ * as `structuredContent`. `ryu pack` bundles the `uiEntry` source into the
26
+ * manifest's `ui_code`.
24
27
  */
25
28
 
26
29
  /** One tool a Ryu App declares. */
@@ -40,6 +43,13 @@ interface AppToolSpec {
40
43
  invoking?: string;
41
44
  /** Tool name (unqualified). The wire id is `<server>.<name>`. */
42
45
  name: string;
46
+ /**
47
+ * Optional executable body for this tool. Core receives it as an
48
+ * `inline_deno` runnable and runs it in the deny-by-default tool sandbox.
49
+ * The body must be self-contained; use the injected `host` capability surface
50
+ * for governed model calls and other platform primitives.
51
+ */
52
+ runnable?: Pick<ToolRunnable, "code" | "id" | "name" | "schema">;
43
53
  }
44
54
  /**
45
55
  * The `requires` block as an AUTHOR writes it: both members optional. Distinct
@@ -78,6 +88,8 @@ interface DefineAppOptions {
78
88
  requires?: DefineAppRequires;
79
89
  /** MCP server namespace for the tool ids. Defaults to `slug`. */
80
90
  server?: string;
91
+ /** Slash commands this app exposes in the chat composer. */
92
+ slashCommands?: SlashCommandContribution[];
81
93
  /**
82
94
  * App slug — used to build the widget uri (`ui://widget/<slug>.html`) and, when
83
95
  * `server` is omitted, the MCP server namespace that qualifies each tool id.
@@ -1,4 +1,5 @@
1
- import { Requires, Surface, PluginManifest } from './manifest.js';
1
+ import { Requires, SlashCommandContribution, Surface, PluginManifest } from './manifest.js';
2
+ import { T as ToolRunnable } from './tool-AjkdFvhE.js';
2
3
 
3
4
  /**
4
5
  * Ryu App authoring factory — `defineApp`.
@@ -16,11 +17,13 @@ import { Requires, Surface, PluginManifest } from './manifest.js';
16
17
  * - A **companion** tool (`accessible:true`) is a call target a mounted widget
17
18
  * may invoke: it carries `widget_accessible:true` and gets no widget template.
18
19
  *
19
- * v1 boundary: this is **declarative pass-through only** there is no `run`
20
- * handler. Third-party tool code execution needs the plugin runtime (out of
21
- * scope); the widget renders from `window.openai.toolInput`/`toolOutput` and Core
22
- * echoes the validated arguments as `structuredContent`. `ryu pack` bundles the
23
- * `uiEntry` source into the manifest's `ui_code`.
20
+ * A tool may also attach a `ToolRunnable`. When present, its self-contained run
21
+ * body is emitted as Core's grant-gated `inline_deno` backend, so the widget and
22
+ * the behavior ship in one bundle. Without a runnable, the tool remains the
23
+ * declarative pass-through form: the widget renders from
24
+ * `window.openai.toolInput`/`toolOutput` and Core echoes the validated arguments
25
+ * as `structuredContent`. `ryu pack` bundles the `uiEntry` source into the
26
+ * manifest's `ui_code`.
24
27
  */
25
28
 
26
29
  /** One tool a Ryu App declares. */
@@ -40,6 +43,13 @@ interface AppToolSpec {
40
43
  invoking?: string;
41
44
  /** Tool name (unqualified). The wire id is `<server>.<name>`. */
42
45
  name: string;
46
+ /**
47
+ * Optional executable body for this tool. Core receives it as an
48
+ * `inline_deno` runnable and runs it in the deny-by-default tool sandbox.
49
+ * The body must be self-contained; use the injected `host` capability surface
50
+ * for governed model calls and other platform primitives.
51
+ */
52
+ runnable?: Pick<ToolRunnable, "code" | "id" | "name" | "schema">;
43
53
  }
44
54
  /**
45
55
  * The `requires` block as an AUTHOR writes it: both members optional. Distinct
@@ -78,6 +88,8 @@ interface DefineAppOptions {
78
88
  requires?: DefineAppRequires;
79
89
  /** MCP server namespace for the tool ids. Defaults to `slug`. */
80
90
  server?: string;
91
+ /** Slash commands this app exposes in the chat composer. */
92
+ slashCommands?: SlashCommandContribution[];
81
93
  /**
82
94
  * App slug — used to build the widget uri (`ui://widget/<slug>.html`) and, when
83
95
  * `server` is omitted, the MCP server namespace that qualifies each tool id.
package/dist/builder.cjs CHANGED
@@ -177,10 +177,18 @@ var ChatWidgetTemplateSchema = import_zod.z.object({
177
177
  }).superRefine((value, ctx) => {
178
178
  const count = Number(Boolean(value.backing.tool_id)) + Number(Boolean(value.backing.view_id));
179
179
  if (count !== 1 && value.availability === "available") {
180
- ctx.addIssue({ code: "custom", path: ["backing"], message: "available templates need exactly one backing tool_id or view_id" });
180
+ ctx.addIssue({
181
+ code: "custom",
182
+ path: ["backing"],
183
+ message: "available templates need exactly one backing tool_id or view_id"
184
+ });
181
185
  }
182
186
  if (count > 1) {
183
- ctx.addIssue({ code: "custom", path: ["backing"], message: "backing must declare at most one of tool_id or view_id" });
187
+ ctx.addIssue({
188
+ code: "custom",
189
+ path: ["backing"],
190
+ message: "backing must declare at most one of tool_id or view_id"
191
+ });
184
192
  }
185
193
  });
186
194
  var ToolAppConfigSchema = import_zod.z.object({
@@ -203,6 +211,33 @@ var ToolAppConfigSchema = import_zod.z.object({
203
211
  /** Optional status label shown when the render tool finishes. */
204
212
  invoked: import_zod.z.string().optional()
205
213
  });
214
+ var SlashCommandOptionSchema = import_zod.z.object({
215
+ description: import_zod.z.string().optional(),
216
+ label: import_zod.z.string().min(1),
217
+ value: import_zod.z.string().min(1)
218
+ }).passthrough();
219
+ var SlashCommandCustomOptionSchema = import_zod.z.union([
220
+ import_zod.z.literal(true),
221
+ import_zod.z.object({
222
+ description: import_zod.z.string().optional(),
223
+ label: import_zod.z.string().min(1).optional()
224
+ }).passthrough()
225
+ ]);
226
+ var SlashCommandArgumentSchema = import_zod.z.object({
227
+ allow_custom: import_zod.z.boolean().optional(),
228
+ custom: SlashCommandCustomOptionSchema.optional(),
229
+ description: import_zod.z.string().optional(),
230
+ name: import_zod.z.string().min(1),
231
+ options: import_zod.z.array(SlashCommandOptionSchema).optional()
232
+ }).passthrough();
233
+ var SlashCommandContributionSchema = import_zod.z.object({
234
+ args: import_zod.z.array(SlashCommandArgumentSchema).optional(),
235
+ body: import_zod.z.string().optional(),
236
+ command: import_zod.z.string().min(1),
237
+ description: import_zod.z.string().optional(),
238
+ id: import_zod.z.string().optional(),
239
+ parameters: import_zod.z.array(SlashCommandArgumentSchema).optional()
240
+ }).passthrough();
206
241
  var ContributesSchema = import_zod.z.object({
207
242
  turn_hooks: import_zod.z.array(TurnHookContributionSchema).default([]),
208
243
  /** App events this plugin EMITS — the provider half of the hook system, whose
@@ -217,7 +252,7 @@ var ContributesSchema = import_zod.z.object({
217
252
  * feature declaration before signing. */
218
253
  chat_features: import_zod.z.array(import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())).default([]),
219
254
  settings_tabs: import_zod.z.array(import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())).default([]),
220
- slash_commands: import_zod.z.array(import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())).default([]),
255
+ slash_commands: import_zod.z.array(SlashCommandContributionSchema).default([]),
221
256
  /** App widgets (Ryu Apps). Each binds a render tool id to its
222
257
  * `ui://widget/<slug>.html` template. Mirrors the Rust-side
223
258
  * `Contributes.widgets` field, without which the CLI's zod parse would strip
@@ -284,7 +319,10 @@ var ContributesSchema = import_zod.z.object({
284
319
  output_styles: import_zod.z.array(OutputStyleContributionSchema).default([]),
285
320
  /** Per-message actions contributed by an enabled plugin. Kept as loose records
286
321
  * so renderer-specific `kind`/`args` payloads survive `ryu pack` unchanged. */
287
- message_actions: import_zod.z.array(import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())).default([])
322
+ message_actions: import_zod.z.array(import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())).default([]),
323
+ /** Buttons contributed to the floating text-selection toolbar. Kept as loose
324
+ * records so host-owned dispatch args survive `ryu pack` unchanged. */
325
+ selection_actions: import_zod.z.array(import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())).default([])
288
326
  });
289
327
  var SetupStepSchema = import_zod.z.object({
290
328
  /** Card heading (e.g. the companion app name). */
@@ -434,6 +472,9 @@ var PluginManifestSchema = import_zod.z.object({
434
472
  /^\d+\.\d+\.\d+(?:-[\w.]+)?(?:\+[\w.]+)?$/,
435
473
  "version must be a valid semver string (e.g. 1.0.0)"
436
474
  ),
475
+ /** Core-owned release maturity metadata. The Rust contract validates the
476
+ * richer shape; the SDK authoring parser must preserve it for pack/publish. */
477
+ stability: import_zod.z.unknown().optional(),
437
478
  /**
438
479
  * Lower-case hex `sha256(utf8_bytes(ui_code))` binding the plugin's bundled
439
480
  * sandboxed-UI code to this manifest. `ryu pack` / `ryu publish` compute it and
@@ -451,8 +492,26 @@ var PluginManifestSchema = import_zod.z.object({
451
492
  * Declarations only — grant enforcement is the Gateway's responsibility.
452
493
  */
453
494
  permission_grants: import_zod.z.array(import_zod.z.string()).default([]),
495
+ /** Deny-by-default sandbox permissions. Core remains authoritative; this
496
+ * schema mirrors the fields so `ryu pack` cannot strip them. */
497
+ permissions: import_zod.z.object({
498
+ fs: import_zod.z.object({
499
+ read: import_zod.z.array(import_zod.z.string()).default([]),
500
+ write: import_zod.z.array(import_zod.z.string()).default([])
501
+ }).optional(),
502
+ child_process: import_zod.z.boolean().optional(),
503
+ run: import_zod.z.array(import_zod.z.string()).default([]),
504
+ network: import_zod.z.union([import_zod.z.boolean(), import_zod.z.array(import_zod.z.string())]).optional(),
505
+ tool: import_zod.z.array(import_zod.z.string()).default([])
506
+ }).optional(),
507
+ /** Core-owned permission presentation levels. Preserved verbatim here and
508
+ * validated by Core's authoritative manifest contract. */
509
+ permission_levels: import_zod.z.unknown().optional(),
454
510
  /** Remote or stdio MCP servers registered by this plugin. */
455
511
  mcp_servers: import_zod.z.record(import_zod.z.string(), McpServerDeclSchema).optional(),
512
+ /** Core-owned sidecar declarations. Their full process/HTTP schema stays in
513
+ * the Rust contract; this authoring layer must never strip them. */
514
+ sidecars: import_zod.z.unknown().optional(),
456
515
  /**
457
516
  * Optional Companion surface (an in-desktop overlay or sidebar panel).
458
517
  * Absent when the plugin has no Companion surface.
@@ -488,6 +547,9 @@ var PluginManifestSchema = import_zod.z.object({
488
547
  * an unsupported-target plugin stays installable and inspectable.
489
548
  */
490
549
  targets: import_zod.z.array(SurfaceSchema).default([]),
550
+ /** Rich per-surface declarations (`support`, UI, contributed commands, …).
551
+ * Core owns and validates the nested vocabulary. */
552
+ surfaces: import_zod.z.unknown().optional(),
491
553
  /**
492
554
  * Host version floors — the semver requirement each surface must satisfy for
493
555
  * this plugin to install. Mirrors Core's `EnginesReq`
@@ -602,7 +664,7 @@ var PluginManifestSchema = import_zod.z.object({
602
664
  * array of steps guiding the user through post-install configuration.
603
665
  */
604
666
  setup: import_zod.z.union([SetupStepSchema, import_zod.z.array(SetupStepSchema)]).optional()
605
- }).superRefine((manifest, context) => {
667
+ }).passthrough().superRefine((manifest, context) => {
606
668
  const hasOAuthServer = Object.values(manifest.mcp_servers ?? {}).some(
607
669
  (server) => server.auth?.type === "oauth"
608
670
  );
@@ -620,6 +682,26 @@ var PluginManifestSchema = import_zod.z.object({
620
682
  }
621
683
  });
622
684
 
685
+ // src/runnable/tool.ts
686
+ function inlineToolRunnable(tool2, options) {
687
+ return {
688
+ id: tool2.id,
689
+ name: tool2.name,
690
+ kind: "tool",
691
+ config: {
692
+ slug: tool2.id,
693
+ backend: "inline_deno",
694
+ code: tool2.code,
695
+ input_schema: tool2.schema,
696
+ ...options?.description === void 0 ? tool2.description === void 0 ? {} : { description: tool2.description } : { description: options.description },
697
+ ...options?.outputSchema ? { output_schema: options.outputSchema } : {},
698
+ ...options?.annotations ? { annotations: options.annotations } : {},
699
+ ...options?.action === void 0 ? {} : { action: options.action },
700
+ ...options?.needsApproval === void 0 ? {} : { needs_approval: options.needsApproval }
701
+ }
702
+ };
703
+ }
704
+
623
705
  // src/runnable/app.ts
624
706
  var DEFAULT_APP_WIDGET_MIME = "text/html+skybridge";
625
707
  var DEFAULT_APP_DISPLAY_MODE = "inline";
@@ -631,6 +713,13 @@ function withWidgetRenderGrant(grants, widgets) {
631
713
  }
632
714
  return out;
633
715
  }
716
+ function withToolExecuteGrant(grants, tools) {
717
+ const out = [...grants];
718
+ if (tools.some((tool2) => tool2.runnable !== void 0) && !out.includes("tool:execute")) {
719
+ out.push("tool:execute");
720
+ }
721
+ return out;
722
+ }
634
723
  function appToolId(server, name) {
635
724
  return `${server}.${name}`;
636
725
  }
@@ -645,7 +734,9 @@ function defineApp(options) {
645
734
  for (const spec of options.tools) {
646
735
  const isRender = spec.accessible !== true;
647
736
  const id = appToolId(server, spec.name);
737
+ const executable = spec.runnable ? inlineToolRunnable(spec.runnable) : void 0;
648
738
  const config = {
739
+ ...executable?.config ?? {},
649
740
  slug: id,
650
741
  description: spec.description,
651
742
  widget: isRender,
@@ -679,7 +770,7 @@ function defineApp(options) {
679
770
  hook_events: [],
680
771
  composer_controls: [],
681
772
  settings_tabs: [],
682
- slash_commands: [],
773
+ slash_commands: options.slashCommands ?? [],
683
774
  sidebar_sections: [],
684
775
  sidebar_buttons: [],
685
776
  dock_panels: [],
@@ -697,6 +788,7 @@ function defineApp(options) {
697
788
  pi_extensions: [],
698
789
  output_styles: [],
699
790
  message_actions: [],
791
+ selection_actions: [],
700
792
  widgets
701
793
  };
702
794
  const raw = {
@@ -710,15 +802,15 @@ function defineApp(options) {
710
802
  // Core gates widget promotion on declared-AND-enabled-AND-granted, and a
711
803
  // missing grant fails as `DeniedNoGrant` — which is an `info!` log and
712
804
  // nothing else. The widget silently renders as plain text, with no error
713
- // in the UI and nothing pointing at the manifest. Every app scaffolded
714
- // through `defineApp` hit that, because the only fix was a grant string
715
- // the templates never mention and the builder never added; the one
716
- // working example on disk hand-writes it.
805
+ // in the UI and nothing pointing at the manifest.
717
806
  //
718
807
  // Added only when there is a widget to render, and unioned rather than
719
808
  // overwritten so an author's own `grants` list survives and re-declaring
720
809
  // it is not an error.
721
- permission_grants: withWidgetRenderGrant(options.grants ?? [], widgets),
810
+ permission_grants: withToolExecuteGrant(
811
+ withWidgetRenderGrant(options.grants ?? [], widgets),
812
+ options.tools
813
+ ),
722
814
  activation_events: options.activationEvents ?? ["*"],
723
815
  contributes,
724
816
  // `targets: []` means EVERY surface, so an app that declares none is
@@ -959,6 +1051,7 @@ var AppBuilder = class {
959
1051
  _dependencies = [];
960
1052
  _requiredCapabilities = [];
961
1053
  _requiredGrants = [];
1054
+ _slashCommands = [];
962
1055
  _targets = [];
963
1056
  /** Set the reverse-domain app id (e.g. `"com.example.checklist"`). */
964
1057
  id(value) {
@@ -1015,6 +1108,11 @@ var AppBuilder = class {
1015
1108
  this._tools.push(spec);
1016
1109
  return this;
1017
1110
  }
1111
+ /** Add a slash command and its optional sequential argument choices. */
1112
+ slashCommand(command) {
1113
+ this._slashCommands.push(command);
1114
+ return this;
1115
+ }
1018
1116
  /**
1019
1117
  * Declare a **plugin-to-plugin dependency** (auto-enabled, in dependency order,
1020
1118
  * before this app). `minVersion` is a MINIMUM (`"1.2.0"` = `">=1.2.0"`).
@@ -1062,6 +1160,7 @@ var AppBuilder = class {
1062
1160
  uiEntry: this._uiEntry,
1063
1161
  tools: this._tools,
1064
1162
  grants: this._grants,
1163
+ slashCommands: this._slashCommands,
1065
1164
  ...this._server ? { server: this._server } : {},
1066
1165
  ...this._displayMode ? { displayMode: this._displayMode } : {},
1067
1166
  ...this._mime ? { mime: this._mime } : {},
@@ -1,6 +1,8 @@
1
- import { RunnableMeta, Surface, PluginManifest, CompanionSurface } from './manifest.cjs';
2
- import { A as AppToolSpec } from './app-DNaGmLVf.cjs';
1
+ import { RunnableMeta, SlashCommandContribution, Surface, PluginManifest, CompanionSurface } from './manifest.cjs';
2
+ import { A as AppToolSpec } from './app-B0Z9Ew_R.cjs';
3
3
  import 'zod';
4
+ import './tool-CgzW92O_.cjs';
5
+ import './client-D5U6ssPc.cjs';
4
6
 
5
7
  /**
6
8
  * Ryu SDK typed builders — one builder per RunnableKind plus a PluginBuilder that
@@ -160,6 +162,7 @@ declare class AppBuilder {
160
162
  private readonly _dependencies;
161
163
  private readonly _requiredCapabilities;
162
164
  private readonly _requiredGrants;
165
+ private readonly _slashCommands;
163
166
  private readonly _targets;
164
167
  /** Set the reverse-domain app id (e.g. `"com.example.checklist"`). */
165
168
  id(value: string): this;
@@ -183,6 +186,8 @@ declare class AppBuilder {
183
186
  activationEvent(event: string): this;
184
187
  /** Append a tool spec (render tool unless `accessible:true`). */
185
188
  tool(spec: AppToolSpec): this;
189
+ /** Add a slash command and its optional sequential argument choices. */
190
+ slashCommand(command: SlashCommandContribution): this;
186
191
  /**
187
192
  * Declare a **plugin-to-plugin dependency** (auto-enabled, in dependency order,
188
193
  * before this app). `minVersion` is a MINIMUM (`"1.2.0"` = `">=1.2.0"`).
package/dist/builder.d.ts CHANGED
@@ -1,6 +1,8 @@
1
- import { RunnableMeta, Surface, PluginManifest, CompanionSurface } from './manifest.js';
2
- import { A as AppToolSpec } from './app-Bkw7LlCK.js';
1
+ import { RunnableMeta, SlashCommandContribution, Surface, PluginManifest, CompanionSurface } from './manifest.js';
2
+ import { A as AppToolSpec } from './app-C-BDJwfG.js';
3
3
  import 'zod';
4
+ import './tool-AjkdFvhE.js';
5
+ import './client-D5U6ssPc.js';
4
6
 
5
7
  /**
6
8
  * Ryu SDK typed builders — one builder per RunnableKind plus a PluginBuilder that
@@ -160,6 +162,7 @@ declare class AppBuilder {
160
162
  private readonly _dependencies;
161
163
  private readonly _requiredCapabilities;
162
164
  private readonly _requiredGrants;
165
+ private readonly _slashCommands;
163
166
  private readonly _targets;
164
167
  /** Set the reverse-domain app id (e.g. `"com.example.checklist"`). */
165
168
  id(value: string): this;
@@ -183,6 +186,8 @@ declare class AppBuilder {
183
186
  activationEvent(event: string): this;
184
187
  /** Append a tool spec (render tool unless `accessible:true`). */
185
188
  tool(spec: AppToolSpec): this;
189
+ /** Add a slash command and its optional sequential argument choices. */
190
+ slashCommand(command: SlashCommandContribution): this;
186
191
  /**
187
192
  * Declare a **plugin-to-plugin dependency** (auto-enabled, in dependency order,
188
193
  * before this app). `minVersion` is a MINIMUM (`"1.2.0"` = `">=1.2.0"`).
package/dist/builder.js CHANGED
@@ -10,9 +10,10 @@ import {
10
10
  skill,
11
11
  tool,
12
12
  workflow
13
- } from "./chunk-IOLP5FFE.js";
14
- import "./chunk-W3KPP4WN.js";
15
- import "./chunk-IEUQ3CDG.js";
13
+ } from "./chunk-HLKJZAFK.js";
14
+ import "./chunk-SN2QBJUF.js";
15
+ import "./chunk-QYFUNJOH.js";
16
+ import "./chunk-NZKVOSC2.js";
16
17
  export {
17
18
  AgentBuilder,
18
19
  AppBuilder,