@ryuhq/sdk 0.2.0 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) 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 +11 -2
  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-schema.test.ts +1 -1
  54. package/src/manifest.fixtures.test.ts +9 -3
  55. package/src/manifest.test.ts +69 -0
  56. package/src/manifest.ts +137 -18
  57. package/src/mcp/server.ts +2 -1
  58. package/src/runnable/action.test.ts +128 -0
  59. package/src/runnable/action.ts +202 -0
  60. package/src/runnable/app.ts +45 -12
  61. package/src/runnable/index.ts +18 -3
  62. package/src/runnable/primitives.test.ts +34 -0
  63. package/src/runnable/primitives.ts +59 -0
  64. package/src/runnable/runnable-types.ts +3 -0
  65. package/src/runnable/tool.ts +35 -4
  66. package/src/runnable/turn-hook.ts +4 -2
  67. package/src/slash-command.test.ts +69 -0
package/dist/index.cjs CHANGED
@@ -49,6 +49,10 @@ __export(src_exports, {
49
49
  RunnableKindSchema: () => RunnableKindSchema,
50
50
  RunnableMetaSchema: () => RunnableMetaSchema,
51
51
  SkillBuilder: () => SkillBuilder,
52
+ SlashCommandArgumentSchema: () => SlashCommandArgumentSchema,
53
+ SlashCommandContributionSchema: () => SlashCommandContributionSchema,
54
+ SlashCommandCustomOptionSchema: () => SlashCommandCustomOptionSchema,
55
+ SlashCommandOptionSchema: () => SlashCommandOptionSchema,
52
56
  SurfaceSchema: () => SurfaceSchema,
53
57
  ToolAppConfigSchema: () => ToolAppConfigSchema,
54
58
  ToolBuilder: () => ToolBuilder,
@@ -62,6 +66,7 @@ __export(src_exports, {
62
66
  coreManifestJsonSchema: () => coreManifestJsonSchema,
63
67
  createAgent: () => createAgent,
64
68
  createPrimitives: () => createPrimitives,
69
+ defineAction: () => defineAction,
65
70
  defineAgent: () => defineAgent,
66
71
  defineApp: () => defineApp,
67
72
  defineModel: () => defineModel,
@@ -87,7 +92,7 @@ __export(src_exports, {
87
92
  });
88
93
  module.exports = __toCommonJS(src_exports);
89
94
 
90
- // ../../node_modules/.bun/tsup@8.5.1+276f2dd5f44b8d39/node_modules/tsup/assets/cjs_shims.js
95
+ // ../../node_modules/.bun/tsup@8.5.1+68ce2442ac00f52e/node_modules/tsup/assets/cjs_shims.js
91
96
  var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
92
97
  var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
93
98
 
@@ -335,6 +340,31 @@ var PRIMITIVE_BINDINGS = {
335
340
  method: "background.stop",
336
341
  grant: "background:control"
337
342
  },
343
+ "storage.get": {
344
+ transport: "bridge",
345
+ method: "storage.get",
346
+ grant: "storage:kv"
347
+ },
348
+ "storage.set": {
349
+ transport: "bridge",
350
+ method: "storage.set",
351
+ grant: "storage:kv"
352
+ },
353
+ "storage.delete": {
354
+ transport: "bridge",
355
+ method: "storage.delete",
356
+ grant: "storage:kv"
357
+ },
358
+ "storage.keys": {
359
+ transport: "bridge",
360
+ method: "storage.keys",
361
+ grant: "storage:kv"
362
+ },
363
+ "storage.compareAndSet": {
364
+ transport: "bridge",
365
+ method: "storage.compareAndSet",
366
+ grant: "storage:kv"
367
+ },
338
368
  // Host-direct media data-path (the host holds the node token; returns data: URLs).
339
369
  "image.generate": {
340
370
  transport: "direct",
@@ -377,6 +407,13 @@ function createPrimitives(transport) {
377
407
  process_id: input.processId
378
408
  })
379
409
  },
410
+ storage: {
411
+ get: (input) => transport.bridge("storage.get", input),
412
+ set: (input) => transport.bridge("storage.set", input).then(() => void 0),
413
+ delete: (input) => transport.bridge("storage.delete", input).then(() => void 0),
414
+ keys: (input) => transport.bridge("storage.keys", input ?? {}).then((value) => Array.isArray(value) ? value : []),
415
+ compareAndSet: (input) => transport.bridge("storage.compareAndSet", input)
416
+ },
380
417
  rag: {
381
418
  retrieve: (input) => brokerCall(transport, "rag", "retrieve", input),
382
419
  embed: (input) => brokerCall(transport, "rag", "embed", input),
@@ -1019,10 +1056,18 @@ var ChatWidgetTemplateSchema = import_zod.z.object({
1019
1056
  }).superRefine((value, ctx) => {
1020
1057
  const count = Number(Boolean(value.backing.tool_id)) + Number(Boolean(value.backing.view_id));
1021
1058
  if (count !== 1 && value.availability === "available") {
1022
- ctx.addIssue({ code: "custom", path: ["backing"], message: "available templates need exactly one backing tool_id or view_id" });
1059
+ ctx.addIssue({
1060
+ code: "custom",
1061
+ path: ["backing"],
1062
+ message: "available templates need exactly one backing tool_id or view_id"
1063
+ });
1023
1064
  }
1024
1065
  if (count > 1) {
1025
- ctx.addIssue({ code: "custom", path: ["backing"], message: "backing must declare at most one of tool_id or view_id" });
1066
+ ctx.addIssue({
1067
+ code: "custom",
1068
+ path: ["backing"],
1069
+ message: "backing must declare at most one of tool_id or view_id"
1070
+ });
1026
1071
  }
1027
1072
  });
1028
1073
  var ToolAppConfigSchema = import_zod.z.object({
@@ -1045,6 +1090,33 @@ var ToolAppConfigSchema = import_zod.z.object({
1045
1090
  /** Optional status label shown when the render tool finishes. */
1046
1091
  invoked: import_zod.z.string().optional()
1047
1092
  });
1093
+ var SlashCommandOptionSchema = import_zod.z.object({
1094
+ description: import_zod.z.string().optional(),
1095
+ label: import_zod.z.string().min(1),
1096
+ value: import_zod.z.string().min(1)
1097
+ }).passthrough();
1098
+ var SlashCommandCustomOptionSchema = import_zod.z.union([
1099
+ import_zod.z.literal(true),
1100
+ import_zod.z.object({
1101
+ description: import_zod.z.string().optional(),
1102
+ label: import_zod.z.string().min(1).optional()
1103
+ }).passthrough()
1104
+ ]);
1105
+ var SlashCommandArgumentSchema = import_zod.z.object({
1106
+ allow_custom: import_zod.z.boolean().optional(),
1107
+ custom: SlashCommandCustomOptionSchema.optional(),
1108
+ description: import_zod.z.string().optional(),
1109
+ name: import_zod.z.string().min(1),
1110
+ options: import_zod.z.array(SlashCommandOptionSchema).optional()
1111
+ }).passthrough();
1112
+ var SlashCommandContributionSchema = import_zod.z.object({
1113
+ args: import_zod.z.array(SlashCommandArgumentSchema).optional(),
1114
+ body: import_zod.z.string().optional(),
1115
+ command: import_zod.z.string().min(1),
1116
+ description: import_zod.z.string().optional(),
1117
+ id: import_zod.z.string().optional(),
1118
+ parameters: import_zod.z.array(SlashCommandArgumentSchema).optional()
1119
+ }).passthrough();
1048
1120
  var ContributesSchema = import_zod.z.object({
1049
1121
  turn_hooks: import_zod.z.array(TurnHookContributionSchema).default([]),
1050
1122
  /** App events this plugin EMITS — the provider half of the hook system, whose
@@ -1059,7 +1131,7 @@ var ContributesSchema = import_zod.z.object({
1059
1131
  * feature declaration before signing. */
1060
1132
  chat_features: import_zod.z.array(import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())).default([]),
1061
1133
  settings_tabs: import_zod.z.array(import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())).default([]),
1062
- slash_commands: import_zod.z.array(import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())).default([]),
1134
+ slash_commands: import_zod.z.array(SlashCommandContributionSchema).default([]),
1063
1135
  /** App widgets (Ryu Apps). Each binds a render tool id to its
1064
1136
  * `ui://widget/<slug>.html` template. Mirrors the Rust-side
1065
1137
  * `Contributes.widgets` field, without which the CLI's zod parse would strip
@@ -1126,7 +1198,10 @@ var ContributesSchema = import_zod.z.object({
1126
1198
  output_styles: import_zod.z.array(OutputStyleContributionSchema).default([]),
1127
1199
  /** Per-message actions contributed by an enabled plugin. Kept as loose records
1128
1200
  * so renderer-specific `kind`/`args` payloads survive `ryu pack` unchanged. */
1129
- message_actions: import_zod.z.array(import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())).default([])
1201
+ message_actions: import_zod.z.array(import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())).default([]),
1202
+ /** Buttons contributed to the floating text-selection toolbar. Kept as loose
1203
+ * records so host-owned dispatch args survive `ryu pack` unchanged. */
1204
+ selection_actions: import_zod.z.array(import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())).default([])
1130
1205
  });
