@ryuhq/sdk 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,7 +1,13 @@
1
1
  #!/usr/bin/env bun
2
2
  import {
3
3
  PluginManifestSchema
4
- } from "./chunk-XTUK5I6I.js";
4
+ } from "./chunk-CUY2QOFC.js";
5
+ import {
6
+ AGENT_PLUGIN_MANIFEST_FILE,
7
+ AGENT_PLUGIN_MCP_FILE,
8
+ isAgentPluginManifest,
9
+ toAgentPlugin
10
+ } from "./chunk-G6FLVEC4.js";
5
11
  import {
6
12
  ModelClient,
7
13
  resolveGatewayUrl
@@ -9,7 +15,13 @@ import {
9
15
 
10
16
  // src/cli.ts
11
17
  import { createHash } from "crypto";
12
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
18
+ import {
19
+ existsSync,
20
+ mkdirSync,
21
+ readFileSync,
22
+ rmSync,
23
+ writeFileSync
24
+ } from "fs";
13
25
  import { join, resolve } from "path";
14
26
 
15
27
  // src/cli/dev.ts
@@ -218,6 +230,8 @@ function printUsage() {
218
230
  " bunx ryu pack <dir> Validate and bundle a manifest.json Plugin",
219
231
  " bunx ryu publish <dir> Validate and publish a manifest.json Plugin to the Ryu Marketplace",
220
232
  " bunx ryu dev <entry> Run a Runnable locally with an interactive chat loop",
233
+ " bunx ryu agent-plugin <dir>",
234
+ " Emit the Agent Plugins v1 interop pair (plugin.json + mcp.json)",
221
235
  ""
222
236
  ].join("\n")
223
237
  );
@@ -232,10 +246,20 @@ var MANIFEST_FILE_NAMES = [
232
246
  "plugin.json",
233
247
  "ryu.json"
234
248
  ];
249
+ function resolveNativeManifestPath(dir) {
250
+ return MANIFEST_FILE_NAMES.map((name) => join(dir, name)).find((candidate) => {
251
+ if (!existsSync(candidate)) {
252
+ return false;
253
+ }
254
+ try {
255
+ return !isAgentPluginManifest(JSON.parse(readFileSync(candidate, "utf8")));
256
+ } catch {
257
+ return true;
258
+ }
259
+ });
260
+ }
235
261
  function loadManifest(dir) {
236
- const manifestPath = MANIFEST_FILE_NAMES.map((name) => join(dir, name)).find(
237
- (candidate) => existsSync(candidate)
238
- );
262
+ const manifestPath = resolveNativeManifestPath(dir);
239
263
  if (!manifestPath) {
240
264
  exitError(`manifest.json not found in: ${dir}`);
241
265
  }
@@ -258,7 +282,7 @@ function loadManifest(dir) {
258
282
  const message = first?.message ?? "validation failed";
259
283
  exitError(`manifest.json validation failed at '${field}': ${message}`);
260
284
  }
261
- return inlineCodeFiles(result.data, dir);
285
+ return inlineOutputStyleFiles(inlineCodeFiles(result.data, dir), dir);
262
286
  }
263
287
  var CODE_FILE_DIRS = ["hooks", "adapters"];
264
288
  var CODE_FILE_PATH = /^(hooks|adapters)\/[A-Za-z0-9_][A-Za-z0-9._-]*\.m?js$/;
@@ -300,6 +324,42 @@ function inlineCodeFiles(manifest, dir) {
300
324
  }
301
325
  return out;
302
326
  }
