@ryuhq/sdk 0.1.14 → 0.2.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.
Files changed (105) hide show
  1. package/README.md +69 -7
  2. package/dist/action.cjs +839 -0
  3. package/dist/action.d.cts +88 -0
  4. package/dist/action.d.ts +88 -0
  5. package/dist/action.js +8 -0
  6. package/dist/agent-plugin.cjs +46 -0
  7. package/dist/agent-plugin.d.cts +43 -33
  8. package/dist/agent-plugin.d.ts +43 -33
  9. package/dist/agent-plugin.js +1 -1
  10. package/dist/agent.cjs +16 -0
  11. package/dist/agent.d.cts +377 -1
  12. package/dist/agent.d.ts +377 -1
  13. package/dist/agent.js +4 -2
  14. package/dist/app-B0Z9Ew_R.d.cts +141 -0
  15. package/dist/app-C-BDJwfG.d.ts +141 -0
  16. package/dist/builder.cjs +1194 -0
  17. package/dist/builder.d.cts +217 -0
  18. package/dist/builder.d.ts +217 -0
  19. package/dist/builder.js +29 -0
  20. package/dist/chunk-4TPUZDTI.js +94 -0
  21. package/dist/chunk-BC3A7HMO.js +164 -0
  22. package/dist/chunk-FZSFZOIN.js +200 -0
  23. package/dist/{chunk-ODFEUVPW.js → chunk-HACAGK65.js} +1 -18
  24. package/dist/chunk-HLKJZAFK.js +361 -0
  25. package/dist/chunk-IKEDLLFY.js +19 -0
  26. package/dist/{chunk-G6FLVEC4.js → chunk-JX6DDRXV.js} +46 -0
  27. package/dist/{chunk-AO2KJRDD.js → chunk-NZKVOSC2.js} +192 -3
  28. package/dist/chunk-QYFUNJOH.js +83 -0
  29. package/dist/chunk-SN2QBJUF.js +148 -0
  30. package/dist/chunk-TLDPEGC7.js +21 -0
  31. package/dist/chunk-TXSHHZF2.js +0 -0
  32. package/dist/chunk-VLIRNNAE.js +154 -0
  33. package/dist/{chunk-MTUBUPIV.js → chunk-Z57QDDJR.js} +8 -227
  34. package/dist/chunk-ZTJWBRUL.js +282 -0
  35. package/dist/cli.cjs +347 -18
  36. package/dist/cli.js +128 -23
  37. package/dist/client-D5U6ssPc.d.cts +84 -0
  38. package/dist/client-D5U6ssPc.d.ts +84 -0
  39. package/dist/index.cjs +810 -86
  40. package/dist/index.d.cts +19 -634
  41. package/dist/index.d.ts +19 -634
  42. package/dist/index.js +82 -699
  43. package/dist/manifest.cjs +200 -4
  44. package/dist/manifest.d.cts +260 -5
  45. package/dist/manifest.d.ts +260 -5
  46. package/dist/manifest.js +15 -1
  47. package/dist/mcp/client.cjs +180 -0
  48. package/dist/mcp/client.d.cts +49 -0
  49. package/dist/mcp/client.d.ts +49 -0
  50. package/dist/mcp/client.js +10 -0
  51. package/dist/mcp/server.cjs +370 -0
  52. package/dist/mcp/server.d.cts +127 -0
  53. package/dist/mcp/server.d.ts +127 -0
  54. package/dist/mcp/server.js +9 -0
  55. package/dist/mcp.cjs +376 -0
  56. package/dist/mcp.d.cts +2 -0
  57. package/dist/mcp.d.ts +2 -0
  58. package/dist/mcp.js +17 -0
  59. package/dist/model.cjs +141 -0
  60. package/dist/model.d.cts +33 -0
  61. package/dist/model.d.ts +33 -0
  62. package/dist/model.js +18 -0
  63. package/dist/plugin.cjs +46 -0
  64. package/dist/plugin.d.cts +215 -0
  65. package/dist/plugin.d.ts +215 -0
  66. package/dist/plugin.js +8 -0
  67. package/dist/runnable.cjs +1438 -0
  68. package/dist/runnable.d.cts +274 -0
  69. package/dist/runnable.d.ts +274 -0
  70. package/dist/runnable.js +34 -0
  71. package/dist/{index-B6SkaAjJ.d.ts → tool-AjkdFvhE.d.ts} +88 -461
  72. package/dist/{index-BvAB5eMk.d.cts → tool-CgzW92O_.d.cts} +88 -461
  73. package/package.json +48 -3
  74. package/src/agent/loop.test.ts +4 -4
  75. package/src/agent/tools.ts +3 -3
  76. package/src/agent-plugin.test.ts +58 -0
  77. package/src/agent-plugin.ts +109 -36
  78. package/src/builder.ts +9 -0
  79. package/src/cli/dev.test.ts +26 -47
  80. package/src/cli/dev.ts +10 -2
  81. package/src/cli-security.test.ts +109 -0
  82. package/src/cli.ts +141 -25
  83. package/src/contracts-lockstep.test.ts +16 -2
  84. package/src/exports-lockstep.test.ts +93 -0
  85. package/src/generated/plugin-manifest.ts +322 -29
  86. package/src/index.ts +53 -0
  87. package/src/manifest-schema.test.ts +30 -1
  88. package/src/manifest.fixtures.test.ts +22 -6
  89. package/src/manifest.test.ts +165 -10
  90. package/src/manifest.ts +454 -188
  91. package/src/mcp/index.ts +18 -0
  92. package/src/mcp/server.ts +2 -1
  93. package/src/model/index.ts +22 -0
  94. package/src/plugin/ryu-plugin.ts +82 -0
  95. package/src/runnable/action.test.ts +128 -0
  96. package/src/runnable/action.ts +202 -0
  97. package/src/runnable/app.test.ts +2 -0
  98. package/src/runnable/app.ts +50 -14
  99. package/src/runnable/index.ts +20 -3
  100. package/src/runnable/primitives.test.ts +34 -0
  101. package/src/runnable/primitives.ts +116 -0
  102. package/src/runnable/runnable-types.ts +3 -0
  103. package/src/runnable/tool.ts +36 -5
  104. package/src/runnable/turn-hook.ts +8 -3
  105. package/src/slash-command.test.ts +69 -0
