@ryuhq/sdk 0.0.5 → 0.0.17

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/index.cjs CHANGED
@@ -194,7 +194,10 @@ function dataUrlToBytes(dataUrl) {
194
194
  }
195
195
  return { bytes, mediaType };
196
196
  }
197
- return { bytes: new TextEncoder().encode(decodeURIComponent(payload)), mediaType };
197
+ return {
198
+ bytes: new TextEncoder().encode(decodeURIComponent(payload)),
199
+ mediaType
200
+ };
198
201
  }
199
202
  function bytesToDataUrl(bytes, mediaType) {
200
203
  let binary = "";
@@ -903,7 +906,39 @@ var TurnHookContributionSchema = import_zod.z.object({
903
906
  /** Turn boundary this fires on. Today only `"post_assistant_turn"`. */
904
907
  on: import_zod.z.string().min(1).default("post_assistant_turn"),
905
908
  /** The JS hook body executed in the sandbox (returns a directive). */
906
- code: import_zod.z.string().min(1)
909
+ code: import_zod.z.string().min(1).optional(),
910
+ /** Path to the hook body, relative to the plugin root (`hooks/<name>.js`). */
911
+ code_file: import_zod.z.string().min(1).optional(),
912
+ /**
913
+ * Cheap pre-gate mirroring Core's `HookMatch` (serde name `match` on
914
+ * `TurnHookContribution.run_when`). MUST round-trip through this schema:
915
+ * `ryu pack`/`publish` persist `safeParse(...).data`, so a field missing here
916
+ * is silently STRIPPED before signing — a tool-gated `pre_tool_use` hook
917
+ * (e.g. `tools: ["bash*"]`) would lose its gate and run on EVERY tool call.
918
+ */
919
+ match: import_zod.z.object({
920
+ /** Run only if the request set this composer flag true. */
921
+ flag: import_zod.z.string().optional(),
922
+ /** Run if the last user message starts with any of these prefixes. */
923
+ commands: import_zod.z.array(import_zod.z.string()).default([]),
924
+ /** Run if the plugin has stored state for this conversation. */
925
+ stateful: import_zod.z.boolean().default(false),
926
+ /** Run if `ctx.tool_name` matches any of these `*`-wildcard patterns. */
927
+ tools: import_zod.z.array(import_zod.z.string()).default([])
928
+ }).optional()
929
+ }).refine((h) => Boolean(h.code) !== Boolean(h.code_file), {
930
+ message: "a turn hook must declare exactly one of 'code' (inline body) or 'code_file' (path to hooks/<name>.js)",
931
+ path: ["code_file"]
932
+ });
933
+ var HookEventContributionSchema = import_zod.z.object({
934
+ /** Fully-qualified event id: `<plugin id>#<event name>`, e.g. `@acme/meetings#meeting.ended`. */
935
+ id: import_zod.z.string().min(1),
936
+ /** Human-readable title for the event picker. */
937
+ title: import_zod.z.string().min(1),
938
+ /** What the event means and when it fires. */
939
+ description: import_zod.z.string().optional(),
940
+ /** Example of the `ctx.event` payload. Documentation, not a validated schema. */
941
+ payload_example: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional()
907
942
  });
908
943
  var DEFAULT_WIDGET_MIME = "text/html+skybridge";
909
944
  var DEFAULT_WIDGET_DISPLAY_MODE = "inline";
@@ -941,6 +976,11 @@ var ToolAppConfigSchema = import_zod.z.object({
941
976
  });
942
977
  var ContributesSchema = import_zod.z.object({
943
978
  turn_hooks: import_zod.z.array(TurnHookContributionSchema).default([]),
979
+ /** App events this plugin EMITS — the provider half of the hook system, whose
980
+ * consumer half is `turn_hooks`. Mirrors the Rust `Contributes.hook_events`;
981
+ * omitting it here would have `ryu pack` strip every declared event before
982
+ * signing, leaving an app that emits events nothing is allowed to subscribe to. */
983
+ hook_events: import_zod.z.array(HookEventContributionSchema).default([]),
944
984
  composer_controls: import_zod.z.array(import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())).default([]),
945
985
  settings_tabs: import_zod.z.array(import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())).default([]),
946
986
  slash_commands: import_zod.z.array(import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())).default([]),
