@ryuhq/sdk 0.0.5 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/manifest.cjs CHANGED
@@ -24,6 +24,8 @@ __export(manifest_exports, {
24
24
  CapabilityReqSchema: () => CapabilityReqSchema,
25
25
  CompanionSurfaceSchema: () => CompanionSurfaceSchema,
26
26
  ContributesSchema: () => ContributesSchema,
27
+ HookEventContributionSchema: () => HookEventContributionSchema,
28
+ PiExtensionContributionSchema: () => PiExtensionContributionSchema,
27
29
  PluginManifestSchema: () => PluginManifestSchema,
28
30
  RequiresSchema: () => RequiresSchema,
29
31
  RunnableKindSchema: () => RunnableKindSchema,
@@ -122,7 +124,47 @@ var TurnHookContributionSchema = import_zod.z.object({
122
124
  /** Turn boundary this fires on. Today only `"post_assistant_turn"`. */
123
125
  on: import_zod.z.string().min(1).default("post_assistant_turn"),
124
126
  /** The JS hook body executed in the sandbox (returns a directive). */
125
- code: import_zod.z.string().min(1)
127
+ code: import_zod.z.string().min(1).optional(),
128
+ /** Path to the hook body, relative to the plugin root (`hooks/<name>.js`). */
129
+ code_file: import_zod.z.string().min(1).optional(),
130
+ /**
131
+ * Cheap pre-gate mirroring Core's `HookMatch` (serde name `match` on
132
+ * `TurnHookContribution.run_when`). MUST round-trip through this schema:
133
+ * `ryu pack`/`publish` persist `safeParse(...).data`, so a field missing here
134
+ * is silently STRIPPED before signing — a tool-gated `pre_tool_use` hook
135
+ * (e.g. `tools: ["bash*"]`) would lose its gate and run on EVERY tool call.
136
+ */
137
+ match: import_zod.z.object({
138
+ /** Run only if the request set this composer flag true. */
139
+ flag: import_zod.z.string().optional(),
140
+ /** Run if the last user message starts with any of these prefixes. */
141
+ commands: import_zod.z.array(import_zod.z.string()).default([]),
142
+ /** Run if the plugin has stored state for this conversation. */
143
+ stateful: import_zod.z.boolean().default(false),
144
+ /** Run if `ctx.tool_name` matches any of these `*`-wildcard patterns. */
145
+ tools: import_zod.z.array(import_zod.z.string()).default([])
146
+ }).optional()
147
+ }).refine((h) => Boolean(h.code) !== Boolean(h.code_file), {
148
+ message: "a turn hook must declare exactly one of 'code' (inline body) or 'code_file' (path to hooks/<name>.js)",
149
+ path: ["code_file"]
150
+ });
151
+ var HookEventContributionSchema = import_zod.z.object({
152
+ /** Fully-qualified event id: `<plugin id>#<event name>`, e.g. `@acme/meetings#meeting.ended`. */
153
+ id: import_zod.z.string().min(1),
154
+ /** Human-readable title for the event picker. */
155
+ title: import_zod.z.string().min(1),
156
+ /** What the event means and when it fires. */
157
+ description: import_zod.z.string().optional(),
158
+ /** Example of the `ctx.event` payload. Documentation, not a validated schema. */
159
+ payload_example: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional()
160
+ });
161
+ var PiExtensionContributionSchema = import_zod.z.object({
162
+ /** Stable id for this extension within the plugin (`[a-z0-9][a-z0-9._-]*`). */
163
+ id: import_zod.z.string().min(1),
164
+ /** Path to the source, relative to the plugin root: `pi-extensions/<name>.ts`. */
165
+ file: import_zod.z.string().min(1),
166
+ /** Optional one-liner describing what the extension adds to the agent. */
167
+ description: import_zod.z.string().optional()
126
168
  });
127
169
  var DEFAULT_WIDGET_MIME = "text/html+skybridge";
128
170
  var DEFAULT_WIDGET_DISPLAY_MODE = "inline";
@@ -160,6 +202,11 @@ var ToolAppConfigSchema = import_zod.z.object({
160
202
  });
161
203
  var ContributesSchema = import_zod.z.object({
162
204
  turn_hooks: import_zod.z.array(TurnHookContributionSchema).default([]),
205
+ /** App events this plugin EMITS — the provider half of the hook system, whose
206
+ * consumer half is `turn_hooks`. Mirrors the Rust `Contributes.hook_events`;
207
+ * omitting it here would have `ryu pack` strip every declared event before
208
+ * signing, leaving an app that emits events nothing is allowed to subscribe to. */
209
+ hook_events: import_zod.z.array(HookEventContributionSchema).default([]),
163
210
  composer_controls: import_zod.z.array(import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())).default([]),
164
211
  settings_tabs: import_zod.z.array(import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())).default([]),
165
212
  slash_commands: import_zod.z.array(import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())).default([]),
@@ -167,7 +214,49 @@ var ContributesSchema = import_zod.z.object({
167
214
  * `ui://widget/<slug>.html` template. Mirrors the Rust-side
168
215
  * `Contributes.widgets` field, without which the CLI's zod parse would strip
169
216
  * every widget an app authored here declares. */
170
- widgets: import_zod.z.array(WidgetContributionSchema).default([])
217
+ widgets: import_zod.z.array(WidgetContributionSchema).default([]),
218
+ /** App-registered sidebar sections (header + live list) and buttons (single nav
219
+ * rows). Loosely typed here — the shell owns the spec vocabulary — matching how
220
+ * `composer_controls`/`settings_tabs` are declared. Mirrors the Rust-side
221
+ * `Contributes.sidebar_sections` / `Contributes.sidebar_buttons`. */
222
+ sidebar_sections: import_zod.z.array(import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())).default([]),
223
+ sidebar_buttons: import_zod.z.array(import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())).default([]),
224
+ /** App-registered workspace dock panels (a tab in the desktop's bottom/right
225
+ * dock). Loosely typed for the same reason as the surfaces above — the shell
226
+ * owns the `panel` render-mode vocabulary and the `spec` payload. Mirrors the
227
+ * Rust-side `Contributes.dock_panels`; without it the CLI's zod parse would
228
+ * strip the dock panel an app declares here. */
229
+ dock_panels: import_zod.z.array(import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())).default([]),
230
+ /** Deletable data categories the app owns — one "Delete all X" row in Settings
231
+ * → Danger Zone. Mirrors the Rust-side `Contributes.data_categories`; without
232
+ * it the CLI's zod parse would strip the declaration before signing, and the
233
+ * app's danger-zone row would simply never appear on any node that installed
234
+ * the packed bundle. Loosely typed here for the same reason as the surfaces
235
+ * above — Core is the layer that types it, because Core is the layer that has
236
+ * to resolve the id to something that can actually delete the rows. */
237
+ data_categories: import_zod.z.array(import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())).default([]),
238
+ /** Language servers the plugin declares, keyed by server name — the mirror of
239
+ * Claude Code's `.lsp.json` / `lspServers`, so a config written for either host
240
+ * loads in the other. Mirrors the Rust-side `Contributes.lsp_servers`; without
241
+ * it the CLI's zod parse would strip every language server a plugin declares,
242
+ * before the manifest is signed.
243
+ *
244
+ * The ENTRY is deliberately a loose record and not a 13-field `z.object()`
245
+ * mirroring `LspServerContribution`. Claude Code owns this field vocabulary,
246
+ * not Ryu: a typed object here would strip a field from a newer Claude release
247
+ * on its way through `ryu pack` — the same silent-deletion bug this field
248
+ * exists to fix, one level down. Core is the layer that types it, because Core
249
+ * is the layer that acts on it. */
250
+ lsp_servers: import_zod.z.record(import_zod.z.string(), import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())).default({}),
251
+ /** Pi extensions the plugin ships — TypeScript the managed `ryu` (Pi) agent
252
+ * loads at process start. Mirrors the Rust-side `Contributes.pi_extensions`;
253
+ * without it the CLI's zod parse would strip the declaration before signing,
254
+ * and the packed plugin would ship a `pi-extensions/` folder nothing loads.
255
+ *
256
+ * Typed (not a loose record) because Ryu owns this vocabulary — three fields,
257
+ * all of them Core-interpreted — unlike `lsp_servers`, whose entry shape is
258
+ * Claude Code's to extend. */
259
+ pi_extensions: import_zod.z.array(PiExtensionContributionSchema).default([])
171
260
  });
