@particle-academy/agent-integrations 0.2.4 → 0.4.0

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 (132) hide show
  1. package/README.md +20 -0
  2. package/dist/bridges/charts.d.cts +39 -0
  3. package/dist/bridges/charts.d.ts +39 -0
  4. package/dist/bridges/code.d.cts +47 -0
  5. package/dist/bridges/code.d.ts +47 -0
  6. package/dist/bridges/flow.d.cts +4 -3
  7. package/dist/bridges/flow.d.ts +4 -3
  8. package/dist/bridges/forms.d.cts +76 -0
  9. package/dist/bridges/forms.d.ts +76 -0
  10. package/dist/bridges/scene.d.cts +54 -0
  11. package/dist/bridges/scene.d.ts +54 -0
  12. package/dist/bridges/screens.d.cts +78 -0
  13. package/dist/bridges/screens.d.ts +78 -0
  14. package/dist/bridges/sheets.d.cts +62 -0
  15. package/dist/bridges/sheets.d.ts +62 -0
  16. package/dist/bridges/whiteboard.d.cts +4 -3
  17. package/dist/bridges/whiteboard.d.ts +4 -3
  18. package/dist/bridges-charts.cjs +167 -0
  19. package/dist/bridges-charts.cjs.map +1 -0
  20. package/dist/bridges-charts.js +6 -0
  21. package/dist/bridges-charts.js.map +1 -0
  22. package/dist/bridges-code.cjs +219 -0
  23. package/dist/bridges-code.cjs.map +1 -0
  24. package/dist/bridges-code.js +6 -0
  25. package/dist/bridges-code.js.map +1 -0
  26. package/dist/bridges-flow.cjs +78 -19
  27. package/dist/bridges-flow.cjs.map +1 -1
  28. package/dist/bridges-flow.js +4 -2
  29. package/dist/bridges-forms.cjs +205 -0
  30. package/dist/bridges-forms.cjs.map +1 -0
  31. package/dist/bridges-forms.js +6 -0
  32. package/dist/bridges-forms.js.map +1 -0
  33. package/dist/bridges-scene.cjs +250 -0
  34. package/dist/bridges-scene.cjs.map +1 -0
  35. package/dist/bridges-scene.js +6 -0
  36. package/dist/bridges-scene.js.map +1 -0
  37. package/dist/bridges-screens.cjs +227 -0
  38. package/dist/bridges-screens.cjs.map +1 -0
  39. package/dist/bridges-screens.js +6 -0
  40. package/dist/bridges-screens.js.map +1 -0
  41. package/dist/bridges-sheets.cjs +327 -0
  42. package/dist/bridges-sheets.cjs.map +1 -0
  43. package/dist/bridges-sheets.js +6 -0
  44. package/dist/bridges-sheets.js.map +1 -0
  45. package/dist/bridges-whiteboard.cjs +226 -40
  46. package/dist/bridges-whiteboard.cjs.map +1 -1
  47. package/dist/bridges-whiteboard.js +5 -2
  48. package/dist/{chunk-5ZUHNNLR.js → chunk-3KSZNGNW.js} +81 -43
  49. package/dist/chunk-3KSZNGNW.js.map +1 -0
  50. package/dist/chunk-4BL5M3U3.js +158 -0
  51. package/dist/chunk-4BL5M3U3.js.map +1 -0
  52. package/dist/{chunk-QGCF7YKW.js → chunk-4KAIV6OD.js} +40 -12
  53. package/dist/chunk-4KAIV6OD.js.map +1 -0
  54. package/dist/chunk-52S7XYZK.js +38 -0
  55. package/dist/chunk-52S7XYZK.js.map +1 -0
  56. package/dist/chunk-57ZDHD53.js +180 -0
  57. package/dist/chunk-57ZDHD53.js.map +1 -0
  58. package/dist/chunk-E4AICMFZ.js +83 -0
  59. package/dist/chunk-E4AICMFZ.js.map +1 -0
  60. package/dist/chunk-GQ7XXK7G.js +124 -0
  61. package/dist/chunk-GQ7XXK7G.js.map +1 -0
  62. package/dist/chunk-HSTW7ZNO.js +172 -0
  63. package/dist/chunk-HSTW7ZNO.js.map +1 -0
  64. package/dist/chunk-IANI25IT.js +280 -0
  65. package/dist/chunk-IANI25IT.js.map +1 -0
  66. package/dist/{chunk-FLEOQUKF.js → chunk-JMYPUAFH.js} +17 -2
  67. package/dist/chunk-JMYPUAFH.js.map +1 -0
  68. package/dist/chunk-JU2N4KK6.js +34 -0
  69. package/dist/chunk-JU2N4KK6.js.map +1 -0
  70. package/dist/{chunk-2VOQJKSU.js → chunk-N3H4DXY5.js} +44 -22
  71. package/dist/chunk-N3H4DXY5.js.map +1 -0
  72. package/dist/chunk-NTDZWGYB.js +120 -0
  73. package/dist/chunk-NTDZWGYB.js.map +1 -0
  74. package/dist/chunk-RGO42EQ6.js +25 -0
  75. package/dist/chunk-RGO42EQ6.js.map +1 -0
  76. package/dist/chunk-X66JWQBB.js +37 -0
  77. package/dist/chunk-X66JWQBB.js.map +1 -0
  78. package/dist/chunk-XRAJSOPS.js +203 -0
  79. package/dist/chunk-XRAJSOPS.js.map +1 -0
  80. package/dist/index.cjs +1766 -127
  81. package/dist/index.cjs.map +1 -1
  82. package/dist/index.d.cts +99 -3
  83. package/dist/index.d.ts +99 -3
  84. package/dist/index.js +115 -9
  85. package/dist/index.js.map +1 -1
  86. package/dist/mcp/index.d.cts +5 -2
  87. package/dist/mcp/index.d.ts +5 -2
  88. package/dist/mcp.cjs +37 -9
  89. package/dist/mcp.cjs.map +1 -1
  90. package/dist/mcp.js +1 -1
  91. package/dist/presence/index.d.cts +136 -0
  92. package/dist/presence/index.d.ts +136 -0
  93. package/dist/presence.cjs +107 -0
  94. package/dist/presence.cjs.map +1 -0
  95. package/dist/presence.js +5 -0
  96. package/dist/presence.js.map +1 -0
  97. package/dist/registry-2DRURS6U.js +3 -0
  98. package/dist/registry-2DRURS6U.js.map +1 -0
  99. package/dist/server-BsSwfemr.d.cts +63 -0
  100. package/dist/server-Du3-IGqM.d.ts +63 -0
  101. package/dist/sharing/index.d.cts +3 -1
  102. package/dist/sharing/index.d.ts +3 -1
  103. package/dist/sharing.cjs +68 -0
  104. package/dist/sharing.cjs.map +1 -1
  105. package/dist/sharing.js +1 -1
  106. package/dist/sheets-adapter.cjs +96 -0
  107. package/dist/sheets-adapter.cjs.map +1 -0
  108. package/dist/sheets-adapter.d.cts +115 -0
  109. package/dist/sheets-adapter.d.ts +115 -0
  110. package/dist/sheets-adapter.js +4 -0
  111. package/dist/sheets-adapter.js.map +1 -0
  112. package/dist/styles.css +57 -0
  113. package/dist/styles.css.map +1 -1
  114. package/dist/tool-host-BQuUygLF.d.cts +60 -0
  115. package/dist/tool-host-C8JMMGYq.d.ts +60 -0
  116. package/dist/{types-CRPA_D0z.d.ts → types-CCSBGW9T.d.cts} +2 -2
  117. package/dist/{types-DR5AS6Rd.d.cts → types-DIVNcIQO.d.ts} +2 -2
  118. package/dist/types-aOQLTW0E.d.cts +112 -0
  119. package/dist/types-aOQLTW0E.d.ts +112 -0
  120. package/dist/undo/index.d.cts +69 -0
  121. package/dist/undo/index.d.ts +69 -0
  122. package/dist/undo.cjs +163 -0
  123. package/dist/undo.cjs.map +1 -0
  124. package/dist/undo.js +5 -0
  125. package/dist/undo.js.map +1 -0
  126. package/package.json +1 -1
  127. package/dist/chunk-2VOQJKSU.js.map +0 -1
  128. package/dist/chunk-5ZUHNNLR.js.map +0 -1
  129. package/dist/chunk-FLEOQUKF.js.map +0 -1
  130. package/dist/chunk-QGCF7YKW.js.map +0 -1
  131. package/dist/server-Bv985us3.d.cts +0 -173
  132. package/dist/server-Bv985us3.d.ts +0 -173