@@ -32,6 +32,17 @@ export type SettingsFieldOption =
32
32
  */
33
33
  value: string;
34
34
  };
35
+ /**
36
+ * Authentication Ryu performs on behalf of the user for a remote MCP server.
37
+ *
38
+ * `deny_unknown_fields` is a security boundary: a publisher cannot smuggle a
39
+ * client secret, token endpoint, redirect URI, token or scope list into a signed
40
+ * manifest and have an older Core silently ignore it.
41
+ */
42
+ export type McpServerAuthDecl = {
43
+ client_id?: string | null;
44
+ type: "oauth";
45
+ };
35
46
  /**
36
47
  * What a capability provider acts on — see [`ProvidesEntry::target`].
37
48
  *
@@ -192,6 +203,13 @@ export interface PluginManifest {
192
203
  * Prompt-chip examples (contract key `examplePrompts`; Ryu extension).
193
204
  */
194
205
  examplePrompts?: string[];
206
+ /**
207
+ * Whether the provider operates outside the local Ryu runtime, for example a
208
+ * hosted browser or remote MCP service. This is a presentation/provenance
209
+ * flag, not a permission grant; the actual remote endpoint remains declared
210
+ * under `mcp_servers` and is governed by the Gateway.
211
+ */
212
+ external?: boolean;
195
213
  /**
196
214
  * Hide this listing from the Store without uninstalling or disabling it.
197
215
  *
@@ -356,7 +374,7 @@ export interface PluginManifest {
356
374
  permission_levels?: PermissionLevel[];
357
375
  /**
358
376
  * **Unified, deny-by-default runtime permission set** — the single typed
359
- * grammar (`{fs, child_process, network, tool}`) Core lowers to every sandbox
377
+ * grammar (`{fs, child_process, run, network, tool}`) Core lowers to every sandbox
360
378
  * backend (wasmtime WASI preopens, Docker `--mount`/`--network` flags, Deno
361
379
  * `--allow-*` flags). Absent = **deny-all** (the default for every manifest
362
380
  * predating this field), so an app that declares nothing keeps today's exact
@@ -403,6 +421,12 @@ export interface PluginManifest {
403
421
  * `sidecar`/`route` exists (like `contributes`).
404
422
  */
405
423
  provides?: ProvidesEntry[];
424
+ /**
425
+ * Public source repository URL (Claude/Codex `repository`). This is listing
426
+ * metadata only; install and signature resolution still use the catalog's
427
+ * authoritative source fields.
428
+ */
429
+ repository?: string | null;
406
430
  /**
407
431
  * **Plugin-to-plugin dependencies** — the other plugins this one needs (the
408
432
  * npm-shaped edge that lets the app decompose into a kernel + features).
@@ -483,6 +507,14 @@ export interface PluginManifest {
483
507
  * Short one-line tagline shown under the name (Ryu extension).
484
508
  */
485
509
  tagline?: string | null;
510
+ /**
511
+ * Curated store-filter tags (Ryu extension). Unlike `keywords`, which is
512
+ * publisher search vocabulary, these stable labels are the values the
513
+ * Marketplace filter exposes. Keeping both fields lets Claude/Codex
514
+ * manifests round-trip their native `keywords` while Ryu authors opt into
515
+ * a deliberately bounded taxonomy.
516
+ */
517
+ tags?: string[];
486
518
  /**
487
519
  * Host surfaces this plugin runs on (desktop / island / mobile / …).
488
520
  *
@@ -545,8 +577,9 @@ export interface CompanionSurface {
545
577
  * Most surfaces added since are **self-contained**: they carry their own payload
546
578
  * and reference no runnable at all (`widgets`, `views`, `dock_panels`,
547
579
  * `sidebar_sections`, `sidebar_buttons`, `settings_tabs`, `composer_controls`,
548
- * `slash_commands`, `turn_hooks`, `tool_filters`, `lsp_servers`,
549
- * `message_actions`, `context_menu_items`).
580
+ * `chat_features`, `slash_commands`, `turn_hooks`, `tool_filters`, `lsp_servers`,
581
+ * `message_actions`, `selection_actions`, `context_menu_items`,
582
+ * `agent_edit_panels`).
550
583
  *
551
584
  * # Extending
552
585
  *
@@ -563,7 +596,8 @@ export interface CompanionSurface {
563
596
  * (`tool_filters`, `turn_hooks`, `widgets`, `lsp_servers`) it gets a fully typed
564
597
  * struct, because a key Core does not know is by construction a key Core cannot
565
598
  * act on. If a client shell renders it (`views`, `dock_panels`,
566
- * `sidebar_sections`, `settings_tabs`, `composer_controls`) it stays opaque
599
+ * `sidebar_sections`, `settings_tabs`, `composer_controls`,
600
+ * `agent_edit_panels`) it stays opaque
567
601
  * JSON, because deserializing into a struct here would DROP any key this Core
568
602
  * build does not know about and a newer desktop would lose exactly the fields it
569
603
  * was shipped to render.
@@ -573,10 +607,34 @@ export interface CompanionSurface {
573
607
  * they are gathered at their own consumption site instead.
574
608
  */
