@particle-academy/agent-integrations 0.2.4 → 0.3.4

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 (107) hide show
  1. package/dist/bridges/charts.d.cts +39 -0
  2. package/dist/bridges/charts.d.ts +39 -0
  3. package/dist/bridges/code.d.cts +47 -0
  4. package/dist/bridges/code.d.ts +47 -0
  5. package/dist/bridges/flow.d.cts +3 -2
  6. package/dist/bridges/flow.d.ts +3 -2
  7. package/dist/bridges/forms.d.cts +76 -0
  8. package/dist/bridges/forms.d.ts +76 -0
  9. package/dist/bridges/scene.d.cts +54 -0
  10. package/dist/bridges/scene.d.ts +54 -0
  11. package/dist/bridges/sheets.d.cts +62 -0
  12. package/dist/bridges/sheets.d.ts +62 -0
  13. package/dist/bridges/whiteboard.d.cts +3 -2
  14. package/dist/bridges/whiteboard.d.ts +3 -2
  15. package/dist/bridges-charts.cjs +167 -0
  16. package/dist/bridges-charts.cjs.map +1 -0
  17. package/dist/bridges-charts.js +6 -0
  18. package/dist/bridges-charts.js.map +1 -0
  19. package/dist/bridges-code.cjs +219 -0
  20. package/dist/bridges-code.cjs.map +1 -0
  21. package/dist/bridges-code.js +6 -0
  22. package/dist/bridges-code.js.map +1 -0
  23. package/dist/bridges-flow.cjs +76 -17
  24. package/dist/bridges-flow.cjs.map +1 -1
  25. package/dist/bridges-flow.js +3 -1
  26. package/dist/bridges-forms.cjs +205 -0
  27. package/dist/bridges-forms.cjs.map +1 -0
  28. package/dist/bridges-forms.js +6 -0
  29. package/dist/bridges-forms.js.map +1 -0
  30. package/dist/bridges-scene.cjs +250 -0
  31. package/dist/bridges-scene.cjs.map +1 -0
  32. package/dist/bridges-scene.js +6 -0
  33. package/dist/bridges-scene.js.map +1 -0
  34. package/dist/bridges-sheets.cjs +327 -0
  35. package/dist/bridges-sheets.cjs.map +1 -0
  36. package/dist/bridges-sheets.js +6 -0
  37. package/dist/bridges-sheets.js.map +1 -0
  38. package/dist/bridges-whiteboard.cjs +224 -38
  39. package/dist/bridges-whiteboard.cjs.map +1 -1
  40. package/dist/bridges-whiteboard.js +4 -1
  41. package/dist/{chunk-2VOQJKSU.js → chunk-4IAVAFUV.js} +41 -19
  42. package/dist/chunk-4IAVAFUV.js.map +1 -0
  43. package/dist/chunk-52S7XYZK.js +38 -0
  44. package/dist/chunk-52S7XYZK.js.map +1 -0
  45. package/dist/chunk-ACBENYYO.js +124 -0
  46. package/dist/chunk-ACBENYYO.js.map +1 -0
  47. package/dist/chunk-DJOWMF6Q.js +25 -0
  48. package/dist/chunk-DJOWMF6Q.js.map +1 -0
  49. package/dist/{chunk-FLEOQUKF.js → chunk-JMYPUAFH.js} +17 -2
  50. package/dist/chunk-JMYPUAFH.js.map +1 -0
  51. package/dist/chunk-JU2N4KK6.js +34 -0
  52. package/dist/chunk-JU2N4KK6.js.map +1 -0
  53. package/dist/chunk-OEIULP2L.js +158 -0
  54. package/dist/chunk-OEIULP2L.js.map +1 -0
  55. package/dist/chunk-PDBF4W7E.js +280 -0
  56. package/dist/chunk-PDBF4W7E.js.map +1 -0
  57. package/dist/chunk-PHPXKSWI.js +120 -0
  58. package/dist/chunk-PHPXKSWI.js.map +1 -0
  59. package/dist/chunk-QJUTISFC.js +203 -0
  60. package/dist/chunk-QJUTISFC.js.map +1 -0
  61. package/dist/{chunk-5ZUHNNLR.js → chunk-TBEITXF4.js} +79 -41
  62. package/dist/chunk-TBEITXF4.js.map +1 -0
  63. package/dist/chunk-X66JWQBB.js +37 -0
  64. package/dist/chunk-X66JWQBB.js.map +1 -0
  65. package/dist/chunk-XYYSTJHW.js +172 -0
  66. package/dist/chunk-XYYSTJHW.js.map +1 -0
  67. package/dist/index.cjs +1411 -57
  68. package/dist/index.cjs.map +1 -1
  69. package/dist/index.d.cts +96 -3
  70. package/dist/index.d.ts +96 -3
  71. package/dist/index.js +111 -7
  72. package/dist/index.js.map +1 -1
  73. package/dist/mcp/index.d.cts +4 -2
  74. package/dist/mcp/index.d.ts +4 -2
  75. package/dist/presence/index.d.cts +136 -0
  76. package/dist/presence/index.d.ts +136 -0
  77. package/dist/presence.cjs +107 -0
  78. package/dist/presence.cjs.map +1 -0
  79. package/dist/presence.js +5 -0
  80. package/dist/presence.js.map +1 -0
  81. package/dist/registry-2DRURS6U.js +3 -0
  82. package/dist/registry-2DRURS6U.js.map +1 -0
  83. package/dist/server-BJu_AMH3.d.ts +64 -0
  84. package/dist/server-si-VvFxI.d.cts +64 -0
  85. package/dist/sharing/index.d.cts +2 -1
  86. package/dist/sharing/index.d.ts +2 -1
  87. package/dist/sharing.cjs +68 -0
  88. package/dist/sharing.cjs.map +1 -1
  89. package/dist/sharing.js +1 -1
  90. package/dist/styles.css +57 -0
  91. package/dist/styles.css.map +1 -1
  92. package/dist/{types-DR5AS6Rd.d.cts → types-Bf1ZoGmI.d.cts} +1 -1
  93. package/dist/{types-CRPA_D0z.d.ts → types-DXKpLuia.d.ts} +1 -1
  94. package/dist/types-DksGd5Y7.d.cts +112 -0
  95. package/dist/types-DksGd5Y7.d.ts +112 -0
  96. package/dist/undo/index.d.cts +69 -0
  97. package/dist/undo/index.d.ts +69 -0
  98. package/dist/undo.cjs +163 -0
  99. package/dist/undo.cjs.map +1 -0
  100. package/dist/undo.js +5 -0
  101. package/dist/undo.js.map +1 -0
  102. package/package.json +1 -1
  103. package/dist/chunk-2VOQJKSU.js.map +0 -1
  104. package/dist/chunk-5ZUHNNLR.js.map +0 -1
  105. package/dist/chunk-FLEOQUKF.js.map +0 -1
  106. package/dist/server-Bv985us3.d.cts +0 -173
  107. package/dist/server-Bv985us3.d.ts +0 -173
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Minimal MCP (Model Context Protocol) types — covers the subset this
3
+ * package implements: initialize, tools/list, tools/call, plus JSON-RPC.
4
+ *
5
+ * Aligned with the public MCP spec but trimmed to v0.1 needs. See
6
+ * https://spec.modelcontextprotocol.io/ for the full surface.
7
+ */
8
+ type JsonValue = string | number | boolean | null | {
9
+ [key: string]: JsonValue;
10
+ } | JsonValue[];
11
+ type JsonObject = {
12
+ [key: string]: JsonValue;
13
+ };
14
+ type JsonRpcId = string | number | null;
15
+ type JsonRpcRequest = {
16
+ jsonrpc: "2.0";
17
+ id: JsonRpcId;
18
+ method: string;
19
+ params?: JsonObject;
20
+ };
21
+ type JsonRpcNotification = {
22
+ jsonrpc: "2.0";
23
+ method: string;
24
+ params?: JsonObject;
25
+ };
26
+ type JsonRpcSuccess = {
27
+ jsonrpc: "2.0";
28
+ id: JsonRpcId;
29
+ result: JsonValue;
30
+ };
31
+ type JsonRpcError = {
32
+ jsonrpc: "2.0";
33
+ id: JsonRpcId;
34
+ error: {
35
+ code: number;
36
+ message: string;
37
+ data?: JsonValue;
38
+ };
39
+ };
40
+ type JsonRpcMessage = JsonRpcRequest | JsonRpcNotification | JsonRpcSuccess | JsonRpcError;
41
+ declare const JSONRPC_PARSE_ERROR = -32700;
42
+ declare const JSONRPC_INVALID_REQUEST = -32600;
43
+ declare const JSONRPC_METHOD_NOT_FOUND = -32601;
44
+ declare const JSONRPC_INVALID_PARAMS = -32602;
45
+ declare const JSONRPC_INTERNAL_ERROR = -32603;
46
+ type ServerCapabilities = {
47
+ tools?: {
48
+ listChanged?: boolean;
49
+ };
50
+ resources?: {
51
+ listChanged?: boolean;
52
+ subscribe?: boolean;
53
+ };
54
+ prompts?: {
55
+ listChanged?: boolean;
56
+ };
57
+ logging?: Record<string, never>;
58
+ };
59
+ type ServerInfo = {
60
+ name: string;
61
+ version: string;
62
+ title?: string;
63
+ };
64
+ type InitializeResult = {
65
+ protocolVersion: string;
66
+ capabilities: ServerCapabilities;
67
+ serverInfo: ServerInfo;
68
+ instructions?: string;
69
+ };
70
+ type ToolInputSchema = {
71
+ type: "object";
72
+ properties?: Record<string, JsonValue>;
73
+ required?: string[];
74
+ additionalProperties?: boolean;
75
+ };
76
+ type ToolDefinition = {
77
+ name: string;
78
+ title?: string;
79
+ description?: string;
80
+ inputSchema: ToolInputSchema;
81
+ };
82
+ type ContentBlock = {
83
+ type: "text";
84
+ text: string;
85
+ } | {
86
+ type: "image";
87
+ data: string;
88
+ mimeType: string;
89
+ } | {
90
+ type: "resource";
91
+ resource: {
92
+ uri: string;
93
+ text?: string;
94
+ mimeType?: string;
95
+ };
96
+ };
97
+ type CallToolResult = {
98
+ content: ContentBlock[];
99
+ isError?: boolean;
100
+ /** Structured tool output — non-spec but useful for typed bridges. */
101
+ structuredContent?: JsonValue;
102
+ };
103
+ /** Handler signature for a tool registered on the MicroMcpServer. */
104
+ type ToolHandler = (args: JsonObject) => Promise<CallToolResult> | CallToolResult;
105
+ /** Internal record kept by the server. */
106
+ type RegisteredTool = {
107
+ definition: ToolDefinition;
108
+ handler: ToolHandler;
109
+ };
110
+ declare const MCP_PROTOCOL_VERSION = "2025-06-18";
111
+
112
+ export { type CallToolResult as C, type InitializeResult as I, type JsonRpcMessage as J, MCP_PROTOCOL_VERSION as M, type RegisteredTool as R, type ServerInfo as S, type ToolDefinition as T, type ServerCapabilities as a, type ToolHandler as b, type ContentBlock as c, type JsonObject as d, type JsonRpcNotification as e, type JsonRpcRequest as f, type JsonValue as g, JSONRPC_INTERNAL_ERROR as h, JSONRPC_INVALID_PARAMS as i, JSONRPC_INVALID_REQUEST as j, JSONRPC_METHOD_NOT_FOUND as k, JSONRPC_PARSE_ERROR as l, type JsonRpcError as m, type JsonRpcId as n, type JsonRpcSuccess as o, type ToolInputSchema as p };
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Minimal MCP (Model Context Protocol) types — covers the subset this
3
+ * package implements: initialize, tools/list, tools/call, plus JSON-RPC.
4
+ *
5
+ * Aligned with the public MCP spec but trimmed to v0.1 needs. See
6
+ * https://spec.modelcontextprotocol.io/ for the full surface.
7
+ */
8
+ type JsonValue = string | number | boolean | null | {
9
+ [key: string]: JsonValue;
10
+ } | JsonValue[];
11
+ type JsonObject = {
12
+ [key: string]: JsonValue;
13
+ };
14
+ type JsonRpcId = string | number | null;
15
+ type JsonRpcRequest = {
16
+ jsonrpc: "2.0";
17
+ id: JsonRpcId;
18
+ method: string;
19
+ params?: JsonObject;
20
+ };
21
+ type JsonRpcNotification = {
22
+ jsonrpc: "2.0";
23
+ method: string;
24
+ params?: JsonObject;
25
+ };
26
+ type JsonRpcSuccess = {
27
+ jsonrpc: "2.0";
28
+ id: JsonRpcId;
29
+ result: JsonValue;
30
+ };
31
+ type JsonRpcError = {
32
+ jsonrpc: "2.0";
33
+ id: JsonRpcId;
34
+ error: {
35
+ code: number;
36
+ message: string;
37
+ data?: JsonValue;
38
+ };
39
+ };
40
+ type JsonRpcMessage = JsonRpcRequest | JsonRpcNotification | JsonRpcSuccess | JsonRpcError;
41
+ declare const JSONRPC_PARSE_ERROR = -32700;
42
+ declare const JSONRPC_INVALID_REQUEST = -32600;
43
+ declare const JSONRPC_METHOD_NOT_FOUND = -32601;
44
+ declare const JSONRPC_INVALID_PARAMS = -32602;
45
+ declare const JSONRPC_INTERNAL_ERROR = -32603;
46
+ type ServerCapabilities = {
47
+ tools?: {
48
+ listChanged?: boolean;
49
+ };
50
+ resources?: {
51
+ listChanged?: boolean;
52
+ subscribe?: boolean;
53
+ };
54
+ prompts?: {
55
+ listChanged?: boolean;
56
+ };
57
+ logging?: Record<string, never>;
58
+ };
59
+ type ServerInfo = {
60
+ name: string;
61
+ version: string;
62
+ title?: string;
63
+ };
64
+ type InitializeResult = {
65
+ protocolVersion: string;
66
+ capabilities: ServerCapabilities;
67
+ serverInfo: ServerInfo;
68
+ instructions?: string;
69
+ };
70
+ type ToolInputSchema = {
71
+ type: "object";
72
+ properties?: Record<string, JsonValue>;
73
+ required?: string[];
74
+ additionalProperties?: boolean;
75
+ };
76
+ type ToolDefinition = {
77
+ name: string;
78
+ title?: string;
79
+ description?: string;
80
+ inputSchema: ToolInputSchema;
81
+ };
82
+ type ContentBlock = {
83
+ type: "text";
84
+ text: string;
85
+ } | {
86
+ type: "image";
87
+ data: string;
88
+ mimeType: string;
89
+ } | {
90
+ type: "resource";
91
+ resource: {
92
+ uri: string;
93
+ text?: string;
94
+ mimeType?: string;
95
+ };
96
+ };
97
+ type CallToolResult = {
98
+ content: ContentBlock[];
99
+ isError?: boolean;
100
+ /** Structured tool output — non-spec but useful for typed bridges. */
101
+ structuredContent?: JsonValue;
102
+ };
103
+ /** Handler signature for a tool registered on the MicroMcpServer. */
104
+ type ToolHandler = (args: JsonObject) => Promise<CallToolResult> | CallToolResult;
105
+ /** Internal record kept by the server. */
106
+ type RegisteredTool = {
107
+ definition: ToolDefinition;
108
+ handler: ToolHandler;
109
+ };
110
+ declare const MCP_PROTOCOL_VERSION = "2025-06-18";
111
+
112
+ export { type CallToolResult as C, type InitializeResult as I, type JsonRpcMessage as J, MCP_PROTOCOL_VERSION as M, type RegisteredTool as R, type ServerInfo as S, type ToolDefinition as T, type ServerCapabilities as a, type ToolHandler as b, type ContentBlock as c, type JsonObject as d, type JsonRpcNotification as e, type JsonRpcRequest as f, type JsonValue as g, JSONRPC_INTERNAL_ERROR as h, JSONRPC_INVALID_PARAMS as i, JSONRPC_INVALID_REQUEST as j, JSONRPC_METHOD_NOT_FOUND as k, JSONRPC_PARSE_ERROR as l, type JsonRpcError as m, type JsonRpcId as n, type JsonRpcSuccess as o, type ToolInputSchema as p };
@@ -0,0 +1,69 @@
1
+ import { M as MicroMcpServer } from '../server-si-VvFxI.cjs';
2
+ import '../types-DksGd5Y7.cjs';
3
+
4
+ /**
5
+ * Generic undo/redo stack keyed by `agentId`. Each entry holds:
6
+ * - `do` — re-applies the action (for redo)
7
+ * - `undo` — reverses it
8
+ * - `label` — human-readable summary surfaced in agent_history
9
+ *
10
+ * Bridges register entries by calling `pushUndoEntry` after a successful
11
+ * mutation. The corresponding MCP tools (`agent_undo`, `agent_redo`,
12
+ * `agent_history`) are registered once per server via `registerUndoTools`.
13
+ *
14
+ * Stacks are per-agent so multiple agents can rewind independently.
15
+ */
16
+ type UndoEntry = {
17
+ /** Wall-clock ms. */
18
+ timestamp: number;
19
+ /** Bridge id (e.g. "whiteboard", "form:signup"). */
20
+ bridgeId: string;
21
+ /** Tool name that produced the entry. */
22
+ action: string;
23
+ /** Short human label, e.g. `Added sticky n_abc`. */
24
+ label: string;
25
+ /** Reverse the action. */
26
+ undo: () => void | Promise<void>;
27
+ /** Re-apply the action (used when redoing after an undo). */
28
+ redo: () => void | Promise<void>;
29
+ };
30
+ /** Push a new undo entry on the agent's stack. Clears the redo (future) stack. */
31
+ declare function pushUndoEntry(agentId: string, entry: UndoEntry): void;
32
+ /** Pop and undo the most recent entry. Returns the entry that ran, or null. */
33
+ declare function undoOne(agentId: string): Promise<UndoEntry | null>;
34
+ /** Re-apply the most recently undone entry. Returns it, or null if no future. */
35
+ declare function redoOne(agentId: string): Promise<UndoEntry | null>;
36
+ /** Read the past stack (oldest first). */
37
+ declare function readHistory(agentId: string): UndoEntry[];
38
+ /** Wipe an agent's stacks. */
39
+ declare function clearStack(agentId: string): void;
40
+ /** Test/teardown helper. */
41
+ declare function resetAllUndoStacks(): void;
42
+
43
+ type UndoToolsOptions = {
44
+ /** Default agent id when the caller doesn't pass one. */
45
+ defaultAgentId?: string;
46
+ };
47
+ /**
48
+ * ensureUndoToolsRegistered — bridges call this on construction. Safe to
49
+ * call repeatedly with the same server; subsequent calls are no-ops.
50
+ */
51
+ declare function ensureUndoToolsRegistered(server: MicroMcpServer, options?: UndoToolsOptions): void;
52
+ /**
53
+ * registerUndoTools — add agent_undo / agent_redo / agent_history to the
54
+ * server. Returns a disposer that unregisters all three.
55
+ */
56
+ declare function registerUndoTools(server: MicroMcpServer, options?: UndoToolsOptions): () => void;
57
+
58
+ /**
59
+ * useUndoStack — minimal React snapshot of an agent's history. Polls every
60
+ * `intervalMs` (default 500). Use this to render an inline "agent timeline"
61
+ * in a sidebar or activity panel. No subscription model in v1 — keeping it
62
+ * simple; bridge mutations are infrequent enough that polling is fine.
63
+ */
64
+ declare function useUndoStack(agentId: string, intervalMs?: number): {
65
+ history: UndoEntry[];
66
+ refresh: () => void;
67
+ };
68
+
69
+ export { type UndoEntry, type UndoToolsOptions, clearStack, ensureUndoToolsRegistered, pushUndoEntry, readHistory, redoOne, registerUndoTools, resetAllUndoStacks, undoOne, useUndoStack };
@@ -0,0 +1,69 @@
1
+ import { M as MicroMcpServer } from '../server-BJu_AMH3.js';
2
+ import '../types-DksGd5Y7.js';
3
+
4
+ /**
5
+ * Generic undo/redo stack keyed by `agentId`. Each entry holds:
6
+ * - `do` — re-applies the action (for redo)
7
+ * - `undo` — reverses it
8
+ * - `label` — human-readable summary surfaced in agent_history
9
+ *
10
+ * Bridges register entries by calling `pushUndoEntry` after a successful
11
+ * mutation. The corresponding MCP tools (`agent_undo`, `agent_redo`,
12
+ * `agent_history`) are registered once per server via `registerUndoTools`.
13
+ *
14
+ * Stacks are per-agent so multiple agents can rewind independently.
15
+ */
16
+ type UndoEntry = {
17
+ /** Wall-clock ms. */
18
+ timestamp: number;
19
+ /** Bridge id (e.g. "whiteboard", "form:signup"). */
20
+ bridgeId: string;
21
+ /** Tool name that produced the entry. */
22
+ action: string;
23
+ /** Short human label, e.g. `Added sticky n_abc`. */
24
+ label: string;
25
+ /** Reverse the action. */
26
+ undo: () => void | Promise<void>;
27
+ /** Re-apply the action (used when redoing after an undo). */
28
+ redo: () => void | Promise<void>;
29
+ };
30
+ /** Push a new undo entry on the agent's stack. Clears the redo (future) stack. */
31
+ declare function pushUndoEntry(agentId: string, entry: UndoEntry): void;
32
+ /** Pop and undo the most recent entry. Returns the entry that ran, or null. */
33
+ declare function undoOne(agentId: string): Promise<UndoEntry | null>;
34
+ /** Re-apply the most recently undone entry. Returns it, or null if no future. */
35
+ declare function redoOne(agentId: string): Promise<UndoEntry | null>;
36
+ /** Read the past stack (oldest first). */
37
+ declare function readHistory(agentId: string): UndoEntry[];
38
+ /** Wipe an agent's stacks. */
39
+ declare function clearStack(agentId: string): void;
40
+ /** Test/teardown helper. */
41
+ declare function resetAllUndoStacks(): void;
42
+
43
+ type UndoToolsOptions = {
44
+ /** Default agent id when the caller doesn't pass one. */
45
+ defaultAgentId?: string;
46
+ };
47
+ /**
48
+ * ensureUndoToolsRegistered — bridges call this on construction. Safe to
49
+ * call repeatedly with the same server; subsequent calls are no-ops.
50
+ */
51
+ declare function ensureUndoToolsRegistered(server: MicroMcpServer, options?: UndoToolsOptions): void;
52
+ /**
53
+ * registerUndoTools — add agent_undo / agent_redo / agent_history to the
54
+ * server. Returns a disposer that unregisters all three.
55
+ */
56
+ declare function registerUndoTools(server: MicroMcpServer, options?: UndoToolsOptions): () => void;
57
+
58
+ /**
59
+ * useUndoStack — minimal React snapshot of an agent's history. Polls every
60
+ * `intervalMs` (default 500). Use this to render an inline "agent timeline"
61
+ * in a sidebar or activity panel. No subscription model in v1 — keeping it
62
+ * simple; bridge mutations are infrequent enough that polling is fine.
63
+ */
64
+ declare function useUndoStack(agentId: string, intervalMs?: number): {
65
+ history: UndoEntry[];
66
+ refresh: () => void;
67
+ };
68
+
69
+ export { type UndoEntry, type UndoToolsOptions, clearStack, ensureUndoToolsRegistered, pushUndoEntry, readHistory, redoOne, registerUndoTools, resetAllUndoStacks, undoOne, useUndoStack };
package/dist/undo.cjs ADDED
@@ -0,0 +1,163 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+
5
+ // src/undo/undo-stack.ts
6
+ var stacks = /* @__PURE__ */ new Map();
7
+ var CAP = 200;
8
+ function getStack(agentId) {
9
+ let s = stacks.get(agentId);
10
+ if (!s) {
11
+ s = { past: [], future: [] };
12
+ stacks.set(agentId, s);
13
+ }
14
+ return s;
15
+ }
16
+ function pushUndoEntry(agentId, entry) {
17
+ const s = getStack(agentId);
18
+ s.past.push(entry);
19
+ if (s.past.length > CAP) s.past.splice(0, s.past.length - CAP);
20
+ s.future.length = 0;
21
+ }
22
+ async function undoOne(agentId) {
23
+ const s = getStack(agentId);
24
+ const entry = s.past.pop();
25
+ if (!entry) return null;
26
+ await entry.undo();
27
+ s.future.push(entry);
28
+ return entry;
29
+ }
30
+ async function redoOne(agentId) {
31
+ const s = getStack(agentId);
32
+ const entry = s.future.pop();
33
+ if (!entry) return null;
34
+ await entry.redo();
35
+ s.past.push(entry);
36
+ return entry;
37
+ }
38
+ function readHistory(agentId) {
39
+ return getStack(agentId).past.slice();
40
+ }
41
+ function clearStack(agentId) {
42
+ stacks.delete(agentId);
43
+ }
44
+ function resetAllUndoStacks() {
45
+ stacks.clear();
46
+ }
47
+
48
+ // src/mcp/server.ts
49
+ function textResult(text, structured) {
50
+ return {
51
+ content: [{ type: "text", text }],
52
+ ...structured !== void 0 ? { structuredContent: structured } : {}
53
+ };
54
+ }
55
+ function errorResult(text) {
56
+ return { content: [{ type: "text", text }], isError: true };
57
+ }
58
+
59
+ // src/undo/undo-tools.ts
60
+ var installedServers = /* @__PURE__ */ new WeakSet();
61
+ function ensureUndoToolsRegistered(server, options = {}) {
62
+ if (installedServers.has(server)) return;
63
+ installedServers.add(server);
64
+ registerUndoTools(server, options);
65
+ }
66
+ function registerUndoTools(server, options = {}) {
67
+ const defaultAgent = options.defaultAgentId ?? "agent";
68
+ const disposers = [];
69
+ const agentOf = (args) => typeof args?.agentId === "string" ? args.agentId : defaultAgent;
70
+ disposers.push(
71
+ server.registerTool(
72
+ {
73
+ name: "agent_undo",
74
+ description: "Undo the most recent action on the agent's stack. Optional agentId targets a specific agent.",
75
+ inputSchema: {
76
+ type: "object",
77
+ properties: { agentId: { type: "string" } },
78
+ additionalProperties: false
79
+ }
80
+ },
81
+ async (args) => {
82
+ const entry = await undoOne(agentOf(args));
83
+ if (!entry) return errorResult("Nothing to undo.");
84
+ return textResult(`Undid: ${entry.label}`, { entry: serialize(entry) });
85
+ }
86
+ )
87
+ );
88
+ disposers.push(
89
+ server.registerTool(
90
+ {
91
+ name: "agent_redo",
92
+ description: "Redo the most recently undone action.",
93
+ inputSchema: {
94
+ type: "object",
95
+ properties: { agentId: { type: "string" } },
96
+ additionalProperties: false
97
+ }
98
+ },
99
+ async (args) => {
100
+ const entry = await redoOne(agentOf(args));
101
+ if (!entry) return errorResult("Nothing to redo.");
102
+ return textResult(`Redid: ${entry.label}`, { entry: serialize(entry) });
103
+ }
104
+ )
105
+ );
106
+ disposers.push(
107
+ server.registerTool(
108
+ {
109
+ name: "agent_history",
110
+ description: "List the agent's undo stack (oldest first). Useful for understanding what's reversible.",
111
+ inputSchema: {
112
+ type: "object",
113
+ properties: { agentId: { type: "string" } },
114
+ additionalProperties: false
115
+ }
116
+ },
117
+ async (args) => {
118
+ const history = readHistory(agentOf(args)).map(serialize);
119
+ const text = history.map((e) => `${new Date(e.timestamp).toISOString()} ${e.bridgeId} ${e.action}: ${e.label}`).join("\n");
120
+ return textResult(text || "(empty)", history);
121
+ }
122
+ )
123
+ );
124
+ return () => disposers.forEach((d) => d());
125
+ }
126
+ function serialize(entry) {
127
+ return {
128
+ timestamp: entry.timestamp,
129
+ bridgeId: entry.bridgeId,
130
+ action: entry.action,
131
+ label: entry.label
132
+ };
133
+ }
134
+ function useUndoStack(agentId, intervalMs = 500) {
135
+ const [history, setHistory] = react.useState(() => readHistory(agentId));
136
+ react.useEffect(() => {
137
+ let cancelled = false;
138
+ const tick = () => {
139
+ if (cancelled) return;
140
+ setHistory(readHistory(agentId));
141
+ };
142
+ const id = setInterval(tick, intervalMs);
143
+ tick();
144
+ return () => {
145
+ cancelled = true;
146
+ clearInterval(id);
147
+ };
148
+ }, [agentId, intervalMs]);
149
+ const refresh = react.useCallback(() => setHistory(readHistory(agentId)), [agentId]);
150
+ return { history, refresh };
151
+ }
152
+
153
+ exports.clearStack = clearStack;
154
+ exports.ensureUndoToolsRegistered = ensureUndoToolsRegistered;
155
+ exports.pushUndoEntry = pushUndoEntry;
156
+ exports.readHistory = readHistory;
157
+ exports.redoOne = redoOne;
158
+ exports.registerUndoTools = registerUndoTools;
159
+ exports.resetAllUndoStacks = resetAllUndoStacks;
160
+ exports.undoOne = undoOne;
161
+ exports.useUndoStack = useUndoStack;
162
+ //# sourceMappingURL=undo.cjs.map
163
+ //# sourceMappingURL=undo.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/undo/undo-stack.ts","../src/mcp/server.ts","../src/undo/undo-tools.ts","../src/undo/use-undo-stack.ts"],"names":["useState","useEffect","useCallback"],"mappings":"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","file":"undo.cjs","sourcesContent":["/**\n * Generic undo/redo stack keyed by `agentId`. Each entry holds:\n * - `do` — re-applies the action (for redo)\n * - `undo` — reverses it\n * - `label` — human-readable summary surfaced in agent_history\n *\n * Bridges register entries by calling `pushUndoEntry` after a successful\n * mutation. The corresponding MCP tools (`agent_undo`, `agent_redo`,\n * `agent_history`) are registered once per server via `registerUndoTools`.\n *\n * Stacks are per-agent so multiple agents can rewind independently.\n */\n\nexport type UndoEntry = {\n /** Wall-clock ms. */\n timestamp: number;\n /** Bridge id (e.g. \"whiteboard\", \"form:signup\"). */\n bridgeId: string;\n /** Tool name that produced the entry. */\n action: string;\n /** Short human label, e.g. `Added sticky n_abc`. */\n label: string;\n /** Reverse the action. */\n undo: () => void | Promise<void>;\n /** Re-apply the action (used when redoing after an undo). */\n redo: () => void | Promise<void>;\n};\n\ntype Stack = { past: UndoEntry[]; future: UndoEntry[] };\n\nconst stacks = new Map<string, Stack>();\nconst CAP = 200;\n\nfunction getStack(agentId: string): Stack {\n let s = stacks.get(agentId);\n if (!s) {\n s = { past: [], future: [] };\n stacks.set(agentId, s);\n }\n return s;\n}\n\n/** Push a new undo entry on the agent's stack. Clears the redo (future) stack. */\nexport function pushUndoEntry(agentId: string, entry: UndoEntry): void {\n const s = getStack(agentId);\n s.past.push(entry);\n if (s.past.length > CAP) s.past.splice(0, s.past.length - CAP);\n s.future.length = 0;\n}\n\n/** Pop and undo the most recent entry. Returns the entry that ran, or null. */\nexport async function undoOne(agentId: string): Promise<UndoEntry | null> {\n const s = getStack(agentId);\n const entry = s.past.pop();\n if (!entry) return null;\n await entry.undo();\n s.future.push(entry);\n return entry;\n}\n\n/** Re-apply the most recently undone entry. Returns it, or null if no future. */\nexport async function redoOne(agentId: string): Promise<UndoEntry | null> {\n const s = getStack(agentId);\n const entry = s.future.pop();\n if (!entry) return null;\n await entry.redo();\n s.past.push(entry);\n return entry;\n}\n\n/** Read the past stack (oldest first). */\nexport function readHistory(agentId: string): UndoEntry[] {\n return getStack(agentId).past.slice();\n}\n\n/** Wipe an agent's stacks. */\nexport function clearStack(agentId: string): void {\n stacks.delete(agentId);\n}\n\n/** Test/teardown helper. */\nexport function resetAllUndoStacks(): void {\n stacks.clear();\n}\n","import {\n type CallToolResult,\n type JsonObject,\n type JsonRpcMessage,\n type JsonRpcRequest,\n type JsonRpcId,\n type RegisteredTool,\n type ServerCapabilities,\n type ServerInfo,\n type ToolDefinition,\n type ToolHandler,\n JSONRPC_INTERNAL_ERROR,\n JSONRPC_INVALID_PARAMS,\n JSONRPC_METHOD_NOT_FOUND,\n MCP_PROTOCOL_VERSION,\n} from \"./types\";\n\nexport type McpServerOptions = {\n info: ServerInfo;\n /** Defaults to { tools: { listChanged: true } } */\n capabilities?: ServerCapabilities;\n /** Free-text instructions surfaced to clients during initialize. */\n instructions?: string;\n};\n\nexport type Transport = {\n /** Called by the server when it has a message to deliver to the client. */\n send: (message: JsonRpcMessage) => void;\n /** Called by the server when it's torn down so the transport can clean up. */\n close?: () => void;\n};\n\n/**\n * MicroMcpServer — protocol-level MCP server, transport-agnostic.\n *\n * Use it like:\n *\n * const server = new MicroMcpServer({ info: { name: \"session\", version: \"0.1\" } });\n * server.registerTool({ name: \"...\", inputSchema: { type: \"object\" } }, async (args) => ({...}));\n * const transport = new InProcessTransport();\n * server.attach(transport);\n * transport.deliver({ ... }); // client → server frames\n *\n * The same server can serve multiple transports (e.g. an in-process agent\n * AND a relayed external client) by attaching each one.\n */\nexport class MicroMcpServer {\n private tools = new Map<string, RegisteredTool>();\n private transports = new Set<Transport>();\n private notifyListChangedScheduled = false;\n\n readonly info: ServerInfo;\n readonly capabilities: ServerCapabilities;\n readonly instructions?: string;\n\n constructor(options: McpServerOptions) {\n this.info = options.info;\n this.capabilities = options.capabilities ?? { tools: { listChanged: true } };\n this.instructions = options.instructions;\n }\n\n attach(transport: Transport): () => void {\n this.transports.add(transport);\n return () => this.detach(transport);\n }\n\n detach(transport: Transport): void {\n if (this.transports.delete(transport)) {\n transport.close?.();\n }\n }\n\n registerTool(definition: ToolDefinition, handler: ToolHandler): () => void {\n this.tools.set(definition.name, { definition, handler });\n this.scheduleListChangedNotification();\n return () => this.unregisterTool(definition.name);\n }\n\n unregisterTool(name: string): void {\n if (this.tools.delete(name)) {\n this.scheduleListChangedNotification();\n }\n }\n\n listTools(): ToolDefinition[] {\n return Array.from(this.tools.values()).map((t) => t.definition);\n }\n\n /**\n * Receive a JSON-RPC frame from a client (called by the transport).\n * The transport is responsible for sending the response back.\n */\n async receive(transport: Transport, message: JsonRpcMessage): Promise<void> {\n if (!(\"method\" in message)) return; // It's a response, not a request — ignore.\n\n const isNotification = !(\"id\" in message);\n if (isNotification) {\n // Notifications are fire-and-forget. We ignore unknown methods.\n return;\n }\n\n const request = message as JsonRpcRequest;\n try {\n const result = await this.handle(request);\n transport.send({ jsonrpc: \"2.0\", id: request.id, result });\n } catch (err) {\n transport.send({\n jsonrpc: \"2.0\",\n id: request.id,\n error: this.toRpcError(err),\n });\n }\n }\n\n private async handle(request: JsonRpcRequest): Promise<any> {\n const { method, params } = request;\n switch (method) {\n case \"initialize\":\n return {\n protocolVersion: MCP_PROTOCOL_VERSION,\n capabilities: this.capabilities,\n serverInfo: this.info,\n ...(this.instructions ? { instructions: this.instructions } : {}),\n };\n\n case \"tools/list\":\n return { tools: this.listTools() };\n\n case \"tools/call\": {\n const name = params?.name;\n const args = (params?.arguments ?? {}) as JsonObject;\n if (typeof name !== \"string\") {\n throw rpcError(JSONRPC_INVALID_PARAMS, \"tools/call requires `name`\");\n }\n const tool = this.tools.get(name);\n if (!tool) {\n throw rpcError(JSONRPC_METHOD_NOT_FOUND, `Unknown tool: ${name}`);\n }\n const result = await tool.handler(args);\n return result satisfies CallToolResult;\n }\n\n case \"ping\":\n return {};\n\n default:\n throw rpcError(JSONRPC_METHOD_NOT_FOUND, `Unsupported method: ${method}`);\n }\n }\n\n private scheduleListChangedNotification(): void {\n if (this.notifyListChangedScheduled) return;\n this.notifyListChangedScheduled = true;\n queueMicrotask(() => {\n this.notifyListChangedScheduled = false;\n this.broadcast({ jsonrpc: \"2.0\", method: \"notifications/tools/list_changed\" });\n });\n }\n\n private broadcast(message: JsonRpcMessage): void {\n for (const t of this.transports) t.send(message);\n }\n\n private toRpcError(err: unknown): { code: number; message: string; data?: any } {\n if (err && typeof err === \"object\" && \"code\" in err && \"message\" in err) {\n return err as any;\n }\n return {\n code: JSONRPC_INTERNAL_ERROR,\n message: err instanceof Error ? err.message : String(err),\n };\n }\n}\n\nexport function rpcError(code: number, message: string, data?: any) {\n return { code, message, ...(data !== undefined ? { data } : {}) };\n}\n\n/**\n * Helper to build a CallToolResult from a string or structured value.\n */\nexport function textResult(text: string, structured?: any): CallToolResult {\n return {\n content: [{ type: \"text\", text }],\n ...(structured !== undefined ? { structuredContent: structured } : {}),\n };\n}\n\nexport function errorResult(text: string): CallToolResult {\n return { content: [{ type: \"text\", text }], isError: true };\n}\n\n// Internal helper so the JsonRpcId import isn't dropped by tsup\ntype _KeepIdImport = JsonRpcId;\n","import { textResult, errorResult } from \"../mcp/server\";\nimport type { MicroMcpServer } from \"../mcp/server\";\nimport { readHistory, redoOne, undoOne } from \"./undo-stack\";\n\nexport type UndoToolsOptions = {\n /** Default agent id when the caller doesn't pass one. */\n defaultAgentId?: string;\n};\n\n/**\n * Idempotent tracker so multiple bridges on the same server only register\n * agent_undo / agent_redo / agent_history once.\n */\nconst installedServers = new WeakSet<MicroMcpServer>();\n\n/**\n * ensureUndoToolsRegistered — bridges call this on construction. Safe to\n * call repeatedly with the same server; subsequent calls are no-ops.\n */\nexport function ensureUndoToolsRegistered(server: MicroMcpServer, options: UndoToolsOptions = {}): void {\n if (installedServers.has(server)) return;\n installedServers.add(server);\n registerUndoTools(server, options);\n}\n\n/**\n * registerUndoTools — add agent_undo / agent_redo / agent_history to the\n * server. Returns a disposer that unregisters all three.\n */\nexport function registerUndoTools(server: MicroMcpServer, options: UndoToolsOptions = {}): () => void {\n const defaultAgent = options.defaultAgentId ?? \"agent\";\n const disposers: Array<() => void> = [];\n const agentOf = (args: any): string =>\n typeof args?.agentId === \"string\" ? args.agentId : defaultAgent;\n\n disposers.push(\n server.registerTool(\n {\n name: \"agent_undo\",\n description: \"Undo the most recent action on the agent's stack. Optional agentId targets a specific agent.\",\n inputSchema: {\n type: \"object\",\n properties: { agentId: { type: \"string\" } },\n additionalProperties: false,\n },\n },\n async (args) => {\n const entry = await undoOne(agentOf(args));\n if (!entry) return errorResult(\"Nothing to undo.\");\n return textResult(`Undid: ${entry.label}`, { entry: serialize(entry) });\n },\n ),\n );\n\n disposers.push(\n server.registerTool(\n {\n name: \"agent_redo\",\n description: \"Redo the most recently undone action.\",\n inputSchema: {\n type: \"object\",\n properties: { agentId: { type: \"string\" } },\n additionalProperties: false,\n },\n },\n async (args) => {\n const entry = await redoOne(agentOf(args));\n if (!entry) return errorResult(\"Nothing to redo.\");\n return textResult(`Redid: ${entry.label}`, { entry: serialize(entry) });\n },\n ),\n );\n\n disposers.push(\n server.registerTool(\n {\n name: \"agent_history\",\n description: \"List the agent's undo stack (oldest first). Useful for understanding what's reversible.\",\n inputSchema: {\n type: \"object\",\n properties: { agentId: { type: \"string\" } },\n additionalProperties: false,\n },\n },\n async (args) => {\n const history = readHistory(agentOf(args)).map(serialize);\n const text = history.map((e) => `${new Date(e.timestamp).toISOString()} ${e.bridgeId} ${e.action}: ${e.label}`).join(\"\\n\");\n return textResult(text || \"(empty)\", history);\n },\n ),\n );\n\n return () => disposers.forEach((d) => d());\n}\n\nfunction serialize(entry: import(\"./undo-stack\").UndoEntry) {\n return {\n timestamp: entry.timestamp,\n bridgeId: entry.bridgeId,\n action: entry.action,\n label: entry.label,\n };\n}\n","import { useState, useEffect, useCallback } from \"react\";\nimport { readHistory } from \"./undo-stack\";\n\n/**\n * useUndoStack — minimal React snapshot of an agent's history. Polls every\n * `intervalMs` (default 500). Use this to render an inline \"agent timeline\"\n * in a sidebar or activity panel. No subscription model in v1 — keeping it\n * simple; bridge mutations are infrequent enough that polling is fine.\n */\nexport function useUndoStack(agentId: string, intervalMs = 500) {\n const [history, setHistory] = useState(() => readHistory(agentId));\n\n useEffect(() => {\n let cancelled = false;\n const tick = () => {\n if (cancelled) return;\n setHistory(readHistory(agentId));\n };\n const id = setInterval(tick, intervalMs);\n tick();\n return () => { cancelled = true; clearInterval(id); };\n }, [agentId, intervalMs]);\n\n const refresh = useCallback(() => setHistory(readHistory(agentId)), [agentId]);\n return { history, refresh };\n}\n"]}
package/dist/undo.js ADDED
@@ -0,0 +1,5 @@
1
+ export { useUndoStack } from './chunk-DJOWMF6Q.js';
2
+ export { clearStack, ensureUndoToolsRegistered, pushUndoEntry, readHistory, redoOne, registerUndoTools, resetAllUndoStacks, undoOne } from './chunk-ACBENYYO.js';
3
+ import './chunk-QGCF7YKW.js';
4
+ //# sourceMappingURL=undo.js.map
5
+ //# sourceMappingURL=undo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"undo.js"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@particle-academy/agent-integrations",
3
- "version": "0.2.4",
3
+ "version": "0.3.4",
4
4
  "description": "MCP-driven agent presence in collab sessions: per-session micro-MCP server, pluggable bridges to fancy-* packages, and agent UX components (panel + on-canvas cursor).",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/bridges/flow.ts"],"names":[],"mappings":"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","file":"chunk-2VOQJKSU.js","sourcesContent":["// Loose types so this bridge builds standalone without a hard dep on\n// fancy-flow. Hosts that have fancy-flow installed get full editor\n// integration via the runtime dynamic imports below.\ntype FlowNode = {\n id: string;\n type?: string;\n position: { x: number; y: number };\n data: { kind?: string; label?: string; description?: string; status?: string; statusText?: string; config?: Record<string, unknown>; [k: string]: unknown };\n};\ntype FlowEdge = {\n id: string;\n source: string;\n target: string;\n sourceHandle?: string;\n targetHandle?: string;\n label?: string;\n [k: string]: unknown;\n};\ntype FlowGraph = { nodes: FlowNode[]; edges: FlowEdge[] };\ntype NodeRunStatus = \"idle\" | \"queued\" | \"running\" | \"done\" | \"error\";\ntype ExecutorRegistry = Record<string, unknown>;\ntype RunResult = { ok: boolean; outputs: Record<string, unknown>; error?: string };\n\nimport { textResult, errorResult } from \"../mcp/server\";\nimport type { MicroMcpServer } from \"../mcp/server\";\nimport type { JsonObject } from \"../mcp/types\";\nimport type { Bridge } from \"./types\";\n\n/**\n * Adapter the host provides — same shape as the editor's local state plus\n * an optional `run`/`cancel` pair so agents can trigger executions.\n */\nexport type FlowBridgeAdapter = {\n getNodes: () => FlowNode[];\n setNodes: (next: FlowNode[] | ((prev: FlowNode[]) => FlowNode[])) => void;\n getEdges: () => FlowEdge[];\n setEdges: (next: FlowEdge[] | ((prev: FlowEdge[]) => FlowEdge[])) => void;\n /** Optional: invoke runFlow with the host's executor registry. */\n run?: (executors?: ExecutorRegistry) => Promise<RunResult>;\n /** Optional: cancel the in-flight run. */\n cancel?: () => void;\n /** Optional: set per-node status text without going through the runner\n * (useful for agents narrating). */\n setNodeStatus?: (id: string, status: NodeRunStatus, text?: string) => void;\n};\n\nexport type FlowBridgeOptions = {\n adapter: FlowBridgeAdapter;\n /** Identity tagged onto agent-authored nodes. */\n agent?: { id: string; name?: string; color?: string };\n};\n\nconst DEFAULT_AGENT = { id: \"agent\", name: \"Agent\", color: \"#a855f7\" };\nconst KINDS: string[] = [\"trigger\", \"action\", \"decision\", \"output\", \"note\", \"subgraph\"];\n\nconst num = (v: unknown, fallback?: number): number =>\n typeof v === \"number\" && Number.isFinite(v) ? v : fallback ?? 0;\nconst str = (v: unknown, fallback = \"\"): string => (typeof v === \"string\" ? v : fallback);\n\nconst newId = (prefix: string) =>\n `${prefix}_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 7)}`;\n\n/**\n * registerFlowBridge — wires an MCP tool set against a fancy-flow editor's\n * controlled state. Mirrors the whiteboard bridge in shape: read tools,\n * mutation tools (add / update / delete nodes + edges), and optional\n * run/cancel if the host provides those callbacks.\n */\nexport function registerFlowBridge(\n server: MicroMcpServer,\n options: FlowBridgeOptions,\n): Bridge {\n const { adapter } = options;\n const agent = { ...DEFAULT_AGENT, ...(options.agent ?? {}) };\n const disposers: Array<() => void> = [];\n\n const reg = (\n name: string,\n description: string,\n properties: Record<string, unknown>,\n required: string[],\n handler: (args: JsonObject) => Promise<any> | any,\n ) => {\n disposers.push(\n server.registerTool(\n {\n name,\n description,\n inputSchema: { type: \"object\", properties: properties as any, required, additionalProperties: false },\n },\n async (args) => {\n try {\n return await handler(args);\n } catch (e) {\n return errorResult(e instanceof Error ? e.message : String(e));\n }\n },\n ),\n );\n };\n\n // ───────────── Read tools ─────────────\n\n reg(\"flow_get_state\", \"Get the full graph: nodes + edges.\", {}, [], () => {\n const state: FlowGraph = { nodes: adapter.getNodes(), edges: adapter.getEdges() };\n return textResult(JSON.stringify(state, null, 2), state);\n });\n\n reg(\"flow_list_nodes\", \"Summarise every node: id, kind, label, position, status.\", {}, [], () => {\n const items = adapter.getNodes().map((n) => ({\n id: n.id,\n kind: n.type,\n label: n.data?.label,\n x: Math.round(n.position.x),\n y: Math.round(n.position.y),\n status: n.data?.status ?? \"idle\",\n }));\n const text = items.map((i) => `${i.kind} ${i.id}: \"${i.label}\" @(${i.x},${i.y}) [${i.status}]`).join(\"\\n\") || \"(empty graph)\";\n return textResult(text, items);\n });\n\n reg(\n \"flow_get_node\",\n \"Get a single node's full record by id.\",\n { id: { type: \"string\" } },\n [\"id\"],\n (args) => {\n const id = str(args.id);\n const node = adapter.getNodes().find((n) => n.id === id);\n if (!node) return errorResult(`No node with id ${id}`);\n return textResult(JSON.stringify(node, null, 2), node);\n },\n );\n\n reg(\n \"flow_list_node_kinds\",\n \"List every node kind registered in fancy-flow's registry. Use this to discover what's authorable before adding nodes.\",\n { category: { type: \"string\", description: \"Optional category filter: trigger | logic | data | ai | io | human | output | custom.\" } },\n [],\n async () => {\n // Dynamic import keeps the bridge usable even when fancy-flow isn't loaded.\n try {\n // @ts-ignore — optional peer dep, may not be installed\n const { listNodeKinds } = await import(\"@particle-academy/fancy-flow\" as any);\n const cat = adapter ? undefined : undefined; // placeholder\n const all = (cat ? listNodeKinds(cat) : listNodeKinds()).map((k: any) => ({\n name: k.name,\n category: k.category,\n label: k.label,\n description: k.description,\n icon: k.icon,\n accent: k.accent,\n inputs: k.inputs ?? [],\n outputs: k.outputs ?? [],\n configFields: (k.configSchema ?? []).map((f: any) => ({ key: f.key, type: f.type, label: f.label, required: !!f.required })),\n }));\n const text = all.map((k: any) => `${k.category}/${k.name}: ${k.label}${k.description ? \" — \" + k.description : \"\"}`).join(\"\\n\");\n return textResult(text || \"(no kinds registered)\", all);\n } catch (e) {\n return errorResult(`fancy-flow registry not available: ${e instanceof Error ? e.message : String(e)}`);\n }\n },\n );\n\n reg(\n \"flow_get_node_schema\",\n \"Get the full configSchema + ports for a node kind. Use to know exactly what fields a kind accepts before calling flow_add_node.\",\n { name: { type: \"string\" } },\n [\"name\"],\n async (args) => {\n try {\n // @ts-ignore — optional peer dep\n const { getNodeKind } = await import(\"@particle-academy/fancy-flow\" as any);\n const k: any = getNodeKind(str(args.name));\n if (!k) return errorResult(`No kind registered: ${args.name}`);\n const summary = {\n name: k.name,\n category: k.category,\n label: k.label,\n description: k.description,\n inputs: k.inputs ?? [],\n outputs: k.outputs ?? [],\n configSchema: k.configSchema ?? [],\n defaultConfig: k.defaultConfig ?? null,\n };\n return textResult(JSON.stringify(summary, null, 2), summary);\n } catch (e) {\n return errorResult(`fancy-flow registry not available: ${e instanceof Error ? e.message : String(e)}`);\n }\n },\n );\n\n reg(\"flow_list_edges\", \"Summarise every edge.\", {}, [], () => {\n const items = adapter.getEdges().map((e) => ({\n id: e.id,\n from: `${e.source}${e.sourceHandle ? `:${e.sourceHandle}` : \"\"}`,\n to: `${e.target}${e.targetHandle ? `:${e.targetHandle}` : \"\"}`,\n }));\n return textResult(items.map((i) => `${i.id}: ${i.from} → ${i.to}`).join(\"\\n\") || \"(no edges)\", items);\n });\n\n // ───────────── Node CRUD ─────────────\n\n reg(\n \"flow_add_node\",\n \"Add a node of any kind registered in fancy-flow's registry. Call flow_list_node_kinds first to discover what's available.\",\n {\n kind: { type: \"string\", description: \"Registry kind name (e.g. memory_store, llm_call, branch).\" },\n label: { type: \"string\" },\n x: { type: \"number\" },\n y: { type: \"number\" },\n description: { type: \"string\" },\n config: { type: \"object\", description: \"Config fields per the kind's configSchema.\" },\n body: { type: \"string\", description: \"Note kinds only — body text.\" },\n },\n [\"kind\", \"label\", \"x\", \"y\"],\n async (args) => {\n const kindName = str(args.kind);\n // Resolve the kind dynamically from the registry. Falls back to the\n // legacy 6-pack so old graphs keep working.\n let kindDef: any = null;\n try {\n // @ts-ignore — optional peer dep\n const { getNodeKind, defaultConfigFor } = await import(\"@particle-academy/fancy-flow\" as any);\n kindDef = getNodeKind(kindName);\n var defaults: Record<string, unknown> = kindDef ? defaultConfigFor(kindDef) : {};\n } catch {\n var defaults: Record<string, unknown> = {};\n }\n const isLegacy = [\"trigger\", \"action\", \"decision\", \"output\", \"note\", \"subgraph\"].includes(kindName);\n if (!kindDef && !isLegacy) {\n return errorResult(`Unknown kind: ${kindName} — call flow_list_node_kinds for the registry.`);\n }\n const id = newId(\"n\");\n const config = { ...defaults, ...((args.config && typeof args.config === \"object\") ? (args.config as Record<string, unknown>) : {}) };\n const node: FlowNode = {\n id,\n type: kindName,\n position: { x: num(args.x), y: num(args.y) },\n data: {\n kind: kindName,\n label: str(args.label),\n ...(args.description ? { description: str(args.description) } : {}),\n config,\n ...(kindName === \"note\" && args.body ? { body: str(args.body) } : {}),\n } as any,\n };\n adapter.setNodes((all) => [...all, node]);\n return textResult(`Added ${kindName} ${id} (\"${str(args.label)}\")`, node);\n },\n );\n\n reg(\n \"flow_update_node\",\n \"Update fields on a node. Only provided fields change.\",\n {\n id: { type: \"string\" },\n label: { type: \"string\" },\n x: { type: \"number\" },\n y: { type: \"number\" },\n description: { type: \"string\" },\n config: { type: \"object\" },\n },\n [\"id\"],\n (args) => {\n const id = str(args.id);\n let updated: FlowNode | null = null;\n adapter.setNodes((all) =>\n all.map((n) => {\n if (n.id !== id) return n;\n updated = {\n ...n,\n position: {\n x: args.x !== undefined ? num(args.x) : n.position.x,\n y: args.y !== undefined ? num(args.y) : n.position.y,\n },\n data: {\n ...n.data,\n ...(args.label !== undefined ? { label: str(args.label) } : {}),\n ...(args.description !== undefined ? { description: str(args.description) } : {}),\n ...(args.config && typeof args.config === \"object\"\n ? { config: { ...(n.data.config ?? {}), ...(args.config as Record<string, unknown>) } }\n : {}),\n },\n };\n return updated;\n }),\n );\n if (!updated) return errorResult(`No node with id ${id}`);\n return textResult(`Updated node ${id}`, updated);\n },\n );\n\n reg(\n \"flow_delete_node\",\n \"Remove a node by id (also removes any connected edges).\",\n { id: { type: \"string\" } },\n [\"id\"],\n (args) => {\n const id = str(args.id);\n // Validate existence BEFORE scheduling the state update — React's\n // functional updaters may run async in strict mode, so checking a\n // flag set inside the updater would race the response.\n if (!adapter.getNodes().some((n) => n.id === id)) {\n return errorResult(`No node with id ${id}`);\n }\n adapter.setNodes((all) => all.filter((n) => n.id !== id));\n adapter.setEdges((all) => all.filter((e) => e.source !== id && e.target !== id));\n return textResult(`Deleted node ${id}`);\n },\n );\n\n // ───────────── Edges ─────────────\n\n reg(\n \"flow_connect\",\n \"Create an edge between two nodes (optionally specifying handle ids).\",\n {\n source: { type: \"string\" },\n target: { type: \"string\" },\n sourceHandle: { type: \"string\" },\n targetHandle: { type: \"string\" },\n label: { type: \"string\" },\n },\n [\"source\", \"target\"],\n (args) => {\n const source = str(args.source);\n const target = str(args.target);\n const all = adapter.getNodes();\n if (!all.find((n) => n.id === source)) return errorResult(`No source node ${source}`);\n if (!all.find((n) => n.id === target)) return errorResult(`No target node ${target}`);\n const edge: FlowEdge = {\n id: newId(\"e\"),\n source,\n target,\n ...(args.sourceHandle ? { sourceHandle: str(args.sourceHandle) } : {}),\n ...(args.targetHandle ? { targetHandle: str(args.targetHandle) } : {}),\n ...(args.label ? { label: str(args.label) } : {}),\n };\n adapter.setEdges((existing) => [...existing, edge]);\n return textResult(`Connected ${source}${edge.sourceHandle ? `:${edge.sourceHandle}` : \"\"} → ${target}${edge.targetHandle ? `:${edge.targetHandle}` : \"\"}`, edge);\n },\n );\n\n reg(\n \"flow_disconnect\",\n \"Remove an edge by id.\",\n { id: { type: \"string\" } },\n [\"id\"],\n (args) => {\n const id = str(args.id);\n if (!adapter.getEdges().some((e) => e.id === id)) {\n return errorResult(`No edge ${id}`);\n }\n adapter.setEdges((all) => all.filter((e) => e.id !== id));\n return textResult(`Disconnected ${id}`);\n },\n );\n\n // ───────────── Status / run ─────────────\n\n reg(\n \"flow_set_node_status\",\n \"Manually set a node's status badge (idle | queued | running | done | error) and optional text. Useful for narration outside a run.\",\n {\n id: { type: \"string\" },\n status: { type: \"string\", enum: [\"idle\", \"queued\", \"running\", \"done\", \"error\"] },\n text: { type: \"string\" },\n },\n [\"id\", \"status\"],\n (args) => {\n const id = str(args.id);\n const status = str(args.status) as NodeRunStatus;\n const text = args.text !== undefined ? str(args.text) : undefined;\n if (adapter.setNodeStatus) {\n adapter.setNodeStatus(id, status, text);\n } else {\n // Fall back to mutating the node data directly.\n let found = false;\n adapter.setNodes((all) =>\n all.map((n) => {\n if (n.id !== id) return n;\n found = true;\n return { ...n, data: { ...n.data, status, statusText: text } };\n }),\n );\n if (!found) return errorResult(`No node with id ${id}`);\n }\n return textResult(`${id} → ${status}${text ? ` (${text})` : \"\"}`);\n },\n );\n\n reg(\n \"flow_run\",\n \"Trigger a run of the current graph. Returns the topological result. Requires the host to have wired `run` into the adapter.\",\n {},\n [],\n async () => {\n if (!adapter.run) return errorResult(\"Host did not provide a run handler.\");\n const result = await adapter.run();\n return textResult(result.ok ? \"Run complete\" : `Run failed: ${result.error ?? \"unknown\"}`, result);\n },\n );\n\n reg(\n \"flow_cancel\",\n \"Cancel an in-flight run.\",\n {},\n [],\n () => {\n if (!adapter.cancel) return errorResult(\"Host did not provide a cancel handler.\");\n adapter.cancel();\n return textResult(\"Run cancelled\");\n },\n );\n\n return {\n id: \"flow\",\n title: \"Flow\",\n dispose: () => {\n for (const d of disposers) d();\n },\n };\n}\n"]}