@vetta-org/plugin-sdk 0.0.4 → 0.1.1

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 (59) hide show
  1. package/dist/agent.d.ts +5 -0
  2. package/dist/agent.d.ts.map +1 -1
  3. package/dist/agent.js.map +1 -1
  4. package/dist/command.d.ts +43 -0
  5. package/dist/command.d.ts.map +1 -1
  6. package/dist/command.js.map +1 -1
  7. package/dist/context.d.ts +6 -2
  8. package/dist/context.d.ts.map +1 -1
  9. package/dist/context.js.map +1 -1
  10. package/dist/conversation.d.ts +8 -1
  11. package/dist/conversation.d.ts.map +1 -1
  12. package/dist/conversation.js.map +1 -1
  13. package/dist/file-explorer.d.ts +87 -0
  14. package/dist/file-explorer.d.ts.map +1 -0
  15. package/dist/file-explorer.js +2 -0
  16. package/dist/file-explorer.js.map +1 -0
  17. package/dist/fs.d.ts +25 -0
  18. package/dist/fs.d.ts.map +1 -1
  19. package/dist/fs.js.map +1 -1
  20. package/dist/hooks.d.ts +4 -6
  21. package/dist/hooks.d.ts.map +1 -1
  22. package/dist/hooks.js +4 -6
  23. package/dist/hooks.js.map +1 -1
  24. package/dist/host-bridge.d.ts +2 -2
  25. package/dist/host-bridge.d.ts.map +1 -1
  26. package/dist/host-bridge.js.map +1 -1
  27. package/dist/images.d.ts +0 -41
  28. package/dist/images.d.ts.map +1 -1
  29. package/dist/images.js.map +1 -1
  30. package/dist/index.d.ts +11 -5
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +2 -1
  33. package/dist/index.js.map +1 -1
  34. package/dist/network.d.ts +38 -0
  35. package/dist/network.d.ts.map +1 -0
  36. package/dist/network.js +2 -0
  37. package/dist/network.js.map +1 -0
  38. package/dist/official.d.ts +0 -1
  39. package/dist/official.d.ts.map +1 -1
  40. package/dist/official.js.map +1 -1
  41. package/dist/permissions.d.ts +1 -1
  42. package/dist/permissions.d.ts.map +1 -1
  43. package/dist/permissions.js.map +1 -1
  44. package/dist/prompt-attachment.d.ts +15 -0
  45. package/dist/prompt-attachment.d.ts.map +1 -0
  46. package/dist/prompt-attachment.js +2 -0
  47. package/dist/prompt-attachment.js.map +1 -0
  48. package/dist/shortcuts.d.ts +60 -0
  49. package/dist/shortcuts.d.ts.map +1 -0
  50. package/dist/shortcuts.js +34 -0
  51. package/dist/shortcuts.js.map +1 -0
  52. package/dist/storage.d.ts +31 -0
  53. package/dist/storage.d.ts.map +1 -0
  54. package/dist/storage.js +2 -0
  55. package/dist/storage.js.map +1 -0
  56. package/dist/ui.d.ts +64 -9
  57. package/dist/ui.d.ts.map +1 -1
  58. package/dist/ui.js.map +1 -1
  59. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"permissions.js","sourceRoot":"","sources":["../src/permissions.ts"],"names":[],"mappings":"","sourcesContent":["export type PluginPermission =\n\t| \"ui.slot.global\"\n\t| \"ui.slot.file-preview\"\n\t| \"ui.slot.activity-tab\"\n\t| \"ui.slot.input-action\"\n\t| \"ui.slot.message\"\n\t| \"ui.slot.tool-call\"\n\t| \"ui.slot.turn-card\"\n\t| \"agent.session.read\"\n\t| \"agent.session.write\"\n\t| \"agent.command.run\"\n\t| \"agent.systemPrompt.read\"\n\t| \"agent.systemPrompt.write\"\n\t| \"agent.systemPrompt.fullControl\"\n\t| \"agent.skills.control\"\n\t| \"agent.mcp.control\"\n\t| \"agent.tools.control\"\n\t| \"agent.tools.register\"\n\t| \"agent.toolHandler.execute\"\n\t| \"agent.state.read\"\n\t| \"agent.state.write\"\n\t| \"agent.continuation.register\"\n\t| \"agent.runtime.configure\"\n\t| \"app.actions.register\"\n\t| \"app.actionHandler.execute\"\n\t| \"fs.read\"\n\t| \"fs.write\"\n\t| \"network.fetch\"\n\t| \"images.generate\"\n\t| \"settings.read\"\n\t| \"settings.write\";\n"]}