575
609
  export interface Contributes {
610
+ /**
611
+ * Client-rendered panels for the agent edit page. Entries are deliberately
612
+ * opaque and self-contained: the desktop owns the panel vocabulary, while
613
+ * Core only stores, tags, and forwards the declaration through the plugin
614
+ * contributions endpoint. This lets a newer desktop add an agent-edit
615
+ * panel type without requiring every Core node to learn that type first.
616
+ * These entries name no runnable ids and therefore are intentionally absent
617
+ * from [`Contributes::referenced_ids`].
618
+ */
619
+ agent_edit_panels?: unknown[];
576
620
  /**
577
621
  * Agents the plugin contributes (referenced by runnable id).
578
622
  */
579
623
  agents?: ContributionId[];
624
+ /**
625
+ * Declarative chat feature descriptors. These are opaque, client-rendered
626
+ * declarations used to feature-detect chat behaviors whose implementation
627
+ * remains in the host (for example side chats or temporary chats). The
628
+ * owning plugin id is stamped by Core when the contribution endpoint serves
629
+ * them, so a disabled plugin removes both the descriptor and its UI affordance.
630
+ */
631
+ chat_features?: unknown[];
632
+ /**
633
+ * Metadata-only chat widget templates. Unlike [`Contributes::widgets`], this
634
+ * catalog is safe to show before a turn runs: it names a host-owned prompt
635
+ * affordance and a tool/view binding, never HTML, React, or capabilities.
636
+ */
637
+ chat_widget_templates?: ChatWidgetTemplateContribution[] | null;
580
638
  /**
581
639
  * Command-palette commands the plugin contributes (referenced by runnable id).
582
640
  */
@@ -737,6 +795,27 @@ export interface Contributes {
737
795
  * **self-contained** and stays out of [`Contributes::referenced_ids`].
738
796
  */
739
797
  hook_events?: HookEventContribution[];
798
+ /**
799
+ * **Live activities** the plugin contributes — small, always-live status cards
800
+ * the desktop shell's "Dynamic Island" dock (empty-shell launchpad + sidebar)
801
+ * renders for something in progress: an agent run, a download, a pending
802
+ * approval, a recording. The desktop half of the same status vocabulary the
803
+ * mobile `AgentActivity` uses, so one mental model spans devices.
804
+ *
805
+ * Each entry is a [`LiveActivityContribution`]: a typed envelope (`id`/`title`/
806
+ * `icon`/`accent`/`order`) around an **opaque** `spec` payload the desktop
807
+ * renderer interprets. Like a [`Contributes::sidebar_sections`] entry it carries
808
+ * a `ViewSource` (a Core `/api/` path the shell polls) and a field-map; unlike a
809
+ * section it maps response ROWS to live-activity cards (status/progress/target)
810
+ * instead of nav rows. The app returns DATA — never code — so a live activity
811
+ * cannot be made ugly and needs zero sidecar code.
812
+ *
813
+ * Self-contained (it names no runnable), so it stays out of
814
+ * [`Contributes::referenced_ids`]; the `spec` stays opaque to Core so a new
815
+ * activity capability is a renderer change, not a Core change. Served + tagged
816
+ * with the owning `plugin` id at `GET /api/plugins/contributions`.
817
+ */
818
+ live_activities?: LiveActivityContribution[];
740
819
  /**
741
820
  * **Language servers** the plugin declares, keyed by server name — the
742
821
  * agent-neutral mirror of Claude Code's `.lsp.json` / `lspServers`, so a config
@@ -883,6 +962,16 @@ export interface Contributes {
883
962
  * Gateway policies the plugin contributes (referenced by runnable id).
884
963
  */
885
964
  policies?: ContributionId[];
965
+ /**
966
+ * Buttons the plugin contributes to the floating text-selection toolbar.
967
+ * This is the bridge between enabled apps/plugins and shared chat blocks:
968
+ * Core validates and tags the declaration, while the desktop owns the
969
+ * rendered toolbar and dispatches the selected text. A selection action may
970
+ * either name a granted `capability` or provide a host-owned `args.dispatch`
971
+ * (for example, a first-party shell action such as Side Chat). Self-contained
972
+ * + opaque for the same forward-compatibility reason as `message_actions`.
973
+ */
974
+ selection_actions?: SelectionActionContribution[];
886
975
  /**
887
976
  * Declarative settings tabs the plugin contributes (model pickers, text
888
977
  * fields bound to preference keys). Served + rendered the same way.
@@ -915,7 +1004,7 @@ export interface Contributes {
915
1004
  * ([`Contributes::sidebar_sections`]) and "what nav rows exist"
916
1005
  * ([`Contributes::sidebar_buttons`]): **how the sidebar as a whole is
917
1006
  * arranged**. The shell ships three modes of its own (every section stacked;
918
- * every section as a tab; Agent mode, which is the pair Sessions ⇄ Agents), and
1007
+ * every section as a tab; Bot mode, which is the pair Sessions ⇄ Agents), and
919
1008
  * before this member an app could add a section to that list but could not
920
1009
  * propose an arrangement — so a plugin wanting the Grok/Hermes bot-mode posture
921
1010
  * had to ask for a shell change. See [`SidebarModeContribution`].
@@ -1058,6 +1147,36 @@ export interface ContributionId {
1058
1147
  */
1059
1148
  title?: string | null;
1060
1149
  }
1150
+ /**
1151
+ * A metadata-only entry the host may offer as a compact chat affordance.
1152
+ *
1153
+ * `backing` selects exactly one existing tool or view by id. The host owns the
1154
+ * eventual rendering and action dispatch; `safe_action_ids` are identifiers only
1155
+ * and are never executable payloads.
1156
+ */
1157
+ export interface ChatWidgetTemplateContribution {
1158
+ /**
1159
+ * `available`, `coming-soon`, or `unavailable`; unknown values are forwarded
1160
+ * for forward compatibility and are not offered by older shells.
1161
+ */
1162
+ availability?: string;
1163
+ backing: ChatWidgetTemplateBacking;
1164
+ description?: string | null;
1165
+ /**
1166
+ * Open vocabulary so newer shells can add display modes without breaking
1167
+ * older Core nodes; the desktop simply ignores modes it does not know.
1168
+ */
1169
+ display_mode: string;
1170
+ examples?: string[];
1171
+ id: string;
1172
+ safe_action_ids?: string[];
1173
+ title: string;
1174
+ triggers?: string[];
1175
+ }
1176
+ export interface ChatWidgetTemplateBacking {
1177
+ tool_id?: string | null;
1178
+ view_id?: string | null;
1179
+ }
1061
1180
  /**
1062
1181
  * One context-menu row a plugin contributes (see
1063
1182
  * [`Contributes::context_menu_items`]).
@@ -1302,6 +1421,50 @@ export interface HookEventContribution {
1302
1421
  */
1303
1422
  title: string;
1304
1423
  }