@@ -948,7 +988,32 @@ var ContributesSchema = import_zod.z.object({
948
988
  * `ui://widget/<slug>.html` template. Mirrors the Rust-side
949
989
  * `Contributes.widgets` field, without which the CLI's zod parse would strip
950
990
  * every widget an app authored here declares. */
951
- widgets: import_zod.z.array(WidgetContributionSchema).default([])
991
+ widgets: import_zod.z.array(WidgetContributionSchema).default([]),
992
+ /** App-registered sidebar sections (header + live list) and buttons (single nav
993
+ * rows). Loosely typed here — the shell owns the spec vocabulary — matching how
994
+ * `composer_controls`/`settings_tabs` are declared. Mirrors the Rust-side
995
+ * `Contributes.sidebar_sections` / `Contributes.sidebar_buttons`. */
996
+ sidebar_sections: import_zod.z.array(import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())).default([]),
997
+ sidebar_buttons: import_zod.z.array(import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())).default([]),
998
+ /** App-registered workspace dock panels (a tab in the desktop's bottom/right
999
+ * dock). Loosely typed for the same reason as the surfaces above — the shell
1000
+ * owns the `panel` render-mode vocabulary and the `spec` payload. Mirrors the
1001
+ * Rust-side `Contributes.dock_panels`; without it the CLI's zod parse would
1002
+ * strip the dock panel an app declares here. */
1003
+ dock_panels: import_zod.z.array(import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())).default([]),
1004
+ /** Language servers the plugin declares, keyed by server name — the mirror of
1005
+ * Claude Code's `.lsp.json` / `lspServers`, so a config written for either host
1006
+ * loads in the other. Mirrors the Rust-side `Contributes.lsp_servers`; without
1007
+ * it the CLI's zod parse would strip every language server a plugin declares,
1008
+ * before the manifest is signed.
1009
+ *
1010
+ * The ENTRY is deliberately a loose record and not a 13-field `z.object()`
1011
+ * mirroring `LspServerContribution`. Claude Code owns this field vocabulary,
1012
+ * not Ryu: a typed object here would strip a field from a newer Claude release
1013
+ * on its way through `ryu pack` — the same silent-deletion bug this field
1014
+ * exists to fix, one level down. Core is the layer that types it, because Core
1015
+ * is the layer that acts on it. */
1016
+ lsp_servers: import_zod.z.record(import_zod.z.string(), import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())).default({})
952
1017
  });