1
+ {"version":3,"file":"permissions.js","sourceRoot":"","sources":["../src/permissions.ts"],"names":[],"mappings":"","sourcesContent":["export type PluginPermission =\n\t| \"ui.slot.global\"\n\t| \"ui.slot.file-preview\"\n\t| \"ui.slot.activity-tab\"\n\t| \"ui.slot.input-action\"\n\t| \"ui.slot.message\"\n\t| \"ui.slot.tool-call\"\n\t| \"ui.slot.turn-card\"\n\t| \"ui.shortcuts.register\"\n\t| \"ui.file-explorer.decorations\"\n\t| \"ui.file-explorer.context-menu\"\n\t| \"ui.file-explorer.toolbar\"\n\t| \"workspace.read\"\n\t| \"agent.session.read\"\n\t| \"agent.session.write\"\n\t| \"agent.command.run\"\n\t| \"agent.command.spawn\"\n\t| \"agent.systemPrompt.read\"\n\t| \"agent.systemPrompt.write\"\n\t| \"agent.systemPrompt.fullControl\"\n\t| \"agent.skills.control\"\n\t| \"agent.mcp.control\"\n\t| \"agent.tools.control\"\n\t| \"agent.tools.register\"\n\t| \"agent.toolHandler.execute\"\n\t| \"agent.state.read\"\n\t| \"agent.state.write\"\n\t| \"agent.continuation.register\"\n\t| \"agent.runtime.configure\"\n\t| \"app.actions.register\"\n\t| \"app.actionHandler.execute\"\n\t| \"fs.read\"\n\t| \"fs.write\"\n\t| \"network.fetch\"\n\t| \"storage.read\"\n\t| \"storage.write\"\n\t| \"settings.read\"\n\t| \"settings.write\";\n"]}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * One-shot plugin-owned context attached to the next outgoing prompt.
3
+ *
4
+ * The host renders only the label/icon, merges metadata and hidden instructions
5
+ * at send time, then clears the attachment. Domain payloads stay in plugin
6
+ * storage or out-of-band references.
7
+ */
8
+ export interface PluginPromptAttachment {
9
+ id: string;
10
+ label: string;
11
+ icon?: string;
12
+ instructions?: string[];
13
+ metadata?: Record<string, unknown>;
14
+ }
15
+ //# sourceMappingURL=prompt-attachment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt-attachment.d.ts","sourceRoot":"","sources":["../src/prompt-attachment.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,WAAW,sBAAsB;IACtC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC","sourcesContent":["/**\n * One-shot plugin-owned context attached to the next outgoing prompt.\n *\n * The host renders only the label/icon, merges metadata and hidden instructions\n * at send time, then clears the attachment. Domain payloads stay in plugin\n * storage or out-of-band references.\n */\nexport interface PluginPromptAttachment {\n\tid: string;\n\tlabel: string;\n\ticon?: string;\n\tinstructions?: string[];\n\tmetadata?: Record<string, unknown>;\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=prompt-attachment.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt-attachment.js","sourceRoot":"","sources":["../src/prompt-attachment.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * One-shot plugin-owned context attached to the next outgoing prompt.\n *\n * The host renders only the label/icon, merges metadata and hidden instructions\n * at send time, then clears the attachment. Domain payloads stay in plugin\n * storage or out-of-band references.\n */\nexport interface PluginPromptAttachment {\n\tid: string;\n\tlabel: string;\n\ticon?: string;\n\tinstructions?: string[];\n\tmetadata?: Record<string, unknown>;\n}\n"]}
@@ -0,0 +1,60 @@
1
+ import type { Disposable } from "./disposable.js";
2
+ /**
3
+ * Keyboard shortcut scopes (priority high → low), mirroring the host stack:
4
+ * - modal: dialogs (often exclusive)
5
+ * - overlay: pickers / floating UI
6
+ * - surface: previews, panels, editors
7
+ *
8
+ * Plugins cannot register `app` — that layer is reserved for host-configurable
9
+ * global actions (settings → shortcuts).
10
+ */
11
+ export type PluginShortcutScopeKind = "surface" | "overlay" | "modal";
12
+ export type PluginShortcutWhen = "always" | "editable" | "not-editable";
13
+ export interface PluginShortcutBinding {
14
+ /**
15
+ * Serialized combo matching the host `eventToShortcut` format, e.g.
16
+ * `"mod+s"`, `"escape"`, `"arrowleft"`, `"="`, `"-"`.
17
+ */
18
+ key: string;
19
+ run: (event: KeyboardEvent) => void;
20
+ /** Default `"always"`. Restrict to / out of editable targets. */
21
+ when?: PluginShortcutWhen;
22
+ /** Default true. */
23
+ preventDefault?: boolean;
24
+ /** Default true. */
25
+ stopPropagation?: boolean;
26
+ }
27
+ export interface PluginShortcutScopeContribution {
28
+ /** Local id; host namespaces as `pluginId:id`. */
29
+ id: string;
30
+ kind: PluginShortcutScopeKind;
31
+ /** When true, unmatched keys do not fall through to lower scopes. */
32
+ exclusive?: boolean;
33
+ /** Dynamic gate; re-checked on every keydown. */
34
+ enabled?: () => boolean;
35
+ /**
36
+ * Static list or factory. Factories are read each keydown so handlers can
37
+ * close over the latest component state without re-registering.
38
+ */
39
+ bindings: readonly PluginShortcutBinding[] | (() => readonly PluginShortcutBinding[]);
40
+ }
41
+ export type PluginRegisterShortcutScope = (contribution: PluginShortcutScopeContribution) => Disposable;
42
+ export interface UsePluginShortcutScopeOptions {
43
+ /** Stable local id (host namespaces with plugin id). */
44
+ id: string;
45
+ kind: PluginShortcutScopeKind;
46
+ /** When false, scope is not registered. Default true. */
47
+ active?: boolean;
48
+ exclusive?: boolean;
49
+ enabled?: () => boolean;
50
+ bindings: readonly PluginShortcutBinding[];
51
+ }
52
+ /**
53
+ * Component-scoped shortcut registration for plugins.
54
+ *
55
+ * Pass `ctx.ui.registerShortcutScope` captured in `activate` (module-level),
56
+ * same pattern as `notify`. While `active` is true, bindings participate in the
57
+ * host ShortcutScopeStack — do not add ad-hoc `document` keydown listeners.
58
+ */
59
+ export declare function usePluginShortcutScope(register: PluginRegisterShortcutScope | null | undefined, options: UsePluginShortcutScopeOptions): void;
60
+ //# sourceMappingURL=shortcuts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shortcuts.d.ts","sourceRoot":"","sources":["../src/shortcuts.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD;;;;;;;;GAQG;AACH,MAAM,MAAM,uBAAuB,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;AAEtE,MAAM,MAAM,kBAAkB,GAAG,QAAQ,GAAG,UAAU,GAAG,cAAc,CAAC;AAExE,MAAM,WAAW,qBAAqB;IACrC;;;OAGG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IACpC,iEAAiE;IACjE,IAAI,CAAC,EAAE,kBAAkB,CAAC;IAC1B,oBAAoB;IACpB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,oBAAoB;IACpB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,+BAA+B;IAC/C,kDAAkD;IAClD,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,uBAAuB,CAAC;IAC9B,qEAAqE;IACrE,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,iDAAiD;IACjD,OAAO,CAAC,EAAE,MAAM,OAAO,CAAC;IACxB;;;OAGG;IACH,QAAQ,EAAE,SAAS,qBAAqB,EAAE,GAAG,CAAC,MAAM,SAAS,qBAAqB,EAAE,CAAC,CAAC;CACtF;AAED,MAAM,MAAM,2BAA2B,GAAG,CAAC,YAAY,EAAE,+BAA+B,KAAK,UAAU,CAAC;AAExG,MAAM,WAAW,6BAA6B;IAC7C,wDAAwD;IACxD,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,uBAAuB,CAAC;IAC9B,yDAAyD;IACzD,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,OAAO,CAAC;IACxB,QAAQ,EAAE,SAAS,qBAAqB,EAAE,CAAC;CAC3C;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACrC,QAAQ,EAAE,2BAA2B,GAAG,IAAI,GAAG,SAAS,EACxD,OAAO,EAAE,6BAA6B,GACpC,IAAI,CAyBN","sourcesContent":["import { useEffect, useRef } from \"react\";\nimport type { Disposable } from \"./disposable.js\";\n\n/**\n * Keyboard shortcut scopes (priority high → low), mirroring the host stack:\n * - modal: dialogs (often exclusive)\n * - overlay: pickers / floating UI\n * - surface: previews, panels, editors\n *\n * Plugins cannot register `app` — that layer is reserved for host-configurable\n * global actions (settings → shortcuts).\n */\nexport type PluginShortcutScopeKind = \"surface\" | \"overlay\" | \"modal\";\n\nexport type PluginShortcutWhen = \"always\" | \"editable\" | \"not-editable\";\n\nexport interface PluginShortcutBinding {\n\t/**\n\t * Serialized combo matching the host `eventToShortcut` format, e.g.\n\t * `\"mod+s\"`, `\"escape\"`, `\"arrowleft\"`, `\"=\"`, `\"-\"`.\n\t */\n\tkey: string;\n\trun: (event: KeyboardEvent) => void;\n\t/** Default `\"always\"`. Restrict to / out of editable targets. */\n\twhen?: PluginShortcutWhen;\n\t/** Default true. */\n\tpreventDefault?: boolean;\n\t/** Default true. */\n\tstopPropagation?: boolean;\n}\n\nexport interface PluginShortcutScopeContribution {\n\t/** Local id; host namespaces as `pluginId:id`. */\n\tid: string;\n\tkind: PluginShortcutScopeKind;\n\t/** When true, unmatched keys do not fall through to lower scopes. */\n\texclusive?: boolean;\n\t/** Dynamic gate; re-checked on every keydown. */\n\tenabled?: () => boolean;\n\t/**\n\t * Static list or factory. Factories are read each keydown so handlers can\n\t * close over the latest component state without re-registering.\n\t */\n\tbindings: readonly PluginShortcutBinding[] | (() => readonly PluginShortcutBinding[]);\n}\n\nexport type PluginRegisterShortcutScope = (contribution: PluginShortcutScopeContribution) => Disposable;\n\nexport interface UsePluginShortcutScopeOptions {\n\t/** Stable local id (host namespaces with plugin id). */\n\tid: string;\n\tkind: PluginShortcutScopeKind;\n\t/** When false, scope is not registered. Default true. */\n\tactive?: boolean;\n\texclusive?: boolean;\n\tenabled?: () => boolean;\n\tbindings: readonly PluginShortcutBinding[];\n}\n\n/**\n * Component-scoped shortcut registration for plugins.\n *\n * Pass `ctx.ui.registerShortcutScope` captured in `activate` (module-level),\n * same pattern as `notify`. While `active` is true, bindings participate in the\n * host ShortcutScopeStack — do not add ad-hoc `document` keydown listeners.\n */\nexport function usePluginShortcutScope(\n\tregister: PluginRegisterShortcutScope | null | undefined,\n\toptions: UsePluginShortcutScopeOptions,\n): void {\n\tconst bindingsRef = useRef(options.bindings);\n\tbindingsRef.current = options.bindings;\n\tconst enabledRef = useRef(options.enabled);\n\tenabledRef.current = options.enabled;\n\n\tconst keySignature = options.bindings.map((b) => `${b.key}:${b.when ?? \"always\"}`).join(\"|\");\n\tconst active = options.active !== false;\n\n\tuseEffect(() => {\n\t\tif (!register || !active) return;\n\t\tconst handle = register({\n\t\t\tid: options.id,\n\t\t\tkind: options.kind,\n\t\t\texclusive: options.exclusive,\n\t\t\tenabled: enabledRef.current\n\t\t\t\t? () => {\n\t\t\t\t\t\tconst gate = enabledRef.current;\n\t\t\t\t\t\treturn gate ? gate() : true;\n\t\t\t\t\t}\n\t\t\t\t: undefined,\n\t\t\tbindings: () => bindingsRef.current,\n\t\t});\n\t\treturn () => handle.dispose();\n\t}, [register, options.id, options.kind, options.exclusive, active, keySignature]);\n}\n"]}
@@ -0,0 +1,34 @@
1
+ import { useEffect, useRef } from "react";
2
+ /**
3
+ * Component-scoped shortcut registration for plugins.
4
+ *
5
+ * Pass `ctx.ui.registerShortcutScope` captured in `activate` (module-level),
6
+ * same pattern as `notify`. While `active` is true, bindings participate in the
7
+ * host ShortcutScopeStack — do not add ad-hoc `document` keydown listeners.
8
+ */
9
+ export function usePluginShortcutScope(register, options) {
10
+ const bindingsRef = useRef(options.bindings);
11
+ bindingsRef.current = options.bindings;
12
+ const enabledRef = useRef(options.enabled);
13
+ enabledRef.current = options.enabled;
14
+ const keySignature = options.bindings.map((b) => `${b.key}:${b.when ?? "always"}`).join("|");
15
+ const active = options.active !== false;
16
+ useEffect(() => {
17
+ if (!register || !active)
18
+ return;
19
+ const handle = register({
20
+ id: options.id,
21
+ kind: options.kind,
22
+ exclusive: options.exclusive,
23
+ enabled: enabledRef.current
24
+ ? () => {
25
+ const gate = enabledRef.current;
26
+ return gate ? gate() : true;
27
+ }
28
+ : undefined,
29
+ bindings: () => bindingsRef.current,
30
+ });
31
+ return () => handle.dispose();
32
+ }, [register, options.id, options.kind, options.exclusive, active, keySignature]);
33
+ }
34
+ //# sourceMappingURL=shortcuts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shortcuts.js","sourceRoot":"","sources":["../src/shortcuts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AA2D1C;;;;;;GAMG;AACH,MAAM,UAAU,sBAAsB,CACrC,QAAwD,EACxD,OAAsC,EAC/B;IACP,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC7C,WAAW,CAAC,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC;IACvC,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3C,UAAU,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAErC,MAAM,YAAY,GAAG,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,IAAI,IAAI,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC7F,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,KAAK,KAAK,CAAC;IAExC,SAAS,CAAC,GAAG,EAAE,CAAC;QACf,IAAI,CAAC,QAAQ,IAAI,CAAC,MAAM;YAAE,OAAO;QACjC,MAAM,MAAM,GAAG,QAAQ,CAAC;YACvB,EAAE,EAAE,OAAO,CAAC,EAAE;YACd,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,OAAO,EAAE,UAAU,CAAC,OAAO;gBAC1B,CAAC,CAAC,GAAG,EAAE,CAAC;oBACN,MAAM,IAAI,GAAG,UAAU,CAAC,OAAO,CAAC;oBAChC,OAAO,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;gBAAA,CAC5B;gBACF,CAAC,CAAC,SAAS;YACZ,QAAQ,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,OAAO;SACnC,CAAC,CAAC;QACH,OAAO,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;IAAA,CAC9B,EAAE,CAAC,QAAQ,EAAE,OAAO,CAAC,EAAE,EAAE,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC;AAAA,CAClF","sourcesContent":["import { useEffect, useRef } from \"react\";\nimport type { Disposable } from \"./disposable.js\";\n\n/**\n * Keyboard shortcut scopes (priority high → low), mirroring the host stack:\n * - modal: dialogs (often exclusive)\n * - overlay: pickers / floating UI\n * - surface: previews, panels, editors\n *\n * Plugins cannot register `app` — that layer is reserved for host-configurable\n * global actions (settings → shortcuts).\n */\nexport type PluginShortcutScopeKind = \"surface\" | \"overlay\" | \"modal\";\n\nexport type PluginShortcutWhen = \"always\" | \"editable\" | \"not-editable\";\n\nexport interface PluginShortcutBinding {\n\t/**\n\t * Serialized combo matching the host `eventToShortcut` format, e.g.\n\t * `\"mod+s\"`, `\"escape\"`, `\"arrowleft\"`, `\"=\"`, `\"-\"`.\n\t */\n\tkey: string;\n\trun: (event: KeyboardEvent) => void;\n\t/** Default `\"always\"`. Restrict to / out of editable targets. */\n\twhen?: PluginShortcutWhen;\n\t/** Default true. */\n\tpreventDefault?: boolean;\n\t/** Default true. */\n\tstopPropagation?: boolean;\n}\n\nexport interface PluginShortcutScopeContribution {\n\t/** Local id; host namespaces as `pluginId:id`. */\n\tid: string;\n\tkind: PluginShortcutScopeKind;\n\t/** When true, unmatched keys do not fall through to lower scopes. */\n\texclusive?: boolean;\n\t/** Dynamic gate; re-checked on every keydown. */\n\tenabled?: () => boolean;\n\t/**\n\t * Static list or factory. Factories are read each keydown so handlers can\n\t * close over the latest component state without re-registering.\n\t */\n\tbindings: readonly PluginShortcutBinding[] | (() => readonly PluginShortcutBinding[]);\n}\n\nexport type PluginRegisterShortcutScope = (contribution: PluginShortcutScopeContribution) => Disposable;\n\nexport interface UsePluginShortcutScopeOptions {\n\t/** Stable local id (host namespaces with plugin id). */\n\tid: string;\n\tkind: PluginShortcutScopeKind;\n\t/** When false, scope is not registered. Default true. */\n\tactive?: boolean;\n\texclusive?: boolean;\n\tenabled?: () => boolean;\n\tbindings: readonly PluginShortcutBinding[];\n}\n\n/**\n * Component-scoped shortcut registration for plugins.\n *\n * Pass `ctx.ui.registerShortcutScope` captured in `activate` (module-level),\n * same pattern as `notify`. While `active` is true, bindings participate in the\n * host ShortcutScopeStack — do not add ad-hoc `document` keydown listeners.\n */\nexport function usePluginShortcutScope(\n\tregister: PluginRegisterShortcutScope | null | undefined,\n\toptions: UsePluginShortcutScopeOptions,\n): void {\n\tconst bindingsRef = useRef(options.bindings);\n\tbindingsRef.current = options.bindings;\n\tconst enabledRef = useRef(options.enabled);\n\tenabledRef.current = options.enabled;\n\n\tconst keySignature = options.bindings.map((b) => `${b.key}:${b.when ?? \"always\"}`).join(\"|\");\n\tconst active = options.active !== false;\n\n\tuseEffect(() => {\n\t\tif (!register || !active) return;\n\t\tconst handle = register({\n\t\t\tid: options.id,\n\t\t\tkind: options.kind,\n\t\t\texclusive: options.exclusive,\n\t\t\tenabled: enabledRef.current\n\t\t\t\t? () => {\n\t\t\t\t\t\tconst gate = enabledRef.current;\n\t\t\t\t\t\treturn gate ? gate() : true;\n\t\t\t\t\t}\n\t\t\t\t: undefined,\n\t\t\tbindings: () => bindingsRef.current,\n\t\t});\n\t\treturn () => handle.dispose();\n\t}, [register, options.id, options.kind, options.exclusive, active, keySignature]);\n}\n"]}
@@ -0,0 +1,31 @@
1
+ export interface PluginStoredBlobRef {
2
+ id: string;
3
+ url: string;
4
+ mimeType: string;
5
+ }
6
+ export interface PluginStoredBlob {
7
+ data: string;
8
+ mimeType: string;
9
+ }
10
+ export interface PluginPutBlobInput {
11
+ id?: string;
12
+ data: string;
13
+ mimeType: string;
14
+ }
15
+ /**
16
+ * Private persistent storage scoped to the current plugin.
17
+ *
18
+ * JSON keys and file paths are relative to the plugin namespace. Blob bytes are
19
+ * base64 encoded at the bridge boundary and exposed through a host media URL.
20
+ */
21
+ export interface PluginStorageApi {
22
+ readJson<T>(key: string): Promise<T | null>;
23
+ writeJson(key: string, value: unknown): Promise<void>;
24
+ list(prefix?: string): Promise<string[]>;
25
+ readFile(path: string): Promise<string | null>;
26
+ writeFile(path: string, data: string): Promise<void>;
27
+ putBlob(input: PluginPutBlobInput): Promise<PluginStoredBlobRef>;
28
+ readBlob(id: string): Promise<PluginStoredBlob | null>;
29
+ getBlobRef(id: string): Promise<PluginStoredBlobRef | null>;
30
+ }
31
+ //# sourceMappingURL=storage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,mBAAmB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB;IAClC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CACjB;AAED;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAC5C,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtD,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACzC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC/C,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,OAAO,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACjE,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IACvD,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAAC;CAC5D","sourcesContent":["export interface PluginStoredBlobRef {\n\tid: string;\n\turl: string;\n\tmimeType: string;\n}\n\nexport interface PluginStoredBlob {\n\tdata: string;\n\tmimeType: string;\n}\n\nexport interface PluginPutBlobInput {\n\tid?: string;\n\tdata: string;\n\tmimeType: string;\n}\n\n/**\n * Private persistent storage scoped to the current plugin.\n *\n * JSON keys and file paths are relative to the plugin namespace. Blob bytes are\n * base64 encoded at the bridge boundary and exposed through a host media URL.\n */\nexport interface PluginStorageApi {\n\treadJson<T>(key: string): Promise<T | null>;\n\twriteJson(key: string, value: unknown): Promise<void>;\n\tlist(prefix?: string): Promise<string[]>;\n\treadFile(path: string): Promise<string | null>;\n\twriteFile(path: string, data: string): Promise<void>;\n\tputBlob(input: PluginPutBlobInput): Promise<PluginStoredBlobRef>;\n\treadBlob(id: string): Promise<PluginStoredBlob | null>;\n\tgetBlobRef(id: string): Promise<PluginStoredBlobRef | null>;\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=storage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.js","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"","sourcesContent":["export interface PluginStoredBlobRef {\n\tid: string;\n\turl: string;\n\tmimeType: string;\n}\n\nexport interface PluginStoredBlob {\n\tdata: string;\n\tmimeType: string;\n}\n\nexport interface PluginPutBlobInput {\n\tid?: string;\n\tdata: string;\n\tmimeType: string;\n}\n\n/**\n * Private persistent storage scoped to the current plugin.\n *\n * JSON keys and file paths are relative to the plugin namespace. Blob bytes are\n * base64 encoded at the bridge boundary and exposed through a host media URL.\n */\nexport interface PluginStorageApi {\n\treadJson<T>(key: string): Promise<T | null>;\n\twriteJson(key: string, value: unknown): Promise<void>;\n\tlist(prefix?: string): Promise<string[]>;\n\treadFile(path: string): Promise<string | null>;\n\twriteFile(path: string, data: string): Promise<void>;\n\tputBlob(input: PluginPutBlobInput): Promise<PluginStoredBlobRef>;\n\treadBlob(id: string): Promise<PluginStoredBlob | null>;\n\tgetBlobRef(id: string): Promise<PluginStoredBlobRef | null>;\n}\n"]}
package/dist/ui.d.ts CHANGED
@@ -2,7 +2,9 @@ import type { ComponentType, ReactNode } from "react";
2
2
  import type { ConversationMessage } from "./conversation.js";