1424
+ /**
1425
+ * One app-registered **live activity** — a small, always-live status card the
1426
+ * desktop's "Dynamic Island" dock (empty-shell launchpad + sidebar) renders for
1427
+ * something in progress: an agent run, a download, a pending approval, a
1428
+ * recording. The desktop half of the status vocabulary the mobile `AgentActivity`
1429
+ * uses (`running` / `waiting` / `review` / `done` / `error`), so one mental model
1430
+ * spans devices.
1431
+ *
1432
+ * A typed envelope around an opaque `spec` (the `LiveActivitySpec` in
1433
+ * `@ryu/app-host/live-activity`: a `ViewSource` for the live rows, a field-map
1434
+ * from rows to card fields, and a `target` route template). Core stores it
1435
+ * verbatim and tags it with the owning `plugin` id; the `spec` stays opaque so a
1436
+ * new activity capability is a renderer change, not a Core change.
1437
+ */
1438
+ export interface LiveActivityContribution {
1439
+ /**
1440
+ * Optional accent colour hint (any CSS color) tinting the card.
1441
+ */
1442
+ accent?: string | null;
1443
+ /**
1444
+ * Optional glyph id resolved by the shell's Icon primitive (Iconify/Hugeicons).
1445
+ */
1446
+ icon?: string | null;
1447
+ /**
1448
+ * Stable id for this activity within the plugin (namespaced into the shell's
1449
+ * dock identity as `plugin:<pluginId>:<id>:<rowId>`).
1450
+ */
1451
+ id: string;
1452
+ /**
1453
+ * Optional placement hint among the dock's activities (lower = first).
1454
+ */
1455
+ order?: number | null;
1456
+ /**
1457
+ * The opaque activity spec (source/map/target). Interpreted by the desktop
1458
+ * renderer, never by Core. Absent = a header-only activity (renders nothing).
1459
+ */
1460
+ spec?: {
1461
+ [k: string]: unknown;
1462
+ };
1463
+ /**
1464
+ * Human-facing title shown on the dock card (falls back to the row title).
1465
+ */
1466
+ title: string;
1467
+ }
1305
1468
  /**
1306
1469
  * One **language server** a plugin declares (see [`Contributes::lsp_servers`]).
1307
1470
  *
@@ -1568,6 +1731,49 @@ export interface PiExtensionContribution {
1568
1731
  */
1569
1732
  id: string;
1570
1733
  }
