@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/runnable.cjs CHANGED
@@ -33,6 +33,7 @@ __export(runnable_exports, {
33
33
  PRIMITIVE_BINDINGS: () => PRIMITIVE_BINDINGS,
34
34
  appToolId: () => appToolId,
35
35
  createPrimitives: () => createPrimitives,
36
+ defineAction: () => defineAction,
36
37
  defineAgent: () => defineAgent,
37
38
  defineApp: () => defineApp,
38
39
  defineSkill: () => defineSkill,
@@ -185,10 +186,18 @@ var ChatWidgetTemplateSchema = import_zod.z.object({
185
186
  }).superRefine((value, ctx) => {
186
187
  const count = Number(Boolean(value.backing.tool_id)) + Number(Boolean(value.backing.view_id));
187
188
  if (count !== 1 && value.availability === "available") {
188
- ctx.addIssue({ code: "custom", path: ["backing"], message: "available templates need exactly one backing tool_id or view_id" });
189
+ ctx.addIssue({
190
+ code: "custom",
191
+ path: ["backing"],
192
+ message: "available templates need exactly one backing tool_id or view_id"
193
+ });
189
194
  }
190
195
  if (count > 1) {
191
- ctx.addIssue({ code: "custom", path: ["backing"], message: "backing must declare at most one of tool_id or view_id" });
196
+ ctx.addIssue({
197
+ code: "custom",
198
+ path: ["backing"],
199
+ message: "backing must declare at most one of tool_id or view_id"
200
+ });
192
201
  }
193
202
  });
194
203
  var ToolAppConfigSchema = import_zod.z.object({
@@ -211,6 +220,33 @@ var ToolAppConfigSchema = import_zod.z.object({
211
220
  /** Optional status label shown when the render tool finishes. */
212
221
  invoked: import_zod.z.string().optional()
213
222
  });
223
+ var SlashCommandOptionSchema = import_zod.z.object({
224
+ description: import_zod.z.string().optional(),
225
+ label: import_zod.z.string().min(1),
226
+ value: import_zod.z.string().min(1)
227
+ }).passthrough();
228
+ var SlashCommandCustomOptionSchema = import_zod.z.union([
229
+ import_zod.z.literal(true),
230
+ import_zod.z.object({
231
+ description: import_zod.z.string().optional(),
232
+ label: import_zod.z.string().min(1).optional()
233
+ }).passthrough()
234
+ ]);
235
+ var SlashCommandArgumentSchema = import_zod.z.object({
236
+ allow_custom: import_zod.z.boolean().optional(),
237
+ custom: SlashCommandCustomOptionSchema.optional(),
238
+ description: import_zod.z.string().optional(),
239
+ name: import_zod.z.string().min(1),
240
+ options: import_zod.z.array(SlashCommandOptionSchema).optional()
241
+ }).passthrough();
242
+ var SlashCommandContributionSchema = import_zod.z.object({
243
+ args: import_zod.z.array(SlashCommandArgumentSchema).optional(),
244
+ body: import_zod.z.string().optional(),
245
+ command: import_zod.z.string().min(1),
246
+ description: import_zod.z.string().optional(),
247
+ id: import_zod.z.string().optional(),
248
+ parameters: import_zod.z.array(SlashCommandArgumentSchema).optional()
249
+ }).passthrough();
214
250
  var ContributesSchema = import_zod.z.object({
215
251
  turn_hooks: import_zod.z.array(TurnHookContributionSchema).default([]),
216
252
  /** App events this plugin EMITS — the provider half of the hook system, whose
@@ -225,7 +261,7 @@ var ContributesSchema = import_zod.z.object({
225
261
  * feature declaration before signing. */
226
262
  chat_features: import_zod.z.array(import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())).default([]),
227
263
  settings_tabs: import_zod.z.array(import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())).default([]),
228
- slash_commands: import_zod.z.array(import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())).default([]),
264
+ slash_commands: import_zod.z.array(SlashCommandContributionSchema).default([]),
229
265
  /** App widgets (Ryu Apps). Each binds a render tool id to its
230
266
  * `ui://widget/<slug>.html` template. Mirrors the Rust-side
231
267
  * `Contributes.widgets` field, without which the CLI's zod parse would strip
@@ -292,7 +328,10 @@ var ContributesSchema = import_zod.z.object({
292
328
  output_styles: import_zod.z.array(OutputStyleContributionSchema).default([]),
293
329
  /** Per-message actions contributed by an enabled plugin. Kept as loose records
294
330
  * so renderer-specific `kind`/`args` payloads survive `ryu pack` unchanged. */
295
- message_actions: import_zod.z.array(import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())).default([])
331
+ message_actions: import_zod.z.array(import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())).default([]),
332
+ /** Buttons contributed to the floating text-selection toolbar. Kept as loose
333
+ * records so host-owned dispatch args survive `ryu pack` unchanged. */
334
+ selection_actions: import_zod.z.array(import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())).default([])
296
335
  });