3
3
  import type { Disposable } from "./disposable.js";
4
4
  import type { PluginImageRef } from "./images.js";
5
+ import type { PluginPromptAttachment } from "./prompt-attachment.js";
5
6
  import type { ConversationScenario } from "./scenario.js";
7
+ import type { PluginShortcutScopeContribution } from "./shortcuts.js";
6
8
  export interface PluginGlobalSlotContribution {
7
9
  id: string;
8
10
  component: ComponentType;
@@ -73,6 +75,13 @@ export interface PluginActivityTabContribution {
73
75
  * `["project", "conversation"]`)。会话页插槽据此随对话类型显隐。
74
76
  */
75
77
  scope_use?: readonly ConversationScenario[];
78
+ /**
79
+ * 注册后是否默认在标签栏里(缺省 `true`)。声明 `false` 表示「出现条件由我
80
+ * 自己决定」——注册只是入池,之后由 {@link PluginUiApi.setActivityTabVisible}
81
+ * 或 {@link PluginUiApi.openActivityTab} 决定何时上栏(如 git 只在仓库目录、
82
+ * 工作台跟随输入栏 toggle)。无论哪种,用户仍可用减号手动隐藏。
83
+ */
84
+ initiallyVisible?: boolean;
76
85
  }
77
86
  /** Options for {@link PluginUiApi.openActivityTab}. */