327
+ var MAX_OUTPUT_STYLE_BYTES = 64 * 1024;
328
+ var OUTPUT_STYLE_DIR = "output-styles";
329
+ var OUTPUT_STYLE_PATH = /^output-styles\/[A-Za-z0-9_-][A-Za-z0-9._-]*\.md$/;
330
+ function inlineOutputStyleFiles(manifest, dir) {
331
+ const out = manifest;
332
+ for (const style of out.contributes?.output_styles ?? []) {
333
+ const rel = style.file;
334
+ if (typeof rel !== "string") {
335
+ continue;
336
+ }
337
+ const label = `output style '${String(style.id)}'`;
338
+ if (!OUTPUT_STYLE_PATH.test(rel) || rel.includes("..")) {
339
+ exitError(
340
+ `${label}: file '${rel}' must be exactly '${OUTPUT_STYLE_DIR}/<name>.md' with no traversal`
341
+ );
342
+ }
343
+ let body;
344
+ try {
345
+ body = readFileSync(join(dir, rel), "utf8");
346
+ } catch (err) {
347
+ exitError(`${label}: could not read file '${rel}': ${String(err)}`);
348
+ }
349
+ if (!body.trim()) {
350
+ exitError(`${label}: file '${rel}' is empty`);
351
+ }
352
+ const bytes = Buffer.byteLength(body, "utf8");
353
+ if (bytes > MAX_OUTPUT_STYLE_BYTES) {
354
+ exitError(
355
+ `${label}: file '${rel}' is ${bytes} bytes, over the ${MAX_OUTPUT_STYLE_BYTES}-byte limit`
356
+ );
357
+ }
358
+ style.source = body;
359
+ style.file = void 0;
360
+ }
361
+ return out;
362
+ }
303
363
  function resolveUiEntry(manifest) {
304
364
  for (const runnable of manifest.runnables) {
305
365
  if (runnable.kind !== "companion") {
@@ -358,6 +418,48 @@ async function bundleUiEntry(dir, uiEntry) {
358
418
  }
359
419
  return await output.text();
360
420
  }
421
+ function emitAgentPlugin(dir) {
422
+ const manifestPath = resolveNativeManifestPath(dir);
423
+ if (!manifestPath) {
424
+ exitError(`manifest.json not found in: ${dir}`);
425
+ }
426
+ let manifest;
427
+ try {
428
+ manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
429
+ } catch (err) {
430
+ exitError(`could not read ${manifestPath}: ${String(err)}`);
431
+ }
432
+ let plugin;
433
+ let mcp;
434
+ let notes;
435
+ try {
436
+ ({ plugin, mcp, notes } = toAgentPlugin(manifest));
437
+ } catch (err) {
438
+ exitError(`could not derive ${AGENT_PLUGIN_MANIFEST_FILE}: ${String(err)}`);
439
+ }
440
+ const pluginPath = join(dir, AGENT_PLUGIN_MANIFEST_FILE);
441
+ writeFileSync(pluginPath, `${JSON.stringify(plugin, null, 2)}
442
+ `, "utf8");
443
+ const mcpPath = join(dir, AGENT_PLUGIN_MCP_FILE);
444
+ if (mcp) {
445
+ writeFileSync(mcpPath, `${JSON.stringify(mcp, null, 2)}
446
+ `, "utf8");
447
+ } else if (existsSync(mcpPath)) {
448
+ rmSync(mcpPath);
449
+ }
450
+ const emitted = mcp ? `${AGENT_PLUGIN_MANIFEST_FILE} + ${AGENT_PLUGIN_MCP_FILE}` : AGENT_PLUGIN_MANIFEST_FILE;
451
+ process.stdout.write(`agent-plugin: ${emitted} \u2192 ${dir}
452
+ `);
453
+ for (const note of notes) {
454
+ process.stdout.write(`agent-plugin: note: ${note}
455
+ `);
456
+ }
457
+ }
458
+ function commandAgentPlugin(rawDir) {
459
+ const dir = resolve(rawDir);
460
+ loadManifest(dir);
461
+ emitAgentPlugin(dir);
462
+ }
361
463
  async function commandPack(rawDir) {
362
464
  const dir = resolve(rawDir);
363
465
  const manifest = loadManifest(dir);
@@ -371,6 +473,7 @@ async function commandPack(rawDir) {
371
473
  const outPath = join(outDir, "plugin.bundle.json");
372
474
  const bundle = uiCode ? { ...manifestWithHash, ui_code: uiCode } : manifestWithHash;
373
475
  writeFileSync(outPath, JSON.stringify(bundle, null, 2), "utf8");
476
+ emitAgentPlugin(dir);
374
477
  const codeNote = uiCode ? ` (+${uiCode.length}B ui_code)` : "";
375
478
  process.stdout.write(
376
479
  `packed ${manifest.id}@${manifest.version}${codeNote} \u2192 ${outPath}
@@ -487,6 +590,14 @@ if (command === "pack") {
487
590
  commandPublish(dir).catch((err) => {
488
591
  exitError(String(err));
489
592
  });
593
+ } else if (command === "agent-plugin") {
594
+ const dir = args[0];
595
+ if (!dir) {
596
+ exitError(
597
+ "agent-plugin requires a directory argument: bunx ryu agent-plugin <dir>"
598
+ );
599
+ }
600
+ commandAgentPlugin(dir);
490
601
  } else if (command === "dev") {
491
602
  const entry = args[0];
492
603
  if (!entry) {
package/dist/index.cjs CHANGED
@@ -948,6 +948,20 @@ var PiExtensionContributionSchema = import_zod.z.object({
948
948
  /** Optional one-liner describing what the extension adds to the agent. */
949
949
  description: import_zod.z.string().optional()
950
950
  });
951
+ var OutputStyleContributionSchema = import_zod.z.object({
952
+ /** Stable id for this style within the plugin (`[a-z0-9][a-z0-9._-]*`). It is
953
+ * also the persisted selection key, so it must survive a settings key and a URL
954
+ * path. */
955
+ id: import_zod.z.string().min(1),
956
+ /** SOURCE form: path to the Markdown file, relative to the plugin root —
957
+ * exactly `output-styles/<name>.md`. `ryu pack` replaces this with `source`. */
958
+ file: import_zod.z.string().min(1).optional(),
959
+ /** WIRE form: the file's contents verbatim, frontmatter INCLUDED. The whole file
960
+ * rather than a pre-split body plus mirrored `name`/`description` keys, so a
961
+ * plugin style and a user's own `output-styles/*.md` go through one parser and
962
+ * the frontmatter stays the single source of truth for a style's metadata. */
963
+ source: import_zod.z.string().optional()
964
+ });
951
965
  var DEFAULT_WIDGET_MIME = "text/html+skybridge";
952
966
  var DEFAULT_WIDGET_DISPLAY_MODE = "inline";
953
967
  var WidgetContributionSchema = import_zod.z.object({
@@ -1038,7 +1052,15 @@ var ContributesSchema = import_zod.z.object({
1038
1052
  * Typed (not a loose record) because Ryu owns this vocabulary — three fields,
1039
1053
  * all of them Core-interpreted — unlike `lsp_servers`, whose entry shape is
1040
1054
  * Claude Code's to extend. */
1041
- pi_extensions: import_zod.z.array(PiExtensionContributionSchema).default([])
1055
+ pi_extensions: import_zod.z.array(PiExtensionContributionSchema).default([]),
1056
+ /** Output styles the plugin ships — Markdown files that rewrite the system
1057
+ * prompt's voice. Mirrors the Rust-side `Contributes.output_styles`; without it
1058
+ * the CLI's zod parse would strip the declaration, and `ryu pack` would sign a
1059
+ * bundle whose styles simply do not exist. Worse than the usual case of that
1060
+ * bug: the styles' `.md` files are not carried by the bundle either, so there
1061
+ * would be no residue to notice — the plugin would install clean and contribute
1062
+ * nothing. */
1063
+ output_styles: import_zod.z.array(OutputStyleContributionSchema).default([])
1042
1064
  });
1043
1065
  var SetupStepSchema = import_zod.z.object({
1044
1066
  /** Card heading (e.g. the companion app name). */
@@ -1335,10 +1357,13 @@ function defineApp(options) {
1335
1357
  // takes no `contributes` passthrough. An app that wants to declare language
1336
1358
  // servers writes them in a hand-authored `manifest.json`.
1337
1359
  lsp_servers: {},
1338
- // Same reason again: a danger-zone category and a Pi extension are both
1339
- // hand-authored declarations, not something derivable from runnables.
1360
+ // Same reason again: a danger-zone category, a Pi extension and an output
1361
+ // style are all hand-authored declarations, not something derivable from
1362
+ // runnables. An output style in particular points at a Markdown file next
1363
+ // to the manifest, which this builder never writes.
1340
1364
  data_categories: [],
1341
1365
  pi_extensions: [],
1366
+ output_styles: [],
1342
1367
  widgets
1343
1368
  };
1344
1369
  const raw = {
@@ -1959,15 +1984,16 @@ function definePlugin(options) {
1959
1984
  slash_commands: options.slashCommands ?? [],
1960
1985
  lsp_servers: options.lspServers ?? {},
1961
1986
  // A turn-hook plugin contributes no app widgets, sidebar entries, dock
1962
- // panels, danger-zone categories or Pi extensions; the fields are required
1963
- // on the resolved `Contributes` type (zod defaults applied), so set them
1964
- // explicitly.
1987
+ // panels, danger-zone categories, Pi extensions or output styles; the
1988
+ // fields are required on the resolved `Contributes` type (zod defaults
1989
+ // applied), so set them explicitly.
1965
1990
  widgets: [],
1966
1991
  sidebar_sections: [],
1967
1992
  sidebar_buttons: [],
1968
1993
  dock_panels: [],
1969
1994
  data_categories: [],
1970
- pi_extensions: []
1995
+ pi_extensions: [],
1996
+ output_styles: []
1971
1997
  };
1972
1998
  const tools = options.tools ?? [];
1973
1999
  const runnables = tools.map((t) => inlineToolRunnable(t));
package/dist/index.js CHANGED
@@ -12,7 +12,7 @@ import {
12
12
  coreManifestJsonSchema,
13
13
  validateManifestStrict,
14
14
  validatePluginId
15
- } from "./chunk-XTUK5I6I.js";
15
+ } from "./chunk-CUY2QOFC.js";
16
16
  import {
17
17
  Agent,
18
18
  PRIMITIVE_BINDINGS,
@@ -98,10 +98,13 @@ function defineApp(options) {
98
98
  // takes no `contributes` passthrough. An app that wants to declare language
99
99
  // servers writes them in a hand-authored `manifest.json`.
100
100
  lsp_servers: {},
101
- // Same reason again: a danger-zone category and a Pi extension are both
102
- // hand-authored declarations, not something derivable from runnables.
101
+ // Same reason again: a danger-zone category, a Pi extension and an output
102
+ // style are all hand-authored declarations, not something derivable from
103
+ // runnables. An output style in particular points at a Markdown file next
104
+ // to the manifest, which this builder never writes.
103
105
  data_categories: [],
104
106
  pi_extensions: [],
107
+ output_styles: [],
105
108
  widgets
106
109
  };
107
110
  const raw = {
@@ -722,15 +725,16 @@ function definePlugin(options) {
722
725
  slash_commands: options.slashCommands ?? [],
723
726
  lsp_servers: options.lspServers ?? {},
724
727
  // A turn-hook plugin contributes no app widgets, sidebar entries, dock
725
- // panels, danger-zone categories or Pi extensions; the fields are required
726
- // on the resolved `Contributes` type (zod defaults applied), so set them
727
- // explicitly.
728
+ // panels, danger-zone categories, Pi extensions or output styles; the
729
+ // fields are required on the resolved `Contributes` type (zod defaults
730
+ // applied), so set them explicitly.
728
731
  widgets: [],
729
732
  sidebar_sections: [],
730
733
  sidebar_buttons: [],
731
734
  dock_panels: [],
732
735
  data_categories: [],
733
- pi_extensions: []
736
+ pi_extensions: [],
737
+ output_styles: []
734
738
  };
735
739
  const tools = options.tools ?? [];
736
740
  const runnables = tools.map((t) => inlineToolRunnable(t));
package/dist/manifest.cjs CHANGED
@@ -25,6 +25,7 @@ __export(manifest_exports, {
25
25
  CompanionSurfaceSchema: () => CompanionSurfaceSchema,
26
26
  ContributesSchema: () => ContributesSchema,
27
27
  HookEventContributionSchema: () => HookEventContributionSchema,
28
+ OutputStyleContributionSchema: () => OutputStyleContributionSchema,
28
29
  PiExtensionContributionSchema: () => PiExtensionContributionSchema,
29
30
  PluginManifestSchema: () => PluginManifestSchema,
30
31
  RequiresSchema: () => RequiresSchema,
@@ -166,6 +167,20 @@ var PiExtensionContributionSchema = import_zod.z.object({
166
167
  /** Optional one-liner describing what the extension adds to the agent. */
167
168
  description: import_zod.z.string().optional()
168
169
  });
170
+ var OutputStyleContributionSchema = import_zod.z.object({
171
+ /** Stable id for this style within the plugin (`[a-z0-9][a-z0-9._-]*`). It is
172
+ * also the persisted selection key, so it must survive a settings key and a URL
173
+ * path. */
174
+ id: import_zod.z.string().min(1),
175
+ /** SOURCE form: path to the Markdown file, relative to the plugin root —
176
+ * exactly `output-styles/<name>.md`. `ryu pack` replaces this with `source`. */
177
+ file: import_zod.z.string().min(1).optional(),
178
+ /** WIRE form: the file's contents verbatim, frontmatter INCLUDED. The whole file
179
+ * rather than a pre-split body plus mirrored `name`/`description` keys, so a
180
+ * plugin style and a user's own `output-styles/*.md` go through one parser and
181
+ * the frontmatter stays the single source of truth for a style's metadata. */
182
+ source: import_zod.z.string().optional()
183
+ });
169
184
  var DEFAULT_WIDGET_MIME = "text/html+skybridge";
170
185
  var DEFAULT_WIDGET_DISPLAY_MODE = "inline";
171
186
  var WidgetContributionSchema = import_zod.z.object({
@@ -256,7 +271,15 @@ var ContributesSchema = import_zod.z.object({
256
271
  * Typed (not a loose record) because Ryu owns this vocabulary — three fields,
257
272
  * all of them Core-interpreted — unlike `lsp_servers`, whose entry shape is
258
273
  * Claude Code's to extend. */
259
- pi_extensions: import_zod.z.array(PiExtensionContributionSchema).default([])
274
+ pi_extensions: import_zod.z.array(PiExtensionContributionSchema).default([]),
275
+ /** Output styles the plugin ships — Markdown files that rewrite the system
276
+ * prompt's voice. Mirrors the Rust-side `Contributes.output_styles`; without it
277
+ * the CLI's zod parse would strip the declaration, and `ryu pack` would sign a
278
+ * bundle whose styles simply do not exist. Worse than the usual case of that
279
+ * bug: the styles' `.md` files are not carried by the bundle either, so there
280
+ * would be no residue to notice — the plugin would install clean and contribute
281
+ * nothing. */
282
+ output_styles: import_zod.z.array(OutputStyleContributionSchema).default([])
260
283
  });
261
284
  var SetupStepSchema = import_zod.z.object({
262
285
  /** Card heading (e.g. the companion app name). */
@@ -492,6 +515,7 @@ function coreManifestJsonSchema() {
492
515
  CompanionSurfaceSchema,
493
516
  ContributesSchema,
494
517
  HookEventContributionSchema,
518
+ OutputStyleContributionSchema,
495
519
  PiExtensionContributionSchema,
496
520
  PluginManifestSchema,
497
521
  RequiresSchema,
@@ -141,6 +141,31 @@ declare const PiExtensionContributionSchema: z.ZodObject<{
141
141
  description: z.ZodOptional<z.ZodString>;
142
142
  }, z.core.$strip>;
143
143
  type PiExtensionContribution = z.infer<typeof PiExtensionContributionSchema>;
144
+ /**
145
+ * One output style the plugin ships — a Markdown file (YAML frontmatter + prose)
146
+ * that rewrites the system prompt's voice for a turn. Mirrors the Rust-side
147
+ * `OutputStyleContribution`.
148
+ *
149
+ * Unlike `pi_extensions` above, this one is INLINED by `ryu pack`: `file` is the
150
+ * source form and `source` is the wire form, exactly as `code_file` → `code`. That
151
+ * is why both fields exist here and only `file` exists there — a style body is
152
+ * prose nothing evaluates, so inlining it costs no auditability (the whole point of
153
+ * keeping `pi-extensions/*.ts` out of the manifest), and it is what keeps the body
154
+ * inside the Gateway-signed surface instead of relying on a directory the installed
155
+ * plugin does not carry.
156
+ *
157
+ * Typed rather than a loose record for the same reason `pi_extensions` is: three
158
+ * fields, all of them Ryu's own vocabulary. Deliberately NOT refined to
159
+ * "exactly one of `file` / `source`" — Core's `Contributes::validate_output_styles`
160
+ * is the single gate for that rule, and a second copy here is a place the two can
161
+ * disagree about a manifest that has already been hydrated once.
162
+ */
163
+ declare const OutputStyleContributionSchema: z.ZodObject<{
164
+ id: z.ZodString;
165
+ file: z.ZodOptional<z.ZodString>;
166
+ source: z.ZodOptional<z.ZodString>;
167
+ }, z.core.$strip>;
168
+ type OutputStyleContribution = z.infer<typeof OutputStyleContributionSchema>;
144
169
  /**
145
170
  * One app-widget contribution (Ryu Apps). Binds the render tool that produces the
146
171
  * widget to its `ui://widget/<slug>.html` template. Shape-identical to Core's
@@ -220,6 +245,11 @@ declare const ContributesSchema: z.ZodObject<{
220
245
  file: z.ZodString;
221
246
  description: z.ZodOptional<z.ZodString>;
222
247
  }, z.core.$strip>>>;
248
+ output_styles: z.ZodDefault<z.ZodArray<z.ZodObject<{
249
+ id: z.ZodString;
250
+ file: z.ZodOptional<z.ZodString>;
251
+ source: z.ZodOptional<z.ZodString>;
252
+ }, z.core.$strip>>>;
223
253
  }, z.core.$strip>;
224
254
  type Contributes = z.infer<typeof ContributesSchema>;
225
255
  /**
@@ -377,6 +407,11 @@ declare const PluginManifestSchema: z.ZodObject<{
377
407
  file: z.ZodString;
378
408
  description: z.ZodOptional<z.ZodString>;
379
409
  }, z.core.$strip>>>;
410
+ output_styles: z.ZodDefault<z.ZodArray<z.ZodObject<{
411
+ id: z.ZodString;
412
+ file: z.ZodOptional<z.ZodString>;
413
+ source: z.ZodOptional<z.ZodString>;
414
+ }, z.core.$strip>>>;
380
415
  }, z.core.$strip>>;
381
416
  requires: z.ZodOptional<z.ZodObject<{
382
417
  apps: z.ZodDefault<z.ZodArray<z.ZodObject<{
@@ -473,4 +508,4 @@ declare function validateManifestStrict(manifestJson: string): string;
473
508
  */
474
509
  declare function coreManifestJsonSchema(): unknown;
475
510
 
476
- export { type AppDependency, AppDependencySchema, type CapabilityReq, CapabilityReqSchema, type CompanionSurface, CompanionSurfaceSchema, type Contributes, ContributesSchema, type HookEventContribution, HookEventContributionSchema, type PiExtensionContribution, PiExtensionContributionSchema, type PluginManifest, PluginManifestSchema, type Requires, RequiresSchema, type RunnableKind, RunnableKindSchema, type RunnableMeta, RunnableMetaSchema, type SetupStep, SetupStepSchema, type Surface, SurfaceSchema, type ToolAppConfig, ToolAppConfigSchema, type TurnHookContribution, TurnHookContributionSchema, type WidgetContribution, WidgetContributionSchema, coreManifestJsonSchema, labelImpersonatesSystemChrome, validateManifestStrict, validatePluginId };
511
+ export { type AppDependency, AppDependencySchema, type CapabilityReq, CapabilityReqSchema, type CompanionSurface, CompanionSurfaceSchema, type Contributes, ContributesSchema, type HookEventContribution, HookEventContributionSchema, type OutputStyleContribution, OutputStyleContributionSchema, type PiExtensionContribution, PiExtensionContributionSchema, type PluginManifest, PluginManifestSchema, type Requires, RequiresSchema, type RunnableKind, RunnableKindSchema, type RunnableMeta, RunnableMetaSchema, type SetupStep, SetupStepSchema, type Surface, SurfaceSchema, type ToolAppConfig, ToolAppConfigSchema, type TurnHookContribution, TurnHookContributionSchema, type WidgetContribution, WidgetContributionSchema, coreManifestJsonSchema, labelImpersonatesSystemChrome, validateManifestStrict, validatePluginId };
@@ -141,6 +141,31 @@ declare const PiExtensionContributionSchema: z.ZodObject<{
141
141
  description: z.ZodOptional<z.ZodString>;
142
142
  }, z.core.$strip>;
143
143
  type PiExtensionContribution = z.infer<typeof PiExtensionContributionSchema>;
144
+ /**
145
+ * One output style the plugin ships — a Markdown file (YAML frontmatter + prose)
146
+ * that rewrites the system prompt's voice for a turn. Mirrors the Rust-side
147
+ * `OutputStyleContribution`.
148
+ *
149
+ * Unlike `pi_extensions` above, this one is INLINED by `ryu pack`: `file` is the
150
+ * source form and `source` is the wire form, exactly as `code_file` → `code`. That
151
+ * is why both fields exist here and only `file` exists there — a style body is
152
+ * prose nothing evaluates, so inlining it costs no auditability (the whole point of
153
+ * keeping `pi-extensions/*.ts` out of the manifest), and it is what keeps the body
154
+ * inside the Gateway-signed surface instead of relying on a directory the installed
155
+ * plugin does not carry.
156
+ *
157
+ * Typed rather than a loose record for the same reason `pi_extensions` is: three
158
+ * fields, all of them Ryu's own vocabulary. Deliberately NOT refined to
159
+ * "exactly one of `file` / `source`" — Core's `Contributes::validate_output_styles`
160
+ * is the single gate for that rule, and a second copy here is a place the two can
161
+ * disagree about a manifest that has already been hydrated once.
162
+ */
163
+ declare const OutputStyleContributionSchema: z.ZodObject<{
164
+ id: z.ZodString;
165
+ file: z.ZodOptional<z.ZodString>;
166
+ source: z.ZodOptional<z.ZodString>;
167
+ }, z.core.$strip>;
168
+ type OutputStyleContribution = z.infer<typeof OutputStyleContributionSchema>;
144
169
  /**
145
170
  * One app-widget contribution (Ryu Apps). Binds the render tool that produces the
146
171
  * widget to its `ui://widget/<slug>.html` template. Shape-identical to Core's
@@ -220,6 +245,11 @@ declare const ContributesSchema: z.ZodObject<{
220
245
  file: z.ZodString;
221
246
  description: z.ZodOptional<z.ZodString>;
222
247
  }, z.core.$strip>>>;
248
+ output_styles: z.ZodDefault<z.ZodArray<z.ZodObject<{
249
+ id: z.ZodString;
250
+ file: z.ZodOptional<z.ZodString>;
251
+ source: z.ZodOptional<z.ZodString>;
252
+ }, z.core.$strip>>>;
223
253
  }, z.core.$strip>;
224
254
  type Contributes = z.infer<typeof ContributesSchema>;
225
255
  /**
@@ -377,6 +407,11 @@ declare const PluginManifestSchema: z.ZodObject<{
377
407
  file: z.ZodString;
378
408
  description: z.ZodOptional<z.ZodString>;
379
409
  }, z.core.$strip>>>;
410
+ output_styles: z.ZodDefault<z.ZodArray<z.ZodObject<{
411
+ id: z.ZodString;
412
+ file: z.ZodOptional<z.ZodString>;
413
+ source: z.ZodOptional<z.ZodString>;
414
+ }, z.core.$strip>>>;
380
415
  }, z.core.$strip>>;
381
416
  requires: z.ZodOptional<z.ZodObject<{
382
417
  apps: z.ZodDefault<z.ZodArray<z.ZodObject<{
@@ -473,4 +508,4 @@ declare function validateManifestStrict(manifestJson: string): string;
473
508
  */
474
509
  declare function coreManifestJsonSchema(): unknown;
475
510
 
476
- export { type AppDependency, AppDependencySchema, type CapabilityReq, CapabilityReqSchema, type CompanionSurface, CompanionSurfaceSchema, type Contributes, ContributesSchema, type HookEventContribution, HookEventContributionSchema, type PiExtensionContribution, PiExtensionContributionSchema, type PluginManifest, PluginManifestSchema, type Requires, RequiresSchema, type RunnableKind, RunnableKindSchema, type RunnableMeta, RunnableMetaSchema, type SetupStep, SetupStepSchema, type Surface, SurfaceSchema, type ToolAppConfig, ToolAppConfigSchema, type TurnHookContribution, TurnHookContributionSchema, type WidgetContribution, WidgetContributionSchema, coreManifestJsonSchema, labelImpersonatesSystemChrome, validateManifestStrict, validatePluginId };
511
+ export { type AppDependency, AppDependencySchema, type CapabilityReq, CapabilityReqSchema, type CompanionSurface, CompanionSurfaceSchema, type Contributes, ContributesSchema, type HookEventContribution, HookEventContributionSchema, type OutputStyleContribution, OutputStyleContributionSchema, type PiExtensionContribution, PiExtensionContributionSchema, type PluginManifest, PluginManifestSchema, type Requires, RequiresSchema, type RunnableKind, RunnableKindSchema, type RunnableMeta, RunnableMetaSchema, type SetupStep, SetupStepSchema, type Surface, SurfaceSchema, type ToolAppConfig, ToolAppConfigSchema, type TurnHookContribution, TurnHookContributionSchema, type WidgetContribution, WidgetContributionSchema, coreManifestJsonSchema, labelImpersonatesSystemChrome, validateManifestStrict, validatePluginId };
package/dist/manifest.js CHANGED
@@ -4,6 +4,7 @@ import {
4
4
  CompanionSurfaceSchema,
5
5
  ContributesSchema,
6
6
  HookEventContributionSchema,
7
+ OutputStyleContributionSchema,
7
8
  PiExtensionContributionSchema,
8
9
  PluginManifestSchema,
9
10
  RequiresSchema,
@@ -18,13 +19,14 @@ import {
18
19
  labelImpersonatesSystemChrome,
19
20
  validateManifestStrict,
20
21
  validatePluginId
21
- } from "./chunk-XTUK5I6I.js";
22
+ } from "./chunk-CUY2QOFC.js";
22
23
  export {
23
24
  AppDependencySchema,
24
25
  CapabilityReqSchema,
25
26
  CompanionSurfaceSchema,
26
27
  ContributesSchema,
27
28
  HookEventContributionSchema,
29
+ OutputStyleContributionSchema,
28
30
  PiExtensionContributionSchema,
29
31
  PluginManifestSchema,
30
32
  RequiresSchema,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ryuhq/sdk",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "type": "module",
5
5
  "description": "Ryu developer SDK: typed builders and CLI for authoring manifest.json Plugin bundles",
6
6
  "main": "./dist/index.cjs",
@@ -16,6 +16,11 @@
16
16
  "import": "./dist/manifest.js",
17
17
  "require": "./dist/manifest.cjs"
18
18
  },
19
+ "./agent-plugin": {
20
+ "types": "./dist/agent-plugin.d.ts",
21
+ "import": "./dist/agent-plugin.js",
22
+ "require": "./dist/agent-plugin.cjs"
23
+ },
19
24
  "./agent": {
20
25
  "types": "./dist/agent.d.ts",
21
26
  "import": "./dist/agent.js",
@@ -44,7 +49,7 @@
44
49
  "clean": "rm -rf dist"
45
50
  },
46
51
  "dependencies": {
47
- "@ryuhq/sdk-native": "0.1.3",
52
+ "@ryuhq/sdk-native": "0.1.5",
48
53
  "zod": "^4.1.13"
49
54
  },
50
55
  "devDependencies": {