1734
+ /**
1735
+ * One button a plugin contributes to the floating text-selection toolbar (see
1736
+ * [`Contributes::selection_actions`]).
1737
+ *
1738
+ * `capability` is optional because a host-owned renderer can use an opaque
1739
+ * `args.dispatch` bridge instead. The desktop never executes manifest code: it
1740
+ * only renders this label and forwards the selected text to the owning host
1741
+ * handler.
1742
+ */
1743
+ export interface SelectionActionContribution {
1744
+ /**
1745
+ * Static renderer/dispatch arguments. The selected text is supplied by the
1746
+ * host at click time and is never serialized into the manifest.
1747
+ */
1748
+ args?: {
1749
+ [k: string]: unknown;
1750
+ };
1751
+ /**
1752
+ * Optional granted capability for a plugin-owned dispatch.
1753
+ */
1754
+ capability?: string | null;
1755
+ /**
1756
+ * Optional glyph id resolved by the shell's icon primitive.
1757
+ */
1758
+ icon?: string | null;
1759
+ /**
1760
+ * Stable id for this action within the plugin.
1761
+ */
1762
+ id: string;
1763
+ /**
1764
+ * Render mode. The current desktop renders `"button"`; this remains open
1765
+ * so newer shells can add a mode without making older cores reject it.
1766
+ */
1767
+ kind: string;
1768
+ /**
1769
+ * Accessible label shown in the selection toolbar.
1770
+ */
1771
+ label: string;
1772
+ /**
1773
+ * Sort position among contributed selection actions (ascending).
1774
+ */
1775
+ order?: number | null;
1776
+ }
1571
1777
  /**
1572
1778
  * One **settings tab** a plugin contributes (see [`Contributes::settings_tabs`]).
1573
1779
  *
@@ -1696,6 +1902,13 @@ export interface SettingsFieldContribution {
1696
1902
  * buttons (e.g. Memory) to the owning app.
1697
1903
  */