297
336
  var SetupStepSchema = import_zod.z.object({
298
337
  /** Card heading (e.g. the companion app name). */
@@ -442,6 +481,9 @@ var PluginManifestSchema = import_zod.z.object({
442
481
  /^\d+\.\d+\.\d+(?:-[\w.]+)?(?:\+[\w.]+)?$/,
443
482
  "version must be a valid semver string (e.g. 1.0.0)"
444
483
  ),
484
+ /** Core-owned release maturity metadata. The Rust contract validates the
485
+ * richer shape; the SDK authoring parser must preserve it for pack/publish. */
486
+ stability: import_zod.z.unknown().optional(),
445
487
  /**
446
488
  * Lower-case hex `sha256(utf8_bytes(ui_code))` binding the plugin's bundled
447
489
  * sandboxed-UI code to this manifest. `ryu pack` / `ryu publish` compute it and
@@ -459,8 +501,26 @@ var PluginManifestSchema = import_zod.z.object({
459
501
  * Declarations only — grant enforcement is the Gateway's responsibility.
460
502
  */
461
503
  permission_grants: import_zod.z.array(import_zod.z.string()).default([]),
504
+ /** Deny-by-default sandbox permissions. Core remains authoritative; this
505
+ * schema mirrors the fields so `ryu pack` cannot strip them. */
506
+ permissions: import_zod.z.object({
507
+ fs: import_zod.z.object({
508
+ read: import_zod.z.array(import_zod.z.string()).default([]),
509
+ write: import_zod.z.array(import_zod.z.string()).default([])
510
+ }).optional(),
511
+ child_process: import_zod.z.boolean().optional(),
512
+ run: import_zod.z.array(import_zod.z.string()).default([]),
513
+ network: import_zod.z.union([import_zod.z.boolean(), import_zod.z.array(import_zod.z.string())]).optional(),
514
+ tool: import_zod.z.array(import_zod.z.string()).default([])
515
+ }).optional(),
516
+ /** Core-owned permission presentation levels. Preserved verbatim here and
517
+ * validated by Core's authoritative manifest contract. */
518
+ permission_levels: import_zod.z.unknown().optional(),
462
519
  /** Remote or stdio MCP servers registered by this plugin. */
463
520
  mcp_servers: import_zod.z.record(import_zod.z.string(), McpServerDeclSchema).optional(),
521
+ /** Core-owned sidecar declarations. Their full process/HTTP schema stays in
522
+ * the Rust contract; this authoring layer must never strip them. */
523
+ sidecars: import_zod.z.unknown().optional(),
464
524
  /**
465
525
  * Optional Companion surface (an in-desktop overlay or sidebar panel).
466
526
  * Absent when the plugin has no Companion surface.
@@ -496,6 +556,9 @@ var PluginManifestSchema = import_zod.z.object({
496
556
  * an unsupported-target plugin stays installable and inspectable.
497
557
  */
498
558
  targets: import_zod.z.array(SurfaceSchema).default([]),
559
+ /** Rich per-surface declarations (`support`, UI, contributed commands, …).
560
+ * Core owns and validates the nested vocabulary. */
561
+ surfaces: import_zod.z.unknown().optional(),
499
562
  /**
500
563
  * Host version floors — the semver requirement each surface must satisfy for
501
564
  * this plugin to install. Mirrors Core's `EnginesReq`
@@ -610,7 +673,7 @@ var PluginManifestSchema = import_zod.z.object({
610
673
  * array of steps guiding the user through post-install configuration.
611
674
  */
612
675
  setup: import_zod.z.union([SetupStepSchema, import_zod.z.array(SetupStepSchema)]).optional()
613
- }).superRefine((manifest, context) => {
676
+ }).passthrough().superRefine((manifest, context) => {
614
677
  const hasOAuthServer = Object.values(manifest.mcp_servers ?? {}).some(
615
678
  (server) => server.auth?.type === "oauth"
616
679
  );
@@ -628,6 +691,168 @@ var PluginManifestSchema = import_zod.z.object({
628
691
  }
629
692
  });
630
693
 
694
+ // src/runnable/tool.ts
695
+ function validateInput(input, schema, toolId) {
696
+ if (typeof input !== "object" || input === null || Array.isArray(input)) {
697
+ throw new Error(
698
+ `[ryu-sdk] Tool "${toolId}" input must be an object, got ${JSON.stringify(input)}`
699
+ );
700
+ }
701
+ const record = input;
702
+ for (const requiredKey of schema.required ?? []) {
703
+ if (!(requiredKey in record)) {
704
+ throw new Error(
705
+ `[ryu-sdk] Tool "${toolId}" input missing required field "${requiredKey}"`
706
+ );
707
+ }
708
+ }
709
+ for (const [key, prop] of Object.entries(schema.properties)) {
710
+ if (!(key in record)) {
711
+ continue;
712
+ }
713
+ const value = record[key];
714
+ if (!checkType(value, prop.type)) {
715
+ throw new Error(
716
+ `[ryu-sdk] Tool "${toolId}" input field "${key}" expected type "${prop.type}", got ${JSON.stringify(value)}`
717
+ );
718
+ }
719
+ }
720
+ }
721
+ function checkType(value, type) {
722
+ switch (type) {
723
+ case "string":
724
+ return typeof value === "string";
725
+ case "number":
726
+ case "integer":
727
+ return typeof value === "number";
728
+ case "boolean":
729
+ return typeof value === "boolean";
730
+ case "array":
731
+ return Array.isArray(value);
732
+ case "object":
733
+ return typeof value === "object" && value !== null && !Array.isArray(value);
734
+ default:
735
+ return false;
736
+ }
737
+ }
738
+ function defineTool(options) {
739
+ const { description, id, name, schema, run } = options;
740
+ const code = `return await (${run.toString()})(input, host);`;
741
+ return {
742
+ id,
743
+ name,
744
+ kind: "tool",
745
+ schema,
746
+ ...description === void 0 ? {} : { description },
747
+ code,
748
+ run(input, ctx) {
749
+ validateInput(input, schema, id);
750
+ return run(input, ctx);
751
+ }
752
+ };
753
+ }
754
+ function inlineToolRunnable(tool, options) {
755
+ return {
756
+ id: tool.id,
757
+ name: tool.name,
758
+ kind: "tool",
759
+ config: {
760
+ slug: tool.id,
761
+ backend: "inline_deno",
762
+ code: tool.code,
763
+ input_schema: tool.schema,
764
+ ...options?.description === void 0 ? tool.description === void 0 ? {} : { description: tool.description } : { description: options.description },
765
+ ...options?.outputSchema ? { output_schema: options.outputSchema } : {},
766
+ ...options?.annotations ? { annotations: options.annotations } : {},
767
+ ...options?.action === void 0 ? {} : { action: options.action },
768
+ ...options?.needsApproval === void 0 ? {} : { needs_approval: options.needsApproval }
769
+ }
770
+ };
771
+ }
772
+
773
+ // src/runnable/action.ts
774
+ function deriveAnnotations(effect, annotations) {
775
+ const resolved = {
776
+ ...annotations,
777
+ readOnlyHint: annotations?.readOnlyHint ?? effect === "read",
778
+ destructiveHint: annotations?.destructiveHint ?? effect === "mutate"
779
+ };
780
+ if (effect === "read" && resolved.destructiveHint) {
781
+ throw new Error(
782
+ "[ryu-sdk] read actions cannot set annotations.destructiveHint=true"
783
+ );
784
+ }
785
+ if (effect === "mutate" && resolved.readOnlyHint) {
786
+ throw new Error(
787
+ "[ryu-sdk] mutate actions cannot set annotations.readOnlyHint=true"
788
+ );
789
+ }
790
+ return resolved;
791
+ }
792
+ function actionManifestEntry(action) {
793
+ const options = {
794
+ action: true,
795
+ annotations: action.annotations,
796
+ description: action.description,
797
+ needsApproval: action.needsApproval,
798
+ ...action.outputSchema ? { outputSchema: action.outputSchema } : {}
799
+ };
800
+ return inlineToolRunnable(action, options);
801
+ }
802
+ function actionToManifest(action, options) {
803
+ const grants = [.../* @__PURE__ */ new Set([...options.grants ?? [], "tool:execute"])];
804
+ const raw = {
805
+ id: options.id,
806
+ name: options.name ?? action.name,
807
+ version: options.version,
808
+ runnables: [actionManifestEntry(action)],
809
+ permission_grants: grants,
810
+ activation_events: [...options.activationEvents ?? ["*"]],
811
+ targets: [...options.targets ?? []]
812
+ };
813
+ const result = PluginManifestSchema.safeParse(raw);
814
+ if (!result.success) {
815
+ const first = result.error.issues[0];
816
+ const field = first?.path.join(".") ?? "unknown";
817
+ const message = first?.message ?? "validation failed";
818
+ throw new Error(
819
+ `[ryu-sdk] action manifest validation failed at '${field}': ${message}`
820
+ );
821
+ }
822
+ return result.data;
823
+ }
824
+ function defineAction(options) {
825
+ const annotations = deriveAnnotations(options.effect, options.annotations);
826
+ const tool = defineTool({
827
+ description: options.description,
828
+ id: options.id,
829
+ name: options.name,
830
+ run: options.run,
831
+ schema: options.schema
832
+ });
833
+ const action = {
834
+ ...tool,
835
+ action: true,
836
+ annotations,
837
+ description: options.description,
838
+ effect: options.effect,
839
+ needsApproval: options.needsApproval ?? false,
840
+ ...options.outputSchema ? { outputSchema: options.outputSchema } : {},
841
+ toManifest(manifestOptions) {
842
+ return actionToManifest(action, manifestOptions);
843
+ },
844
+ toMcpTool(context) {
845
+ return {
846
+ name: action.id,
847
+ description: action.description,
848
+ inputSchema: action.schema,
849
+ run: (input) => action.run(input, context)
850
+ };
851
+ }
852
+ };
853
+ return action;
854
+ }
855
+
631
856
  // src/runnable/agent.ts
632
857
  var CAPABILITY_SLOTS = ["rag", "memory", "tts", "stt"];
633
858
  function lowerChatSlot(chat) {
@@ -772,6 +997,13 @@ function withWidgetRenderGrant(grants, widgets) {
772
997
  }
773
998
  return out;
774
999
  }
1000
+ function withToolExecuteGrant(grants, tools) {
1001
+ const out = [...grants];
1002
+ if (tools.some((tool) => tool.runnable !== void 0) && !out.includes("tool:execute")) {
1003
+ out.push("tool:execute");
1004
+ }
1005
+ return out;
1006
+ }
775
1007
  function appToolId(server, name) {
776
1008
  return `${server}.${name}`;
777
1009
  }
@@ -786,7 +1018,9 @@ function defineApp(options) {
786
1018
  for (const spec of options.tools) {
787
1019
  const isRender = spec.accessible !== true;
788
1020
  const id = appToolId(server, spec.name);
1021
+ const executable = spec.runnable ? inlineToolRunnable(spec.runnable) : void 0;
789
1022
  const config = {
1023
+ ...executable?.config ?? {},
790
1024
  slug: id,
791
1025
  description: spec.description,
792
1026
  widget: isRender,
@@ -820,7 +1054,7 @@ function defineApp(options) {
820
1054
  hook_events: [],
821
1055
  composer_controls: [],
822
1056
  settings_tabs: [],
823
- slash_commands: [],
1057
+ slash_commands: options.slashCommands ?? [],
824
1058
  sidebar_sections: [],
825
1059
  sidebar_buttons: [],
826
1060
  dock_panels: [],
@@ -838,6 +1072,7 @@ function defineApp(options) {
838
1072
  pi_extensions: [],
839
1073
  output_styles: [],
840
1074
  message_actions: [],
1075
+ selection_actions: [],
841
1076
  widgets
842
1077
  };
843
1078
  const raw = {
@@ -851,15 +1086,15 @@ function defineApp(options) {
851
1086
  // Core gates widget promotion on declared-AND-enabled-AND-granted, and a
852
1087
  // missing grant fails as `DeniedNoGrant` — which is an `info!` log and
853
1088
  // nothing else. The widget silently renders as plain text, with no error
854
- // in the UI and nothing pointing at the manifest. Every app scaffolded
855
- // through `defineApp` hit that, because the only fix was a grant string
856
- // the templates never mention and the builder never added; the one
857
- // working example on disk hand-writes it.
1089
+ // in the UI and nothing pointing at the manifest.
858
1090
  //
859
1091
  // Added only when there is a widget to render, and unioned rather than
860
1092
  // overwritten so an author's own `grants` list survives and re-declaring
861
1093
  // it is not an error.
862
- permission_grants: withWidgetRenderGrant(options.grants ?? [], widgets),
1094
+ permission_grants: withToolExecuteGrant(
1095
+ withWidgetRenderGrant(options.grants ?? [], widgets),
1096
+ options.tools
1097
+ ),
863
1098
  activation_events: options.activationEvents ?? ["*"],
864
1099
  contributes,
865
1100
  // `targets: []` means EVERY surface, so an app that declares none is
@@ -1046,6 +1281,31 @@ var PRIMITIVE_BINDINGS = {
1046
1281
  method: "background.stop",
1047
1282
  grant: "background:control"
1048
1283
  },
1284
+ "storage.get": {
1285
+ transport: "bridge",
1286
+ method: "storage.get",
1287
+ grant: "storage:kv"
1288
+ },
1289
+ "storage.set": {
1290
+ transport: "bridge",
1291
+ method: "storage.set",
1292
+ grant: "storage:kv"
1293
+ },
1294
+ "storage.delete": {
1295
+ transport: "bridge",
1296
+ method: "storage.delete",
1297
+ grant: "storage:kv"
1298
+ },
1299
+ "storage.keys": {
1300
+ transport: "bridge",
1301
+ method: "storage.keys",
1302
+ grant: "storage:kv"
1303
+ },
1304
+ "storage.compareAndSet": {
1305
+ transport: "bridge",
1306
+ method: "storage.compareAndSet",
1307
+ grant: "storage:kv"
1308
+ },
1049
1309
  // Host-direct media data-path (the host holds the node token; returns data: URLs).
1050
1310
  "image.generate": {
1051
1311
  transport: "direct",
@@ -1088,6 +1348,13 @@ function createPrimitives(transport) {
1088
1348
  process_id: input.processId
1089
1349
  })
1090
1350
  },
1351
+ storage: {
1352
+ get: (input) => transport.bridge("storage.get", input),
1353
+ set: (input) => transport.bridge("storage.set", input).then(() => void 0),
1354
+ delete: (input) => transport.bridge("storage.delete", input).then(() => void 0),
1355
+ keys: (input) => transport.bridge("storage.keys", input ?? {}).then((value) => Array.isArray(value) ? value : []),
1356
+ compareAndSet: (input) => transport.bridge("storage.compareAndSet", input)
1357
+ },
1091
1358
  rag: {
1092
1359
  retrieve: (input) => brokerCall(transport, "rag", "retrieve", input),
1093
1360
  embed: (input) => brokerCall(transport, "rag", "embed", input),
@@ -1146,66 +1413,6 @@ function defineSkill(options) {
1146
1413
  };
1147
1414
  }
1148
1415
 
1149
- // src/runnable/tool.ts
1150
- function validateInput(input, schema, toolId) {
1151
- if (typeof input !== "object" || input === null || Array.isArray(input)) {
1152
- throw new Error(
1153
- `[ryu-sdk] Tool "${toolId}" input must be an object, got ${JSON.stringify(input)}`
1154
- );
1155
- }
1156
- const record = input;
1157
- for (const requiredKey of schema.required ?? []) {
1158
- if (!(requiredKey in record)) {
1159
- throw new Error(
1160
- `[ryu-sdk] Tool "${toolId}" input missing required field "${requiredKey}"`
1161
- );
1162
- }
1163
- }
1164
- for (const [key, prop] of Object.entries(schema.properties)) {
1165
- if (!(key in record)) {
1166
- continue;
1167
- }
1168
- const value = record[key];
1169
- if (!checkType(value, prop.type)) {
1170
- throw new Error(
1171
- `[ryu-sdk] Tool "${toolId}" input field "${key}" expected type "${prop.type}", got ${JSON.stringify(value)}`
1172
- );
1173
- }
1174
- }
1175
- }
1176
- function checkType(value, type) {
1177
- switch (type) {
1178
- case "string":
1179
- return typeof value === "string";
1180
- case "number":
1181
- case "integer":
1182
- return typeof value === "number";
1183
- case "boolean":
1184
- return typeof value === "boolean";
1185
- case "array":
1186
- return Array.isArray(value);
1187
- case "object":
1188
- return typeof value === "object" && value !== null && !Array.isArray(value);
1189
- default:
1190
- return false;
1191
- }
1192
- }
1193
- function defineTool(options) {
1194
- const { id, name, schema, run } = options;
1195
- const code = `return await (${run.toString()})(input, host);`;
1196
- return {
1197
- id,
1198
- name,
1199
- kind: "tool",
1200
- schema,
1201
- code,
1202
- run(input, ctx) {
1203
- validateInput(input, schema, id);
1204
- return run(input, ctx);
1205
- }
1206
- };
1207
- }
1208
-
1209
1416
  // src/runnable/workflow.ts
1210
1417
  function defineWorkflow(options) {
1211
1418
  const { id, name, run } = options;
@@ -1221,6 +1428,7 @@ function defineWorkflow(options) {
1221
1428
  PRIMITIVE_BINDINGS,
1222
1429
  appToolId,
1223
1430
  createPrimitives,
1431
+ defineAction,
1224
1432
  defineAgent,
1225
1433
  defineApp,
1226
1434
  defineSkill,
@@ -1,7 +1,10 @@
1
+ export { ActionAnnotations, ActionEffect, ActionManifestOptions, ActionOptions, ActionRunnable, defineAction } from './action.cjs';
1
2
  import { CapabilityReq, PluginManifest } from './manifest.cjs';
2
- import { i as Runnable, j as RunnableContext } from './tool-u-VR0fLF.cjs';
3
- 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, k as RyuPrimitives, S as SttClient, l as ToolOptions, m as ToolSchema, n as TtsClient, o as createPrimitives, p as defineTool, q as httpPrimitiveTransport } from './tool-u-VR0fLF.cjs';
4
- export { A as AppToolSpec, a as DefineAppOptions, b as appToolId, d as defineApp } from './app-DNaGmLVf.cjs';
3
+ import { j as Runnable, k as RunnableContext } from './tool-CgzW92O_.cjs';
4
+ 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, 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 } from './tool-CgzW92O_.cjs';
5
+ export { A as AppToolSpec, a as DefineAppOptions, b as appToolId, d as defineApp } from './app-B0Z9Ew_R.cjs';
6
+ import './mcp/server.cjs';
7
+ import './mcp/client.cjs';
5
8
  import 'zod';
6
9
  import './client-D5U6ssPc.cjs';
7
10
 
@@ -1,7 +1,10 @@
1
+ export { ActionAnnotations, ActionEffect, ActionManifestOptions, ActionOptions, ActionRunnable, defineAction } from './action.js';
1
2
  import { CapabilityReq, PluginManifest } from './manifest.js';
2
- import { i as Runnable, j as RunnableContext } from './tool-DSx2bFx8.js';
3
- 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, k as RyuPrimitives, S as SttClient, l as ToolOptions, m as ToolSchema, n as TtsClient, o as createPrimitives, p as defineTool, q as httpPrimitiveTransport } from './tool-DSx2bFx8.js';
4
- export { A as AppToolSpec, a as DefineAppOptions, b as appToolId, d as defineApp } from './app-Bkw7LlCK.js';
3
+ import { j as Runnable, k as RunnableContext } from './tool-AjkdFvhE.js';
4
+ 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, 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 } from './tool-AjkdFvhE.js';
5
+ export { A as AppToolSpec, a as DefineAppOptions, b as appToolId, d as defineApp } from './app-C-BDJwfG.js';
6
+ import './mcp/server.js';
7
+ import './mcp/client.js';
5
8
  import 'zod';
6
9
  import './client-D5U6ssPc.js';
7
10
 
package/dist/runnable.js CHANGED
@@ -1,24 +1,30 @@
1
1
  import {
2
2
  defineAgent,
3
3
  defineSkill,
4
- defineTool,
5
4
  defineWorkflow
6
- } from "./chunk-T5676WL2.js";
5
+ } from "./chunk-BC3A7HMO.js";
7
6
  import {
8
7
  appToolId,
9
8
  defineApp
10
- } from "./chunk-W3KPP4WN.js";
11
- import "./chunk-IEUQ3CDG.js";
9
+ } from "./chunk-SN2QBJUF.js";
12
10
  import {
13
11
  PRIMITIVE_BINDINGS,
14
12
  createPrimitives,
15
13
  httpPrimitiveTransport
16
- } from "./chunk-A3RGEPDG.js";
14
+ } from "./chunk-ZTJWBRUL.js";
15
+ import {
16
+ defineAction
17
+ } from "./chunk-4TPUZDTI.js";
18
+ import {
19
+ defineTool
20
+ } from "./chunk-QYFUNJOH.js";
21
+ import "./chunk-NZKVOSC2.js";
17
22
  import "./chunk-IKEDLLFY.js";
18
23
  export {
19
24
  PRIMITIVE_BINDINGS,
20
25
  appToolId,
21
26
  createPrimitives,
27
+ defineAction,
22
28
  defineAgent,
23
29
  defineApp,
24
30
  defineSkill,
@@ -278,6 +278,36 @@ interface BackgroundClient {
278
278
  process_id: string;
279
279
  }>;
280
280
  }
281
+ /** Durable app-owned key/value state (`storage:kv` via the host bridge). */
282
+ interface StorageClient {
283
+ /** Atomically replace a value when it still equals `expected`. */
284
+ compareAndSet(input: {
285
+ expected: string | null;
286
+ key: string;
287
+ namespace?: string;
288
+ value: string | null;
289
+ }): Promise<boolean>;
290
+ /** Delete one value. */
291
+ delete(input: {
292
+ key: string;
293
+ namespace?: string;
294
+ }): Promise<void>;
295
+ /** Read one value; missing keys return `null`. */
296
+ get(input: {
297
+ key: string;
298
+ namespace?: string;
299
+ }): Promise<string | null>;
300
+ /** List keys in an app-owned namespace. */
301
+ keys(input?: {
302
+ namespace?: string;
303
+ }): Promise<string[]>;
304
+ /** Write one string value. JSON can be encoded by the app when needed. */
305
+ set(input: {
306
+ key: string;
307
+ namespace?: string;
308
+ value: string;
309
+ }): Promise<void>;
310
+ }
281
311
  /** TTS primitive — speech synthesis (`crates/ryu-tts`). */
282
312
  interface TtsClient {
283
313
  /**
@@ -330,6 +360,7 @@ interface RyuPrimitives {
330
360
  memory: MemoryClient;
331
361
  rag: RagClient;
332
362
  realtime: RealtimeClient;
363
+ storage: StorageClient;
333
364
  stt: SttClient;
334
365
  tts: TtsClient;
335
366
  }
@@ -393,6 +424,8 @@ interface RunnableContext {
393
424
  sessionId?: string;
394
425
  /** Signal to abort a long-running run. */
395
426
  signal?: AbortSignal;
427
+ /** Durable app-owned KV state, gated by the manifest's `storage:kv` grant. */
428
+ storage?: StorageClient;
396
429
  /** STT primitive: transcribe (`crates/ryu-stt`). */
397
430
  stt?: SttClient;
398
431
  /** TTS primitive: speak (`crates/ryu-tts`). */
@@ -472,9 +505,13 @@ interface ToolSchema {
472
505
  required?: string[];
473
506
  /** Type is always "object" for a tool's top-level input schema. */
474
507
  type: "object";
508
+ /** Preserve additional JSON Schema keywords for Core/MCP consumers. */
509
+ [key: string]: unknown;
475
510
  }
476
511
  /** Options accepted by `defineTool`. */
477
512
  interface ToolOptions<TInput extends Record<string, unknown>, TOutput> {
513
+ /** Optional description surfaced to models and tool discovery. */
514
+ description?: string;
478
515
  /** Stable unique identifier (e.g. "tool-web-search"). */
479
516
  id: string;
480
517
  /** Human-readable display name. */
@@ -514,6 +551,8 @@ interface ToolRunnable<TInput extends Record<string, unknown> = Record<string, u
514
551
  * the sandbox form is the second parameter aliased to `host`.
515
552
  */
516
553
  readonly code: string;
554
+ /** Optional description surfaced to models and tool discovery. */
555
+ readonly description?: string;
517
556
  readonly kind: "tool";
518
557
  /** JSON Schema for this tool's input — compatible with Core's ToolInfo.schema. */
519
558
  readonly schema: ToolSchema;
@@ -548,6 +587,19 @@ interface ToolRunnable<TInput extends Record<string, unknown> = Record<string, u
548
587
  * ```
549
588
  */
550
589
  declare function defineTool<TInput extends Record<string, unknown> = Record<string, unknown>, TOutput = unknown>(options: ToolOptions<TInput, TOutput>): ToolRunnable<TInput, TOutput>;
590
+ /** Optional metadata lowered alongside a tool's executable backend. */
591
+ interface InlineToolManifestOptions {
592
+ /** Marks the entry as a semantic Ryu Action. */
593
+ action?: boolean;
594
+ /** MCP-style effect annotations, for example `readOnlyHint`. */
595
+ annotations?: Record<string, boolean | undefined>;
596
+ /** Description shown in Core's unified tool catalog. */
597
+ description?: string;
598
+ /** Force the human approval gate before the tool runs. */
599
+ needsApproval?: boolean;
600
+ /** JSON Schema for the structured result. */
601
+ outputSchema?: Record<string, unknown>;
602
+ }
551
603
  /**
552
604
  * Convert a {@link ToolRunnable} into a `manifest.json` `kind:"tool"` runnable that
553
605
  * ships its `run` body as Core's `inline_deno` backend. The emitted config
@@ -558,8 +610,6 @@ declare function defineTool<TInput extends Record<string, unknown> = Record<stri
558
610
  *
559
611
  * The plugin must declare the `tool:execute` grant (see `definePlugin`).
560
612
  */
561
- declare function inlineToolRunnable(tool: ToolRunnable, options?: {
562
- description?: string;
563
- }): RunnableMeta;
613
+ declare function inlineToolRunnable(tool: Pick<ToolRunnable, "code" | "description" | "id" | "name" | "schema">, options?: InlineToolManifestOptions): RunnableMeta;
564
614
 
565
- export { type BackgroundClient as B, type DurableClient as D, type EnginesClient as E, type GatewayClient as G, type HttpPrimitiveTransportOptions as H, type ImageClient as I, type JsonSchemaProperty as J, type MemoryClient as M, PRIMITIVE_BINDINGS as P, type RagChunk as R, type SttClient as S, type ToolRunnable as T, type BackgroundProcess as a, type MemoryItem as b, type PrimitiveBinding as c, type PrimitiveTransport as d, type RagClient as e, type RagRerankResult as f, type RealtimeClient as g, type RealtimeSubscription as h, type Runnable as i, type RunnableContext as j, type RyuPrimitives as k, type ToolOptions as l, type ToolSchema as m, type TtsClient as n, createPrimitives as o, defineTool as p, httpPrimitiveTransport as q, inlineToolRunnable as r };
615
+ export { type BackgroundClient as B, type DurableClient as D, type EnginesClient as E, type GatewayClient as G, type HttpPrimitiveTransportOptions as H, type ImageClient as I, type JsonSchemaProperty as J, type MemoryClient as M, PRIMITIVE_BINDINGS as P, type RagChunk as R, type StorageClient as S, type ToolRunnable as T, type BackgroundProcess as a, type InlineToolManifestOptions as b, type MemoryItem as c, type PrimitiveBinding as d, type PrimitiveTransport as e, type RagClient as f, type RagRerankResult as g, type RealtimeClient as h, type RealtimeSubscription as i, type Runnable as j, type RunnableContext as k, type RyuPrimitives as l, type SttClient as m, type ToolOptions as n, type ToolSchema as o, type TtsClient as p, createPrimitives as q, defineTool as r, httpPrimitiveTransport as s, inlineToolRunnable as t };