@@ -0,0 +1,60 @@
1
+ import { T as ToolDefinition, b as ToolHandler, R as RegisteredTool, c as JsonObject, C as CallToolResult } from './types-aOQLTW0E.js';
2
+
3
+ /**
4
+ * ToolHost — the minimal surface a bridge needs to register its tools.
5
+ *
6
+ * Bridges (whiteboard, flow, sheets, code, charts, screens, scene, form)
7
+ * speak only through this interface. The MCP server implements it
8
+ * alongside its transport / JSON-RPC duties, and the standalone
9
+ * {@link ToolRegistry} implements it for in-process agents that don't
10
+ * need any of MCP's wire framing.
11
+ *
12
+ * Net effect: every bridge works equally well behind an MCP server
13
+ * (browser ↔ external agents over SSE) or behind a plain registry
14
+ * (in-process agent calling `host.callTool("sheet_set_cell", { … })`).
15
+ */
16
+ interface ToolHost {
17
+ /** Register a tool. Returns a disposer that unregisters it. */
18
+ registerTool(definition: ToolDefinition, handler: ToolHandler): () => void;
19
+ /** Look up an already-registered tool's definition + handler, or null. */
20
+ getTool(name: string): RegisteredTool | null;
21
+ /** Snapshot of all currently-registered tool definitions. */
22
+ listTools(): ToolDefinition[];
23
+ /**
24
+ * Invoke a registered tool directly, bypassing any transport layer.
25
+ * Throws if no tool is registered under `name`.
26
+ *
27
+ * This is the single entry point in-process agents use to drive the
28
+ * surface — no JSON-RPC framing, no transport plumbing.
29
+ */
30
+ callTool(name: string, args?: JsonObject): Promise<CallToolResult>;
31
+ }
32
+ /**
33
+ * Standalone in-memory ToolHost. Use this when no MCP server is needed —
34
+ * e.g. an in-process agent that just wants to register the same bridges
35
+ * and call them directly.
36
+ *
37
+ * Example:
38
+ *
39
+ * const host = new ToolRegistry();
40
+ * registerSheetsBridge(host, { adapter });
41
+ * const result = await host.callTool("sheet_set_cell", { address: "B3", value: 42 });
42
+ *
43
+ * Pair with a MicroMcpServer in the same app to expose the same tools
44
+ * to remote agents over SSE while in-process agents still get
45
+ * zero-overhead direct calls.
46
+ */
47
+ declare class ToolRegistry implements ToolHost {
48
+ protected readonly tools: Map<string, RegisteredTool>;
49
+ registerTool(definition: ToolDefinition, handler: ToolHandler): () => void;
50
+ getTool(name: string): RegisteredTool | null;
51
+ listTools(): ToolDefinition[];
52
+ callTool(name: string, args?: JsonObject): Promise<CallToolResult>;
53
+ /**
54
+ * Hook for subclasses (e.g. MicroMcpServer) to notify subscribers
55
+ * when the tool catalog changes. Default no-op.
56
+ */
57
+ protected onToolsChanged(): void;
58
+ }
59
+
60
+ export { type ToolHost as T, ToolRegistry as a };
@@ -1,4 +1,4 @@
1
- import { M as MicroMcpServer } from './server-Bv985us3.js';
1
+ import { T as ToolHost } from './tool-host-BQuUygLF.cjs';
2
2
 