1698
1904
  export interface SidebarButtonContribution {
1905
+ /**
1906
+ * Optional mount context passed to the owning Companion when the button opens it.
1907
+ * The host applies this only to the button's own app surface.
1908
+ */
1909
+ context?: {
1910
+ [k: string]: unknown;
1911
+ } | null;
1699
1912
  /**
1700
1913
  * Optional glyph id resolved by the shell's Icon primitive.
1701
1914
  */
@@ -1746,7 +1959,7 @@ export interface SidebarModeContribution {
1746
1959
  /**
1747
1960
  * Which of `sections` the mode opens on. Absent (or naming a section not in
1748
1961
  * `sections`) = the first one. This is the field that makes a mode an opinion
1749
- * rather than a filter: the shell's own Agent mode lists Sessions first but
1962
+ * rather than a filter: the shell's own Bot mode lists Sessions first but
1750
1963
  * opens on Agents, because the roster is what the mode is for.
1751
1964
  */
1752
1965
  default_section?: string | null;
@@ -1926,10 +2139,10 @@ export interface ThemePreview {
1926
2139
  * One **tool filter**: a fully-qualified tool id a plugin wants withheld from the
1927
2140
  * model's offered tool list.
1928
2141
  *
1929
- * Tools are namespaced `<server>__<tool>` (e.g. `browser__navigate`), so `tool`
2142
+ * Tools are namespaced `<server>.<tool>` (e.g. `browser.navigate`), so `tool`
1930
2143
  * must carry the namespace — a bare `navigate` would be ambiguous across servers
1931
2144
  * and is rejected at load. A **trailing** `*` is a prefix wildcard, which is how a
1932
- * plugin withholds a whole server (`shadow__*`); it is the only wildcard position
2145
+ * plugin withholds a whole server (`shadow.*`); it is the only wildcard position
1933
2146
  * allowed, because an interior or leading `*` invites a pattern that silently
1934
2147
  * matches far more than the author pictured.
1935
2148
  *
@@ -1947,7 +2160,7 @@ export interface ToolFilterContribution {
1947
2160
  */
1948
2161
  reason?: string | null;
1949
2162
  /**
1950
- * Fully-qualified tool id (`<server>__<tool>`), optionally ending in `*` to
2163
+ * Fully-qualified tool id (`<server>.<tool>`), optionally ending in `*` to
1951
2164
  * hide every tool whose id starts with the preceding prefix.
1952
2165
  */
1953
2166
  tool: string;
@@ -1999,6 +2212,11 @@ export interface TurnHookContribution {
1999
2212
  * The turn boundary this hook fires on. Today only `"post_assistant_turn"`.
2000
2213
  */
2001
2214
  on: string;
2215
+ /**
2216
+ * Higher-priority hooks run first within a phase. Ties are resolved by
2217
+ * plugin id and hook id, which makes first-writer-wins directives stable.
2218
+ */
2219
+ priority?: number;
2002
2220
  }
2003
2221
  /**
2004
2222
  * A declarative pre-gate for a [`TurnHookContribution`]. The conditions are
@@ -2169,26 +2387,41 @@ export interface EnginesReq {
2169
2387
  web?: string | null;
2170
2388
  }
2171
2389
  /**
2172
- * One declarative **stdio MCP server** a plugin registers (see
2173
- * [`PluginManifest::mcp_servers`]).
2390
+ * One declarative **MCP server** a plugin registers (see
2391
+ * [`PluginManifest::mcp_servers`]) — either a stdio command to spawn or a remote
2392
+ * HTTP endpoint to call.
2174
2393
  *
2175
2394
  * This is the manifest-side, dependency-free mirror of Core's runtime
2176
2395
  * `McpServerConfig`: pure data (schemars/serde only) so it can live in
2177
2396
  * kernel-contracts, with Core lowering it into its registry type on enable. A
2178
- * server is spawned per request as `command args…` (stdio); `command_env` lets
2397
+ * stdio server is spawned per request as `command args…`; `command_env` lets
2179
2398
  * the manifest name an env var Core resolves to an absolute binary path
2180
2399
  * (e.g. `RYU_GHOST_BIN`) so a downloaded `~/.ryu/bin` binary can override the
2181
- * bare `command`.
2400
+ * bare `command`. An HTTP server names a [`url`](McpServerDecl::url) instead and
2401
+ * spawns nothing at all.
2402
+ *
2403
+ * The field names mirror the `mcp.json` dialect users already paste from Cursor
2404
+ * and Claude Desktop (`type` / `url` / `headers`) precisely so a manifest and a
2405
+ * hand-written config entry are the same shape. Static API-key auth may live in
2406
+ * [`headers`](McpServerDecl::headers). User-delegated OAuth is declared through
2407
+ * [`auth`](McpServerDecl::auth), and Core owns the resulting token lifecycle.
2182
2408
  */
2183
2409
  export interface McpServerDecl {
2184
2410
  /**
2185
2411
  * Arguments passed to the command.
2186
2412
  */
2187
2413
  args?: string[];
2414
+ /**
2415
+ * Core-owned OAuth for this remote MCP server. The manifest may name only an
2416
+ * optional public client id; discovery, PKCE, tokens and redirect URIs are
2417
+ * intentionally outside the publisher-controlled manifest.
2418
+ */
2419
+ auth?: McpServerAuthDecl | null;
2188
2420
  /**
2189
2421
  * Executable to spawn (e.g. `npx`, an absolute path, or a `~/.ryu/bin` name).
2422
+ * Absent for a remote (`url`) server.
2190
2423
  */
2191
- command: string;
2424
+ command?: string | null;
2192
2425
  /**
2193
2426
  * Optional env var whose value, when set, OVERRIDES [`command`] with an
2194
2427
  * absolute binary path. Lets a plugin ship a bare `command` that Core repoints
@@ -2212,6 +2445,22 @@ export interface McpServerDecl {
2212
2445
  env?: {
2213
2446
  [k: string]: string;
2214
2447
  };
2448
+ /**
2449
+ * Request headers sent with every call to a remote server (auth lives here).
2450
+ */
2451
+ headers?: {
2452
+ [k: string]: string;
2453
+ };
2454
+ /**
2455
+ * Transport: `stdio`, `http`, `streamable-http`, or `sse`. Absent ⇒ inferred
2456
+ * from whichever of `command`/`url` is present. `http` and `streamable-http`
2457
+ * select Streamable HTTP; `sse` selects the legacy HTTP+SSE transport.
2458
+ */
2459
+ type?: string | null;
2460
+ /**
2461
+ * Endpoint URL for a remote (HTTP) server. Absent for a stdio server.
2462
+ */
2463
+ url?: string | null;
2215
2464
  }
2216
2465
  /**
2217
2466
  * One entry in an app's **user-facing permission vocabulary** — a level an admin
@@ -2289,6 +2538,12 @@ export interface PermissionSet {
2289
2538
  * Deno's `--allow-net` supports). See [`NetworkPermission`].
2290
2539
  */
2291
2540
  network?: boolean | string[];
2541
+ /**
2542
+ * Executable names sandboxed code may spawn when [`Self::child_process`] is
2543
+ * true. Core lowers this to Deno's scoped `--allow-run=<name,...>` list in
2544
+ * addition to declared capability shims. Empty grants no arbitrary binary.
2545
+ */
2546
+ run?: string[];
2292
2547
  /**
2293
2548
  * **Declaration-only** in v1: the registry tool ids this plugin's sandboxed
2294
2549
  * code may call through the stdio `tools.*` bridge. Tools are brokered over
@@ -2413,7 +2668,7 @@ export interface ProvidesEntry {
2413
2668
  * model-visible tool surface stable across a swap.
2414
2669
  *
2415
2670
  * The key is a canonical verb from the host's capability verb table (e.g.
2416
- * `"web__search"`); the value names the provider's own registered tool plus the
2671
+ * `"web.search"`); the value names the provider's own registered tool plus the
2417
2672
  * argument/response mapping into the canonical shape. A provider that omits a
2418
2673
  * verb simply does not serve it — the facade reports the verb unavailable
2419
2674
  * rather than guessing.
@@ -2431,7 +2686,7 @@ export interface ProvidesEntry {
2431
2686
  /**
2432
2687
  * How one capability **verb** maps onto a concrete provider tool.
2433
2688
  *
2434
- * The facade tool (`web__search`, `browser__navigate`, …) is registered by the host
2689
+ * The facade tool (`web.search`, `browser.navigate`, …) is registered by the host
2435
2690
  * from its canonical verb table; at call time it resolves the capability's bound
2436
2691
  * provider, reads this binding, renames the arguments, re-enters tool dispatch on
2437
2692
  * [`Self::tool`], and maps the response back. Swapping the provider therefore
@@ -2464,7 +2719,7 @@ export interface CapabilityToolBinding {
2464
2719
  * **canonical** argument name (before any rename).
2465
2720
  *
2466
2721
  * Exists because canonical schemas describe what agents may ask for, while
2467
- * providers differ in what they accept: `web__search.limit` allows up to 100,
2722
+ * providers differ in what they accept: `web.search.limit` allows up to 100,
2468
2723
  * but Brave's `count` maxes at 20. Without this, selecting Brave turns a
2469
2724
  * perfectly valid `limit: 50` into an upstream 4xx — the swap stops being
2470
2725
  * transparent, which is the entire point of the facade. Clamping is the right
@@ -2514,8 +2769,8 @@ export interface CapabilityToolBinding {
2514
2769
  */
2515
2770
  response?: CapabilityResponseMap | null;
2516
2771
  /**
2517
- * The provider's own fully-qualified tool id (e.g. `"exa__search"`,
2518
- * `"app__firecrawl_scrape"`) that implements this verb.
2772
+ * The provider's own fully-qualified tool id (e.g. `"exa.search"`,
2773
+ * `"app.firecrawl_scrape"`) that implements this verb.
2519
2774
  */
2520
2775
  tool: string;
2521
2776
  }
@@ -2801,17 +3056,20 @@ export interface ExternalRuntimeConfig {
2801
3056
  }
2802
3057
  /**
2803
3058
  * A single asset an external runtime needs, fetched before first run. Either a
2804
- * direct https URL or an `hf:<owner>/<repo>/<path>` reference; `dest_under_ryu`
2805
- * is the relative directory beneath `~/.ryu` where it lands (Core-owned) — the
2806
- * filename is derived from the source's last path segment.
3059
+ * direct https URL or an `hf:<owner>/<repo>/<path>` reference; the destination
3060
+ * is a relative directory beneath the plugin's dedicated runtime `assets/`
3061
+ * directory — the filename is derived from the source's last path segment.
2807
3062
  */
2808
3063
  export interface AssetSpec {
2809
3064
  /**
2810
- * Destination directory relative to `~/.ryu` (e.g. `"models/hf"`); the
2811
- * fetched file lands at `~/.ryu/<dest_under_ryu>/<filename>`. Must be a
2812
- * traversal-safe relative path (no `..`, not absolute).
3065
+ * Destination directory relative to the runtime's `assets/` directory
3066
+ * (e.g. `"models/hf"`). The fetched file lands at
3067
+ * `<runtime>/assets/<dest_under_runtime>/<filename>`. Must be a
3068
+ * traversal-safe relative path (no `..`, not absolute). The old
3069
+ * `dest_under_ryu` spelling is accepted as a wire alias but is never
3070
+ * resolved against the shared Core data directory.
2813
3071
  */
2814
- dest_under_ryu: string;
3072
+ dest_under_runtime: string;
2815
3073
  /**
2816
3074
  * Optional SHA-256 for checksum verification (direct-URL assets).
2817
3075
  */
@@ -2908,6 +3166,16 @@ export interface SidecarSpec {
2908
3166
  * until the first proxy/broker hit wakes it on demand; a bounded health-wait
2909
3167
  * warms it before the request is forwarded. `false` (the default) keeps the
2910
3168
  * eager behaviour every existing manifest has: started at enable. Additive.
3169
+ *
3170
+ * **Ignored when [`provides_provider`] is set.** Such a sidecar is always started
3171
+ * eagerly, because the two declarations are mutually exclusive by construction: a
3172
+ * lazy sidecar's only wake trigger is a proxy/broker hit, while a provider's only
3173
+ * client is Pi, which dials the registered `baseUrl` directly and never traverses
3174
+ * the proxy. Nothing could ever wake it, and Core's boot purge of stale
3175
+ * sidecar-owned provider entries would then leave the provider permanently dead.
3176
+ * The coercion is logged at `info`; the manifest is NOT rejected.
3177
+ *
3178
+ * [`provides_provider`]: SidecarSpec::provides_provider
2911
3179
  */
2912
3180
  lazy?: boolean;
2913
3181
  /**
@@ -2918,9 +3186,16 @@ export interface SidecarSpec {
2918
3186
  name: string;
2919
3187
  /**
2920
3188
  * TCP port the process's HTTP server binds to, used to build the health-check
2921
- * URL. The plugin is responsible for choosing a free port there is **no port
2922
- * registry in v1**, so a collision with a built-in (e.g. llama.cpp on 8080) is
2923
- * the plugin author's responsibility to avoid.
3189
+ * URL. The plugin is responsible for choosing a free port: there is **no
3190
+ * allocator** this number is what Core tries (after the profile offset), and a
3191
+ * collision is not repaired by moving anyone.
3192
+ *
3193
+ * There *is* a gate. `SidecarManager::claim_port` checks a live-sidecar registry
3194
+ * and bind-probes the port, and **refuses to start** the sidecar if either fails,
3195
+ * so a collision with a built-in (e.g. llama.cpp on 8080) surfaces as an app that
3196
+ * does not come up rather than as silent breakage. Detection, not resolution:
3197
+ * picking a free port is still the author's job. See `docs/port-allocation.md`
3198
+ * for the band map.
2924
3199
  */
2925
3200
  port: number;
2926
3201
  /**
@@ -2939,6 +3214,17 @@ export interface SidecarSpec {
2939
3214
  * ext-proxy hop and `/api/host/*`), and the host-RPC vocabulary has no
2940
3215
  * provider-registration method. Registration is therefore Core-side, driven by
2941
3216
  * this declaration.
3217
+ *
3218
+ * **Declaring this forces eager start and disables idle-stop**, overriding
3219
+ * [`lazy`] and [`idle_stop_secs`]. Pi bypasses the ext proxy and dials the
3220
+ * registered `baseUrl` directly, so no request can ever wake this sidecar on
3221
+ * demand; a sidecar that is never started never reaches the Healthy edge that
3222
+ * registers it, and one that is scaled to zero drops out of Pi's model list until
3223
+ * a wake that will never come. Both coercions are logged at `info` rather than
3224
+ * rejected by the validator, so existing manifests keep loading.
3225
+ *
3226
+ * [`lazy`]: SidecarSpec::lazy
3227
+ * [`idle_stop_secs`]: SidecarSpec::idle_stop_secs
2942
3228
  */
2943
3229
  provides_provider?: ProviderRegistrationSpec | null;
2944
3230
  }
@@ -3013,6 +3299,13 @@ export interface RouteSpec {
3013
3299
  * webhook whose external caller cannot hold the node token).
3014
3300
  */
3015
3301
  auth?: "protected" | "public";
3302
+ /**
3303
+ * Optional HTTP method selector for this path (canonical uppercase such as
3304
+ * `GET` or `POST`). Absent preserves the legacy behavior and matches every
3305
+ * method. Declare one row per method when reads and writes share a path but
3306
+ * require different permission levels.
3307
+ */
3308
+ method?: string | null;
3016
3309
  /**
3017
3310
  * Path pattern for the sub-path after `/api/ext/<plugin_id>` (must start with
3018
3311
  * `/`). Supports `:param` (matches one non-empty segment) and a trailing
package/src/index.ts CHANGED
@@ -37,10 +37,16 @@ export type {
37
37
  CapabilityReq,
38
38
  CompanionSurface,
39
39
  Contributes,
40
+ McpServerAuth,
41
+ McpServerDecl,
40
42
  PluginManifest,
41
43
  Requires,
42
44
  RunnableKind,
43
45
  RunnableMeta,
46
+ SlashCommandArgument,
47
+ SlashCommandContribution,
48
+ SlashCommandCustomOption,
49
+ SlashCommandOption,
44
50
  Surface,
45
51
  ToolAppConfig,
46
52
  TurnHookContribution,
@@ -51,16 +57,36 @@ export {
51
57
  CapabilityReqSchema,
52
58
  CompanionSurfaceSchema,
53
59
  coreManifestJsonSchema,
60
+ McpServerAuthSchema,
61
+ McpServerDeclSchema,
54
62
  PluginManifestSchema,
55
63
  RequiresSchema,
56
64
  RunnableKindSchema,
57
65
  RunnableMetaSchema,
66
+ SlashCommandArgumentSchema,
67
+ SlashCommandContributionSchema,
68
+ SlashCommandCustomOptionSchema,
69
+ SlashCommandOptionSchema,
58
70
  SurfaceSchema,
59
71
  ToolAppConfigSchema,
60
72
  validateManifestStrict,
61
73
  validatePluginId,
62
74
  WidgetContributionSchema,
63
75
  } from "./manifest.ts";
76
+ export type {
77
+ JsonSchema,
78
+ McpStdioCommand,
79
+ McpTool,
80
+ PassthroughRegistration,
81
+ SdkRunnable,
82
+ } from "./mcp/index.ts";
83
+ export {
84
+ callTool,
85
+ listTools,
86
+ MCP_PROTOCOL_VERSION,
87
+ McpServer,
88
+ unwrapContent,
89
+ } from "./mcp/index.ts";
64
90
  export type {
65
91
  ChatDelta,
66
92
  ChatMessage,
@@ -74,6 +100,29 @@ export {
74
100
  resolveGatewayToken,
75
101
  resolveGatewayUrl,
76
102
  } from "./model/gateway.ts";
103
+ export type {
104
+ CommandContribution,
105
+ Disposable,
106
+ PanelContribution,
107
+ PanelRegion,
108
+ PluginContext,
109
+ RouteContribution,
110
+ RyuHostServices,
111
+ RyuPlugin,
112
+ RyuPluginModule,
113
+ SettingsSectionContribution,
114
+ StoreSectionContribution,
115
+ ThemeContribution,
116
+ } from "./plugin/ryu-plugin.ts";
117
+ export { toDisposable } from "./plugin/ryu-plugin.ts";
118
+ export type {
119
+ ActionAnnotations,
120
+ ActionEffect,
121
+ ActionManifestOptions,
122
+ ActionOptions,
123
+ ActionRunnable,
124
+ } from "./runnable/action.ts";
125
+ export { defineAction } from "./runnable/action.ts";
77
126
  export type {
78
127
  AgentCard,
79
128
  AgentManifestOptions,
@@ -91,6 +140,8 @@ export type {
91
140
  } from "./runnable/app.ts";
92
141
  export { appToolId, defineApp } from "./runnable/app.ts";
93
142
  export type {
143
+ BackgroundClient,
144
+ BackgroundProcess,
94
145
  DurableClient,
95
146
  EnginesClient,
96
147
  HttpPrimitiveTransportOptions,
@@ -105,6 +156,7 @@ export type {
105
156
  RealtimeClient,
106
157
  RealtimeSubscription,
107
158
  RyuPrimitives,
159
+ StorageClient,
108
160
  SttClient,
109
161
  TtsClient,
110
162
  } from "./runnable/primitives.ts";
@@ -121,6 +173,7 @@ export type {
121
173
  export type { SkillOptions } from "./runnable/skill.ts";
122
174
  export { defineSkill } from "./runnable/skill.ts";
123
175
  export type {
176
+ InlineToolManifestOptions,
124
177
  JsonSchemaProperty,
125
178
  ToolOptions,
126
179
  ToolRunnable,