953
1018
  var SetupStepSchema = import_zod.z.object({
954
1019
  /** Card heading (e.g. the companion app name). */
@@ -1129,6 +1194,25 @@ var PluginManifestSchema = import_zod.z.object({
1129
1194
  license: import_zod.z.string().optional(),
1130
1195
  /** Square logo/icon URL for the listing card + detail header. */
1131
1196
  iconUrl: import_zod.z.string().optional(),
1197
+ /**
1198
+ * Icon-primitive id for the listing card (Ryu extension): an Iconify/icons0
1199
+ * `prefix:name`, a bare Hugeicons name, or a URL, resolved by the shared `Icon`
1200
+ * primitive. A monochrome GLYPH masked with the current text colour — distinct
1201
+ * from `iconUrl` (a raster logo). Falls back to `iconUrl` when omitted.
1202
+ */
1203
+ icon: import_zod.z.string().optional(),
1204
+ /**
1205
+ * Dithered-gradient background for the card's icon square (Ryu extension),
1206
+ * mirroring dither-kit's `DitherGradient` props. `from`/`to` are a palette-colour
1207
+ * name (`green`, `blue`, `purple`, `pink`, `orange`, `red`, `grey`) or a hue
1208
+ * number (0–360); `direction` is where `to` ends up. Renders behind the glyph in
1209
+ * place of a flat `iconBackground`; the render layer validates + falls back.
1210
+ */
1211
+ iconDither: import_zod.z.object({
1212
+ from: import_zod.z.union([import_zod.z.string(), import_zod.z.number()]),
1213
+ to: import_zod.z.union([import_zod.z.string(), import_zod.z.number()]).optional(),
1214
+ direction: import_zod.z.enum(["up", "down", "left", "right"]).optional()
1215
+ }).optional(),
1132
1216
  /** Ordered App-Store-style screenshot gallery URLs (Ryu extension). */
1133
1217
  screenshots: import_zod.z.array(import_zod.z.string()).optional(),
1134
1218
  /** Privacy policy URL surfaced on detail (Ryu extension). */
@@ -1162,6 +1246,14 @@ function coreManifestJsonSchema() {
1162
1246
  // src/runnable/app.ts
1163
1247
  var DEFAULT_APP_WIDGET_MIME = "text/html+skybridge";
1164
1248
  var DEFAULT_APP_DISPLAY_MODE = "inline";
1249
+ var WIDGET_RENDER_GRANT = "widget:render";
1250
+ function withWidgetRenderGrant(grants, widgets) {
1251
+ const out = [...grants];
1252
+ if (widgets.length > 0 && !out.includes(WIDGET_RENDER_GRANT)) {
1253
+ out.push(WIDGET_RENDER_GRANT);
1254
+ }
1255
+ return out;
1256
+ }
1165
1257
  function appToolId(server, name) {
1166
1258
  return `${server}__${name}`;
1167
1259
  }
@@ -1203,9 +1295,21 @@ function defineApp(options) {
1203
1295
  }
1204
1296
  const contributes = {
1205
1297
  turn_hooks: [],
1298
+ // This builder synthesises an app from its runnables; an app that emits
1299
+ // events declares them in a hand-authored `manifest.json`, same as
1300
+ // `lsp_servers` below.
1301
+ hook_events: [],
1206
1302
  composer_controls: [],
1207
1303
  settings_tabs: [],
1208
1304
  slash_commands: [],
1305
+ sidebar_sections: [],
1306
+ sidebar_buttons: [],
1307
+ dock_panels: [],
1308
+ // Empty for the same reason as every sibling family above: this builder
1309
+ // synthesises `widgets` from the app's own runnables and nothing else, and
1310
+ // takes no `contributes` passthrough. An app that wants to declare language
1311
+ // servers writes them in a hand-authored `manifest.json`.
1312
+ lsp_servers: {},
1209
1313
  widgets
1210
1314
  };
1211
1315
  const raw = {
@@ -1213,7 +1317,21 @@ function defineApp(options) {
1213
1317
  name: options.title,
1214
1318
  version: options.version,
1215
1319
  runnables,
1216
- permission_grants: options.grants ?? [],
1320
+ // An app that synthesises widgets MUST hold `widget:render`, so this
1321
+ // builder declares it rather than leaving the author to discover it.
1322
+ //
1323
+ // Core gates widget promotion on declared-AND-enabled-AND-granted, and a
1324
+ // missing grant fails as `DeniedNoGrant` — which is an `info!` log and
1325
+ // nothing else. The widget silently renders as plain text, with no error
1326
+ // in the UI and nothing pointing at the manifest. Every app scaffolded
1327
+ // through `defineApp` hit that, because the only fix was a grant string
1328
+ // the templates never mention and the builder never added; the one
1329
+ // working example on disk hand-writes it.
1330
+ //
1331
+ // Added only when there is a widget to render, and unioned rather than
1332
+ // overwritten so an author's own `grants` list survives and re-declaring
1333
+ // it is not an error.
1334
+ permission_grants: withWidgetRenderGrant(options.grants ?? [], widgets),
1217
1335
  activation_events: options.activationEvents ?? ["*"],
1218
1336
  contributes,
1219
1337
  // `targets: []` means EVERY surface, so an app that declares none is
@@ -1235,7 +1353,9 @@ function defineApp(options) {
1235
1353
  const first = result.error.issues[0];
1236
1354
  const field = first?.path.join(".") ?? "unknown";
1237
1355
  const message = first?.message ?? "validation failed";
1238
- throw new Error(`plugin.json validation failed at '${field}': ${message}`);
1356
+ throw new Error(
1357
+ `manifest.json validation failed at '${field}': ${message}`
1358
+ );
1239
1359
  }
1240
1360
  return result.data;
1241
1361
  }
@@ -1431,7 +1551,7 @@ var PluginBuilder = class {
1431
1551
  const field = first?.path.join(".") ?? "unknown";
1432
1552
  const message = first?.message ?? "validation failed";
1433
1553
  throw new Error(
1434
- `plugin.json validation failed at '${field}': ${message}`
1554
+ `manifest.json validation failed at '${field}': ${message}`
1435
1555
  );
1436
1556
  }
1437
1557
  return result.data;
@@ -1804,12 +1924,18 @@ function defineTurnHook(options) {
1804
1924
  function definePlugin(options) {
1805
1925
  const contributes = {
1806
1926
  turn_hooks: options.turnHooks ?? [],
1927
+ hook_events: options.hookEvents ?? [],
1807
1928
  composer_controls: options.composerControls ?? [],
1808
1929
  settings_tabs: options.settingsTabs ?? [],
1809
1930
  slash_commands: options.slashCommands ?? [],
1810
- // A turn-hook plugin contributes no app widgets; the field is required on the
1811
- // resolved `Contributes` type (zod default applied), so set it explicitly.
1812
- widgets: []
1931
+ lsp_servers: options.lspServers ?? {},
1932
+ // A turn-hook plugin contributes no app widgets, sidebar entries or dock
1933
+ // panels; the fields are required on the resolved `Contributes` type (zod
1934
+ // defaults applied), so set them explicitly.
1935
+ widgets: [],
1936
+ sidebar_sections: [],
1937
+ sidebar_buttons: [],
1938
+ dock_panels: []
1813
1939
  };
1814
1940
  const tools = options.tools ?? [];
1815
1941
  const runnables = tools.map((t) => inlineToolRunnable(t));
package/dist/index.d.cts CHANGED
@@ -1,6 +1,6 @@
1
- import { R as Runnable, a as RunnableContext, T as ToolRunnable } from './index-CEbS1SlS.cjs';
2
- export { A as Agent, b as AgentConfig, c as AgentEvent, d as AgentTool, C as ChatDelta, e as ChatMessage, f as ChatResult, D as DurableClient, E as Endpoint, g as EnginesClient, G as GatewayClient, h as GenerateResult, H as HttpPrimitiveTransportOptions, I as ImageClient, J as JsonSchemaProperty, M as MemoryClient, i as MemoryItem, j as ModelClient, k as ModelClientOptions, P as PRIMITIVE_BINDINGS, l as PrimitiveBinding, m as PrimitiveTransport, Q as QueryInput, n as QueryOptions, o as RagChunk, p as RagClient, q as RagRerankResult, r as RealtimeClient, s as RealtimeSubscription, t as RemoteToolRef, u as RyuPrimitives, S as SttClient, v as ToolOptions, w as ToolSchema, x as TtsClient, y as createAgent, z as createPrimitives, B as defineModel, F as defineTool, K as httpPrimitiveTransport, L as inlineToolRunnable, N as query, O as ryuTool } from './index-CEbS1SlS.cjs';
3
- import { Requires, Surface, PluginManifest, RunnableMeta, CompanionSurface, CapabilityReq, TurnHookContribution } from './manifest.cjs';
1
+ import { R as Runnable, a as RunnableContext, T as ToolRunnable } from './index-BvAB5eMk.cjs';
2
+ export { A as Agent, b as AgentConfig, c as AgentEvent, d as AgentTool, C as ChatDelta, e as ChatMessage, f as ChatResult, D as DurableClient, E as Endpoint, g as EnginesClient, G as GatewayClient, h as GenerateResult, H as HttpPrimitiveTransportOptions, I as ImageClient, J as JsonSchemaProperty, M as MemoryClient, i as MemoryItem, j as ModelClient, k as ModelClientOptions, P as PRIMITIVE_BINDINGS, l as PrimitiveBinding, m as PrimitiveTransport, Q as QueryInput, n as QueryOptions, o as RagChunk, p as RagClient, q as RagRerankResult, r as RealtimeClient, s as RealtimeSubscription, t as RemoteToolRef, u as RyuPrimitives, S as SttClient, v as ToolOptions, w as ToolSchema, x as TtsClient, y as createAgent, z as createPrimitives, B as defineModel, F as defineTool, K as httpPrimitiveTransport, L as inlineToolRunnable, N as query, O as ryuTool } from './index-BvAB5eMk.cjs';
3
+ import { Requires, Surface, PluginManifest, RunnableMeta, CompanionSurface, CapabilityReq, HookEventContribution, TurnHookContribution } from './manifest.cjs';
4
4
  export { AppDependency, AppDependencySchema, CapabilityReqSchema, CompanionSurfaceSchema, Contributes, PluginManifestSchema, RequiresSchema, RunnableKind, RunnableKindSchema, RunnableMetaSchema, SurfaceSchema, ToolAppConfig, ToolAppConfigSchema, WidgetContribution, WidgetContributionSchema, coreManifestJsonSchema, validateManifestStrict, validatePluginId } from './manifest.cjs';
5
5
  import 'zod';
6
6
 
@@ -9,7 +9,7 @@ import 'zod';
9
9
  *
10
10
  * A "Ryu App" bundles one or more tools whose results render an interactive
11
11
  * widget inline in chat (the ChatGPT-Apps-style surface). `defineApp` assembles a
12
- * complete `plugin.json` `PluginManifest` from a declarative description, deriving
12
+ * complete `manifest.json` `PluginManifest` from a declarative description, deriving
13
13
  * the render-vs-companion split exactly the way Core's in-process provider does
14
14
  * (`apps/core/src/sidecar/mcp/apps/mod.rs` `tools()`):
15
15
  *
@@ -107,7 +107,7 @@ interface DefineAppOptions {
107
107
  /** Build a fully-qualified tool id from a server namespace and tool name. */
108
108
  declare function appToolId(server: string, name: string): string;
109
109
  /**
110
- * Assemble a `plugin.json` manifest for a Ryu App. The result matches Core's
110
+ * Assemble a `manifest.json` manifest for a Ryu App. The result matches Core's
111
111
  * `PluginManifest` serde shape (validated through `PluginManifestSchema`) and can
112
112
  * be written to disk, packed with `ryu pack`, or published with `ryu publish`.
113
113
  *
@@ -132,7 +132,7 @@ declare function defineApp(options: DefineAppOptions): PluginManifest;
132
132
 
133
133
  /**
134
134
  * Ryu SDK typed builders — one builder per RunnableKind plus a PluginBuilder that
135
- * assembles a complete, validated `plugin.json` manifest.
135
+ * assembles a complete, validated `manifest.json` manifest.
136
136
  *
137
137
  * Each builder follows a fluent interface: construct, chain setter calls, then
138
138
  * call `.build()` to get a validated result. Invalid manifests throw a
@@ -174,7 +174,7 @@ declare const tool: () => ToolBuilder;
174
174
  /** Create a SkillBuilder. */
175
175
  declare const skill: () => SkillBuilder;
176
176
  /**
177
- * Fluent builder for a complete `plugin.json` Plugin manifest. Produces a
177
+ * Fluent builder for a complete `manifest.json` Plugin manifest. Produces a
178
178
  * validated `PluginManifest` on `.build()` or throws a descriptive `Error`
179
179
  * naming the first invalid field.
180
180
  *
@@ -252,7 +252,7 @@ declare class PluginBuilder {
252
252
  build(): PluginManifest;
253
253
  }
254
254
  /**
255
- * Fluent builder for a Ryu App — a `plugin.json` whose tools render interactive
255
+ * Fluent builder for a Ryu App — a `manifest.json` whose tools render interactive
256
256
  * widgets inline in chat. Delegates to {@link defineApp} on `.build()`, so it
257
257
  * derives the render-vs-companion split and validates through
258
258
  * `PluginManifestSchema` (throwing a descriptive `Error` on bad input) exactly
@@ -470,7 +470,7 @@ interface AgentRunnable<TInput = unknown, TOutput = unknown> extends Runnable<TI
470
470
  /** The lowered slot card (empty edges when no slots were declared). */
471
471
  readonly card: AgentCard;
472
472
  /**
473
- * Lower this agent (card + run identity) to a single-agent `plugin.json`
473
+ * Lower this agent (card + run identity) to a single-agent `manifest.json`
474
474
  * `PluginManifest`: the agent `RunnableMeta` carries the persona/model
475
475
  * config; `requires.capabilities` carries the slot edges. Throws if the
476
476
  * assembled manifest is invalid.
@@ -493,7 +493,7 @@ interface AgentManifestOptions {
493
493
  *
494
494
  * The returned value satisfies `Runnable<TInput, TOutput>` with `kind = "agent"`
495
495
  * and additionally exposes the lowered {@link AgentCard} + a `toManifest()`
496
- * lowering, so a slot-composed agent round-trips to a valid `plugin.json`.
496
+ * lowering, so a slot-composed agent round-trips to a valid `manifest.json`.
497
497
  *
498
498
  * @example Classic (unchanged, back-compat):
499
499
  * ```ts
@@ -651,7 +651,9 @@ interface DefineTurnHookOptions {
651
651
  * is serialized into the sandbox `code` string and invoked with `ctx`/`host` at
652
652
  * run time.
653
653
  */
654
- declare function defineTurnHook(options: DefineTurnHookOptions): TurnHookContribution;
654
+ declare function defineTurnHook(options: DefineTurnHookOptions): TurnHookContribution & {
655
+ code: string;
656
+ };
655
657
  interface DefinePluginOptions {
656
658
  /** Activation events (default `["*"]` — driven by the enabled flag). */
657
659
  activationEvents?: string[];
@@ -659,8 +661,24 @@ interface DefinePluginOptions {
659
661
  composerControls?: Record<string, unknown>[];
660
662
  /** Capability grants the hooks need (e.g. `["hook:side-model", "storage:kv"]`). */
661
663
  grants?: string[];
664
+ /**
665
+ * App events this plugin EMITS — the provider half of the hook system whose
666
+ * consumer half is {@link DefinePluginOptions.turnHooks}. Each `id` must be
667
+ * namespaced to this plugin's own `id`; Core validates that at load and again
668
+ * on every emit.
669
+ */
670
+ hookEvents?: HookEventContribution[];
662
671
  /** Reverse-domain id (e.g. `"com.example.my-plugin"`). */
663
672
  id: string;
673
+ /**
674
+ * Language servers the plugin declares, keyed by server name — the same shape
675
+ * as Claude Code's `lspServers` / `.lsp.json`, passed verbatim. Loose records
676
+ * rather than a typed entry on purpose: Claude Code owns this field
677
+ * vocabulary, so typing it here would strip a field from a newer Claude
678
+ * release on its way through `ryu pack`. Core types it, because Core acts on
679
+ * it.
680
+ */
681
+ lspServers?: Record<string, Record<string, unknown>>;
664
682
  /** Display name. */
665
683
  name: string;
666
684
  /**
@@ -690,7 +708,7 @@ interface DefinePluginOptions {
690
708
  version: string;
691
709
  }
692
710
  /**
693
- * Assemble a `plugin.json` manifest for a turn-hook plugin. The result matches
711
+ * Assemble a `manifest.json` manifest for a turn-hook plugin. The result matches
694
712
  * Core's `PluginManifest` serde shape and can be written to disk or validated via
695
713
  * `validateManifestStrict`.
696
714
  */
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { R as Runnable, a as RunnableContext, T as ToolRunnable } from './index-DAxq7Y0R.js';
2
- export { A as Agent, b as AgentConfig, c as AgentEvent, d as AgentTool, C as ChatDelta, e as ChatMessage, f as ChatResult, D as DurableClient, E as Endpoint, g as EnginesClient, G as GatewayClient, h as GenerateResult, H as HttpPrimitiveTransportOptions, I as ImageClient, J as JsonSchemaProperty, M as MemoryClient, i as MemoryItem, j as ModelClient, k as ModelClientOptions, P as PRIMITIVE_BINDINGS, l as PrimitiveBinding, m as PrimitiveTransport, Q as QueryInput, n as QueryOptions, o as RagChunk, p as RagClient, q as RagRerankResult, r as RealtimeClient, s as RealtimeSubscription, t as RemoteToolRef, u as RyuPrimitives, S as SttClient, v as ToolOptions, w as ToolSchema, x as TtsClient, y as createAgent, z as createPrimitives, B as defineModel, F as defineTool, K as httpPrimitiveTransport, L as inlineToolRunnable, N as query, O as ryuTool } from './index-DAxq7Y0R.js';
3
- import { Requires, Surface, PluginManifest, RunnableMeta, CompanionSurface, CapabilityReq, TurnHookContribution } from './manifest.js';
1
+ import { R as Runnable, a as RunnableContext, T as ToolRunnable } from './index-B6SkaAjJ.js';
2
+ export { A as Agent, b as AgentConfig, c as AgentEvent, d as AgentTool, C as ChatDelta, e as ChatMessage, f as ChatResult, D as DurableClient, E as Endpoint, g as EnginesClient, G as GatewayClient, h as GenerateResult, H as HttpPrimitiveTransportOptions, I as ImageClient, J as JsonSchemaProperty, M as MemoryClient, i as MemoryItem, j as ModelClient, k as ModelClientOptions, P as PRIMITIVE_BINDINGS, l as PrimitiveBinding, m as PrimitiveTransport, Q as QueryInput, n as QueryOptions, o as RagChunk, p as RagClient, q as RagRerankResult, r as RealtimeClient, s as RealtimeSubscription, t as RemoteToolRef, u as RyuPrimitives, S as SttClient, v as ToolOptions, w as ToolSchema, x as TtsClient, y as createAgent, z as createPrimitives, B as defineModel, F as defineTool, K as httpPrimitiveTransport, L as inlineToolRunnable, N as query, O as ryuTool } from './index-B6SkaAjJ.js';
3
+ import { Requires, Surface, PluginManifest, RunnableMeta, CompanionSurface, CapabilityReq, HookEventContribution, TurnHookContribution } from './manifest.js';
4
4
  export { AppDependency, AppDependencySchema, CapabilityReqSchema, CompanionSurfaceSchema, Contributes, PluginManifestSchema, RequiresSchema, RunnableKind, RunnableKindSchema, RunnableMetaSchema, SurfaceSchema, ToolAppConfig, ToolAppConfigSchema, WidgetContribution, WidgetContributionSchema, coreManifestJsonSchema, validateManifestStrict, validatePluginId } from './manifest.js';
5
5
  import 'zod';
6
6
 
@@ -9,7 +9,7 @@ import 'zod';
9
9
  *
10
10
  * A "Ryu App" bundles one or more tools whose results render an interactive
11
11
  * widget inline in chat (the ChatGPT-Apps-style surface). `defineApp` assembles a
12
- * complete `plugin.json` `PluginManifest` from a declarative description, deriving
12
+ * complete `manifest.json` `PluginManifest` from a declarative description, deriving
13
13
  * the render-vs-companion split exactly the way Core's in-process provider does
14
14
  * (`apps/core/src/sidecar/mcp/apps/mod.rs` `tools()`):
15
15
  *
@@ -107,7 +107,7 @@ interface DefineAppOptions {
107
107
  /** Build a fully-qualified tool id from a server namespace and tool name. */
108
108
  declare function appToolId(server: string, name: string): string;
109
109
  /**
110
- * Assemble a `plugin.json` manifest for a Ryu App. The result matches Core's
110
+ * Assemble a `manifest.json` manifest for a Ryu App. The result matches Core's
111
111
  * `PluginManifest` serde shape (validated through `PluginManifestSchema`) and can
112
112
  * be written to disk, packed with `ryu pack`, or published with `ryu publish`.
113
113
  *
@@ -132,7 +132,7 @@ declare function defineApp(options: DefineAppOptions): PluginManifest;
132
132
 
133
133
  /**
134
134
  * Ryu SDK typed builders — one builder per RunnableKind plus a PluginBuilder that
135
- * assembles a complete, validated `plugin.json` manifest.
135
+ * assembles a complete, validated `manifest.json` manifest.
136
136
  *
137
137
  * Each builder follows a fluent interface: construct, chain setter calls, then
138
138
  * call `.build()` to get a validated result. Invalid manifests throw a
@@ -174,7 +174,7 @@ declare const tool: () => ToolBuilder;
174
174
  /** Create a SkillBuilder. */
175
175
  declare const skill: () => SkillBuilder;
176
176
  /**
177
- * Fluent builder for a complete `plugin.json` Plugin manifest. Produces a
177
+ * Fluent builder for a complete `manifest.json` Plugin manifest. Produces a
178
178
  * validated `PluginManifest` on `.build()` or throws a descriptive `Error`
179
179
  * naming the first invalid field.
180
180
  *
@@ -252,7 +252,7 @@ declare class PluginBuilder {
252
252
  build(): PluginManifest;
253
253
  }
254
254
  /**
255
- * Fluent builder for a Ryu App — a `plugin.json` whose tools render interactive
255
+ * Fluent builder for a Ryu App — a `manifest.json` whose tools render interactive
256
256
  * widgets inline in chat. Delegates to {@link defineApp} on `.build()`, so it
257
257
  * derives the render-vs-companion split and validates through
258
258
  * `PluginManifestSchema` (throwing a descriptive `Error` on bad input) exactly
@@ -470,7 +470,7 @@ interface AgentRunnable<TInput = unknown, TOutput = unknown> extends Runnable<TI
470
470
  /** The lowered slot card (empty edges when no slots were declared). */
471
471
  readonly card: AgentCard;
472
472
  /**
473
- * Lower this agent (card + run identity) to a single-agent `plugin.json`
473
+ * Lower this agent (card + run identity) to a single-agent `manifest.json`
474
474
  * `PluginManifest`: the agent `RunnableMeta` carries the persona/model
475
475
  * config; `requires.capabilities` carries the slot edges. Throws if the
476
476
  * assembled manifest is invalid.
@@ -493,7 +493,7 @@ interface AgentManifestOptions {
493
493
  *
494
494
  * The returned value satisfies `Runnable<TInput, TOutput>` with `kind = "agent"`
495
495
  * and additionally exposes the lowered {@link AgentCard} + a `toManifest()`
496
- * lowering, so a slot-composed agent round-trips to a valid `plugin.json`.
496
+ * lowering, so a slot-composed agent round-trips to a valid `manifest.json`.
497
497
  *
498
498
  * @example Classic (unchanged, back-compat):
499
499
  * ```ts
@@ -651,7 +651,9 @@ interface DefineTurnHookOptions {
651
651
  * is serialized into the sandbox `code` string and invoked with `ctx`/`host` at
652
652
  * run time.
653
653
  */
654
- declare function defineTurnHook(options: DefineTurnHookOptions): TurnHookContribution;
654
+ declare function defineTurnHook(options: DefineTurnHookOptions): TurnHookContribution & {
655
+ code: string;
656
+ };
655
657
  interface DefinePluginOptions {
656
658
  /** Activation events (default `["*"]` — driven by the enabled flag). */
657
659
  activationEvents?: string[];
@@ -659,8 +661,24 @@ interface DefinePluginOptions {
659
661
  composerControls?: Record<string, unknown>[];
660
662
  /** Capability grants the hooks need (e.g. `["hook:side-model", "storage:kv"]`). */
661
663
  grants?: string[];
664
+ /**
665
+ * App events this plugin EMITS — the provider half of the hook system whose
666
+ * consumer half is {@link DefinePluginOptions.turnHooks}. Each `id` must be
667
+ * namespaced to this plugin's own `id`; Core validates that at load and again
668
+ * on every emit.
669
+ */
670
+ hookEvents?: HookEventContribution[];
662
671
  /** Reverse-domain id (e.g. `"com.example.my-plugin"`). */
663
672
  id: string;
673
+ /**
674
+ * Language servers the plugin declares, keyed by server name — the same shape
675
+ * as Claude Code's `lspServers` / `.lsp.json`, passed verbatim. Loose records
676
+ * rather than a typed entry on purpose: Claude Code owns this field
677
+ * vocabulary, so typing it here would strip a field from a newer Claude
678
+ * release on its way through `ryu pack`. Core types it, because Core acts on
679
+ * it.
680
+ */
681
+ lspServers?: Record<string, Record<string, unknown>>;
664
682
  /** Display name. */
665
683
  name: string;
666
684
  /**
@@ -690,7 +708,7 @@ interface DefinePluginOptions {
690
708
  version: string;
691
709
  }
692
710
  /**
693
- * Assemble a `plugin.json` manifest for a turn-hook plugin. The result matches
711
+ * Assemble a `manifest.json` manifest for a turn-hook plugin. The result matches
694
712
  * Core's `PluginManifest` serde shape and can be written to disk or validated via
695
713
  * `validateManifestStrict`.
696
714
  */
package/dist/index.js CHANGED
@@ -12,7 +12,7 @@ import {
12
12
  coreManifestJsonSchema,
13
13
  validateManifestStrict,
14
14
  validatePluginId
15
- } from "./chunk-GXHL5CO7.js";
15
+ } from "./chunk-SKVIJH5I.js";
16
16
  import {
17
17
  Agent,
18
18
  PRIMITIVE_BINDINGS,
@@ -21,7 +21,7 @@ import {
21
21
  httpPrimitiveTransport,
22
22
  query,
23
23
  ryuTool
24
- } from "./chunk-KPKMMGVC.js";
24
+ } from "./chunk-MTUBUPIV.js";
25
25
  import {
26
26
  DEFAULT_GATEWAY_URL,
27
27
  ModelClient,
@@ -34,6 +34,14 @@ import {
34
34
  // src/runnable/app.ts
35
35
  var DEFAULT_APP_WIDGET_MIME = "text/html+skybridge";
36
36
  var DEFAULT_APP_DISPLAY_MODE = "inline";
37
+ var WIDGET_RENDER_GRANT = "widget:render";
38
+ function withWidgetRenderGrant(grants, widgets) {
39
+ const out = [...grants];
40
+ if (widgets.length > 0 && !out.includes(WIDGET_RENDER_GRANT)) {
41
+ out.push(WIDGET_RENDER_GRANT);
42
+ }
43
+ return out;
44
+ }
37
45
  function appToolId(server, name) {
38
46
  return `${server}__${name}`;
39
47
  }
@@ -75,9 +83,21 @@ function defineApp(options) {
75
83
  }
76
84
  const contributes = {
77
85
  turn_hooks: [],
86
+ // This builder synthesises an app from its runnables; an app that emits
87
+ // events declares them in a hand-authored `manifest.json`, same as
88
+ // `lsp_servers` below.
89
+ hook_events: [],
78
90
  composer_controls: [],
79
91
  settings_tabs: [],
80
92
  slash_commands: [],
93
+ sidebar_sections: [],
94
+ sidebar_buttons: [],
95
+ dock_panels: [],
96
+ // Empty for the same reason as every sibling family above: this builder
97
+ // synthesises `widgets` from the app's own runnables and nothing else, and
98
+ // takes no `contributes` passthrough. An app that wants to declare language
99
+ // servers writes them in a hand-authored `manifest.json`.
100
+ lsp_servers: {},
81
101
  widgets
82
102
  };
83
103
  const raw = {
@@ -85,7 +105,21 @@ function defineApp(options) {
85
105
  name: options.title,
86
106
  version: options.version,
87
107
  runnables,
88
- permission_grants: options.grants ?? [],
108
+ // An app that synthesises widgets MUST hold `widget:render`, so this
109
+ // builder declares it rather than leaving the author to discover it.
110
+ //
111
+ // Core gates widget promotion on declared-AND-enabled-AND-granted, and a
112
+ // missing grant fails as `DeniedNoGrant` — which is an `info!` log and
113
+ // nothing else. The widget silently renders as plain text, with no error
114
+ // in the UI and nothing pointing at the manifest. Every app scaffolded
115
+ // through `defineApp` hit that, because the only fix was a grant string
116
+ // the templates never mention and the builder never added; the one
117
+ // working example on disk hand-writes it.
118
+ //
119
+ // Added only when there is a widget to render, and unioned rather than
120
+ // overwritten so an author's own `grants` list survives and re-declaring
121
+ // it is not an error.
122
+ permission_grants: withWidgetRenderGrant(options.grants ?? [], widgets),
89
123
  activation_events: options.activationEvents ?? ["*"],
90
124
  contributes,
91
125
  // `targets: []` means EVERY surface, so an app that declares none is
@@ -107,7 +141,9 @@ function defineApp(options) {
107
141
  const first = result.error.issues[0];
108
142
  const field = first?.path.join(".") ?? "unknown";
109
143
  const message = first?.message ?? "validation failed";
110
- throw new Error(`plugin.json validation failed at '${field}': ${message}`);
144
+ throw new Error(
145
+ `manifest.json validation failed at '${field}': ${message}`
146
+ );
111
147
  }
112
148
  return result.data;
113
149
  }
@@ -303,7 +339,7 @@ var PluginBuilder = class {
303
339
  const field = first?.path.join(".") ?? "unknown";
304
340
  const message = first?.message ?? "validation failed";
305
341
  throw new Error(
306
- `plugin.json validation failed at '${field}': ${message}`
342
+ `manifest.json validation failed at '${field}': ${message}`
307
343
  );
308
344
  }
309
345
  return result.data;
@@ -676,12 +712,18 @@ function defineTurnHook(options) {
676
712
  function definePlugin(options) {
677
713
  const contributes = {
678
714
  turn_hooks: options.turnHooks ?? [],
715
+ hook_events: options.hookEvents ?? [],
679
716
  composer_controls: options.composerControls ?? [],
680
717
  settings_tabs: options.settingsTabs ?? [],
681
718
  slash_commands: options.slashCommands ?? [],
682
- // A turn-hook plugin contributes no app widgets; the field is required on the
683
- // resolved `Contributes` type (zod default applied), so set it explicitly.
684
- widgets: []
719
+ lsp_servers: options.lspServers ?? {},
720
+ // A turn-hook plugin contributes no app widgets, sidebar entries or dock
721
+ // panels; the fields are required on the resolved `Contributes` type (zod
722
+ // defaults applied), so set them explicitly.
723
+ widgets: [],
724
+ sidebar_sections: [],
725
+ sidebar_buttons: [],
726
+ dock_panels: []
685
727
  };
686
728
  const tools = options.tools ?? [];
687
729
  const runnables = tools.map((t) => inlineToolRunnable(t));