78
87
  export interface PluginOpenActivityTabOptions {
@@ -83,13 +92,21 @@ export interface PluginOpenActivityTabOptions {
83
92
  */
84
93
  width?: number | "max";
85
94
  }
95
+ export interface PluginCaptureRegion {
96
+ x: number;
97
+ y: number;
98
+ width: number;
99
+ height: number;
100
+ }
86
101
  /**
87
102
  * Decoration a plugin contributes to the next outgoing prompt while its input
88
- * action is active. `metadata` is shallow-merged into the prompt request's
89
- * `metadata` bag, which the agent turn can read (e.g. `{ imageMode: true }`).
103
+ * action is active. `metadata` is shallow-merged into the prompt request and
104
+ * `instructions` are appended as hidden, model-visible guidance.
90
105
  */
91
106
  export interface PluginPromptDecoration {
92
107
  metadata?: Record<string, unknown>;
108
+ /** Hidden plugin-owned instructions appended to the next agent turn. */
109
+ instructions?: string[];
93
110
  }
94
111
  /**
95
112
  * An action button shown in a row beneath the AI input bar. Rendered as a
@@ -290,6 +307,14 @@ export interface PluginUiApi {
290
307
  * (fail-closed). Needs the `ui.slot.turn-card` permission.
291
308
  */
292
309
  registerTurnCard(contribution: PluginTurnCardContribution): Disposable;
310
+ /**
311
+ * Register a keyboard shortcut scope on the host's shared ShortcutScopeStack
312
+ * (same path as host UI: modal > overlay > surface > app). Needs
313
+ * `ui.shortcuts.register`. Plugins cannot use kind `"app"` (reserved for
314
+ * host-configurable global actions). Prefer {@link usePluginShortcutScope}
315
+ * from React components with a module-captured `registerShortcutScope`.
316
+ */
317
+ registerShortcutScope(contribution: PluginShortcutScopeContribution): Disposable;
293
318
  /**
294
319
  * Programmatically attach (if needed) and activate one of this plugin's
295
320
  * own activity tabs in the current conversation's activity panel. `tabId`
@@ -303,14 +328,31 @@ export interface PluginUiApi {
303
328
  */
304
329
  openActivityTab(tabId: string, options?: PluginOpenActivityTabOptions): void;
305
330
  /**
306
- * Bind (or clear, with `null`) an image as the "edit target" for the next
307
- * outgoing prompt. The host renders it as a thumbnail capsule in the AI input
308
- * bar's top capsule strip and, at send time, injects `metadata.editImageId`
309
- * (the ref's id) so this turn edits that image. One-shot: cleared after the
310
- * prompt is sent (or when the user closes the capsule). Only the image
311
- * reference (id/url) crosses over — bytes stay out-of-band.
331
+ * 把本插件的某个活动面板标签卡在当前会话里上栏 / 下栏,**不激活也不展开
332
+ * 面板**——`openActivityTab` 是「用户此刻要看它」,这个是「它现在该不该在
333
+ * 栏里」。配合 `initiallyVisible: false` 使用,插件即可完全掌握自己标签卡的
334
+ * 出现条件(如 git 只在仓库目录里上栏、工作台跟随输入栏 toggle)。
335
+ *
336
+ * 上栏记录按会话 cwd 持久化(ADR-0026),所以只需在条件变化时调用一次;
337
+ * 用户随后用减号手动隐藏的结果不会被重复调用覆盖。当前没有活动会话时为
338
+ * no-op(无处记录),插件应在会话就绪后重新判定。
312
339
  */
313
- setEditImageAttachment(ref: PluginImageRef | null): void;
340
+ setActivityTabVisible(tabId: string, visible: boolean): void;
341
+ /**
342
+ * 直接设置活动面板宽度:像素值,或 `"max"` 表示当前窗口下的最大宽度(宿主仍
343
+ * 会夹到自己的 min/max 内,必要时自动收起侧边栏)。
344
+ *
345
+ * 与 `openActivityTab(id, { width })` 的区别:那里的宽度只在标签卡首次 attach
346
+ * 时生效(避免 activate 重放覆盖用户手拖的宽度);这个是命令式的,每次调用都
347
+ * 生效,供插件在自己的标签卡被激活、进入某种视图时按需调整。用户随后仍可拖动。
348
+ */
349
+ setActivityPanelWidth(width: number | "max"): void;
350
+ /**
351
+ * Bind or clear plugin-owned one-shot context for the next outgoing prompt.
352
+ * The host renders its label/icon, merges metadata and hidden instructions,
353
+ * then clears it after send or when the user closes the capsule.
354
+ */
355
+ setPromptAttachment(attachment: PluginPromptAttachment | null): void;
314
356
  /**
315
357
  * Open the host's global full-screen image previewer for the given image.
316
358
  * Only the image reference (id/url) crosses over — bytes stay out-of-band.
@@ -325,6 +367,19 @@ export interface PluginUiApi {
325
367
  * The host owns the navigation; the plugin only asks to jump there.
326
368
  */
327
369
  openPluginSettings(): void;
370
+ /**
371
+ * Capture a rectangle in the current Vetta window and open the host save
372
+ * dialog. Coordinates use renderer DIP values such as getBoundingClientRect().
373
+ * Requires `ui.slot.activity-tab`.
374
+ */
375
+ captureRegion(rect: PluginCaptureRegion, defaultFileName: string): Promise<string | null>;
376
+ /**
377
+ * Copy an image to the system clipboard. Takes a `data:image/...;base64,`
378
+ * URL and goes through the native clipboard, so it does not depend on the
379
+ * renderer's `ClipboardItem` support. No permission required — the plugin
380
+ * can only write what it already rendered.
381
+ */
382
+ copyImage(dataUrl: string): Promise<void>;
328
383
  /**
329
384
  * Show a global toast in the host UI (bottom-right). No permission required.
330
385
  * Prefer this over swallowing errors into opaque UI copy: pass `error` so
package/dist/ui.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ComponentType, ReactNode } from \"react\";\nimport type { ConversationMessage } from \"./conversation.js\";\nimport type { Disposable } from \"./disposable.js\";\nimport type { PluginImageRef } from \"./images.js\";\nimport type { ConversationScenario } from \"./scenario.js\";\n\nexport interface PluginGlobalSlotContribution {\n\tid: string;\n\tcomponent: ComponentType;\n}\n\nexport interface PluginAudioMetadata {\n\t/** Embedded title; preview renderers should fall back to file.name. */\n\ttitle?: string;\n\tartist?: string;\n\t/** Embedded cover art as a Data URL when available. */\n\tcoverDataUrl?: string;\n}\n\nexport interface PluginPreviewUrlOptions {\n\t/** Optional media hint for ambiguous containers such as webm. */\n\tmediaKind?: \"audio\" | \"video\";\n}\n\n/**\n * The file handed to a file-preview plugin component. The host does NOT\n * pre-read or guess encoding — the plugin decides whether to read text or\n * bytes. `path` is also exposed for plugins that prefer native fetch.\n */\nexport interface PluginPreviewFile {\n\tpath: string | null;\n\tname: string;\n\textension: string;\n\tmime: string;\n\tsize: number;\n\t/** Read the file as a UTF-8 string. */\n\treadText(): Promise<string>;\n\t/** Read the raw file bytes. */\n\treadBytes(): Promise<ArrayBuffer>;\n\t/** A fetchable streaming URL for the file (Range-capable). */\n\tgetUrl(options?: PluginPreviewUrlOptions): string;\n\t/**\n\t * Subscribe to on-disk changes of this file. The listener fires (debounced\n\t * by the host) whenever the file's contents change, letting previews update\n\t * live. Returns a Disposable; call dispose() to stop watching. A no-op for\n\t * files without a real path (url-only sources).\n\t */\n\twatch(listener: () => void): Disposable;\n\t/**\n\t * Host-provided audio metadata for local media files. Returns null when the\n\t * host cannot provide metadata, e.g. url-only sources or unsupported files.\n\t */\n\tgetAudioMetadata?(): Promise<PluginAudioMetadata | null>;\n}\n\nexport interface PluginFilePreviewProps {\n\tfile: PluginPreviewFile;\n}\n\nexport interface PluginFilePreviewContribution {\n\t/** Lower-case extensions without the dot, e.g. [\"svg\"]. */\n\textensions: string[];\n\tcomponent: ComponentType<PluginFilePreviewProps>;\n}\n\n/**\n * An activity-panel tab contributed by a plugin. Registering only adds the\n * tab to the \"addable pool\" — it renders only after the user attaches it in\n * the activity panel (scoped by session cwd).\n */\nexport interface PluginActivityTabContribution {\n\tid: string;\n\tlabel: string;\n\t/** Tab icon as a React node (not an iconify class string). */\n\ticon?: ReactNode;\n\tcomponent: ComponentType;\n\t/**\n\t * 允许该标签卡出现的对话场景 slug 列表(镜像工具的 scope_use)。**fail-closed**:\n\t * 未声明/空数组 = 任何会话都不显示;声明后仅在列出的场景里出现(如\n\t * `[\"project\", \"conversation\"]`)。会话页插槽据此随对话类型显隐。\n\t */\n\tscope_use?: readonly ConversationScenario[];\n}\n\n/** Options for {@link PluginUiApi.openActivityTab}. */\nexport interface PluginOpenActivityTabOptions {\n\t/**\n\t * Desired panel width as it opens: a pixel number, or `\"max\"` for the widest\n\t * the current window allows. The host clamps to its min/max bounds. Omit to\n\t * keep the user's current width.\n\t */\n\twidth?: number | \"max\";\n}\n\n/**\n * Decoration a plugin contributes to the next outgoing prompt while its input\n * action is active. `metadata` is shallow-merged into the prompt request's\n * `metadata` bag, which the agent turn can read (e.g. `{ imageMode: true }`).\n */\nexport interface PluginPromptDecoration {\n\tmetadata?: Record<string, unknown>;\n}\n\n/**\n * An action button shown in a row beneath the AI input bar. Rendered as a\n * toggle: clicking activates, clicking again deactivates. While active, the\n * host calls `decoratePrompt()` before each send and merges the returned\n * metadata into the outgoing prompt request. The plugin owns any side state\n * (it is the same Module Federation instance as its other slots).\n */\nexport interface PluginInputActionContribution {\n\tid: string;\n\tlabel: string;\n\t/** Button icon as a React node (not an iconify class string). */\n\ticon?: ReactNode;\n\t/** Whether the action begins in the active state. Defaults to false. */\n\tdefaultActive?: boolean;\n\t/**\n\t * 该输入动作所依赖的 agent 工具名。设置后,仅当该工具在当前会话处于激活(按工具的\n\t * scope_use 解析)时才显示这个 badge——避免在工具被场景屏蔽(如批量任务)时仍显示一个\n\t * 点了也无效的开关。不设则始终显示。例如「图像生成」设为 \"generate_image\"。\n\t */\n\trequiresActiveTool?: string;\n\t/**\n\t * 允许该 toggle 出现的对话场景 slug 列表(镜像工具的 scope_use)。**fail-closed**:\n\t * 未声明/空数组 = 任何会话都不显示;声明后仅在列出的场景里出现。与 `requiresActiveTool`\n\t * 取「与」:两者都满足才显示。\n\t */\n\tscope_use?: readonly ConversationScenario[];\n\t/**\n\t * When true, this plugin's agent contributions (tools / skills / MCP /\n\t * systemPrompt) and Activity Tabs are hard-isolated: inactive until this\n\t * toggle is on (ADR-0041, knowledgeMode-style). Default false.\n\t */\n\thardIsolation?: boolean;\n\t/**\n\t * Fired when the user toggles the action; `active` is the new state. Return\n\t * `false` when `active` is true to VETO the activation (e.g. the plugin needs\n\t * configuration first) — the toggle stays off. Deactivation can't be vetoed.\n\t */\n\tonToggle?(active: boolean): boolean | void;\n\t/**\n\t * Called by the host before sending while the action is active. The\n\t * returned metadata is merged into the outgoing prompt request.\n\t */\n\tdecoratePrompt?(): PluginPromptDecoration | void;\n}\n\n/**\n * A declarative, serializable descriptor for one card rendered beneath a\n * message. `type` selects a registered card renderer; `key` drives cross-turn\n * dedup (same key across turns = one logical card, shown only under its latest\n * anchor); `payload` carries STABLE REFERENCES (e.g. image ids), not a content\n * snapshot — the renderer resolves live state from it. `title`/`icon` label the\n * card's tab (override the renderer's registration defaults). Rides a tool\n * result's out-of-band `details.cards`; crosses the agent→host boundary, so\n * `icon` is an icon-symbol string, not a node.\n */\nexport interface CardDescriptor {\n\ttype: string;\n\tkey?: string;\n\tpayload?: unknown;\n\ttitle?: string;\n\ticon?: string;\n}\n\n/** A pending (in-flight) tool call handed to a renderer's `pendingFor`. */\nexport interface PluginPendingToolCall {\n\ttoolName: string;\n\targs: Record<string, unknown>;\n}\n\n/**\n * Props for a card renderer. `descriptor` is the card's data; `pending` is true\n * while it was synthesized from an in-flight tool (render a skeleton); `message`\n * is the anchoring conversation message.\n */\nexport interface PluginCardProps {\n\tdescriptor: CardDescriptor;\n\tpending: boolean;\n\tmessage: ConversationMessage;\n}\n\n/**\n * A card renderer registered by a plugin, keyed by `type`. A descriptor whose\n * `type` matches is rendered by `component`. `title`/`icon` are the default tab\n * label/icon (a descriptor may override `title`). `pendingFor`, given an\n * in-flight tool call, returns a provisional descriptor so a skeleton card\n * appears (and claims a tab) before the tool's result lands — or null when this\n * renderer doesn't handle that tool. The `type` must be globally unique across\n * plugins (convention: prefix with the plugin id, e.g. \"image-gen:preview\").\n */\nexport interface PluginCardRendererContribution {\n\ttype: string;\n\tcomponent: ComponentType<PluginCardProps>;\n\ttitle?: string;\n\t/** Default tab icon as a React node (not an iconify class string). */\n\ticon?: ReactNode;\n\tpendingFor?: (toolCall: PluginPendingToolCall) => CardDescriptor | null;\n}\n\n/** A tool call handed to a tool-call slot renderer. */\nexport interface PluginToolCallSlotToolCall {\n\ttoolCallId: string;\n\ttoolName: string;\n\targs: Record<string, unknown>;\n\tstatus: \"pending\" | \"success\" | \"error\";\n\tresult?: string;\n\tisError?: boolean;\n}\n\nexport interface PluginToolCallSlotProps {\n\ttoolCall: PluginToolCallSlotToolCall;\n}\n\n/**\n * A component that **replaces the host's default inline transcript rendering**\n * for a specific tool (matched by `toolName`; first registered renderer wins).\n * This is how a plugin renders rich UI for its OWN agent tool's output — plugin\n * tools cannot emit `details.cards`, so the message card system (registerCardRenderer)\n * does not serve them; the tool-call slot does.\n */\nexport interface PluginToolCallSlotContribution {\n\tid: string;\n\ttoolName: string;\n\tcomponent: ComponentType<PluginToolCallSlotProps>;\n}\n\n/**\n * A card rendered at the bottom of the message list for the LATEST turn — NOT\n * bound to a tool call. The host mounts `component` in the footer slot and\n * re-mounts it each session/turn; the plugin owns visibility entirely (it reads\n * live state via SDK hooks — `useActiveConversation`, `useConversationMessages`,\n * `ctx.conversation.on(\"turn-end\")` — and returns `null` to render nothing,\n * e.g. git renders only inside a repo that has changes). `scope_use` gates which\n * conversation scenarios it may appear in (**fail-closed**, mirrors the other\n * slots). `id` must be unique within the plugin.\n */\nexport interface PluginTurnCardContribution {\n\tid: string;\n\tcomponent: ComponentType;\n\t/**\n\t * 允许该 turn 卡出现的对话场景 slug 列表(镜像其它插槽的 scope_use)。**fail-closed**:\n\t * 未声明/空数组 = 任何会话都不显示;声明后仅在列出的场景里出现(如 `[\"project\"]`)。\n\t */\n\tscope_use?: readonly ConversationScenario[];\n}\n\n/**\n * Options for {@link PluginUiApi.notify}. Surfaces a host toast so users can\n * see plugin failures without opening DevTools.\n */\nexport interface PluginNotifyOptions {\n\t/** Toast body. Required even when `error` is set (user-facing summary). */\n\tmessage: string;\n\t/** Defaults to plugin display name when omitted. */\n\ttitle?: string;\n\t/** Defaults to `\"info\"`; when `error` is set and variant omitted, host uses `\"error\"`. */\n\tvariant?: \"info\" | \"success\" | \"warning\" | \"error\";\n\t/**\n\t * Optional failure cause. Host formats message + stack and adds a\n\t * \"复制堆栈\" action so the user can paste it into bug reports / chat.\n\t */\n\terror?: unknown;\n\t/**\n\t * Auto-dismiss delay in ms. `0` keeps the toast until dismissed.\n\t * Defaults: sticky (`0`) when `error` is present, otherwise host default (~4s).\n\t */\n\tdurationMs?: number;\n}\n\nexport interface PluginUiApi {\n\tregisterGlobalSlot(contribution: PluginGlobalSlotContribution): Disposable;\n\t/**\n\t * Register a preview component keyed by file extension. The host dispatches\n\t * registered extensions before its built-in fallback renderers; first\n\t * registrant wins on conflict.\n\t */\n\tregisterFilePreview(contribution: PluginFilePreviewContribution): Disposable;\n\t/**\n\t * Register an activity-panel tab into the addable pool. The user attaches\n\t * it manually via the panel's \"+\" picker; attach records are keyed by the\n\t * session cwd.\n\t */\n\tregisterActivityTab(contribution: PluginActivityTabContribution): Disposable;\n\t/**\n\t * Register a toggle action shown beneath the AI input bar. While active,\n\t * its `decoratePrompt()` annotates the next outgoing prompt.\n\t */\n\tregisterInputAction(contribution: PluginInputActionContribution): Disposable;\n\t/**\n\t * Register a card renderer keyed by `type`. Cards rendered beneath a message\n\t * come from tool results' out-of-band `details.cards` (or `pendingFor` for\n\t * in-flight tools); the host resolves each card's `type` to a registered\n\t * renderer. Multiple cards under one message are shown as a tab-switcher\n\t * (\"收纳\") or a flat list.\n\t */\n\tregisterCardRenderer(contribution: PluginCardRendererContribution): Disposable;\n\t/**\n\t * Register a renderer that replaces the host's default inline transcript UI\n\t * for a tool call (matched by `toolName`). The complement to card renderers:\n\t * use it to render UI for a plugin's own agent tool output.\n\t */\n\tregisterToolCallSlot(contribution: PluginToolCallSlotContribution): Disposable;\n\t/**\n\t * Register a card rendered at the bottom of the message list for the latest\n\t * turn — NOT bound to a tool call. The host mounts the component in the\n\t * footer slot; the plugin owns visibility (return `null` to render nothing,\n\t * e.g. git renders only in a repo with changes). Gated by `scope_use`\n\t * (fail-closed). Needs the `ui.slot.turn-card` permission.\n\t */\n\tregisterTurnCard(contribution: PluginTurnCardContribution): Disposable;\n\t/**\n\t * Programmatically attach (if needed) and activate one of this plugin's\n\t * own activity tabs in the current conversation's activity panel. `tabId`\n\t * is the contribution id passed to registerActivityTab. Any payload (e.g.\n\t * which image to edit) is passed via the plugin's own in-memory state.\n\t *\n\t * Pass `options.width` to size the panel as it opens — a pixel number, or\n\t * `\"max\"` to expand it to the widest the current window allows (the host\n\t * still clamps to its min/max and auto-hides the sidebar when needed). Omit\n\t * to leave the user's current width untouched.\n\t */\n\topenActivityTab(tabId: string, options?: PluginOpenActivityTabOptions): void;\n\t/**\n\t * Bind (or clear, with `null`) an image as the \"edit target\" for the next\n\t * outgoing prompt. The host renders it as a thumbnail capsule in the AI input\n\t * bar's top capsule strip and, at send time, injects `metadata.editImageId`\n\t * (the ref's id) so this turn edits that image. One-shot: cleared after the\n\t * prompt is sent (or when the user closes the capsule). Only the image\n\t * reference (id/url) crosses over — bytes stay out-of-band.\n\t */\n\tsetEditImageAttachment(ref: PluginImageRef | null): void;\n\t/**\n\t * Open the host's global full-screen image previewer for the given image.\n\t * Only the image reference (id/url) crosses over — bytes stay out-of-band.\n\t *\n\t * Pass `group` (e.g. all images of the message) to open as an image group:\n\t * the previewer shows a thumbnail strip + arrows and starts at `ref`.\n\t */\n\tpreviewImage(ref: PluginImageRef, group?: PluginImageRef[]): void;\n\t/**\n\t * Open the app settings, scrolled to and highlighting THIS plugin's own\n\t * settings section (e.g. so the user can fill in a required API key/model).\n\t * The host owns the navigation; the plugin only asks to jump there.\n\t */\n\topenPluginSettings(): void;\n\t/**\n\t * Show a global toast in the host UI (bottom-right). No permission required.\n\t * Prefer this over swallowing errors into opaque UI copy: pass `error` so\n\t * the host attaches a one-click \"copy stack\" action for the user.\n\t *\n\t * Capture `ctx.ui.notify` in `activate` if React components need it\n\t * (components do not receive `ctx`).\n\t */\n\tnotify(options: PluginNotifyOptions): void;\n}\n"]}
1
+ {"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ComponentType, ReactNode } from \"react\";\nimport type { ConversationMessage } from \"./conversation.js\";\nimport type { Disposable } from \"./disposable.js\";\nimport type { PluginImageRef } from \"./images.js\";\nimport type { PluginPromptAttachment } from \"./prompt-attachment.js\";\nimport type { ConversationScenario } from \"./scenario.js\";\nimport type { PluginShortcutScopeContribution } from \"./shortcuts.js\";\n\nexport interface PluginGlobalSlotContribution {\n\tid: string;\n\tcomponent: ComponentType;\n}\n\nexport interface PluginAudioMetadata {\n\t/** Embedded title; preview renderers should fall back to file.name. */\n\ttitle?: string;\n\tartist?: string;\n\t/** Embedded cover art as a Data URL when available. */\n\tcoverDataUrl?: string;\n}\n\nexport interface PluginPreviewUrlOptions {\n\t/** Optional media hint for ambiguous containers such as webm. */\n\tmediaKind?: \"audio\" | \"video\";\n}\n\n/**\n * The file handed to a file-preview plugin component. The host does NOT\n * pre-read or guess encoding — the plugin decides whether to read text or\n * bytes. `path` is also exposed for plugins that prefer native fetch.\n */\nexport interface PluginPreviewFile {\n\tpath: string | null;\n\tname: string;\n\textension: string;\n\tmime: string;\n\tsize: number;\n\t/** Read the file as a UTF-8 string. */\n\treadText(): Promise<string>;\n\t/** Read the raw file bytes. */\n\treadBytes(): Promise<ArrayBuffer>;\n\t/** A fetchable streaming URL for the file (Range-capable). */\n\tgetUrl(options?: PluginPreviewUrlOptions): string;\n\t/**\n\t * Subscribe to on-disk changes of this file. The listener fires (debounced\n\t * by the host) whenever the file's contents change, letting previews update\n\t * live. Returns a Disposable; call dispose() to stop watching. A no-op for\n\t * files without a real path (url-only sources).\n\t */\n\twatch(listener: () => void): Disposable;\n\t/**\n\t * Host-provided audio metadata for local media files. Returns null when the\n\t * host cannot provide metadata, e.g. url-only sources or unsupported files.\n\t */\n\tgetAudioMetadata?(): Promise<PluginAudioMetadata | null>;\n}\n\nexport interface PluginFilePreviewProps {\n\tfile: PluginPreviewFile;\n}\n\nexport interface PluginFilePreviewContribution {\n\t/** Lower-case extensions without the dot, e.g. [\"svg\"]. */\n\textensions: string[];\n\tcomponent: ComponentType<PluginFilePreviewProps>;\n}\n\n/**\n * An activity-panel tab contributed by a plugin. Registering only adds the\n * tab to the \"addable pool\" — it renders only after the user attaches it in\n * the activity panel (scoped by session cwd).\n */\nexport interface PluginActivityTabContribution {\n\tid: string;\n\tlabel: string;\n\t/** Tab icon as a React node (not an iconify class string). */\n\ticon?: ReactNode;\n\tcomponent: ComponentType;\n\t/**\n\t * 允许该标签卡出现的对话场景 slug 列表(镜像工具的 scope_use)。**fail-closed**:\n\t * 未声明/空数组 = 任何会话都不显示;声明后仅在列出的场景里出现(如\n\t * `[\"project\", \"conversation\"]`)。会话页插槽据此随对话类型显隐。\n\t */\n\tscope_use?: readonly ConversationScenario[];\n\t/**\n\t * 注册后是否默认在标签栏里(缺省 `true`)。声明 `false` 表示「出现条件由我\n\t * 自己决定」——注册只是入池,之后由 {@link PluginUiApi.setActivityTabVisible}\n\t * 或 {@link PluginUiApi.openActivityTab} 决定何时上栏(如 git 只在仓库目录、\n\t * 工作台跟随输入栏 toggle)。无论哪种,用户仍可用减号手动隐藏。\n\t */\n\tinitiallyVisible?: boolean;\n}\n\n/** Options for {@link PluginUiApi.openActivityTab}. */\nexport interface PluginOpenActivityTabOptions {\n\t/**\n\t * Desired panel width as it opens: a pixel number, or `\"max\"` for the widest\n\t * the current window allows. The host clamps to its min/max bounds. Omit to\n\t * keep the user's current width.\n\t */\n\twidth?: number | \"max\";\n}\n\nexport interface PluginCaptureRegion {\n\tx: number;\n\ty: number;\n\twidth: number;\n\theight: number;\n}\n\n/**\n * Decoration a plugin contributes to the next outgoing prompt while its input\n * action is active. `metadata` is shallow-merged into the prompt request and\n * `instructions` are appended as hidden, model-visible guidance.\n */\nexport interface PluginPromptDecoration {\n\tmetadata?: Record<string, unknown>;\n\t/** Hidden plugin-owned instructions appended to the next agent turn. */\n\tinstructions?: string[];\n}\n\n/**\n * An action button shown in a row beneath the AI input bar. Rendered as a\n * toggle: clicking activates, clicking again deactivates. While active, the\n * host calls `decoratePrompt()` before each send and merges the returned\n * metadata into the outgoing prompt request. The plugin owns any side state\n * (it is the same Module Federation instance as its other slots).\n */\nexport interface PluginInputActionContribution {\n\tid: string;\n\tlabel: string;\n\t/** Button icon as a React node (not an iconify class string). */\n\ticon?: ReactNode;\n\t/** Whether the action begins in the active state. Defaults to false. */\n\tdefaultActive?: boolean;\n\t/**\n\t * 该输入动作所依赖的 agent 工具名。设置后,仅当该工具在当前会话处于激活(按工具的\n\t * scope_use 解析)时才显示这个 badge——避免在工具被场景屏蔽(如批量任务)时仍显示一个\n\t * 点了也无效的开关。不设则始终显示。例如「图像生成」设为 \"generate_image\"。\n\t */\n\trequiresActiveTool?: string;\n\t/**\n\t * 允许该 toggle 出现的对话场景 slug 列表(镜像工具的 scope_use)。**fail-closed**:\n\t * 未声明/空数组 = 任何会话都不显示;声明后仅在列出的场景里出现。与 `requiresActiveTool`\n\t * 取「与」:两者都满足才显示。\n\t */\n\tscope_use?: readonly ConversationScenario[];\n\t/**\n\t * When true, this plugin's agent contributions (tools / skills / MCP /\n\t * systemPrompt) and Activity Tabs are hard-isolated: inactive until this\n\t * toggle is on (ADR-0041, knowledgeMode-style). Default false.\n\t */\n\thardIsolation?: boolean;\n\t/**\n\t * Fired when the user toggles the action; `active` is the new state. Return\n\t * `false` when `active` is true to VETO the activation (e.g. the plugin needs\n\t * configuration first) — the toggle stays off. Deactivation can't be vetoed.\n\t */\n\tonToggle?(active: boolean): boolean | void;\n\t/**\n\t * Called by the host before sending while the action is active. The\n\t * returned metadata is merged into the outgoing prompt request.\n\t */\n\tdecoratePrompt?(): PluginPromptDecoration | void;\n}\n\n/**\n * A declarative, serializable descriptor for one card rendered beneath a\n * message. `type` selects a registered card renderer; `key` drives cross-turn\n * dedup (same key across turns = one logical card, shown only under its latest\n * anchor); `payload` carries STABLE REFERENCES (e.g. image ids), not a content\n * snapshot — the renderer resolves live state from it. `title`/`icon` label the\n * card's tab (override the renderer's registration defaults). Rides a tool\n * result's out-of-band `details.cards`; crosses the agent→host boundary, so\n * `icon` is an icon-symbol string, not a node.\n */\nexport interface CardDescriptor {\n\ttype: string;\n\tkey?: string;\n\tpayload?: unknown;\n\ttitle?: string;\n\ticon?: string;\n}\n\n/** A pending (in-flight) tool call handed to a renderer's `pendingFor`. */\nexport interface PluginPendingToolCall {\n\ttoolName: string;\n\targs: Record<string, unknown>;\n}\n\n/**\n * Props for a card renderer. `descriptor` is the card's data; `pending` is true\n * while it was synthesized from an in-flight tool (render a skeleton); `message`\n * is the anchoring conversation message.\n */\nexport interface PluginCardProps {\n\tdescriptor: CardDescriptor;\n\tpending: boolean;\n\tmessage: ConversationMessage;\n}\n\n/**\n * A card renderer registered by a plugin, keyed by `type`. A descriptor whose\n * `type` matches is rendered by `component`. `title`/`icon` are the default tab\n * label/icon (a descriptor may override `title`). `pendingFor`, given an\n * in-flight tool call, returns a provisional descriptor so a skeleton card\n * appears (and claims a tab) before the tool's result lands — or null when this\n * renderer doesn't handle that tool. The `type` must be globally unique across\n * plugins (convention: prefix with the plugin id, e.g. \"image-gen:preview\").\n */\nexport interface PluginCardRendererContribution {\n\ttype: string;\n\tcomponent: ComponentType<PluginCardProps>;\n\ttitle?: string;\n\t/** Default tab icon as a React node (not an iconify class string). */\n\ticon?: ReactNode;\n\tpendingFor?: (toolCall: PluginPendingToolCall) => CardDescriptor | null;\n}\n\n/** A tool call handed to a tool-call slot renderer. */\nexport interface PluginToolCallSlotToolCall {\n\ttoolCallId: string;\n\ttoolName: string;\n\targs: Record<string, unknown>;\n\tstatus: \"pending\" | \"success\" | \"error\";\n\tresult?: string;\n\tisError?: boolean;\n}\n\nexport interface PluginToolCallSlotProps {\n\ttoolCall: PluginToolCallSlotToolCall;\n}\n\n/**\n * A component that **replaces the host's default inline transcript rendering**\n * for a specific tool (matched by `toolName`; first registered renderer wins).\n * This is how a plugin renders rich UI for its OWN agent tool's output — plugin\n * tools cannot emit `details.cards`, so the message card system (registerCardRenderer)\n * does not serve them; the tool-call slot does.\n */\nexport interface PluginToolCallSlotContribution {\n\tid: string;\n\ttoolName: string;\n\tcomponent: ComponentType<PluginToolCallSlotProps>;\n}\n\n/**\n * A card rendered at the bottom of the message list for the LATEST turn — NOT\n * bound to a tool call. The host mounts `component` in the footer slot and\n * re-mounts it each session/turn; the plugin owns visibility entirely (it reads\n * live state via SDK hooks — `useActiveConversation`, `useConversationMessages`,\n * `ctx.conversation.on(\"turn-end\")` — and returns `null` to render nothing,\n * e.g. git renders only inside a repo that has changes). `scope_use` gates which\n * conversation scenarios it may appear in (**fail-closed**, mirrors the other\n * slots). `id` must be unique within the plugin.\n */\nexport interface PluginTurnCardContribution {\n\tid: string;\n\tcomponent: ComponentType;\n\t/**\n\t * 允许该 turn 卡出现的对话场景 slug 列表(镜像其它插槽的 scope_use)。**fail-closed**:\n\t * 未声明/空数组 = 任何会话都不显示;声明后仅在列出的场景里出现(如 `[\"project\"]`)。\n\t */\n\tscope_use?: readonly ConversationScenario[];\n}\n\n/**\n * Options for {@link PluginUiApi.notify}. Surfaces a host toast so users can\n * see plugin failures without opening DevTools.\n */\nexport interface PluginNotifyOptions {\n\t/** Toast body. Required even when `error` is set (user-facing summary). */\n\tmessage: string;\n\t/** Defaults to plugin display name when omitted. */\n\ttitle?: string;\n\t/** Defaults to `\"info\"`; when `error` is set and variant omitted, host uses `\"error\"`. */\n\tvariant?: \"info\" | \"success\" | \"warning\" | \"error\";\n\t/**\n\t * Optional failure cause. Host formats message + stack and adds a\n\t * \"复制堆栈\" action so the user can paste it into bug reports / chat.\n\t */\n\terror?: unknown;\n\t/**\n\t * Auto-dismiss delay in ms. `0` keeps the toast until dismissed.\n\t * Defaults: sticky (`0`) when `error` is present, otherwise host default (~4s).\n\t */\n\tdurationMs?: number;\n}\n\nexport interface PluginUiApi {\n\tregisterGlobalSlot(contribution: PluginGlobalSlotContribution): Disposable;\n\t/**\n\t * Register a preview component keyed by file extension. The host dispatches\n\t * registered extensions before its built-in fallback renderers; first\n\t * registrant wins on conflict.\n\t */\n\tregisterFilePreview(contribution: PluginFilePreviewContribution): Disposable;\n\t/**\n\t * Register an activity-panel tab into the addable pool. The user attaches\n\t * it manually via the panel's \"+\" picker; attach records are keyed by the\n\t * session cwd.\n\t */\n\tregisterActivityTab(contribution: PluginActivityTabContribution): Disposable;\n\t/**\n\t * Register a toggle action shown beneath the AI input bar. While active,\n\t * its `decoratePrompt()` annotates the next outgoing prompt.\n\t */\n\tregisterInputAction(contribution: PluginInputActionContribution): Disposable;\n\t/**\n\t * Register a card renderer keyed by `type`. Cards rendered beneath a message\n\t * come from tool results' out-of-band `details.cards` (or `pendingFor` for\n\t * in-flight tools); the host resolves each card's `type` to a registered\n\t * renderer. Multiple cards under one message are shown as a tab-switcher\n\t * (\"收纳\") or a flat list.\n\t */\n\tregisterCardRenderer(contribution: PluginCardRendererContribution): Disposable;\n\t/**\n\t * Register a renderer that replaces the host's default inline transcript UI\n\t * for a tool call (matched by `toolName`). The complement to card renderers:\n\t * use it to render UI for a plugin's own agent tool output.\n\t */\n\tregisterToolCallSlot(contribution: PluginToolCallSlotContribution): Disposable;\n\t/**\n\t * Register a card rendered at the bottom of the message list for the latest\n\t * turn — NOT bound to a tool call. The host mounts the component in the\n\t * footer slot; the plugin owns visibility (return `null` to render nothing,\n\t * e.g. git renders only in a repo with changes). Gated by `scope_use`\n\t * (fail-closed). Needs the `ui.slot.turn-card` permission.\n\t */\n\tregisterTurnCard(contribution: PluginTurnCardContribution): Disposable;\n\t/**\n\t * Register a keyboard shortcut scope on the host's shared ShortcutScopeStack\n\t * (same path as host UI: modal > overlay > surface > app). Needs\n\t * `ui.shortcuts.register`. Plugins cannot use kind `\"app\"` (reserved for\n\t * host-configurable global actions). Prefer {@link usePluginShortcutScope}\n\t * from React components with a module-captured `registerShortcutScope`.\n\t */\n\tregisterShortcutScope(contribution: PluginShortcutScopeContribution): Disposable;\n\t/**\n\t * Programmatically attach (if needed) and activate one of this plugin's\n\t * own activity tabs in the current conversation's activity panel. `tabId`\n\t * is the contribution id passed to registerActivityTab. Any payload (e.g.\n\t * which image to edit) is passed via the plugin's own in-memory state.\n\t *\n\t * Pass `options.width` to size the panel as it opens — a pixel number, or\n\t * `\"max\"` to expand it to the widest the current window allows (the host\n\t * still clamps to its min/max and auto-hides the sidebar when needed). Omit\n\t * to leave the user's current width untouched.\n\t */\n\topenActivityTab(tabId: string, options?: PluginOpenActivityTabOptions): void;\n\t/**\n\t * 把本插件的某个活动面板标签卡在当前会话里上栏 / 下栏,**不激活也不展开\n\t * 面板**——`openActivityTab` 是「用户此刻要看它」,这个是「它现在该不该在\n\t * 栏里」。配合 `initiallyVisible: false` 使用,插件即可完全掌握自己标签卡的\n\t * 出现条件(如 git 只在仓库目录里上栏、工作台跟随输入栏 toggle)。\n\t *\n\t * 上栏记录按会话 cwd 持久化(ADR-0026),所以只需在条件变化时调用一次;\n\t * 用户随后用减号手动隐藏的结果不会被重复调用覆盖。当前没有活动会话时为\n\t * no-op(无处记录),插件应在会话就绪后重新判定。\n\t */\n\tsetActivityTabVisible(tabId: string, visible: boolean): void;\n\t/**\n\t * 直接设置活动面板宽度:像素值,或 `\"max\"` 表示当前窗口下的最大宽度(宿主仍\n\t * 会夹到自己的 min/max 内,必要时自动收起侧边栏)。\n\t *\n\t * 与 `openActivityTab(id, { width })` 的区别:那里的宽度只在标签卡首次 attach\n\t * 时生效(避免 activate 重放覆盖用户手拖的宽度);这个是命令式的,每次调用都\n\t * 生效,供插件在自己的标签卡被激活、进入某种视图时按需调整。用户随后仍可拖动。\n\t */\n\tsetActivityPanelWidth(width: number | \"max\"): void;\n\t/**\n\t * Bind or clear plugin-owned one-shot context for the next outgoing prompt.\n\t * The host renders its label/icon, merges metadata and hidden instructions,\n\t * then clears it after send or when the user closes the capsule.\n\t */\n\tsetPromptAttachment(attachment: PluginPromptAttachment | null): void;\n\t/**\n\t * Open the host's global full-screen image previewer for the given image.\n\t * Only the image reference (id/url) crosses over — bytes stay out-of-band.\n\t *\n\t * Pass `group` (e.g. all images of the message) to open as an image group:\n\t * the previewer shows a thumbnail strip + arrows and starts at `ref`.\n\t */\n\tpreviewImage(ref: PluginImageRef, group?: PluginImageRef[]): void;\n\t/**\n\t * Open the app settings, scrolled to and highlighting THIS plugin's own\n\t * settings section (e.g. so the user can fill in a required API key/model).\n\t * The host owns the navigation; the plugin only asks to jump there.\n\t */\n\topenPluginSettings(): void;\n\t/**\n\t * Capture a rectangle in the current Vetta window and open the host save\n\t * dialog. Coordinates use renderer DIP values such as getBoundingClientRect().\n\t * Requires `ui.slot.activity-tab`.\n\t */\n\tcaptureRegion(rect: PluginCaptureRegion, defaultFileName: string): Promise<string | null>;\n\t/**\n\t * Copy an image to the system clipboard. Takes a `data:image/...;base64,`\n\t * URL and goes through the native clipboard, so it does not depend on the\n\t * renderer's `ClipboardItem` support. No permission required — the plugin\n\t * can only write what it already rendered.\n\t */\n\tcopyImage(dataUrl: string): Promise<void>;\n\t/**\n\t * Show a global toast in the host UI (bottom-right). No permission required.\n\t * Prefer this over swallowing errors into opaque UI copy: pass `error` so\n\t * the host attaches a one-click \"copy stack\" action for the user.\n\t *\n\t * Capture `ctx.ui.notify` in `activate` if React components need it\n\t * (components do not receive `ctx`).\n\t */\n\tnotify(options: PluginNotifyOptions): void;\n}\n"]}
package/dist/ui.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"ui.js","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"","sourcesContent":["import type { ComponentType, ReactNode } from \"react\";\nimport type { ConversationMessage } from \"./conversation.js\";\nimport type { Disposable } from \"./disposable.js\";\nimport type { PluginImageRef } from \"./images.js\";\nimport type { ConversationScenario } from \"./scenario.js\";\n\nexport interface PluginGlobalSlotContribution {\n\tid: string;\n\tcomponent: ComponentType;\n}\n\nexport interface PluginAudioMetadata {\n\t/** Embedded title; preview renderers should fall back to file.name. */\n\ttitle?: string;\n\tartist?: string;\n\t/** Embedded cover art as a Data URL when available. */\n\tcoverDataUrl?: string;\n}\n\nexport interface PluginPreviewUrlOptions {\n\t/** Optional media hint for ambiguous containers such as webm. */\n\tmediaKind?: \"audio\" | \"video\";\n}\n\n/**\n * The file handed to a file-preview plugin component. The host does NOT\n * pre-read or guess encoding — the plugin decides whether to read text or\n * bytes. `path` is also exposed for plugins that prefer native fetch.\n */\nexport interface PluginPreviewFile {\n\tpath: string | null;\n\tname: string;\n\textension: string;\n\tmime: string;\n\tsize: number;\n\t/** Read the file as a UTF-8 string. */\n\treadText(): Promise<string>;\n\t/** Read the raw file bytes. */\n\treadBytes(): Promise<ArrayBuffer>;\n\t/** A fetchable streaming URL for the file (Range-capable). */\n\tgetUrl(options?: PluginPreviewUrlOptions): string;\n\t/**\n\t * Subscribe to on-disk changes of this file. The listener fires (debounced\n\t * by the host) whenever the file's contents change, letting previews update\n\t * live. Returns a Disposable; call dispose() to stop watching. A no-op for\n\t * files without a real path (url-only sources).\n\t */\n\twatch(listener: () => void): Disposable;\n\t/**\n\t * Host-provided audio metadata for local media files. Returns null when the\n\t * host cannot provide metadata, e.g. url-only sources or unsupported files.\n\t */\n\tgetAudioMetadata?(): Promise<PluginAudioMetadata | null>;\n}\n\nexport interface PluginFilePreviewProps {\n\tfile: PluginPreviewFile;\n}\n\nexport interface PluginFilePreviewContribution {\n\t/** Lower-case extensions without the dot, e.g. [\"svg\"]. */\n\textensions: string[];\n\tcomponent: ComponentType<PluginFilePreviewProps>;\n}\n\n/**\n * An activity-panel tab contributed by a plugin. Registering only adds the\n * tab to the \"addable pool\" — it renders only after the user attaches it in\n * the activity panel (scoped by session cwd).\n */\nexport interface PluginActivityTabContribution {\n\tid: string;\n\tlabel: string;\n\t/** Tab icon as a React node (not an iconify class string). */\n\ticon?: ReactNode;\n\tcomponent: ComponentType;\n\t/**\n\t * 允许该标签卡出现的对话场景 slug 列表(镜像工具的 scope_use)。**fail-closed**:\n\t * 未声明/空数组 = 任何会话都不显示;声明后仅在列出的场景里出现(如\n\t * `[\"project\", \"conversation\"]`)。会话页插槽据此随对话类型显隐。\n\t */\n\tscope_use?: readonly ConversationScenario[];\n}\n\n/** Options for {@link PluginUiApi.openActivityTab}. */\nexport interface PluginOpenActivityTabOptions {\n\t/**\n\t * Desired panel width as it opens: a pixel number, or `\"max\"` for the widest\n\t * the current window allows. The host clamps to its min/max bounds. Omit to\n\t * keep the user's current width.\n\t */\n\twidth?: number | \"max\";\n}\n\n/**\n * Decoration a plugin contributes to the next outgoing prompt while its input\n * action is active. `metadata` is shallow-merged into the prompt request's\n * `metadata` bag, which the agent turn can read (e.g. `{ imageMode: true }`).\n */\nexport interface PluginPromptDecoration {\n\tmetadata?: Record<string, unknown>;\n}\n\n/**\n * An action button shown in a row beneath the AI input bar. Rendered as a\n * toggle: clicking activates, clicking again deactivates. While active, the\n * host calls `decoratePrompt()` before each send and merges the returned\n * metadata into the outgoing prompt request. The plugin owns any side state\n * (it is the same Module Federation instance as its other slots).\n */\nexport interface PluginInputActionContribution {\n\tid: string;\n\tlabel: string;\n\t/** Button icon as a React node (not an iconify class string). */\n\ticon?: ReactNode;\n\t/** Whether the action begins in the active state. Defaults to false. */\n\tdefaultActive?: boolean;\n\t/**\n\t * 该输入动作所依赖的 agent 工具名。设置后,仅当该工具在当前会话处于激活(按工具的\n\t * scope_use 解析)时才显示这个 badge——避免在工具被场景屏蔽(如批量任务)时仍显示一个\n\t * 点了也无效的开关。不设则始终显示。例如「图像生成」设为 \"generate_image\"。\n\t */\n\trequiresActiveTool?: string;\n\t/**\n\t * 允许该 toggle 出现的对话场景 slug 列表(镜像工具的 scope_use)。**fail-closed**:\n\t * 未声明/空数组 = 任何会话都不显示;声明后仅在列出的场景里出现。与 `requiresActiveTool`\n\t * 取「与」:两者都满足才显示。\n\t */\n\tscope_use?: readonly ConversationScenario[];\n\t/**\n\t * When true, this plugin's agent contributions (tools / skills / MCP /\n\t * systemPrompt) and Activity Tabs are hard-isolated: inactive until this\n\t * toggle is on (ADR-0041, knowledgeMode-style). Default false.\n\t */\n\thardIsolation?: boolean;\n\t/**\n\t * Fired when the user toggles the action; `active` is the new state. Return\n\t * `false` when `active` is true to VETO the activation (e.g. the plugin needs\n\t * configuration first) — the toggle stays off. Deactivation can't be vetoed.\n\t */\n\tonToggle?(active: boolean): boolean | void;\n\t/**\n\t * Called by the host before sending while the action is active. The\n\t * returned metadata is merged into the outgoing prompt request.\n\t */\n\tdecoratePrompt?(): PluginPromptDecoration | void;\n}\n\n/**\n * A declarative, serializable descriptor for one card rendered beneath a\n * message. `type` selects a registered card renderer; `key` drives cross-turn\n * dedup (same key across turns = one logical card, shown only under its latest\n * anchor); `payload` carries STABLE REFERENCES (e.g. image ids), not a content\n * snapshot — the renderer resolves live state from it. `title`/`icon` label the\n * card's tab (override the renderer's registration defaults). Rides a tool\n * result's out-of-band `details.cards`; crosses the agent→host boundary, so\n * `icon` is an icon-symbol string, not a node.\n */\nexport interface CardDescriptor {\n\ttype: string;\n\tkey?: string;\n\tpayload?: unknown;\n\ttitle?: string;\n\ticon?: string;\n}\n\n/** A pending (in-flight) tool call handed to a renderer's `pendingFor`. */\nexport interface PluginPendingToolCall {\n\ttoolName: string;\n\targs: Record<string, unknown>;\n}\n\n/**\n * Props for a card renderer. `descriptor` is the card's data; `pending` is true\n * while it was synthesized from an in-flight tool (render a skeleton); `message`\n * is the anchoring conversation message.\n */\nexport interface PluginCardProps {\n\tdescriptor: CardDescriptor;\n\tpending: boolean;\n\tmessage: ConversationMessage;\n}\n\n/**\n * A card renderer registered by a plugin, keyed by `type`. A descriptor whose\n * `type` matches is rendered by `component`. `title`/`icon` are the default tab\n * label/icon (a descriptor may override `title`). `pendingFor`, given an\n * in-flight tool call, returns a provisional descriptor so a skeleton card\n * appears (and claims a tab) before the tool's result lands — or null when this\n * renderer doesn't handle that tool. The `type` must be globally unique across\n * plugins (convention: prefix with the plugin id, e.g. \"image-gen:preview\").\n */\nexport interface PluginCardRendererContribution {\n\ttype: string;\n\tcomponent: ComponentType<PluginCardProps>;\n\ttitle?: string;\n\t/** Default tab icon as a React node (not an iconify class string). */\n\ticon?: ReactNode;\n\tpendingFor?: (toolCall: PluginPendingToolCall) => CardDescriptor | null;\n}\n\n/** A tool call handed to a tool-call slot renderer. */\nexport interface PluginToolCallSlotToolCall {\n\ttoolCallId: string;\n\ttoolName: string;\n\targs: Record<string, unknown>;\n\tstatus: \"pending\" | \"success\" | \"error\";\n\tresult?: string;\n\tisError?: boolean;\n}\n\nexport interface PluginToolCallSlotProps {\n\ttoolCall: PluginToolCallSlotToolCall;\n}\n\n/**\n * A component that **replaces the host's default inline transcript rendering**\n * for a specific tool (matched by `toolName`; first registered renderer wins).\n * This is how a plugin renders rich UI for its OWN agent tool's output — plugin\n * tools cannot emit `details.cards`, so the message card system (registerCardRenderer)\n * does not serve them; the tool-call slot does.\n */\nexport interface PluginToolCallSlotContribution {\n\tid: string;\n\ttoolName: string;\n\tcomponent: ComponentType<PluginToolCallSlotProps>;\n}\n\n/**\n * A card rendered at the bottom of the message list for the LATEST turn — NOT\n * bound to a tool call. The host mounts `component` in the footer slot and\n * re-mounts it each session/turn; the plugin owns visibility entirely (it reads\n * live state via SDK hooks — `useActiveConversation`, `useConversationMessages`,\n * `ctx.conversation.on(\"turn-end\")` — and returns `null` to render nothing,\n * e.g. git renders only inside a repo that has changes). `scope_use` gates which\n * conversation scenarios it may appear in (**fail-closed**, mirrors the other\n * slots). `id` must be unique within the plugin.\n */\nexport interface PluginTurnCardContribution {\n\tid: string;\n\tcomponent: ComponentType;\n\t/**\n\t * 允许该 turn 卡出现的对话场景 slug 列表(镜像其它插槽的 scope_use)。**fail-closed**:\n\t * 未声明/空数组 = 任何会话都不显示;声明后仅在列出的场景里出现(如 `[\"project\"]`)。\n\t */\n\tscope_use?: readonly ConversationScenario[];\n}\n\n/**\n * Options for {@link PluginUiApi.notify}. Surfaces a host toast so users can\n * see plugin failures without opening DevTools.\n */\nexport interface PluginNotifyOptions {\n\t/** Toast body. Required even when `error` is set (user-facing summary). */\n\tmessage: string;\n\t/** Defaults to plugin display name when omitted. */\n\ttitle?: string;\n\t/** Defaults to `\"info\"`; when `error` is set and variant omitted, host uses `\"error\"`. */\n\tvariant?: \"info\" | \"success\" | \"warning\" | \"error\";\n\t/**\n\t * Optional failure cause. Host formats message + stack and adds a\n\t * \"复制堆栈\" action so the user can paste it into bug reports / chat.\n\t */\n\terror?: unknown;\n\t/**\n\t * Auto-dismiss delay in ms. `0` keeps the toast until dismissed.\n\t * Defaults: sticky (`0`) when `error` is present, otherwise host default (~4s).\n\t */\n\tdurationMs?: number;\n}\n\nexport interface PluginUiApi {\n\tregisterGlobalSlot(contribution: PluginGlobalSlotContribution): Disposable;\n\t/**\n\t * Register a preview component keyed by file extension. The host dispatches\n\t * registered extensions before its built-in fallback renderers; first\n\t * registrant wins on conflict.\n\t */\n\tregisterFilePreview(contribution: PluginFilePreviewContribution): Disposable;\n\t/**\n\t * Register an activity-panel tab into the addable pool. The user attaches\n\t * it manually via the panel's \"+\" picker; attach records are keyed by the\n\t * session cwd.\n\t */\n\tregisterActivityTab(contribution: PluginActivityTabContribution): Disposable;\n\t/**\n\t * Register a toggle action shown beneath the AI input bar. While active,\n\t * its `decoratePrompt()` annotates the next outgoing prompt.\n\t */\n\tregisterInputAction(contribution: PluginInputActionContribution): Disposable;\n\t/**\n\t * Register a card renderer keyed by `type`. Cards rendered beneath a message\n\t * come from tool results' out-of-band `details.cards` (or `pendingFor` for\n\t * in-flight tools); the host resolves each card's `type` to a registered\n\t * renderer. Multiple cards under one message are shown as a tab-switcher\n\t * (\"收纳\") or a flat list.\n\t */\n\tregisterCardRenderer(contribution: PluginCardRendererContribution): Disposable;\n\t/**\n\t * Register a renderer that replaces the host's default inline transcript UI\n\t * for a tool call (matched by `toolName`). The complement to card renderers:\n\t * use it to render UI for a plugin's own agent tool output.\n\t */\n\tregisterToolCallSlot(contribution: PluginToolCallSlotContribution): Disposable;\n\t/**\n\t * Register a card rendered at the bottom of the message list for the latest\n\t * turn — NOT bound to a tool call. The host mounts the component in the\n\t * footer slot; the plugin owns visibility (return `null` to render nothing,\n\t * e.g. git renders only in a repo with changes). Gated by `scope_use`\n\t * (fail-closed). Needs the `ui.slot.turn-card` permission.\n\t */\n\tregisterTurnCard(contribution: PluginTurnCardContribution): Disposable;\n\t/**\n\t * Programmatically attach (if needed) and activate one of this plugin's\n\t * own activity tabs in the current conversation's activity panel. `tabId`\n\t * is the contribution id passed to registerActivityTab. Any payload (e.g.\n\t * which image to edit) is passed via the plugin's own in-memory state.\n\t *\n\t * Pass `options.width` to size the panel as it opens — a pixel number, or\n\t * `\"max\"` to expand it to the widest the current window allows (the host\n\t * still clamps to its min/max and auto-hides the sidebar when needed). Omit\n\t * to leave the user's current width untouched.\n\t */\n\topenActivityTab(tabId: string, options?: PluginOpenActivityTabOptions): void;\n\t/**\n\t * Bind (or clear, with `null`) an image as the \"edit target\" for the next\n\t * outgoing prompt. The host renders it as a thumbnail capsule in the AI input\n\t * bar's top capsule strip and, at send time, injects `metadata.editImageId`\n\t * (the ref's id) so this turn edits that image. One-shot: cleared after the\n\t * prompt is sent (or when the user closes the capsule). Only the image\n\t * reference (id/url) crosses over — bytes stay out-of-band.\n\t */\n\tsetEditImageAttachment(ref: PluginImageRef | null): void;\n\t/**\n\t * Open the host's global full-screen image previewer for the given image.\n\t * Only the image reference (id/url) crosses over — bytes stay out-of-band.\n\t *\n\t * Pass `group` (e.g. all images of the message) to open as an image group:\n\t * the previewer shows a thumbnail strip + arrows and starts at `ref`.\n\t */\n\tpreviewImage(ref: PluginImageRef, group?: PluginImageRef[]): void;\n\t/**\n\t * Open the app settings, scrolled to and highlighting THIS plugin's own\n\t * settings section (e.g. so the user can fill in a required API key/model).\n\t * The host owns the navigation; the plugin only asks to jump there.\n\t */\n\topenPluginSettings(): void;\n\t/**\n\t * Show a global toast in the host UI (bottom-right). No permission required.\n\t * Prefer this over swallowing errors into opaque UI copy: pass `error` so\n\t * the host attaches a one-click \"copy stack\" action for the user.\n\t *\n\t * Capture `ctx.ui.notify` in `activate` if React components need it\n\t * (components do not receive `ctx`).\n\t */\n\tnotify(options: PluginNotifyOptions): void;\n}\n"]}
1
+ {"version":3,"file":"ui.js","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"","sourcesContent":["import type { ComponentType, ReactNode } from \"react\";\nimport type { ConversationMessage } from \"./conversation.js\";\nimport type { Disposable } from \"./disposable.js\";\nimport type { PluginImageRef } from \"./images.js\";\nimport type { PluginPromptAttachment } from \"./prompt-attachment.js\";\nimport type { ConversationScenario } from \"./scenario.js\";\nimport type { PluginShortcutScopeContribution } from \"./shortcuts.js\";\n\nexport interface PluginGlobalSlotContribution {\n\tid: string;\n\tcomponent: ComponentType;\n}\n\nexport interface PluginAudioMetadata {\n\t/** Embedded title; preview renderers should fall back to file.name. */\n\ttitle?: string;\n\tartist?: string;\n\t/** Embedded cover art as a Data URL when available. */\n\tcoverDataUrl?: string;\n}\n\nexport interface PluginPreviewUrlOptions {\n\t/** Optional media hint for ambiguous containers such as webm. */\n\tmediaKind?: \"audio\" | \"video\";\n}\n\n/**\n * The file handed to a file-preview plugin component. The host does NOT\n * pre-read or guess encoding — the plugin decides whether to read text or\n * bytes. `path` is also exposed for plugins that prefer native fetch.\n */\nexport interface PluginPreviewFile {\n\tpath: string | null;\n\tname: string;\n\textension: string;\n\tmime: string;\n\tsize: number;\n\t/** Read the file as a UTF-8 string. */\n\treadText(): Promise<string>;\n\t/** Read the raw file bytes. */\n\treadBytes(): Promise<ArrayBuffer>;\n\t/** A fetchable streaming URL for the file (Range-capable). */\n\tgetUrl(options?: PluginPreviewUrlOptions): string;\n\t/**\n\t * Subscribe to on-disk changes of this file. The listener fires (debounced\n\t * by the host) whenever the file's contents change, letting previews update\n\t * live. Returns a Disposable; call dispose() to stop watching. A no-op for\n\t * files without a real path (url-only sources).\n\t */\n\twatch(listener: () => void): Disposable;\n\t/**\n\t * Host-provided audio metadata for local media files. Returns null when the\n\t * host cannot provide metadata, e.g. url-only sources or unsupported files.\n\t */\n\tgetAudioMetadata?(): Promise<PluginAudioMetadata | null>;\n}\n\nexport interface PluginFilePreviewProps {\n\tfile: PluginPreviewFile;\n}\n\nexport interface PluginFilePreviewContribution {\n\t/** Lower-case extensions without the dot, e.g. [\"svg\"]. */\n\textensions: string[];\n\tcomponent: ComponentType<PluginFilePreviewProps>;\n}\n\n/**\n * An activity-panel tab contributed by a plugin. Registering only adds the\n * tab to the \"addable pool\" — it renders only after the user attaches it in\n * the activity panel (scoped by session cwd).\n */\nexport interface PluginActivityTabContribution {\n\tid: string;\n\tlabel: string;\n\t/** Tab icon as a React node (not an iconify class string). */\n\ticon?: ReactNode;\n\tcomponent: ComponentType;\n\t/**\n\t * 允许该标签卡出现的对话场景 slug 列表(镜像工具的 scope_use)。**fail-closed**:\n\t * 未声明/空数组 = 任何会话都不显示;声明后仅在列出的场景里出现(如\n\t * `[\"project\", \"conversation\"]`)。会话页插槽据此随对话类型显隐。\n\t */\n\tscope_use?: readonly ConversationScenario[];\n\t/**\n\t * 注册后是否默认在标签栏里(缺省 `true`)。声明 `false` 表示「出现条件由我\n\t * 自己决定」——注册只是入池,之后由 {@link PluginUiApi.setActivityTabVisible}\n\t * 或 {@link PluginUiApi.openActivityTab} 决定何时上栏(如 git 只在仓库目录、\n\t * 工作台跟随输入栏 toggle)。无论哪种,用户仍可用减号手动隐藏。\n\t */\n\tinitiallyVisible?: boolean;\n}\n\n/** Options for {@link PluginUiApi.openActivityTab}. */\nexport interface PluginOpenActivityTabOptions {\n\t/**\n\t * Desired panel width as it opens: a pixel number, or `\"max\"` for the widest\n\t * the current window allows. The host clamps to its min/max bounds. Omit to\n\t * keep the user's current width.\n\t */\n\twidth?: number | \"max\";\n}\n\nexport interface PluginCaptureRegion {\n\tx: number;\n\ty: number;\n\twidth: number;\n\theight: number;\n}\n\n/**\n * Decoration a plugin contributes to the next outgoing prompt while its input\n * action is active. `metadata` is shallow-merged into the prompt request and\n * `instructions` are appended as hidden, model-visible guidance.\n */\nexport interface PluginPromptDecoration {\n\tmetadata?: Record<string, unknown>;\n\t/** Hidden plugin-owned instructions appended to the next agent turn. */\n\tinstructions?: string[];\n}\n\n/**\n * An action button shown in a row beneath the AI input bar. Rendered as a\n * toggle: clicking activates, clicking again deactivates. While active, the\n * host calls `decoratePrompt()` before each send and merges the returned\n * metadata into the outgoing prompt request. The plugin owns any side state\n * (it is the same Module Federation instance as its other slots).\n */\nexport interface PluginInputActionContribution {\n\tid: string;\n\tlabel: string;\n\t/** Button icon as a React node (not an iconify class string). */\n\ticon?: ReactNode;\n\t/** Whether the action begins in the active state. Defaults to false. */\n\tdefaultActive?: boolean;\n\t/**\n\t * 该输入动作所依赖的 agent 工具名。设置后,仅当该工具在当前会话处于激活(按工具的\n\t * scope_use 解析)时才显示这个 badge——避免在工具被场景屏蔽(如批量任务)时仍显示一个\n\t * 点了也无效的开关。不设则始终显示。例如「图像生成」设为 \"generate_image\"。\n\t */\n\trequiresActiveTool?: string;\n\t/**\n\t * 允许该 toggle 出现的对话场景 slug 列表(镜像工具的 scope_use)。**fail-closed**:\n\t * 未声明/空数组 = 任何会话都不显示;声明后仅在列出的场景里出现。与 `requiresActiveTool`\n\t * 取「与」:两者都满足才显示。\n\t */\n\tscope_use?: readonly ConversationScenario[];\n\t/**\n\t * When true, this plugin's agent contributions (tools / skills / MCP /\n\t * systemPrompt) and Activity Tabs are hard-isolated: inactive until this\n\t * toggle is on (ADR-0041, knowledgeMode-style). Default false.\n\t */\n\thardIsolation?: boolean;\n\t/**\n\t * Fired when the user toggles the action; `active` is the new state. Return\n\t * `false` when `active` is true to VETO the activation (e.g. the plugin needs\n\t * configuration first) — the toggle stays off. Deactivation can't be vetoed.\n\t */\n\tonToggle?(active: boolean): boolean | void;\n\t/**\n\t * Called by the host before sending while the action is active. The\n\t * returned metadata is merged into the outgoing prompt request.\n\t */\n\tdecoratePrompt?(): PluginPromptDecoration | void;\n}\n\n/**\n * A declarative, serializable descriptor for one card rendered beneath a\n * message. `type` selects a registered card renderer; `key` drives cross-turn\n * dedup (same key across turns = one logical card, shown only under its latest\n * anchor); `payload` carries STABLE REFERENCES (e.g. image ids), not a content\n * snapshot — the renderer resolves live state from it. `title`/`icon` label the\n * card's tab (override the renderer's registration defaults). Rides a tool\n * result's out-of-band `details.cards`; crosses the agent→host boundary, so\n * `icon` is an icon-symbol string, not a node.\n */\nexport interface CardDescriptor {\n\ttype: string;\n\tkey?: string;\n\tpayload?: unknown;\n\ttitle?: string;\n\ticon?: string;\n}\n\n/** A pending (in-flight) tool call handed to a renderer's `pendingFor`. */\nexport interface PluginPendingToolCall {\n\ttoolName: string;\n\targs: Record<string, unknown>;\n}\n\n/**\n * Props for a card renderer. `descriptor` is the card's data; `pending` is true\n * while it was synthesized from an in-flight tool (render a skeleton); `message`\n * is the anchoring conversation message.\n */\nexport interface PluginCardProps {\n\tdescriptor: CardDescriptor;\n\tpending: boolean;\n\tmessage: ConversationMessage;\n}\n\n/**\n * A card renderer registered by a plugin, keyed by `type`. A descriptor whose\n * `type` matches is rendered by `component`. `title`/`icon` are the default tab\n * label/icon (a descriptor may override `title`). `pendingFor`, given an\n * in-flight tool call, returns a provisional descriptor so a skeleton card\n * appears (and claims a tab) before the tool's result lands — or null when this\n * renderer doesn't handle that tool. The `type` must be globally unique across\n * plugins (convention: prefix with the plugin id, e.g. \"image-gen:preview\").\n */\nexport interface PluginCardRendererContribution {\n\ttype: string;\n\tcomponent: ComponentType<PluginCardProps>;\n\ttitle?: string;\n\t/** Default tab icon as a React node (not an iconify class string). */\n\ticon?: ReactNode;\n\tpendingFor?: (toolCall: PluginPendingToolCall) => CardDescriptor | null;\n}\n\n/** A tool call handed to a tool-call slot renderer. */\nexport interface PluginToolCallSlotToolCall {\n\ttoolCallId: string;\n\ttoolName: string;\n\targs: Record<string, unknown>;\n\tstatus: \"pending\" | \"success\" | \"error\";\n\tresult?: string;\n\tisError?: boolean;\n}\n\nexport interface PluginToolCallSlotProps {\n\ttoolCall: PluginToolCallSlotToolCall;\n}\n\n/**\n * A component that **replaces the host's default inline transcript rendering**\n * for a specific tool (matched by `toolName`; first registered renderer wins).\n * This is how a plugin renders rich UI for its OWN agent tool's output — plugin\n * tools cannot emit `details.cards`, so the message card system (registerCardRenderer)\n * does not serve them; the tool-call slot does.\n */\nexport interface PluginToolCallSlotContribution {\n\tid: string;\n\ttoolName: string;\n\tcomponent: ComponentType<PluginToolCallSlotProps>;\n}\n\n/**\n * A card rendered at the bottom of the message list for the LATEST turn — NOT\n * bound to a tool call. The host mounts `component` in the footer slot and\n * re-mounts it each session/turn; the plugin owns visibility entirely (it reads\n * live state via SDK hooks — `useActiveConversation`, `useConversationMessages`,\n * `ctx.conversation.on(\"turn-end\")` — and returns `null` to render nothing,\n * e.g. git renders only inside a repo that has changes). `scope_use` gates which\n * conversation scenarios it may appear in (**fail-closed**, mirrors the other\n * slots). `id` must be unique within the plugin.\n */\nexport interface PluginTurnCardContribution {\n\tid: string;\n\tcomponent: ComponentType;\n\t/**\n\t * 允许该 turn 卡出现的对话场景 slug 列表(镜像其它插槽的 scope_use)。**fail-closed**:\n\t * 未声明/空数组 = 任何会话都不显示;声明后仅在列出的场景里出现(如 `[\"project\"]`)。\n\t */\n\tscope_use?: readonly ConversationScenario[];\n}\n\n/**\n * Options for {@link PluginUiApi.notify}. Surfaces a host toast so users can\n * see plugin failures without opening DevTools.\n */\nexport interface PluginNotifyOptions {\n\t/** Toast body. Required even when `error` is set (user-facing summary). */\n\tmessage: string;\n\t/** Defaults to plugin display name when omitted. */\n\ttitle?: string;\n\t/** Defaults to `\"info\"`; when `error` is set and variant omitted, host uses `\"error\"`. */\n\tvariant?: \"info\" | \"success\" | \"warning\" | \"error\";\n\t/**\n\t * Optional failure cause. Host formats message + stack and adds a\n\t * \"复制堆栈\" action so the user can paste it into bug reports / chat.\n\t */\n\terror?: unknown;\n\t/**\n\t * Auto-dismiss delay in ms. `0` keeps the toast until dismissed.\n\t * Defaults: sticky (`0`) when `error` is present, otherwise host default (~4s).\n\t */\n\tdurationMs?: number;\n}\n\nexport interface PluginUiApi {\n\tregisterGlobalSlot(contribution: PluginGlobalSlotContribution): Disposable;\n\t/**\n\t * Register a preview component keyed by file extension. The host dispatches\n\t * registered extensions before its built-in fallback renderers; first\n\t * registrant wins on conflict.\n\t */\n\tregisterFilePreview(contribution: PluginFilePreviewContribution): Disposable;\n\t/**\n\t * Register an activity-panel tab into the addable pool. The user attaches\n\t * it manually via the panel's \"+\" picker; attach records are keyed by the\n\t * session cwd.\n\t */\n\tregisterActivityTab(contribution: PluginActivityTabContribution): Disposable;\n\t/**\n\t * Register a toggle action shown beneath the AI input bar. While active,\n\t * its `decoratePrompt()` annotates the next outgoing prompt.\n\t */\n\tregisterInputAction(contribution: PluginInputActionContribution): Disposable;\n\t/**\n\t * Register a card renderer keyed by `type`. Cards rendered beneath a message\n\t * come from tool results' out-of-band `details.cards` (or `pendingFor` for\n\t * in-flight tools); the host resolves each card's `type` to a registered\n\t * renderer. Multiple cards under one message are shown as a tab-switcher\n\t * (\"收纳\") or a flat list.\n\t */\n\tregisterCardRenderer(contribution: PluginCardRendererContribution): Disposable;\n\t/**\n\t * Register a renderer that replaces the host's default inline transcript UI\n\t * for a tool call (matched by `toolName`). The complement to card renderers:\n\t * use it to render UI for a plugin's own agent tool output.\n\t */\n\tregisterToolCallSlot(contribution: PluginToolCallSlotContribution): Disposable;\n\t/**\n\t * Register a card rendered at the bottom of the message list for the latest\n\t * turn — NOT bound to a tool call. The host mounts the component in the\n\t * footer slot; the plugin owns visibility (return `null` to render nothing,\n\t * e.g. git renders only in a repo with changes). Gated by `scope_use`\n\t * (fail-closed). Needs the `ui.slot.turn-card` permission.\n\t */\n\tregisterTurnCard(contribution: PluginTurnCardContribution): Disposable;\n\t/**\n\t * Register a keyboard shortcut scope on the host's shared ShortcutScopeStack\n\t * (same path as host UI: modal > overlay > surface > app). Needs\n\t * `ui.shortcuts.register`. Plugins cannot use kind `\"app\"` (reserved for\n\t * host-configurable global actions). Prefer {@link usePluginShortcutScope}\n\t * from React components with a module-captured `registerShortcutScope`.\n\t */\n\tregisterShortcutScope(contribution: PluginShortcutScopeContribution): Disposable;\n\t/**\n\t * Programmatically attach (if needed) and activate one of this plugin's\n\t * own activity tabs in the current conversation's activity panel. `tabId`\n\t * is the contribution id passed to registerActivityTab. Any payload (e.g.\n\t * which image to edit) is passed via the plugin's own in-memory state.\n\t *\n\t * Pass `options.width` to size the panel as it opens — a pixel number, or\n\t * `\"max\"` to expand it to the widest the current window allows (the host\n\t * still clamps to its min/max and auto-hides the sidebar when needed). Omit\n\t * to leave the user's current width untouched.\n\t */\n\topenActivityTab(tabId: string, options?: PluginOpenActivityTabOptions): void;\n\t/**\n\t * 把本插件的某个活动面板标签卡在当前会话里上栏 / 下栏,**不激活也不展开\n\t * 面板**——`openActivityTab` 是「用户此刻要看它」,这个是「它现在该不该在\n\t * 栏里」。配合 `initiallyVisible: false` 使用,插件即可完全掌握自己标签卡的\n\t * 出现条件(如 git 只在仓库目录里上栏、工作台跟随输入栏 toggle)。\n\t *\n\t * 上栏记录按会话 cwd 持久化(ADR-0026),所以只需在条件变化时调用一次;\n\t * 用户随后用减号手动隐藏的结果不会被重复调用覆盖。当前没有活动会话时为\n\t * no-op(无处记录),插件应在会话就绪后重新判定。\n\t */\n\tsetActivityTabVisible(tabId: string, visible: boolean): void;\n\t/**\n\t * 直接设置活动面板宽度:像素值,或 `\"max\"` 表示当前窗口下的最大宽度(宿主仍\n\t * 会夹到自己的 min/max 内,必要时自动收起侧边栏)。\n\t *\n\t * 与 `openActivityTab(id, { width })` 的区别:那里的宽度只在标签卡首次 attach\n\t * 时生效(避免 activate 重放覆盖用户手拖的宽度);这个是命令式的,每次调用都\n\t * 生效,供插件在自己的标签卡被激活、进入某种视图时按需调整。用户随后仍可拖动。\n\t */\n\tsetActivityPanelWidth(width: number | \"max\"): void;\n\t/**\n\t * Bind or clear plugin-owned one-shot context for the next outgoing prompt.\n\t * The host renders its label/icon, merges metadata and hidden instructions,\n\t * then clears it after send or when the user closes the capsule.\n\t */\n\tsetPromptAttachment(attachment: PluginPromptAttachment | null): void;\n\t/**\n\t * Open the host's global full-screen image previewer for the given image.\n\t * Only the image reference (id/url) crosses over — bytes stay out-of-band.\n\t *\n\t * Pass `group` (e.g. all images of the message) to open as an image group:\n\t * the previewer shows a thumbnail strip + arrows and starts at `ref`.\n\t */\n\tpreviewImage(ref: PluginImageRef, group?: PluginImageRef[]): void;\n\t/**\n\t * Open the app settings, scrolled to and highlighting THIS plugin's own\n\t * settings section (e.g. so the user can fill in a required API key/model).\n\t * The host owns the navigation; the plugin only asks to jump there.\n\t */\n\topenPluginSettings(): void;\n\t/**\n\t * Capture a rectangle in the current Vetta window and open the host save\n\t * dialog. Coordinates use renderer DIP values such as getBoundingClientRect().\n\t * Requires `ui.slot.activity-tab`.\n\t */\n\tcaptureRegion(rect: PluginCaptureRegion, defaultFileName: string): Promise<string | null>;\n\t/**\n\t * Copy an image to the system clipboard. Takes a `data:image/...;base64,`\n\t * URL and goes through the native clipboard, so it does not depend on the\n\t * renderer's `ClipboardItem` support. No permission required — the plugin\n\t * can only write what it already rendered.\n\t */\n\tcopyImage(dataUrl: string): Promise<void>;\n\t/**\n\t * Show a global toast in the host UI (bottom-right). No permission required.\n\t * Prefer this over swallowing errors into opaque UI copy: pass `error` so\n\t * the host attaches a one-click \"copy stack\" action for the user.\n\t *\n\t * Capture `ctx.ui.notify` in `activate` if React components need it\n\t * (components do not receive `ctx`).\n\t */\n\tnotify(options: PluginNotifyOptions): void;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vetta-org/plugin-sdk",
3
- "version": "0.0.4",
3
+ "version": "0.1.1",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",