3
3
  /**
4
4
  * Bridge — registers a cohesive set of MCP tools/resources for a single
@@ -13,6 +13,6 @@ type Bridge = {
13
13
  /** Disposes the bridge. Must remove every tool it registered. */
14
14
  dispose: () => void;
15
15
  };
16
- type BridgeFactory<TOptions> = (server: MicroMcpServer, options: TOptions) => Bridge;
16
+ type BridgeFactory<TOptions> = (host: ToolHost, options: TOptions) => Bridge;
17
17
 
18
18
  export type { Bridge as B, BridgeFactory as a };
@@ -1,4 +1,4 @@
1
- import { M as MicroMcpServer } from './server-Bv985us3.cjs';
1
+ import { T as ToolHost } from './tool-host-C8JMMGYq.js';
2
2
 
3
3
  /**
4
4
  * Bridge — registers a cohesive set of MCP tools/resources for a single
@@ -13,6 +13,6 @@ type Bridge = {
13
13
  /** Disposes the bridge. Must remove every tool it registered. */
14
14
  dispose: () => void;
15
15
  };
16
- type BridgeFactory<TOptions> = (server: MicroMcpServer, options: TOptions) => Bridge;
16
+ type BridgeFactory<TOptions> = (host: ToolHost, options: TOptions) => Bridge;
17
17
 
18
18
  export type { Bridge as B, BridgeFactory as a };
@@ -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 JsonObject as c, type ContentBlock 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 JsonObject as c, type ContentBlock 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 { T as ToolHost } from '../tool-host-BQuUygLF.cjs';
2
+ import '../types-aOQLTW0E.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(host: ToolHost, 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(host: ToolHost, 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 { T as ToolHost } from '../tool-host-C8JMMGYq.js';
2
+ import '../types-aOQLTW0E.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(host: ToolHost, 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(host: ToolHost, 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 installedHosts = /* @__PURE__ */ new WeakSet();
61
+ function ensureUndoToolsRegistered(host, options = {}) {
62
+ if (installedHosts.has(host)) return;
63
+ installedHosts.add(host);
64
+ registerUndoTools(host, options);
65
+ }
66
+ function registerUndoTools(host, 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
+ host.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
+ host.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
+ host.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\";\nimport { ToolRegistry } from \"./tool-host\";\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 extends ToolRegistry {\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 super();\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 unregisterTool(name: string): void {\n if (this.tools.delete(name)) {\n this.scheduleListChangedNotification();\n }\n }\n\n protected onToolsChanged(): void {\n this.scheduleListChangedNotification();\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 { ToolHost } from \"../mcp/tool-host\";\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 installedHosts = new WeakSet<ToolHost>();\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(host: ToolHost, options: UndoToolsOptions = {}): void {\n if (installedHosts.has(host)) return;\n installedHosts.add(host);\n registerUndoTools(host, 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(host: ToolHost, 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 host.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 host.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 host.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-RGO42EQ6.js';
2
+ export { clearStack, ensureUndoToolsRegistered, pushUndoEntry, readHistory, redoOne, registerUndoTools, resetAllUndoStacks, undoOne } from './chunk-GQ7XXK7G.js';
3
+ import './chunk-4KAIV6OD.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.4.0",
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",