172
261
  var SetupStepSchema = import_zod.z.object({
173
262
  /** Card heading (e.g. the companion app name). */
@@ -348,6 +437,25 @@ var PluginManifestSchema = import_zod.z.object({
348
437
  license: import_zod.z.string().optional(),
349
438
  /** Square logo/icon URL for the listing card + detail header. */
350
439
  iconUrl: import_zod.z.string().optional(),
440
+ /**
441
+ * Icon-primitive id for the listing card (Ryu extension): an Iconify/icons0
442
+ * `prefix:name`, a bare Hugeicons name, or a URL, resolved by the shared `Icon`
443
+ * primitive. A monochrome GLYPH masked with the current text colour — distinct
444
+ * from `iconUrl` (a raster logo). Falls back to `iconUrl` when omitted.
445
+ */
446
+ icon: import_zod.z.string().optional(),
447
+ /**
448
+ * Dithered-gradient background for the card's icon square (Ryu extension),
449
+ * mirroring dither-kit's `DitherGradient` props. `from`/`to` are a palette-colour
450
+ * name (`green`, `blue`, `purple`, `pink`, `orange`, `red`, `grey`) or a hue
451
+ * number (0–360); `direction` is where `to` ends up. Renders behind the glyph in
452
+ * place of a flat `iconBackground`; the render layer validates + falls back.
453
+ */
454
+ iconDither: import_zod.z.object({
455
+ from: import_zod.z.union([import_zod.z.string(), import_zod.z.number()]),
456
+ to: import_zod.z.union([import_zod.z.string(), import_zod.z.number()]).optional(),
457
+ direction: import_zod.z.enum(["up", "down", "left", "right"]).optional()
458
+ }).optional(),
351
459
  /** Ordered App-Store-style screenshot gallery URLs (Ryu extension). */
352
460
  screenshots: import_zod.z.array(import_zod.z.string()).optional(),
353
461
  /** Privacy policy URL surfaced on detail (Ryu extension). */
@@ -383,6 +491,8 @@ function coreManifestJsonSchema() {
383
491
  CapabilityReqSchema,
384
492
  CompanionSurfaceSchema,
385
493
  ContributesSchema,
494
+ HookEventContributionSchema,
495
+ PiExtensionContributionSchema,
386
496
  PluginManifestSchema,
387
497
  RequiresSchema,
388
498
  RunnableKindSchema,
@@ -67,16 +67,80 @@ declare const CompanionSurfaceSchema: z.ZodObject<{
67
67
  type CompanionSurface = z.infer<typeof CompanionSurfaceSchema>;
68
68
  /**
69
69
  * A server-side chat turn hook. Mirrors `TurnHookContribution` in
70
- * `apps/core/src/plugin_manifest/mod.rs`. `code` is a JS body run in the plugin
71
- * sandbox with `ctx` + `host` in scope; it returns a directive. Authors usually
72
- * build this via `defineTurnHook` rather than writing the string by hand.
70
+ * `crates/core/kernel-contracts/src/manifest.rs`. The body is a JS fragment run in
71
+ * the plugin sandbox with `ctx` + `host` in scope; it returns a directive.
72
+ *
73
+ * It arrives one of two ways, and **exactly one** must be present:
74
+ *
75
+ * - `code_file` — the authoring form: a path to a real `hooks/<name>.js` file next
76
+ * to the manifest. Readable, lintable, diffable, and reviewable for what it
77
+ * actually does. Every first-party plugin uses this.
78
+ * - `code` — the wire form: the body inline. `ryu pack` produces it by reading
79
+ * `code_file`, which is what keeps the whole hook body INSIDE the Gateway-signed
80
+ * surface; Core also accepts it directly for a hand-written or `defineTurnHook`
81
+ * generated manifest.
73
82
  */
74
83
  declare const TurnHookContributionSchema: z.ZodObject<{
75
84
  id: z.ZodString;
76
85
  on: z.ZodDefault<z.ZodString>;
77
- code: z.ZodString;
86
+ code: z.ZodOptional<z.ZodString>;
87
+ code_file: z.ZodOptional<z.ZodString>;
88
+ match: z.ZodOptional<z.ZodObject<{
89
+ flag: z.ZodOptional<z.ZodString>;
90
+ commands: z.ZodDefault<z.ZodArray<z.ZodString>>;
91
+ stateful: z.ZodDefault<z.ZodBoolean>;
92
+ tools: z.ZodDefault<z.ZodArray<z.ZodString>>;
93
+ }, z.core.$strip>>;
78
94
  }, z.core.$strip>;
79
95
  type TurnHookContribution = z.infer<typeof TurnHookContributionSchema>;
96
+ /**
97
+ * One **app event** this plugin declares it emits. Mirrors `HookEventContribution`
98
+ * in `crates/core/kernel-contracts/src/manifest.rs`.
99
+ *
100
+ * `turn_hooks` is the *consuming* half of the hook system; this is the *providing*
101
+ * half. Declaring an event here lets any other plugin react to it with a
102
+ * `turn_hooks[].on` naming the event, and any workflow react to it with an `event`
103
+ * trigger — without the emitter knowing a consumer exists. The event is raised at
104
+ * runtime by this plugin's own sidecar calling the `events.emit` host capability.
105
+ *
106
+ * `id` MUST be `<this plugin's id>#<event name>`. Core validates the namespace half
107
+ * against the owning manifest at load and re-checks it on every emit, which is both
108
+ * what makes collisions with Core's own hook phases impossible (a Core phase never
109
+ * contains `#`) and what stops one app emitting another's events.
110
+ */
111
+ declare const HookEventContributionSchema: z.ZodObject<{
112
+ id: z.ZodString;
113
+ title: z.ZodString;
114
+ description: z.ZodOptional<z.ZodString>;
115
+ payload_example: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
116
+ }, z.core.$strip>;
117
+ type HookEventContribution = z.infer<typeof HookEventContributionSchema>;
118
+ /**
119
+ * One Pi extension the plugin ships — a TypeScript file the managed `ryu` (Pi)
120
+ * agent loads at process start. Mirrors Rust `PiExtensionContribution`.
121
+ *
122
+ * Carries a PATH, never a body: unlike `turn_hooks` there is no inline `code`
123
+ * twin, because nothing downstream reads the source as a string.
124
+ *
125
+ * That makes it a SIDECAR FILE, and `ryu pack` emits a single JSON bundle — so a
126
+ * plugin installed from a packed bundle arrives without its `pi-extensions/`
127
+ * directory and Core resolves the declaration to a visible skip. Same open gap as
128
+ * `skills/**`, which the bundle likewise does not carry. Today the path that works
129
+ * is a plugin whose directory is on disk (a built-in, a satellite checkout, a dev
130
+ * tree). Do not "fix" this by inlining the source into the manifest: a 50 KB
131
+ * TypeScript program escaped into a JSON string is the unauditable form the whole
132
+ * `code_file` extraction exists to prevent.
133
+ *
134
+ * Note this is UNSANDBOXED code: it runs inside the agent process with full host
135
+ * privilege, so Core gates it behind the operator-only `pi:extension` grant for
136
+ * any non-built-in plugin.
137
+ */
138
+ declare const PiExtensionContributionSchema: z.ZodObject<{
139
+ id: z.ZodString;
140
+ file: z.ZodString;
141
+ description: z.ZodOptional<z.ZodString>;
142
+ }, z.core.$strip>;
143
+ type PiExtensionContribution = z.infer<typeof PiExtensionContributionSchema>;
80
144
  /**
81
145
  * One app-widget contribution (Ryu Apps). Binds the render tool that produces the
82
146
  * widget to its `ui://widget/<slug>.html` template. Shape-identical to Core's
@@ -121,7 +185,20 @@ declare const ContributesSchema: z.ZodObject<{
121
185
  turn_hooks: z.ZodDefault<z.ZodArray<z.ZodObject<{
122
186
  id: z.ZodString;
123
187
  on: z.ZodDefault<z.ZodString>;
124
- code: z.ZodString;
188
+ code: z.ZodOptional<z.ZodString>;
189
+ code_file: z.ZodOptional<z.ZodString>;
190
+ match: z.ZodOptional<z.ZodObject<{
191
+ flag: z.ZodOptional<z.ZodString>;
192
+ commands: z.ZodDefault<z.ZodArray<z.ZodString>>;
193
+ stateful: z.ZodDefault<z.ZodBoolean>;
194
+ tools: z.ZodDefault<z.ZodArray<z.ZodString>>;
195
+ }, z.core.$strip>>;
196
+ }, z.core.$strip>>>;
197
+ hook_events: z.ZodDefault<z.ZodArray<z.ZodObject<{
198
+ id: z.ZodString;
199
+ title: z.ZodString;
200
+ description: z.ZodOptional<z.ZodString>;
201
+ payload_example: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
125
202
  }, z.core.$strip>>>;
126
203
  composer_controls: z.ZodDefault<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
127
204
  settings_tabs: z.ZodDefault<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
@@ -133,6 +210,16 @@ declare const ContributesSchema: z.ZodObject<{
133
210
  mime: z.ZodDefault<z.ZodString>;
134
211
  default_display_mode: z.ZodDefault<z.ZodString>;
135
212
  }, z.core.$strip>>>;
213
+ sidebar_sections: z.ZodDefault<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
214
+ sidebar_buttons: z.ZodDefault<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
215
+ dock_panels: z.ZodDefault<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
216
+ data_categories: z.ZodDefault<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
217
+ lsp_servers: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
218
+ pi_extensions: z.ZodDefault<z.ZodArray<z.ZodObject<{
219
+ id: z.ZodString;
220
+ file: z.ZodString;
221
+ description: z.ZodOptional<z.ZodString>;
222
+ }, z.core.$strip>>>;
136
223
  }, z.core.$strip>;
137
224
  type Contributes = z.infer<typeof ContributesSchema>;
138
225
  /**
@@ -218,7 +305,7 @@ declare const SurfaceSchema: z.ZodEnum<{
218
305
  }>;
219
306
  type Surface = z.infer<typeof SurfaceSchema>;
220
307
  /**
221
- * Full schema for a `plugin.json` Plugin manifest. Mirrors `PluginManifest` in
308
+ * Full schema for a `manifest.json` Plugin manifest. Mirrors `PluginManifest` in
222
309
  * `apps/core/src/plugin_manifest/mod.rs`.
223
310
  *
224
311
  * Validation rules (matching Core's `PluginManifestLoader`):
@@ -255,7 +342,20 @@ declare const PluginManifestSchema: z.ZodObject<{
255
342
  turn_hooks: z.ZodDefault<z.ZodArray<z.ZodObject<{
256
343
  id: z.ZodString;
257
344
  on: z.ZodDefault<z.ZodString>;
258
- code: z.ZodString;
345
+ code: z.ZodOptional<z.ZodString>;
346
+ code_file: z.ZodOptional<z.ZodString>;
347
+ match: z.ZodOptional<z.ZodObject<{
348
+ flag: z.ZodOptional<z.ZodString>;
349
+ commands: z.ZodDefault<z.ZodArray<z.ZodString>>;
350
+ stateful: z.ZodDefault<z.ZodBoolean>;
351
+ tools: z.ZodDefault<z.ZodArray<z.ZodString>>;
352
+ }, z.core.$strip>>;
353
+ }, z.core.$strip>>>;
354
+ hook_events: z.ZodDefault<z.ZodArray<z.ZodObject<{
355
+ id: z.ZodString;
356
+ title: z.ZodString;
357
+ description: z.ZodOptional<z.ZodString>;
358
+ payload_example: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
259
359
  }, z.core.$strip>>>;
260
360
  composer_controls: z.ZodDefault<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
261
361
  settings_tabs: z.ZodDefault<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
@@ -267,6 +367,16 @@ declare const PluginManifestSchema: z.ZodObject<{
267
367
  mime: z.ZodDefault<z.ZodString>;
268
368
  default_display_mode: z.ZodDefault<z.ZodString>;
269
369
  }, z.core.$strip>>>;
370
+ sidebar_sections: z.ZodDefault<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
371
+ sidebar_buttons: z.ZodDefault<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
372
+ dock_panels: z.ZodDefault<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
373
+ data_categories: z.ZodDefault<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
374
+ lsp_servers: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
375
+ pi_extensions: z.ZodDefault<z.ZodArray<z.ZodObject<{
376
+ id: z.ZodString;
377
+ file: z.ZodString;
378
+ description: z.ZodOptional<z.ZodString>;
379
+ }, z.core.$strip>>>;
270
380
  }, z.core.$strip>>;
271
381
  requires: z.ZodOptional<z.ZodObject<{
272
382
  apps: z.ZodDefault<z.ZodArray<z.ZodObject<{
@@ -317,6 +427,17 @@ declare const PluginManifestSchema: z.ZodObject<{
317
427
  category: z.ZodOptional<z.ZodString>;
318
428
  license: z.ZodOptional<z.ZodString>;
319
429
  iconUrl: z.ZodOptional<z.ZodString>;
430
+ icon: z.ZodOptional<z.ZodString>;
431
+ iconDither: z.ZodOptional<z.ZodObject<{
432
+ from: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
433
+ to: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
434
+ direction: z.ZodOptional<z.ZodEnum<{
435
+ up: "up";
436
+ down: "down";
437
+ left: "left";
438
+ right: "right";
439
+ }>>;
440
+ }, z.core.$strip>>;
320
441
  screenshots: z.ZodOptional<z.ZodArray<z.ZodString>>;
321
442
  privacyPolicyUrl: z.ZodOptional<z.ZodString>;
322
443
  termsOfServiceUrl: z.ZodOptional<z.ZodString>;
@@ -341,15 +462,15 @@ type PluginManifest = z.infer<typeof PluginManifestSchema>;
341
462
  */
342
463
  declare function validatePluginId(id: string): void;
343
464
  /**
344
- * Validate a full `plugin.json` string against Core's authoritative rules
465
+ * Validate a full `manifest.json` string against Core's authoritative rules
345
466
  * (id, semver, per-kind runnable config contracts). Returns the normalized
346
467
  * manifest JSON string, or throws.
347
468
  */
348
469
  declare function validateManifestStrict(manifestJson: string): string;
349
470
  /**
350
- * The Core-derived JSON Schema for a `plugin.json`, as a parsed object. Stays in
471
+ * The Core-derived JSON Schema for a `manifest.json`, as a parsed object. Stays in
351
472
  * lockstep with the Rust types because it is emitted from them.
352
473
  */
353
474
  declare function coreManifestJsonSchema(): unknown;
354
475
 
355
- export { type AppDependency, AppDependencySchema, type CapabilityReq, CapabilityReqSchema, type CompanionSurface, CompanionSurfaceSchema, type Contributes, ContributesSchema, 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 };
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 };
@@ -67,16 +67,80 @@ declare const CompanionSurfaceSchema: z.ZodObject<{
67
67
  type CompanionSurface = z.infer<typeof CompanionSurfaceSchema>;
68
68
  /**
69
69
  * A server-side chat turn hook. Mirrors `TurnHookContribution` in
70
- * `apps/core/src/plugin_manifest/mod.rs`. `code` is a JS body run in the plugin
71
- * sandbox with `ctx` + `host` in scope; it returns a directive. Authors usually
72
- * build this via `defineTurnHook` rather than writing the string by hand.
70
+ * `crates/core/kernel-contracts/src/manifest.rs`. The body is a JS fragment run in
71
+ * the plugin sandbox with `ctx` + `host` in scope; it returns a directive.
72
+ *
73
+ * It arrives one of two ways, and **exactly one** must be present:
74
+ *
75
+ * - `code_file` — the authoring form: a path to a real `hooks/<name>.js` file next
76
+ * to the manifest. Readable, lintable, diffable, and reviewable for what it
77
+ * actually does. Every first-party plugin uses this.
78
+ * - `code` — the wire form: the body inline. `ryu pack` produces it by reading
79
+ * `code_file`, which is what keeps the whole hook body INSIDE the Gateway-signed
80
+ * surface; Core also accepts it directly for a hand-written or `defineTurnHook`
81
+ * generated manifest.
73
82
  */
74
83
  declare const TurnHookContributionSchema: z.ZodObject<{
75
84
  id: z.ZodString;
76
85
  on: z.ZodDefault<z.ZodString>;
77
- code: z.ZodString;
86
+ code: z.ZodOptional<z.ZodString>;
87
+ code_file: z.ZodOptional<z.ZodString>;
88
+ match: z.ZodOptional<z.ZodObject<{
89
+ flag: z.ZodOptional<z.ZodString>;
90
+ commands: z.ZodDefault<z.ZodArray<z.ZodString>>;
91
+ stateful: z.ZodDefault<z.ZodBoolean>;
92
+ tools: z.ZodDefault<z.ZodArray<z.ZodString>>;
93
+ }, z.core.$strip>>;
78
94
  }, z.core.$strip>;
79
95
  type TurnHookContribution = z.infer<typeof TurnHookContributionSchema>;
96
+ /**
97
+ * One **app event** this plugin declares it emits. Mirrors `HookEventContribution`
98
+ * in `crates/core/kernel-contracts/src/manifest.rs`.
99
+ *
100
+ * `turn_hooks` is the *consuming* half of the hook system; this is the *providing*
101
+ * half. Declaring an event here lets any other plugin react to it with a
102
+ * `turn_hooks[].on` naming the event, and any workflow react to it with an `event`
103
+ * trigger — without the emitter knowing a consumer exists. The event is raised at
104
+ * runtime by this plugin's own sidecar calling the `events.emit` host capability.
105
+ *
106
+ * `id` MUST be `<this plugin's id>#<event name>`. Core validates the namespace half
107
+ * against the owning manifest at load and re-checks it on every emit, which is both
108
+ * what makes collisions with Core's own hook phases impossible (a Core phase never
109
+ * contains `#`) and what stops one app emitting another's events.
110
+ */
111
+ declare const HookEventContributionSchema: z.ZodObject<{
112
+ id: z.ZodString;
113
+ title: z.ZodString;
114
+ description: z.ZodOptional<z.ZodString>;
115
+ payload_example: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
116
+ }, z.core.$strip>;
117
+ type HookEventContribution = z.infer<typeof HookEventContributionSchema>;
118
+ /**
119
+ * One Pi extension the plugin ships — a TypeScript file the managed `ryu` (Pi)
120
+ * agent loads at process start. Mirrors Rust `PiExtensionContribution`.
121
+ *
122
+ * Carries a PATH, never a body: unlike `turn_hooks` there is no inline `code`
123
+ * twin, because nothing downstream reads the source as a string.
124
+ *
125
+ * That makes it a SIDECAR FILE, and `ryu pack` emits a single JSON bundle — so a
126
+ * plugin installed from a packed bundle arrives without its `pi-extensions/`
127
+ * directory and Core resolves the declaration to a visible skip. Same open gap as
128
+ * `skills/**`, which the bundle likewise does not carry. Today the path that works
129
+ * is a plugin whose directory is on disk (a built-in, a satellite checkout, a dev
130
+ * tree). Do not "fix" this by inlining the source into the manifest: a 50 KB
131
+ * TypeScript program escaped into a JSON string is the unauditable form the whole
132
+ * `code_file` extraction exists to prevent.
133
+ *
134
+ * Note this is UNSANDBOXED code: it runs inside the agent process with full host
135
+ * privilege, so Core gates it behind the operator-only `pi:extension` grant for
136
+ * any non-built-in plugin.
137
+ */
138
+ declare const PiExtensionContributionSchema: z.ZodObject<{
139
+ id: z.ZodString;
140
+ file: z.ZodString;
141
+ description: z.ZodOptional<z.ZodString>;
142
+ }, z.core.$strip>;
143
+ type PiExtensionContribution = z.infer<typeof PiExtensionContributionSchema>;
80
144
  /**
81
145
  * One app-widget contribution (Ryu Apps). Binds the render tool that produces the
82
146
  * widget to its `ui://widget/<slug>.html` template. Shape-identical to Core's
@@ -121,7 +185,20 @@ declare const ContributesSchema: z.ZodObject<{
121
185
  turn_hooks: z.ZodDefault<z.ZodArray<z.ZodObject<{
122
186
  id: z.ZodString;
123
187
  on: z.ZodDefault<z.ZodString>;
124
- code: z.ZodString;
188
+ code: z.ZodOptional<z.ZodString>;
189
+ code_file: z.ZodOptional<z.ZodString>;
190
+ match: z.ZodOptional<z.ZodObject<{
191
+ flag: z.ZodOptional<z.ZodString>;
192
+ commands: z.ZodDefault<z.ZodArray<z.ZodString>>;
193
+ stateful: z.ZodDefault<z.ZodBoolean>;
194
+ tools: z.ZodDefault<z.ZodArray<z.ZodString>>;
195
+ }, z.core.$strip>>;
196
+ }, z.core.$strip>>>;
197
+ hook_events: z.ZodDefault<z.ZodArray<z.ZodObject<{
198
+ id: z.ZodString;
199
+ title: z.ZodString;
200
+ description: z.ZodOptional<z.ZodString>;
201
+ payload_example: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
125
202
  }, z.core.$strip>>>;
126
203
  composer_controls: z.ZodDefault<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
127
204
  settings_tabs: z.ZodDefault<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
@@ -133,6 +210,16 @@ declare const ContributesSchema: z.ZodObject<{
133
210
  mime: z.ZodDefault<z.ZodString>;
134
211
  default_display_mode: z.ZodDefault<z.ZodString>;
135
212
  }, z.core.$strip>>>;
213
+ sidebar_sections: z.ZodDefault<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
214
+ sidebar_buttons: z.ZodDefault<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
215
+ dock_panels: z.ZodDefault<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
216
+ data_categories: z.ZodDefault<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
217
+ lsp_servers: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
218
+ pi_extensions: z.ZodDefault<z.ZodArray<z.ZodObject<{
219
+ id: z.ZodString;
220
+ file: z.ZodString;
221
+ description: z.ZodOptional<z.ZodString>;
222
+ }, z.core.$strip>>>;
136
223
  }, z.core.$strip>;
137
224
  type Contributes = z.infer<typeof ContributesSchema>;
138
225
  /**
@@ -218,7 +305,7 @@ declare const SurfaceSchema: z.ZodEnum<{
218
305
  }>;
219
306
  type Surface = z.infer<typeof SurfaceSchema>;
220
307
  /**
221
- * Full schema for a `plugin.json` Plugin manifest. Mirrors `PluginManifest` in
308
+ * Full schema for a `manifest.json` Plugin manifest. Mirrors `PluginManifest` in
222
309
  * `apps/core/src/plugin_manifest/mod.rs`.
223
310
  *
224
311
  * Validation rules (matching Core's `PluginManifestLoader`):
@@ -255,7 +342,20 @@ declare const PluginManifestSchema: z.ZodObject<{
255
342
  turn_hooks: z.ZodDefault<z.ZodArray<z.ZodObject<{
256
343
  id: z.ZodString;
257
344
  on: z.ZodDefault<z.ZodString>;
258
- code: z.ZodString;
345
+ code: z.ZodOptional<z.ZodString>;
346
+ code_file: z.ZodOptional<z.ZodString>;
347
+ match: z.ZodOptional<z.ZodObject<{
348
+ flag: z.ZodOptional<z.ZodString>;
349
+ commands: z.ZodDefault<z.ZodArray<z.ZodString>>;
350
+ stateful: z.ZodDefault<z.ZodBoolean>;
351
+ tools: z.ZodDefault<z.ZodArray<z.ZodString>>;
352
+ }, z.core.$strip>>;
353
+ }, z.core.$strip>>>;
354
+ hook_events: z.ZodDefault<z.ZodArray<z.ZodObject<{
355
+ id: z.ZodString;
356
+ title: z.ZodString;
357
+ description: z.ZodOptional<z.ZodString>;
358
+ payload_example: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
259
359
  }, z.core.$strip>>>;
260
360
  composer_controls: z.ZodDefault<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
261
361
  settings_tabs: z.ZodDefault<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
@@ -267,6 +367,16 @@ declare const PluginManifestSchema: z.ZodObject<{
267
367
  mime: z.ZodDefault<z.ZodString>;
268
368
  default_display_mode: z.ZodDefault<z.ZodString>;
269
369
  }, z.core.$strip>>>;
370
+ sidebar_sections: z.ZodDefault<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
371
+ sidebar_buttons: z.ZodDefault<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
372
+ dock_panels: z.ZodDefault<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
373
+ data_categories: z.ZodDefault<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
374
+ lsp_servers: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
375
+ pi_extensions: z.ZodDefault<z.ZodArray<z.ZodObject<{
376
+ id: z.ZodString;
377
+ file: z.ZodString;
378
+ description: z.ZodOptional<z.ZodString>;
379
+ }, z.core.$strip>>>;
270
380
  }, z.core.$strip>>;
271
381
  requires: z.ZodOptional<z.ZodObject<{
272
382
  apps: z.ZodDefault<z.ZodArray<z.ZodObject<{
@@ -317,6 +427,17 @@ declare const PluginManifestSchema: z.ZodObject<{
317
427
  category: z.ZodOptional<z.ZodString>;
318
428
  license: z.ZodOptional<z.ZodString>;
319
429
  iconUrl: z.ZodOptional<z.ZodString>;
430
+ icon: z.ZodOptional<z.ZodString>;
431
+ iconDither: z.ZodOptional<z.ZodObject<{
432
+ from: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
433
+ to: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
434
+ direction: z.ZodOptional<z.ZodEnum<{
435
+ up: "up";
436
+ down: "down";
437
+ left: "left";
438
+ right: "right";
439
+ }>>;
440
+ }, z.core.$strip>>;
320
441
  screenshots: z.ZodOptional<z.ZodArray<z.ZodString>>;
321
442
  privacyPolicyUrl: z.ZodOptional<z.ZodString>;
322
443
  termsOfServiceUrl: z.ZodOptional<z.ZodString>;
@@ -341,15 +462,15 @@ type PluginManifest = z.infer<typeof PluginManifestSchema>;
341
462
  */
342
463
  declare function validatePluginId(id: string): void;
343
464
  /**
344
- * Validate a full `plugin.json` string against Core's authoritative rules
465
+ * Validate a full `manifest.json` string against Core's authoritative rules
345
466
  * (id, semver, per-kind runnable config contracts). Returns the normalized
346
467
  * manifest JSON string, or throws.
347
468
  */
348
469
  declare function validateManifestStrict(manifestJson: string): string;
349
470
  /**
350
- * The Core-derived JSON Schema for a `plugin.json`, as a parsed object. Stays in
471
+ * The Core-derived JSON Schema for a `manifest.json`, as a parsed object. Stays in
351
472
  * lockstep with the Rust types because it is emitted from them.
352
473
  */
353
474
  declare function coreManifestJsonSchema(): unknown;
354
475
 
355
- export { type AppDependency, AppDependencySchema, type CapabilityReq, CapabilityReqSchema, type CompanionSurface, CompanionSurfaceSchema, type Contributes, ContributesSchema, 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 };
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 };
package/dist/manifest.js CHANGED
@@ -3,6 +3,8 @@ import {
3
3
  CapabilityReqSchema,
4
4
  CompanionSurfaceSchema,
5
5
  ContributesSchema,
6
+ HookEventContributionSchema,
7
+ PiExtensionContributionSchema,
6
8
  PluginManifestSchema,
7
9
  RequiresSchema,
8
10
  RunnableKindSchema,
@@ -16,12 +18,14 @@ import {
16
18
  labelImpersonatesSystemChrome,
17
19
  validateManifestStrict,
18
20
  validatePluginId
19
- } from "./chunk-GXHL5CO7.js";
21
+ } from "./chunk-XTUK5I6I.js";
20
22
  export {
21
23
  AppDependencySchema,
22
24
  CapabilityReqSchema,
23
25
  CompanionSurfaceSchema,
24
26
  ContributesSchema,
27
+ HookEventContributionSchema,
28
+ PiExtensionContributionSchema,
25
29
  PluginManifestSchema,
26
30
  RequiresSchema,
27
31
  RunnableKindSchema,
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@ryuhq/sdk",
3
- "version": "0.0.5",
3
+ "version": "0.1.2",
4
4
  "type": "module",
5
- "description": "Ryu developer SDK: typed builders and CLI for authoring plugin.json Plugin bundles",
5
+ "description": "Ryu developer SDK: typed builders and CLI for authoring manifest.json Plugin bundles",
6
6
  "main": "./dist/index.cjs",
7
7
  "types": "./dist/index.d.ts",
8
8
  "exports": {
@@ -44,11 +44,11 @@
44
44
  "clean": "rm -rf dist"
45
45
  },
46
46
  "dependencies": {
47
- "@ryuhq/sdk-native": "0.1.0",
47
+ "@ryuhq/sdk-native": "workspace:*",
48
48
  "zod": "^4.1.13"
49
49
  },
50
50
  "devDependencies": {
51
- "@types/bun": "^1.3.4",
51
+ "@types/bun": "catalog:",
52
52
  "json-schema-to-typescript": "^15.0.4",
53
53
  "tsup": "^8.5.1",
54
54
  "typescript": "^5"
package/src/builder.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Ryu SDK typed builders — one builder per RunnableKind plus a PluginBuilder that
3
- * assembles a complete, validated `plugin.json` manifest.
3
+ * assembles a complete, validated `manifest.json` manifest.
4
4
  *
5
5
  * Each builder follows a fluent interface: construct, chain setter calls, then
6
6
  * call `.build()` to get a validated result. Invalid manifests throw a
@@ -125,7 +125,7 @@ export const skill = () => new SkillBuilder();
125
125
  // ── PluginBuilder ─────────────────────────────────────────────────────────────
126
126
 
127
127
  /**
128
- * Fluent builder for a complete `plugin.json` Plugin manifest. Produces a
128
+ * Fluent builder for a complete `manifest.json` Plugin manifest. Produces a
129
129
  * validated `PluginManifest` on `.build()` or throws a descriptive `Error`
130
130
  * naming the first invalid field.
131
131
  *
@@ -279,7 +279,7 @@ export class PluginBuilder {
279
279
  const field = first?.path.join(".") ?? "unknown";
280
280
  const message = first?.message ?? "validation failed";
281
281
  throw new Error(
282
- `plugin.json validation failed at '${field}': ${message}`
282
+ `manifest.json validation failed at '${field}': ${message}`
283
283
  );
284
284
  }
285
285
  return result.data;
@@ -289,7 +289,7 @@ export class PluginBuilder {
289
289
  // ── AppBuilder (Ryu Apps) ─────────────────────────────────────────────────────
290
290
 
291
291
  /**
292
- * Fluent builder for a Ryu App — a `plugin.json` whose tools render interactive
292
+ * Fluent builder for a Ryu App — a `manifest.json` whose tools render interactive
293
293
  * widgets inline in chat. Delegates to {@link defineApp} on `.build()`, so it
294
294
  * derives the render-vs-companion split and validates through
295
295
  * `PluginManifestSchema` (throwing a descriptive `Error` on bad input) exactly