1131
1206
  var SetupStepSchema = import_zod.z.object({
1132
1207
  /** Card heading (e.g. the companion app name). */
@@ -1276,6 +1351,9 @@ var PluginManifestSchema = import_zod.z.object({
1276
1351
  /^\d+\.\d+\.\d+(?:-[\w.]+)?(?:\+[\w.]+)?$/,
1277
1352
  "version must be a valid semver string (e.g. 1.0.0)"
1278
1353
  ),
1354
+ /** Core-owned release maturity metadata. The Rust contract validates the
1355
+ * richer shape; the SDK authoring parser must preserve it for pack/publish. */
1356
+ stability: import_zod.z.unknown().optional(),
1279
1357
  /**
1280
1358
  * Lower-case hex `sha256(utf8_bytes(ui_code))` binding the plugin's bundled
1281
1359
  * sandboxed-UI code to this manifest. `ryu pack` / `ryu publish` compute it and
@@ -1293,8 +1371,26 @@ var PluginManifestSchema = import_zod.z.object({
1293
1371
  * Declarations only — grant enforcement is the Gateway's responsibility.
1294
1372
  */
1295
1373
  permission_grants: import_zod.z.array(import_zod.z.string()).default([]),
1374
+ /** Deny-by-default sandbox permissions. Core remains authoritative; this
1375
+ * schema mirrors the fields so `ryu pack` cannot strip them. */
1376
+ permissions: import_zod.z.object({
1377
+ fs: import_zod.z.object({
1378
+ read: import_zod.z.array(import_zod.z.string()).default([]),
1379
+ write: import_zod.z.array(import_zod.z.string()).default([])
1380
+ }).optional(),
1381
+ child_process: import_zod.z.boolean().optional(),
1382
+ run: import_zod.z.array(import_zod.z.string()).default([]),
1383
+ network: import_zod.z.union([import_zod.z.boolean(), import_zod.z.array(import_zod.z.string())]).optional(),
1384
+ tool: import_zod.z.array(import_zod.z.string()).default([])
1385
+ }).optional(),
1386
+ /** Core-owned permission presentation levels. Preserved verbatim here and
1387
+ * validated by Core's authoritative manifest contract. */
1388
+ permission_levels: import_zod.z.unknown().optional(),
1296
1389
  /** Remote or stdio MCP servers registered by this plugin. */
1297
1390
  mcp_servers: import_zod.z.record(import_zod.z.string(), McpServerDeclSchema).optional(),
1391
+ /** Core-owned sidecar declarations. Their full process/HTTP schema stays in
1392
+ * the Rust contract; this authoring layer must never strip them. */
1393
+ sidecars: import_zod.z.unknown().optional(),
1298
1394
  /**
1299
1395
  * Optional Companion surface (an in-desktop overlay or sidebar panel).
1300
1396
  * Absent when the plugin has no Companion surface.
@@ -1330,6 +1426,9 @@ var PluginManifestSchema = import_zod.z.object({
1330
1426
  * an unsupported-target plugin stays installable and inspectable.
1331
1427
  */
1332
1428
  targets: import_zod.z.array(SurfaceSchema).default([]),
1429
+ /** Rich per-surface declarations (`support`, UI, contributed commands, …).
1430
+ * Core owns and validates the nested vocabulary. */
1431
+ surfaces: import_zod.z.unknown().optional(),
1333
1432
  /**
1334
1433
  * Host version floors — the semver requirement each surface must satisfy for
1335
1434
  * this plugin to install. Mirrors Core's `EnginesReq`
@@ -1444,7 +1543,7 @@ var PluginManifestSchema = import_zod.z.object({
1444
1543
  * array of steps guiding the user through post-install configuration.
1445
1544
  */
1446
1545
  setup: import_zod.z.union([SetupStepSchema, import_zod.z.array(SetupStepSchema)]).optional()
1447
- }).superRefine((manifest, context) => {
1546
+ }).passthrough().superRefine((manifest, context) => {
1448
1547
  const hasOAuthServer = Object.values(manifest.mcp_servers ?? {}).some(
1449
1548
  (server) => server.auth?.type === "oauth"
1450
1549
  );
@@ -1471,6 +1570,85 @@ function coreManifestJsonSchema() {
1471
1570
  return JSON.parse(loadNative().pluginManifestJsonSchema());
1472
1571
  }
1473
1572
 
1573
+ // src/runnable/tool.ts
1574
+ function validateInput(input, schema, toolId) {
1575
+ if (typeof input !== "object" || input === null || Array.isArray(input)) {
1576
+ throw new Error(
1577
+ `[ryu-sdk] Tool "${toolId}" input must be an object, got ${JSON.stringify(input)}`
1578
+ );
1579
+ }
1580
+ const record = input;
1581
+ for (const requiredKey of schema.required ?? []) {
1582
+ if (!(requiredKey in record)) {
1583
+ throw new Error(
1584
+ `[ryu-sdk] Tool "${toolId}" input missing required field "${requiredKey}"`
1585
+ );
1586
+ }
1587
+ }
1588
+ for (const [key, prop] of Object.entries(schema.properties)) {
1589
+ if (!(key in record)) {
1590
+ continue;
1591
+ }
1592
+ const value = record[key];
1593
+ if (!checkType(value, prop.type)) {
1594
+ throw new Error(
1595
+ `[ryu-sdk] Tool "${toolId}" input field "${key}" expected type "${prop.type}", got ${JSON.stringify(value)}`
1596
+ );
1597
+ }
1598
+ }
1599
+ }
1600
+ function checkType(value, type) {
1601
+ switch (type) {
1602
+ case "string":
1603
+ return typeof value === "string";
1604
+ case "number":
1605
+ case "integer":
1606
+ return typeof value === "number";
1607
+ case "boolean":
1608
+ return typeof value === "boolean";
1609
+ case "array":
1610
+ return Array.isArray(value);
1611
+ case "object":
1612
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1613
+ default:
1614
+ return false;
1615
+ }
1616
+ }
1617
+ function defineTool(options) {
1618
+ const { description, id, name, schema, run } = options;
1619
+ const code = `return await (${run.toString()})(input, host);`;
1620
+ return {
1621
+ id,
1622
+ name,
1623
+ kind: "tool",
1624
+ schema,
1625
+ ...description === void 0 ? {} : { description },
1626
+ code,
1627
+ run(input, ctx) {
1628
+ validateInput(input, schema, id);
1629
+ return run(input, ctx);
1630
+ }
1631
+ };
1632
+ }
1633
+ function inlineToolRunnable(tool2, options) {
1634
+ return {
1635
+ id: tool2.id,
1636
+ name: tool2.name,
1637
+ kind: "tool",
1638
+ config: {
1639
+ slug: tool2.id,
1640
+ backend: "inline_deno",
1641
+ code: tool2.code,
1642
+ input_schema: tool2.schema,
1643
+ ...options?.description === void 0 ? tool2.description === void 0 ? {} : { description: tool2.description } : { description: options.description },
1644
+ ...options?.outputSchema ? { output_schema: options.outputSchema } : {},
1645
+ ...options?.annotations ? { annotations: options.annotations } : {},
1646
+ ...options?.action === void 0 ? {} : { action: options.action },
1647
+ ...options?.needsApproval === void 0 ? {} : { needs_approval: options.needsApproval }
1648
+ }
1649
+ };
1650
+ }
1651
+
1474
1652
  // src/runnable/app.ts
1475
1653
  var DEFAULT_APP_WIDGET_MIME = "text/html+skybridge";
1476
1654
  var DEFAULT_APP_DISPLAY_MODE = "inline";
@@ -1482,6 +1660,13 @@ function withWidgetRenderGrant(grants, widgets) {
1482
1660
  }
1483
1661
  return out;
1484
1662
  }
1663
+ function withToolExecuteGrant(grants, tools) {
1664
+ const out = [...grants];
1665
+ if (tools.some((tool2) => tool2.runnable !== void 0) && !out.includes("tool:execute")) {
1666
+ out.push("tool:execute");
1667
+ }
1668
+ return out;
1669
+ }
1485
1670
  function appToolId(server, name) {
1486
1671
  return `${server}.${name}`;
1487
1672
  }
@@ -1496,7 +1681,9 @@ function defineApp(options) {
1496
1681
  for (const spec of options.tools) {
1497
1682
  const isRender = spec.accessible !== true;
1498
1683
  const id = appToolId(server, spec.name);
1684
+ const executable = spec.runnable ? inlineToolRunnable(spec.runnable) : void 0;
1499
1685
  const config = {
1686
+ ...executable?.config ?? {},
1500
1687
  slug: id,
1501
1688
  description: spec.description,
1502
1689
  widget: isRender,
@@ -1530,7 +1717,7 @@ function defineApp(options) {
1530
1717
  hook_events: [],
1531
1718
  composer_controls: [],
1532
1719
  settings_tabs: [],
1533
- slash_commands: [],
1720
+ slash_commands: options.slashCommands ?? [],
1534
1721
  sidebar_sections: [],
1535
1722
  sidebar_buttons: [],
1536
1723
  dock_panels: [],
@@ -1548,6 +1735,7 @@ function defineApp(options) {
1548
1735
  pi_extensions: [],
1549
1736
  output_styles: [],
1550
1737
  message_actions: [],
1738
+ selection_actions: [],
1551
1739
  widgets
1552
1740
  };
1553
1741
  const raw = {
@@ -1561,15 +1749,15 @@ function defineApp(options) {
1561
1749
  // Core gates widget promotion on declared-AND-enabled-AND-granted, and a
1562
1750
  // missing grant fails as `DeniedNoGrant` — which is an `info!` log and
1563
1751
  // nothing else. The widget silently renders as plain text, with no error
1564
- // in the UI and nothing pointing at the manifest. Every app scaffolded
1565
- // through `defineApp` hit that, because the only fix was a grant string
1566
- // the templates never mention and the builder never added; the one
1567
- // working example on disk hand-writes it.
1752
+ // in the UI and nothing pointing at the manifest.
1568
1753
  //
1569
1754
  // Added only when there is a widget to render, and unioned rather than
1570
1755
  // overwritten so an author's own `grants` list survives and re-declaring
1571
1756
  // it is not an error.
1572
- permission_grants: withWidgetRenderGrant(options.grants ?? [], widgets),
1757
+ permission_grants: withToolExecuteGrant(
1758
+ withWidgetRenderGrant(options.grants ?? [], widgets),
1759
+ options.tools
1760
+ ),
1573
1761
  activation_events: options.activationEvents ?? ["*"],
1574
1762
  contributes,
1575
1763
  // `targets: []` means EVERY surface, so an app that declares none is
@@ -1810,6 +1998,7 @@ var AppBuilder = class {
1810
1998
  _dependencies = [];
1811
1999
  _requiredCapabilities = [];
1812
2000
  _requiredGrants = [];
2001
+ _slashCommands = [];
1813
2002
  _targets = [];
1814
2003
  /** Set the reverse-domain app id (e.g. `"com.example.checklist"`). */
1815
2004
  id(value) {
@@ -1866,6 +2055,11 @@ var AppBuilder = class {
1866
2055
  this._tools.push(spec);
1867
2056
  return this;
1868
2057
  }
2058
+ /** Add a slash command and its optional sequential argument choices. */
2059
+ slashCommand(command) {
2060
+ this._slashCommands.push(command);
2061
+ return this;
2062
+ }
1869
2063
  /**
1870
2064
  * Declare a **plugin-to-plugin dependency** (auto-enabled, in dependency order,
1871
2065
  * before this app). `minVersion` is a MINIMUM (`"1.2.0"` = `">=1.2.0"`).
@@ -1913,6 +2107,7 @@ var AppBuilder = class {
1913
2107
  uiEntry: this._uiEntry,
1914
2108
  tools: this._tools,
1915
2109
  grants: this._grants,
2110
+ slashCommands: this._slashCommands,
1916
2111
  ...this._server ? { server: this._server } : {},
1917
2112
  ...this._displayMode ? { displayMode: this._displayMode } : {},
1918
2113
  ...this._mime ? { mime: this._mime } : {},
@@ -2284,6 +2479,89 @@ function toDisposable(teardown) {
2284
2479
  };
2285
2480
  }
2286
2481
 
2482
+ // src/runnable/action.ts
2483
+ function deriveAnnotations(effect, annotations) {
2484
+ const resolved = {
2485
+ ...annotations,
2486
+ readOnlyHint: annotations?.readOnlyHint ?? effect === "read",
2487
+ destructiveHint: annotations?.destructiveHint ?? effect === "mutate"
2488
+ };
2489
+ if (effect === "read" && resolved.destructiveHint) {
2490
+ throw new Error(
2491
+ "[ryu-sdk] read actions cannot set annotations.destructiveHint=true"
2492
+ );
2493
+ }
2494
+ if (effect === "mutate" && resolved.readOnlyHint) {
2495
+ throw new Error(
2496
+ "[ryu-sdk] mutate actions cannot set annotations.readOnlyHint=true"
2497
+ );
2498
+ }
2499
+ return resolved;
2500
+ }
2501
+ function actionManifestEntry(action) {
2502
+ const options = {
2503
+ action: true,
2504
+ annotations: action.annotations,
2505
+ description: action.description,
2506
+ needsApproval: action.needsApproval,
2507
+ ...action.outputSchema ? { outputSchema: action.outputSchema } : {}
2508
+ };
2509
+ return inlineToolRunnable(action, options);
2510
+ }
2511
+ function actionToManifest(action, options) {
2512
+ const grants = [.../* @__PURE__ */ new Set([...options.grants ?? [], "tool:execute"])];
2513
+ const raw = {
2514
+ id: options.id,
2515
+ name: options.name ?? action.name,
2516
+ version: options.version,
2517
+ runnables: [actionManifestEntry(action)],
2518
+ permission_grants: grants,
2519
+ activation_events: [...options.activationEvents ?? ["*"]],
2520
+ targets: [...options.targets ?? []]
2521
+ };
2522
+ const result = PluginManifestSchema.safeParse(raw);
2523
+ if (!result.success) {
2524
+ const first = result.error.issues[0];
2525
+ const field = first?.path.join(".") ?? "unknown";
2526
+ const message = first?.message ?? "validation failed";
2527
+ throw new Error(
2528
+ `[ryu-sdk] action manifest validation failed at '${field}': ${message}`
2529
+ );
2530
+ }
2531
+ return result.data;
2532
+ }
2533
+ function defineAction(options) {
2534
+ const annotations = deriveAnnotations(options.effect, options.annotations);
2535
+ const tool2 = defineTool({
2536
+ description: options.description,
2537
+ id: options.id,
2538
+ name: options.name,
2539
+ run: options.run,
2540
+ schema: options.schema
2541
+ });
2542
+ const action = {
2543
+ ...tool2,
2544
+ action: true,
2545
+ annotations,
2546
+ description: options.description,
2547
+ effect: options.effect,
2548
+ needsApproval: options.needsApproval ?? false,
2549
+ ...options.outputSchema ? { outputSchema: options.outputSchema } : {},
2550
+ toManifest(manifestOptions) {
2551
+ return actionToManifest(action, manifestOptions);
2552
+ },
2553
+ toMcpTool(context) {
2554
+ return {
2555
+ name: action.id,
2556
+ description: action.description,
2557
+ inputSchema: action.schema,
2558
+ run: (input) => action.run(input, context)
2559
+ };
2560
+ }
2561
+ };
2562
+ return action;
2563
+ }
2564
+
2287
2565
  // src/runnable/agent.ts
2288
2566
  var CAPABILITY_SLOTS = ["rag", "memory", "tts", "stt"];
2289
2567
  function lowerChatSlot(chat) {
@@ -2428,80 +2706,6 @@ function defineSkill(options) {
2428
2706
  };
2429
2707
  }
2430
2708
 
2431
- // src/runnable/tool.ts
2432
- function validateInput(input, schema, toolId) {
2433
- if (typeof input !== "object" || input === null || Array.isArray(input)) {
2434
- throw new Error(
2435
- `[ryu-sdk] Tool "${toolId}" input must be an object, got ${JSON.stringify(input)}`
2436
- );
2437
- }
2438
- const record = input;
2439
- for (const requiredKey of schema.required ?? []) {
2440
- if (!(requiredKey in record)) {
2441
- throw new Error(
2442
- `[ryu-sdk] Tool "${toolId}" input missing required field "${requiredKey}"`
2443
- );
2444
- }
2445
- }
2446
- for (const [key, prop] of Object.entries(schema.properties)) {
2447
- if (!(key in record)) {
2448
- continue;
2449
- }
2450
- const value = record[key];
2451
- if (!checkType(value, prop.type)) {
2452
- throw new Error(
2453
- `[ryu-sdk] Tool "${toolId}" input field "${key}" expected type "${prop.type}", got ${JSON.stringify(value)}`
2454
- );
2455
- }
2456
- }
2457
- }
2458
- function checkType(value, type) {
2459
- switch (type) {
2460
- case "string":
2461
- return typeof value === "string";
2462
- case "number":
2463
- case "integer":
2464
- return typeof value === "number";
2465
- case "boolean":
2466
- return typeof value === "boolean";
2467
- case "array":
2468
- return Array.isArray(value);
2469
- case "object":
2470
- return typeof value === "object" && value !== null && !Array.isArray(value);
2471
- default:
2472
- return false;
2473
- }
2474
- }
2475
- function defineTool(options) {
2476
- const { id, name, schema, run } = options;
2477
- const code = `return await (${run.toString()})(input, host);`;
2478
- return {
2479
- id,
2480
- name,
2481
- kind: "tool",
2482
- schema,
2483
- code,
2484
- run(input, ctx) {
2485
- validateInput(input, schema, id);
2486
- return run(input, ctx);
2487
- }
2488
- };
2489
- }
2490
- function inlineToolRunnable(tool2, options) {
2491
- return {
2492
- id: tool2.id,
2493
- name: tool2.name,
2494
- kind: "tool",
2495
- config: {
2496
- slug: tool2.id,
2497
- backend: "inline_deno",
2498
- code: tool2.code,
2499
- input_schema: tool2.schema,
2500
- ...options?.description ? { description: options.description } : {}
2501
- }
2502
- };
2503
- }
2504
-
2505
2709
  // src/runnable/turn-hook.ts
2506
2710
  function defineTurnHook(options) {
2507
2711
  const source = options.run.toString();
@@ -2533,7 +2737,8 @@ function definePlugin(options) {
2533
2737
  data_categories: [],
2534
2738
  pi_extensions: [],
2535
2739
  output_styles: [],
2536
- message_actions: []
2740
+ message_actions: [],
2741
+ selection_actions: []
2537
2742
  };
2538
2743
  const tools = options.tools ?? [];
2539
2744
  const runnables = tools.map((t) => inlineToolRunnable(t));
@@ -2594,6 +2799,10 @@ function defineWorkflow(options) {
2594
2799
  RunnableKindSchema,
2595
2800
  RunnableMetaSchema,
2596
2801
  SkillBuilder,
2802
+ SlashCommandArgumentSchema,
2803
+ SlashCommandContributionSchema,
2804
+ SlashCommandCustomOptionSchema,
2805
+ SlashCommandOptionSchema,
2597
2806
  SurfaceSchema,
2598
2807
  ToolAppConfigSchema,
2599
2808
  ToolBuilder,
@@ -2607,6 +2816,7 @@ function defineWorkflow(options) {
2607
2816
  coreManifestJsonSchema,
2608
2817
  createAgent,
2609
2818
  createPrimitives,
2819
+ defineAction,
2610
2820
  defineAgent,
2611
2821
  defineApp,
2612
2822
  defineModel,
package/dist/index.d.cts CHANGED
@@ -1,17 +1,18 @@
1
1
  export { Agent, AgentConfig, AgentEvent, AgentTool, Endpoint, GenerateResult, QueryInput, QueryOptions, RemoteToolRef, createAgent, query, ryuTool } from './agent.cjs';
2
2
  export { AgentBuilder, AppBuilder, PluginBuilder, SkillBuilder, ToolBuilder, WorkflowBuilder, agent, app, skill, tool, workflow } from './builder.cjs';
3
- import { HookEventContribution, Surface, TurnHookContribution, PluginManifest } from './manifest.cjs';
4
- export { AppDependency, AppDependencySchema, CapabilityReq, CapabilityReqSchema, CompanionSurface, CompanionSurfaceSchema, Contributes, McpServerAuth, McpServerAuthSchema, McpServerDecl, McpServerDeclSchema, PluginManifestSchema, Requires, RequiresSchema, RunnableKind, RunnableKindSchema, RunnableMeta, RunnableMetaSchema, SurfaceSchema, ToolAppConfig, ToolAppConfigSchema, WidgetContribution, WidgetContributionSchema, coreManifestJsonSchema, validateManifestStrict, validatePluginId } from './manifest.cjs';
3
+ import { HookEventContribution, SlashCommandContribution, Surface, TurnHookContribution, PluginManifest } from './manifest.cjs';
4
+ export { AppDependency, AppDependencySchema, CapabilityReq, CapabilityReqSchema, CompanionSurface, CompanionSurfaceSchema, Contributes, McpServerAuth, McpServerAuthSchema, McpServerDecl, McpServerDeclSchema, PluginManifestSchema, Requires, RequiresSchema, RunnableKind, RunnableKindSchema, RunnableMeta, RunnableMetaSchema, SlashCommandArgument, SlashCommandArgumentSchema, SlashCommandContributionSchema, SlashCommandCustomOption, SlashCommandCustomOptionSchema, SlashCommandOption, SlashCommandOptionSchema, SurfaceSchema, ToolAppConfig, ToolAppConfigSchema, WidgetContribution, WidgetContributionSchema, coreManifestJsonSchema, validateManifestStrict, validatePluginId } from './manifest.cjs';
5
5
  export { MCP_PROTOCOL_VERSION, McpStdioCommand, McpTool, callTool, listTools } from './mcp/client.cjs';
6
6
  export { JsonSchema, McpServer, PassthroughRegistration, SdkRunnable, unwrapContent } from './mcp/server.cjs';
7
7
  export { C as ChatDelta, a as ChatMessage, b as ChatResult, M as ModelClient, c as ModelClientOptions, d as defineModel } from './client-D5U6ssPc.cjs';
8
8
  export { DEFAULT_GATEWAY_URL, assertAllowedEgressUrl, resolveGatewayToken, resolveGatewayUrl } from './model.cjs';
9
9
  export { CommandContribution, Disposable, PanelContribution, PanelRegion, PluginContext, RouteContribution, RyuHostServices, RyuPlugin, RyuPluginModule, SettingsSectionContribution, StoreSectionContribution, ThemeContribution, toDisposable } from './plugin.cjs';
10
+ export { ActionAnnotations, ActionEffect, ActionManifestOptions, ActionOptions, ActionRunnable, defineAction } from './action.cjs';
10
11
  export { AgentCard, AgentManifestOptions, AgentOptions, AgentRunnable, AgentSlots, CapabilitySlot, ChatSlot, SkillOptions, WorkflowOptions, WorkflowStep, defineAgent, defineSkill, defineWorkflow } from './runnable.cjs';
11
- import { D as DefineAppRequires } from './app-DNaGmLVf.cjs';
12
- export { A as AppToolSpec, a as DefineAppOptions, b as appToolId, d as defineApp } from './app-DNaGmLVf.cjs';
13
- import { T as ToolRunnable } from './tool-u-VR0fLF.cjs';
14
- export { B as BackgroundClient, a as BackgroundProcess, D as DurableClient, E as EnginesClient, G as GatewayClient, H as HttpPrimitiveTransportOptions, I as ImageClient, J as JsonSchemaProperty, M as MemoryClient, b as MemoryItem, P as PRIMITIVE_BINDINGS, c as PrimitiveBinding, d as PrimitiveTransport, R as RagChunk, e as RagClient, f as RagRerankResult, g as RealtimeClient, h as RealtimeSubscription, i as Runnable, j as RunnableContext, k as RyuPrimitives, S as SttClient, l as ToolOptions, m as ToolSchema, n as TtsClient, o as createPrimitives, p as defineTool, q as httpPrimitiveTransport, r as inlineToolRunnable } from './tool-u-VR0fLF.cjs';
12
+ import { D as DefineAppRequires } from './app-B0Z9Ew_R.cjs';
13
+ export { A as AppToolSpec, a as DefineAppOptions, b as appToolId, d as defineApp } from './app-B0Z9Ew_R.cjs';
14
+ import { T as ToolRunnable } from './tool-CgzW92O_.cjs';
15
+ export { B as BackgroundClient, a as BackgroundProcess, D as DurableClient, E as EnginesClient, G as GatewayClient, H as HttpPrimitiveTransportOptions, I as ImageClient, b as InlineToolManifestOptions, J as JsonSchemaProperty, M as MemoryClient, c as MemoryItem, P as PRIMITIVE_BINDINGS, d as PrimitiveBinding, e as PrimitiveTransport, R as RagChunk, f as RagClient, g as RagRerankResult, h as RealtimeClient, i as RealtimeSubscription, j as Runnable, k as RunnableContext, l as RyuPrimitives, S as StorageClient, m as SttClient, n as ToolOptions, o as ToolSchema, p as TtsClient, q as createPrimitives, r as defineTool, s as httpPrimitiveTransport, t as inlineToolRunnable } from './tool-CgzW92O_.cjs';
15
16
  import 'zod';
16
17
 
17
18
  /**
@@ -133,8 +134,8 @@ interface DefinePluginOptions {
133
134
  requires?: DefineAppRequires;
134
135
  /** Declarative settings tabs (model pickers, fields), passed verbatim. */
135
136
  settingsTabs?: Record<string, unknown>[];
136
- /** Declarative slash commands, passed verbatim. */
137
- slashCommands?: Record<string, unknown>[];
137
+ /** Declarative slash commands, including sequential argument choices. */
138
+ slashCommands?: SlashCommandContribution[];
138
139
  /**
139
140
  * Host surfaces this plugin runs on. **Omitted/empty = every surface** (the
140
141
  * backward-compatible default); it never means "hidden".
@@ -158,4 +159,4 @@ interface DefinePluginOptions {
158
159
  */
159
160
  declare function definePlugin(options: DefinePluginOptions): PluginManifest;
160
161
 
161
- export { DefineAppRequires, type DefinePluginOptions, type DefineTurnHookOptions, type HookContext, type HookDirective, type HookRun, type HostApi, PluginManifest, type SideModelArgs, Surface, ToolRunnable, TurnHookContribution, definePlugin, defineTurnHook };
162
+ export { DefineAppRequires, type DefinePluginOptions, type DefineTurnHookOptions, type HookContext, type HookDirective, type HookRun, type HostApi, PluginManifest, type SideModelArgs, SlashCommandContribution, Surface, ToolRunnable, TurnHookContribution, definePlugin, defineTurnHook };
package/dist/index.d.ts CHANGED
@@ -1,17 +1,18 @@
1
1
  export { Agent, AgentConfig, AgentEvent, AgentTool, Endpoint, GenerateResult, QueryInput, QueryOptions, RemoteToolRef, createAgent, query, ryuTool } from './agent.js';
2
2
  export { AgentBuilder, AppBuilder, PluginBuilder, SkillBuilder, ToolBuilder, WorkflowBuilder, agent, app, skill, tool, workflow } from './builder.js';
3
- import { HookEventContribution, Surface, TurnHookContribution, PluginManifest } from './manifest.js';
4
- export { AppDependency, AppDependencySchema, CapabilityReq, CapabilityReqSchema, CompanionSurface, CompanionSurfaceSchema, Contributes, McpServerAuth, McpServerAuthSchema, McpServerDecl, McpServerDeclSchema, PluginManifestSchema, Requires, RequiresSchema, RunnableKind, RunnableKindSchema, RunnableMeta, RunnableMetaSchema, SurfaceSchema, ToolAppConfig, ToolAppConfigSchema, WidgetContribution, WidgetContributionSchema, coreManifestJsonSchema, validateManifestStrict, validatePluginId } from './manifest.js';
3
+ import { HookEventContribution, SlashCommandContribution, Surface, TurnHookContribution, PluginManifest } from './manifest.js';
4
+ export { AppDependency, AppDependencySchema, CapabilityReq, CapabilityReqSchema, CompanionSurface, CompanionSurfaceSchema, Contributes, McpServerAuth, McpServerAuthSchema, McpServerDecl, McpServerDeclSchema, PluginManifestSchema, Requires, RequiresSchema, RunnableKind, RunnableKindSchema, RunnableMeta, RunnableMetaSchema, SlashCommandArgument, SlashCommandArgumentSchema, SlashCommandContributionSchema, SlashCommandCustomOption, SlashCommandCustomOptionSchema, SlashCommandOption, SlashCommandOptionSchema, SurfaceSchema, ToolAppConfig, ToolAppConfigSchema, WidgetContribution, WidgetContributionSchema, coreManifestJsonSchema, validateManifestStrict, validatePluginId } from './manifest.js';
5
5
  export { MCP_PROTOCOL_VERSION, McpStdioCommand, McpTool, callTool, listTools } from './mcp/client.js';
6
6
  export { JsonSchema, McpServer, PassthroughRegistration, SdkRunnable, unwrapContent } from './mcp/server.js';
7
7
  export { C as ChatDelta, a as ChatMessage, b as ChatResult, M as ModelClient, c as ModelClientOptions, d as defineModel } from './client-D5U6ssPc.js';
8
8
  export { DEFAULT_GATEWAY_URL, assertAllowedEgressUrl, resolveGatewayToken, resolveGatewayUrl } from './model.js';
9
9
  export { CommandContribution, Disposable, PanelContribution, PanelRegion, PluginContext, RouteContribution, RyuHostServices, RyuPlugin, RyuPluginModule, SettingsSectionContribution, StoreSectionContribution, ThemeContribution, toDisposable } from './plugin.js';
10
+ export { ActionAnnotations, ActionEffect, ActionManifestOptions, ActionOptions, ActionRunnable, defineAction } from './action.js';
10
11
  export { AgentCard, AgentManifestOptions, AgentOptions, AgentRunnable, AgentSlots, CapabilitySlot, ChatSlot, SkillOptions, WorkflowOptions, WorkflowStep, defineAgent, defineSkill, defineWorkflow } from './runnable.js';
11
- import { D as DefineAppRequires } from './app-Bkw7LlCK.js';
12
- export { A as AppToolSpec, a as DefineAppOptions, b as appToolId, d as defineApp } from './app-Bkw7LlCK.js';
13
- import { T as ToolRunnable } from './tool-DSx2bFx8.js';
14
- export { B as BackgroundClient, a as BackgroundProcess, D as DurableClient, E as EnginesClient, G as GatewayClient, H as HttpPrimitiveTransportOptions, I as ImageClient, J as JsonSchemaProperty, M as MemoryClient, b as MemoryItem, P as PRIMITIVE_BINDINGS, c as PrimitiveBinding, d as PrimitiveTransport, R as RagChunk, e as RagClient, f as RagRerankResult, g as RealtimeClient, h as RealtimeSubscription, i as Runnable, j as RunnableContext, k as RyuPrimitives, S as SttClient, l as ToolOptions, m as ToolSchema, n as TtsClient, o as createPrimitives, p as defineTool, q as httpPrimitiveTransport, r as inlineToolRunnable } from './tool-DSx2bFx8.js';
12
+ import { D as DefineAppRequires } from './app-C-BDJwfG.js';
13
+ export { A as AppToolSpec, a as DefineAppOptions, b as appToolId, d as defineApp } from './app-C-BDJwfG.js';
14
+ import { T as ToolRunnable } from './tool-AjkdFvhE.js';
15
+ export { B as BackgroundClient, a as BackgroundProcess, D as DurableClient, E as EnginesClient, G as GatewayClient, H as HttpPrimitiveTransportOptions, I as ImageClient, b as InlineToolManifestOptions, J as JsonSchemaProperty, M as MemoryClient, c as MemoryItem, P as PRIMITIVE_BINDINGS, d as PrimitiveBinding, e as PrimitiveTransport, R as RagChunk, f as RagClient, g as RagRerankResult, h as RealtimeClient, i as RealtimeSubscription, j as Runnable, k as RunnableContext, l as RyuPrimitives, S as StorageClient, m as SttClient, n as ToolOptions, o as ToolSchema, p as TtsClient, q as createPrimitives, r as defineTool, s as httpPrimitiveTransport, t as inlineToolRunnable } from './tool-AjkdFvhE.js';
15
16
  import 'zod';
16
17
 
17
18
  /**
@@ -133,8 +134,8 @@ interface DefinePluginOptions {
133
134
  requires?: DefineAppRequires;
134
135
  /** Declarative settings tabs (model pickers, fields), passed verbatim. */
135
136
  settingsTabs?: Record<string, unknown>[];
136
- /** Declarative slash commands, passed verbatim. */
137
- slashCommands?: Record<string, unknown>[];
137
+ /** Declarative slash commands, including sequential argument choices. */
138
+ slashCommands?: SlashCommandContribution[];
138
139
  /**
139
140
  * Host surfaces this plugin runs on. **Omitted/empty = every surface** (the
140
141
  * backward-compatible default); it never means "hidden".
@@ -158,4 +159,4 @@ interface DefinePluginOptions {
158
159
  */
159
160
  declare function definePlugin(options: DefinePluginOptions): PluginManifest;
160
161
 
161
- export { DefineAppRequires, type DefinePluginOptions, type DefineTurnHookOptions, type HookContext, type HookDirective, type HookRun, type HostApi, PluginManifest, type SideModelArgs, Surface, ToolRunnable, TurnHookContribution, definePlugin, defineTurnHook };
162
+ export { DefineAppRequires, type DefinePluginOptions, type DefineTurnHookOptions, type HookContext, type HookDirective, type HookRun, type HostApi, PluginManifest, type SideModelArgs, SlashCommandContribution, Surface, ToolRunnable, TurnHookContribution, definePlugin, defineTurnHook };