@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
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/sharing/token.ts","../src/sharing/sse-relay.ts"],"names":[],"mappings":"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","file":"sharing.cjs","sourcesContent":["/**\n * Session-token utilities. The token is a high-entropy secret; possession\n * grants read/write on the session. We don't HMAC frames — frames carry\n * the token directly (which is fine for in-process / same-origin / TLS\n * transports). For lower-trust transports, host apps can layer signing\n * on top of the BroadcastChannelTransport.\n */\n\nconst TOKEN_BYTES = 24; // 192 bits, base64url-encoded → 32 chars\n\nexport type SessionDescriptor = {\n /** Stable session identifier. Channel name = `fai:share:${id}`. */\n id: string;\n /** Secret token. Treat as a password — anyone with it can read/write. */\n token: string;\n /** Pretty hash for display (first 8 chars of token). */\n display: string;\n};\n\nexport function createSessionDescriptor(): SessionDescriptor {\n const id = randomId(8);\n const token = randomToken();\n return { id, token, display: token.slice(0, 8) };\n}\n\nexport function describeSession(id: string, token: string): SessionDescriptor {\n return { id, token, display: token.slice(0, 8) };\n}\n\n/** Build the shareable URL for the current page (preserves path, adds session+token). */\nexport function buildShareUrl(\n descriptor: SessionDescriptor,\n baseUrl: string = typeof window !== \"undefined\" ? window.location.href.split(\"?\")[0] : \"\",\n): string {\n const u = new URL(baseUrl);\n u.searchParams.set(\"session\", descriptor.id);\n u.searchParams.set(\"token\", descriptor.token);\n return u.toString();\n}\n\n/** Build the JSON config form (suitable for Claude Desktop / Cline / etc.). */\nexport function buildShareConfig(descriptor: SessionDescriptor, transport = \"broadcast-channel\") {\n return {\n name: `whiteboard-${descriptor.id}`,\n transport,\n session: descriptor.id,\n token: descriptor.token,\n channel: `fai:share:${descriptor.id}`,\n protocol_version: \"2025-06-18\",\n };\n}\n\n/** Read session descriptor from current URL, or null if not a shared link. */\nexport function readSessionFromUrl(): SessionDescriptor | null {\n if (typeof window === \"undefined\") return null;\n const params = new URL(window.location.href).searchParams;\n const id = params.get(\"session\");\n const token = params.get(\"token\");\n if (!id || !token) return null;\n return describeSession(id, token);\n}\n\nfunction randomToken(): string {\n const bytes = new Uint8Array(TOKEN_BYTES);\n crypto.getRandomValues(bytes);\n return base64Url(bytes);\n}\n\nfunction randomId(len: number): string {\n const bytes = new Uint8Array(Math.ceil((len * 3) / 4));\n crypto.getRandomValues(bytes);\n return base64Url(bytes).slice(0, len);\n}\n\nfunction base64Url(bytes: Uint8Array): string {\n let s = \"\";\n for (const b of bytes) s += String.fromCharCode(b);\n return btoa(s).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n\n/** Constant-time string compare so a mismatched token leaks no timing info. */\nexport function constantTimeEqual(a: string, b: string): boolean {\n if (a.length !== b.length) return false;\n let diff = 0;\n for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);\n return diff === 0;\n}\n","import type { JsonRpcMessage } from \"../mcp/types\";\nimport type { Transport } from \"../mcp/server\";\nimport type { MicroMcpServer } from \"../mcp/server\";\nimport { constantTimeEqual } from \"./token\";\n\n/**\n * SseRelayTransport — bridges the in-page MicroMcpServer to a host-app\n * relay broker over Server-Sent Events (inbound) + POST (outbound).\n *\n * Wire model:\n * - Browser opens an EventSource at `${baseUrl}/${sessionId}/events?token=…`.\n * Each `event: mcp` carries one JSON-RPC frame from a remote client.\n * - Browser POSTs JSON-RPC frames to `${baseUrl}/${sessionId}/outbox?token=…`\n * when the local server has a response/notification to send.\n *\n * The host provides the relay endpoint (any HTTP server). See the demo\n * `WhiteboardShareController` for the reference implementation.\n *\n * Token authentication is the host's job — this transport just carries the\n * token in the query string. For lower-trust deployments, layer signing on\n * top by wrapping `send` / `deliverFromRemote`.\n */\nexport type SseRelayOptions = {\n baseUrl: string;\n sessionId: string;\n token: string;\n /** Override fetch (testing / non-browser). Defaults to global fetch. */\n fetch?: typeof fetch;\n};\n\nexport class SseRelayTransport implements Transport {\n private server?: MicroMcpServer;\n private es?: EventSource;\n private opts: SseRelayOptions;\n private sendQueue: JsonRpcMessage[] = [];\n private connected = false;\n private listeners = new Set<(state: RelayState) => void>();\n private state: RelayState = \"idle\";\n private expectedToken: string;\n\n constructor(options: SseRelayOptions) {\n this.opts = options;\n this.expectedToken = options.token;\n }\n\n bindServer(server: MicroMcpServer): void {\n this.server = server;\n }\n\n /** Open the SSE channel. Idempotent. */\n start(): void {\n if (this.connected || typeof window === \"undefined\") return;\n const url = `${this.opts.baseUrl}/${encodeURIComponent(this.opts.sessionId)}/events?token=${encodeURIComponent(this.opts.token)}`;\n this.setState(\"connecting\");\n const es = new EventSource(url, { withCredentials: false });\n this.es = es;\n\n es.addEventListener(\"open\", () => {\n this.connected = true;\n this.setState(\"open\");\n // Flush queued outbound frames (tool list_changed notifications, etc.)\n const queued = this.sendQueue.splice(0);\n for (const msg of queued) this.postOut(msg);\n });\n\n es.addEventListener(\"mcp\", (ev: MessageEvent) => {\n const raw = ev.data;\n this.handleInbound(raw);\n });\n\n es.addEventListener(\"error\", () => {\n this.setState(\"error\");\n // EventSource auto-reconnects; no need to dispose.\n });\n }\n\n send(message: JsonRpcMessage): void {\n if (!this.connected) {\n this.sendQueue.push(message);\n return;\n }\n this.postOut(message);\n }\n\n close(): void {\n this.es?.close();\n this.es = undefined;\n this.connected = false;\n this.setState(\"closed\");\n }\n\n onStateChange(listener: (state: RelayState) => void): () => void {\n this.listeners.add(listener);\n listener(this.state);\n return () => this.listeners.delete(listener);\n }\n\n /**\n * For relays that wrap each frame with auth metadata: hosts can call this\n * directly when a frame arrives via a non-SSE path. The transport will\n * dispatch it to the bound server.\n */\n async deliverFromRemote(payload: JsonRpcMessage | string, token?: string): Promise<void> {\n if (token !== undefined && !constantTimeEqual(token, this.expectedToken)) return;\n if (!this.server) throw new Error(\"SseRelayTransport has no bound server\");\n const message: JsonRpcMessage = typeof payload === \"string\" ? JSON.parse(payload) : payload;\n await this.server.receive(this, message);\n }\n\n private async postOut(message: JsonRpcMessage): Promise<void> {\n const url = `${this.opts.baseUrl}/${encodeURIComponent(this.opts.sessionId)}/outbox?token=${encodeURIComponent(this.opts.token)}`;\n const f = this.opts.fetch ?? fetch;\n try {\n await f(url, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\", \"accept\": \"application/json\" },\n body: JSON.stringify(message),\n });\n } catch {\n // Drop — relay errors are surfaced via state change separately.\n }\n }\n\n private async handleInbound(raw: string): Promise<void> {\n if (!this.server) return;\n let message: JsonRpcMessage;\n try {\n message = JSON.parse(raw);\n } catch {\n return;\n }\n await this.server.receive(this, message);\n }\n\n private setState(state: RelayState): void {\n this.state = state;\n for (const l of this.listeners) l(state);\n }\n}\n\nexport type RelayState = \"idle\" | \"connecting\" | \"open\" | \"closed\" | \"error\";\n\nexport function attachSseRelay(server: MicroMcpServer, options: SseRelayOptions): SseRelayTransport {\n const transport = new SseRelayTransport(options);\n transport.bindServer(server);\n server.attach(transport);\n transport.start();\n return transport;\n}\n"]}
1
+ {"version":3,"sources":["../src/presence/registry.ts","../src/sharing/token.ts","../src/sharing/sse-relay.ts"],"names":["onActivity"],"mappings":"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","file":"sharing.cjs","sourcesContent":["import type { ActivityFilter, AgentActivityEvent, AgentActivityListener } from \"./types\";\n\n/**\n * In-process registry of agent activity events. Bridges call `emitActivity`\n * after a tool runs; React hooks + the SSE relay subscribe via\n * `onActivity()`.\n *\n * Holds a short scrollback of recent events (default 200) so newly-mounted\n * subscribers can render the recent past — useful for activity-log UIs\n * that rejoin a session mid-stream.\n */\n\nconst HISTORY_CAP = 200;\n\nconst listeners = new Set<AgentActivityListener>();\nconst history: AgentActivityEvent[] = [];\n\n/** Emit an activity event. All current listeners receive it synchronously. */\nexport function emitActivity(event: AgentActivityEvent): void {\n history.push(event);\n if (history.length > HISTORY_CAP) history.splice(0, history.length - HISTORY_CAP);\n for (const l of listeners) l(event);\n}\n\n/**\n * Subscribe to all events (or a filtered subset). Returns an unsubscribe\n * function. Filter checks all provided keys with strict equality; omit a\n * key to ignore it.\n */\nexport function onActivity(listener: AgentActivityListener, filter?: ActivityFilter): () => void {\n const wrapped: AgentActivityListener = filter\n ? (e) => { if (matches(e, filter)) listener(e); }\n : listener;\n listeners.add(wrapped);\n return () => listeners.delete(wrapped);\n}\n\n/** Read the recent history (newest last). Optional filter. */\nexport function readActivityHistory(filter?: ActivityFilter): AgentActivityEvent[] {\n if (!filter) return history.slice();\n return history.filter((e) => matches(e, filter));\n}\n\n/** Wipe history + clear listeners. Test/teardown helper. */\nexport function resetActivityRegistry(): void {\n listeners.clear();\n history.length = 0;\n}\n\nfunction matches(e: AgentActivityEvent, f: ActivityFilter): boolean {\n if (f.agentId !== undefined && e.agentId !== f.agentId) return false;\n if (f.screenId !== undefined && e.target.screenId !== f.screenId) return false;\n if (f.kind !== undefined && e.target.kind !== f.kind) return false;\n return true;\n}\n","/**\n * Session-token utilities. The token is a high-entropy secret; possession\n * grants read/write on the session. We don't HMAC frames — frames carry\n * the token directly (which is fine for in-process / same-origin / TLS\n * transports). For lower-trust transports, host apps can layer signing\n * on top of the BroadcastChannelTransport.\n */\n\nconst TOKEN_BYTES = 24; // 192 bits, base64url-encoded → 32 chars\n\nexport type SessionDescriptor = {\n /** Stable session identifier. Channel name = `fai:share:${id}`. */\n id: string;\n /** Secret token. Treat as a password — anyone with it can read/write. */\n token: string;\n /** Pretty hash for display (first 8 chars of token). */\n display: string;\n};\n\nexport function createSessionDescriptor(): SessionDescriptor {\n const id = randomId(8);\n const token = randomToken();\n return { id, token, display: token.slice(0, 8) };\n}\n\nexport function describeSession(id: string, token: string): SessionDescriptor {\n return { id, token, display: token.slice(0, 8) };\n}\n\n/** Build the shareable URL for the current page (preserves path, adds session+token). */\nexport function buildShareUrl(\n descriptor: SessionDescriptor,\n baseUrl: string = typeof window !== \"undefined\" ? window.location.href.split(\"?\")[0] : \"\",\n): string {\n const u = new URL(baseUrl);\n u.searchParams.set(\"session\", descriptor.id);\n u.searchParams.set(\"token\", descriptor.token);\n return u.toString();\n}\n\n/** Build the JSON config form (suitable for Claude Desktop / Cline / etc.). */\nexport function buildShareConfig(descriptor: SessionDescriptor, transport = \"broadcast-channel\") {\n return {\n name: `whiteboard-${descriptor.id}`,\n transport,\n session: descriptor.id,\n token: descriptor.token,\n channel: `fai:share:${descriptor.id}`,\n protocol_version: \"2025-06-18\",\n };\n}\n\n/** Read session descriptor from current URL, or null if not a shared link. */\nexport function readSessionFromUrl(): SessionDescriptor | null {\n if (typeof window === \"undefined\") return null;\n const params = new URL(window.location.href).searchParams;\n const id = params.get(\"session\");\n const token = params.get(\"token\");\n if (!id || !token) return null;\n return describeSession(id, token);\n}\n\nfunction randomToken(): string {\n const bytes = new Uint8Array(TOKEN_BYTES);\n crypto.getRandomValues(bytes);\n return base64Url(bytes);\n}\n\nfunction randomId(len: number): string {\n const bytes = new Uint8Array(Math.ceil((len * 3) / 4));\n crypto.getRandomValues(bytes);\n return base64Url(bytes).slice(0, len);\n}\n\nfunction base64Url(bytes: Uint8Array): string {\n let s = \"\";\n for (const b of bytes) s += String.fromCharCode(b);\n return btoa(s).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n\n/** Constant-time string compare so a mismatched token leaks no timing info. */\nexport function constantTimeEqual(a: string, b: string): boolean {\n if (a.length !== b.length) return false;\n let diff = 0;\n for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);\n return diff === 0;\n}\n","import type { JsonRpcMessage } from \"../mcp/types\";\nimport type { Transport } from \"../mcp/server\";\nimport type { MicroMcpServer } from \"../mcp/server\";\nimport { constantTimeEqual } from \"./token\";\n\n/**\n * SseRelayTransport — bridges the in-page MicroMcpServer to a host-app\n * relay broker over Server-Sent Events (inbound) + POST (outbound).\n *\n * Wire model:\n * - Browser opens an EventSource at `${baseUrl}/${sessionId}/events?token=…`.\n * Each `event: mcp` carries one JSON-RPC frame from a remote client.\n * - Browser POSTs JSON-RPC frames to `${baseUrl}/${sessionId}/outbox?token=…`\n * when the local server has a response/notification to send.\n *\n * The host provides the relay endpoint (any HTTP server). See the demo\n * `WhiteboardShareController` for the reference implementation.\n *\n * Token authentication is the host's job — this transport just carries the\n * token in the query string. For lower-trust deployments, layer signing on\n * top by wrapping `send` / `deliverFromRemote`.\n */\nexport type SseRelayOptions = {\n baseUrl: string;\n sessionId: string;\n token: string;\n /** Override fetch (testing / non-browser). Defaults to global fetch. */\n fetch?: typeof fetch;\n};\n\nexport class SseRelayTransport implements Transport {\n private server?: MicroMcpServer;\n private es?: EventSource;\n private opts: SseRelayOptions;\n private sendQueue: JsonRpcMessage[] = [];\n private connected = false;\n private listeners = new Set<(state: RelayState) => void>();\n private state: RelayState = \"idle\";\n private expectedToken: string;\n\n constructor(options: SseRelayOptions) {\n this.opts = options;\n this.expectedToken = options.token;\n }\n\n bindServer(server: MicroMcpServer): void {\n this.server = server;\n }\n\n /** Open the SSE channel. Idempotent. */\n start(): void {\n if (this.connected || typeof window === \"undefined\") return;\n const url = `${this.opts.baseUrl}/${encodeURIComponent(this.opts.sessionId)}/events?token=${encodeURIComponent(this.opts.token)}`;\n this.setState(\"connecting\");\n const es = new EventSource(url, { withCredentials: false });\n this.es = es;\n\n es.addEventListener(\"open\", () => {\n this.connected = true;\n this.setState(\"open\");\n // Flush queued outbound frames (tool list_changed notifications, etc.)\n const queued = this.sendQueue.splice(0);\n for (const msg of queued) this.postOut(msg);\n });\n\n es.addEventListener(\"mcp\", (ev: MessageEvent) => {\n const raw = ev.data;\n this.handleInbound(raw);\n });\n\n es.addEventListener(\"error\", () => {\n this.setState(\"error\");\n // EventSource auto-reconnects; no need to dispose.\n });\n }\n\n send(message: JsonRpcMessage): void {\n if (!this.connected) {\n this.sendQueue.push(message);\n return;\n }\n this.postOut(message);\n }\n\n close(): void {\n this.es?.close();\n this.es = undefined;\n this.connected = false;\n this.setState(\"closed\");\n }\n\n onStateChange(listener: (state: RelayState) => void): () => void {\n this.listeners.add(listener);\n listener(this.state);\n return () => this.listeners.delete(listener);\n }\n\n /**\n * For relays that wrap each frame with auth metadata: hosts can call this\n * directly when a frame arrives via a non-SSE path. The transport will\n * dispatch it to the bound server.\n */\n async deliverFromRemote(payload: JsonRpcMessage | string, token?: string): Promise<void> {\n if (token !== undefined && !constantTimeEqual(token, this.expectedToken)) return;\n if (!this.server) throw new Error(\"SseRelayTransport has no bound server\");\n const message: JsonRpcMessage = typeof payload === \"string\" ? JSON.parse(payload) : payload;\n await this.server.receive(this, message);\n }\n\n private async postOut(message: JsonRpcMessage): Promise<void> {\n const url = `${this.opts.baseUrl}/${encodeURIComponent(this.opts.sessionId)}/outbox?token=${encodeURIComponent(this.opts.token)}`;\n const f = this.opts.fetch ?? fetch;\n try {\n await f(url, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\", \"accept\": \"application/json\" },\n body: JSON.stringify(message),\n });\n } catch {\n // Drop — relay errors are surfaced via state change separately.\n }\n }\n\n private async handleInbound(raw: string): Promise<void> {\n if (!this.server) return;\n let message: JsonRpcMessage;\n try {\n message = JSON.parse(raw);\n } catch {\n return;\n }\n await this.server.receive(this, message);\n }\n\n private setState(state: RelayState): void {\n this.state = state;\n for (const l of this.listeners) l(state);\n }\n}\n\nexport type RelayState = \"idle\" | \"connecting\" | \"open\" | \"closed\" | \"error\";\n\nexport function attachSseRelay(server: MicroMcpServer, options: SseRelayOptions): SseRelayTransport {\n const transport = new SseRelayTransport(options);\n transport.bindServer(server);\n server.attach(transport);\n transport.start();\n\n // Forward in-process agent activity events out over the relay so external\n // subscribers can render presence indicators in real time. Uses a dynamic\n // import so the relay doesn't hard-depend on the presence module if it's\n // tree-shaken out.\n import(\"../presence/registry\").then(({ onActivity }) => {\n const off = onActivity((event) => {\n transport.send({\n jsonrpc: \"2.0\",\n method: \"notifications/agent_activity\",\n params: event as any,\n });\n });\n // Tear down the subscription when the transport closes.\n const origClose = transport.close.bind(transport);\n transport.close = () => {\n off();\n origClose();\n };\n }).catch(() => {\n // Presence module unavailable — silently no-op (relay still works).\n });\n\n return transport;\n}\n"]}
package/dist/sharing.js CHANGED
@@ -1,3 +1,3 @@
1
- export { SseRelayTransport, attachSseRelay, buildShareConfig, buildShareUrl, constantTimeEqual, createSessionDescriptor, describeSession, readSessionFromUrl } from './chunk-FLEOQUKF.js';
1
+ export { SseRelayTransport, attachSseRelay, buildShareConfig, buildShareUrl, constantTimeEqual, createSessionDescriptor, describeSession, readSessionFromUrl } from './chunk-JMYPUAFH.js';
2
2
  //# sourceMappingURL=sharing.js.map
3
3
  //# sourceMappingURL=sharing.js.map
@@ -0,0 +1,96 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+
5
+ // src/sheets-adapter.ts
6
+
7
+ // src/presence/registry.ts
8
+ var listeners = /* @__PURE__ */ new Set();
9
+ function onActivity(listener, filter) {
10
+ const wrapped = listener;
11
+ listeners.add(wrapped);
12
+ return () => listeners.delete(wrapped);
13
+ }
14
+
15
+ // src/sheets-adapter.ts
16
+ function useSheetsAdapter(initial, options = {}) {
17
+ const [workbook, setWorkbook] = react.useState(initial);
18
+ const [activeCell, setActiveCellState] = react.useState(null);
19
+ const workbookRef = react.useRef(workbook);
20
+ workbookRef.current = workbook;
21
+ const setActiveCell = react.useCallback((sheetId, address) => {
22
+ setWorkbook((cur) => cur.activeSheetId === sheetId ? cur : { ...cur, activeSheetId: sheetId });
23
+ setActiveCellState(address);
24
+ }, []);
25
+ const onActiveCellChange = react.useCallback((address) => {
26
+ setActiveCellState(address);
27
+ }, []);
28
+ const setWorkbookRef = react.useRef(setWorkbook);
29
+ setWorkbookRef.current = setWorkbook;
30
+ const adapter = react.useMemo(
31
+ () => ({
32
+ screenId: options.screenId,
33
+ getWorkbook: () => workbookRef.current,
34
+ setWorkbook: (next) => setWorkbookRef.current(next),
35
+ setActiveCell
36
+ }),
37
+ [options.screenId, setActiveCell]
38
+ );
39
+ return {
40
+ workbook,
41
+ setWorkbook,
42
+ onActiveCellChange,
43
+ adapter,
44
+ setActiveCell,
45
+ activeCell
46
+ };
47
+ }
48
+ function useSheetsActivityHighlights(options = {}) {
49
+ const ttlMs = options.ttlMs ?? 2200;
50
+ const screenId = options.screenId;
51
+ const [, force] = react.useState(0);
52
+ const hitsRef = react.useRef(/* @__PURE__ */ new Map());
53
+ react.useEffect(() => {
54
+ const off = onActivity((event) => {
55
+ if (event.target?.kind !== "sheet") return;
56
+ if (screenId && event.target.screenId && event.target.screenId !== screenId) return;
57
+ const elementId = event.target.elementId;
58
+ if (!elementId || !elementId.includes("!")) return;
59
+ hitsRef.current.set(elementId, { event, expiresAt: Date.now() + ttlMs });
60
+ force((n) => n + 1);
61
+ });
62
+ return off;
63
+ }, [screenId, ttlMs]);
64
+ react.useEffect(() => {
65
+ const t = window.setInterval(() => {
66
+ const now = Date.now();
67
+ let dirty = false;
68
+ for (const [k, v] of hitsRef.current) {
69
+ if (v.expiresAt < now) {
70
+ hitsRef.current.delete(k);
71
+ dirty = true;
72
+ }
73
+ }
74
+ if (dirty) force((n) => n + 1);
75
+ }, 500);
76
+ return () => window.clearInterval(t);
77
+ }, []);
78
+ const out = {};
79
+ for (const [elementId, { event }] of hitsRef.current) {
80
+ const idx = elementId.indexOf("!");
81
+ const address = elementId.slice(idx + 1);
82
+ if (!address) continue;
83
+ const color = event.agentColor ?? "#a855f7";
84
+ out[address] = {
85
+ color,
86
+ background: color + "33",
87
+ label: event.agentName ?? event.agentId ?? "agent"
88
+ };
89
+ }
90
+ return out;
91
+ }
92
+
93
+ exports.useSheetsActivityHighlights = useSheetsActivityHighlights;
94
+ exports.useSheetsAdapter = useSheetsAdapter;
95
+ //# sourceMappingURL=sheets-adapter.cjs.map
96
+ //# sourceMappingURL=sheets-adapter.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/presence/registry.ts","../src/sheets-adapter.ts"],"names":["useState","useRef","useCallback","useMemo","useEffect"],"mappings":"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","file":"sheets-adapter.cjs","sourcesContent":["import type { ActivityFilter, AgentActivityEvent, AgentActivityListener } from \"./types\";\n\n/**\n * In-process registry of agent activity events. Bridges call `emitActivity`\n * after a tool runs; React hooks + the SSE relay subscribe via\n * `onActivity()`.\n *\n * Holds a short scrollback of recent events (default 200) so newly-mounted\n * subscribers can render the recent past — useful for activity-log UIs\n * that rejoin a session mid-stream.\n */\n\nconst HISTORY_CAP = 200;\n\nconst listeners = new Set<AgentActivityListener>();\nconst history: AgentActivityEvent[] = [];\n\n/** Emit an activity event. All current listeners receive it synchronously. */\nexport function emitActivity(event: AgentActivityEvent): void {\n history.push(event);\n if (history.length > HISTORY_CAP) history.splice(0, history.length - HISTORY_CAP);\n for (const l of listeners) l(event);\n}\n\n/**\n * Subscribe to all events (or a filtered subset). Returns an unsubscribe\n * function. Filter checks all provided keys with strict equality; omit a\n * key to ignore it.\n */\nexport function onActivity(listener: AgentActivityListener, filter?: ActivityFilter): () => void {\n const wrapped: AgentActivityListener = filter\n ? (e) => { if (matches(e, filter)) listener(e); }\n : listener;\n listeners.add(wrapped);\n return () => listeners.delete(wrapped);\n}\n\n/** Read the recent history (newest last). Optional filter. */\nexport function readActivityHistory(filter?: ActivityFilter): AgentActivityEvent[] {\n if (!filter) return history.slice();\n return history.filter((e) => matches(e, filter));\n}\n\n/** Wipe history + clear listeners. Test/teardown helper. */\nexport function resetActivityRegistry(): void {\n listeners.clear();\n history.length = 0;\n}\n\nfunction matches(e: AgentActivityEvent, f: ActivityFilter): boolean {\n if (f.agentId !== undefined && e.agentId !== f.agentId) return false;\n if (f.screenId !== undefined && e.target.screenId !== f.screenId) return false;\n if (f.kind !== undefined && e.target.kind !== f.kind) return false;\n return true;\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport type { SheetsBridgeAdapter } from \"./bridges/sheets\";\nimport { onActivity } from \"./presence/registry\";\nimport type { AgentActivityEvent } from \"./presence/types\";\n\n/**\n * Shared-session helpers for `@particle-academy/fancy-sheets`.\n *\n * fancy-sheets' `SheetWorkbook` is already controlled (`data` + `onChange`).\n * The two missing pieces for a clean shared-session experience are:\n *\n * 1. an adapter object the host can hand to {@link registerSheetsBridge}\n * without writing boilerplate, and\n * 2. a derived `CellHighlightMap` so agent edits visibly pulse on the\n * humans' screens — wired from the presence registry's per-bridge\n * activity stream.\n *\n * These are kept as host-side hooks (not part of the bridge itself) so\n * agent-integrations keeps zero hard deps on fancy-sheets. The host\n * imports SheetWorkbook directly and feeds these hooks' outputs into\n * its props.\n *\n * const wb = useSheetsAdapter(initial, { screenId: \"deal-sheet\" });\n * const highlights = useSheetsActivityHighlights({ screenId: \"deal-sheet\" });\n *\n * useEffect(() => {\n * const bridge = registerSheetsBridge(host, { adapter: wb.adapter });\n * return bridge.dispose;\n * }, [host, wb.adapter]);\n *\n * <SheetWorkbook\n * data={wb.workbook}\n * onChange={wb.setWorkbook}\n * highlights={highlights}\n * onActiveCellChange={wb.onActiveCellChange}\n * />\n */\n\n// Loose type mirror of fancy-sheets' WorkbookData — kept local so this\n// helper doesn't pull a runtime dep on the package. Apps using the helper\n// import the real `WorkbookData` from fancy-sheets and pass it through.\nexport type WorkbookLike = {\n sheets: Array<{ id: string; name: string; [k: string]: unknown }>;\n activeSheetId: string;\n};\n\nexport type SheetsAdapterOptions = {\n /** Tags the bridge's screen id so presence events route correctly. */\n screenId?: string;\n};\n\nexport type UseSheetsAdapterResult<W extends WorkbookLike> = {\n /** Controlled workbook state. Wire to `<SheetWorkbook data={…} />`. */\n workbook: W;\n /** Setter for the controlled state. Wire to `<SheetWorkbook onChange={…} />`. */\n setWorkbook: (next: W) => void;\n /** Wire to `<SheetWorkbook onActiveCellChange={…} />` to track focus. */\n onActiveCellChange: (address: string) => void;\n /** Stable adapter to hand to `registerSheetsBridge({ adapter })`. */\n adapter: SheetsBridgeAdapter;\n /** Imperative: set the active sheet + cell. Mirrors the adapter's hook. */\n setActiveCell: (sheetId: string, address: string) => void;\n /** Read-only: the address last focused (any source). */\n activeCell: string | null;\n};\n\n/**\n * useSheetsAdapter — one-liner glue between fancy-sheets' SheetWorkbook\n * and the sheets bridge.\n *\n * const wb = useSheetsAdapter(initialWorkbook, { screenId: \"...\" });\n *\n * useEffect(() => registerSheetsBridge(host, { adapter: wb.adapter }).dispose,\n * [host, wb.adapter]);\n *\n * <SheetWorkbook\n * data={wb.workbook}\n * onChange={wb.setWorkbook}\n * onActiveCellChange={wb.onActiveCellChange}\n * />\n */\nexport function useSheetsAdapter<W extends WorkbookLike>(\n initial: W,\n options: SheetsAdapterOptions = {},\n): UseSheetsAdapterResult<W> {\n const [workbook, setWorkbook] = useState<W>(initial);\n const [activeCell, setActiveCellState] = useState<string | null>(null);\n const workbookRef = useRef(workbook);\n workbookRef.current = workbook;\n\n const setActiveCell = useCallback((sheetId: string, address: string) => {\n setWorkbook((cur) => (cur.activeSheetId === sheetId ? cur : { ...cur, activeSheetId: sheetId }));\n setActiveCellState(address);\n }, []);\n\n const onActiveCellChange = useCallback((address: string) => {\n setActiveCellState(address);\n }, []);\n\n // Adapter must be stable across renders so the bridge's tool catalog\n // doesn't churn — bind it to refs that hold the latest state + setter.\n const setWorkbookRef = useRef(setWorkbook);\n setWorkbookRef.current = setWorkbook;\n\n const adapter = useMemo<SheetsBridgeAdapter>(\n () => ({\n screenId: options.screenId,\n getWorkbook: () => workbookRef.current as unknown as ReturnType<SheetsBridgeAdapter[\"getWorkbook\"]>,\n setWorkbook: (next) => setWorkbookRef.current(next as unknown as W),\n setActiveCell,\n }),\n [options.screenId, setActiveCell],\n );\n\n return {\n workbook,\n setWorkbook,\n onActiveCellChange,\n adapter,\n setActiveCell,\n activeCell,\n };\n}\n\n/**\n * Loose mirror of fancy-sheets' `CellHighlightMap`. Each key is a cell\n * address (`\"B12\"`); each value is the visual treatment to apply.\n */\nexport type SheetsCellHighlight = {\n color?: string;\n /** Background tint; if omitted, derived from `color` at low alpha. */\n background?: string;\n /** Optional label rendered in a chip on the cell. */\n label?: string;\n /** Optional className appended to the cell. */\n className?: string;\n};\n\nexport type SheetsCellHighlightMap = Record<string, SheetsCellHighlight>;\n\nexport type SheetsHighlightOptions = {\n /** Only include events for this screen (recommended). */\n screenId?: string;\n /** Highlight TTL in ms before a hit fades from the map. Default 2200. */\n ttlMs?: number;\n};\n\n/**\n * useSheetsActivityHighlights — subscribe to the presence registry,\n * produce a CellHighlightMap reflecting recent sheet-bridge activity.\n *\n * Pass the result straight into `<SheetWorkbook highlights={…} />`. Each\n * agent edit pulses in the agent's color for `ttlMs` then fades out.\n *\n * The bridge's target shape is `${sheetId}!${address}` — this hook\n * filters for the currently-active sheet and exposes only its cells.\n *\n * const highlights = useSheetsActivityHighlights({ screenId: \"deal-sheet\" });\n * <SheetWorkbook highlights={highlights} … />\n */\nexport function useSheetsActivityHighlights(\n options: SheetsHighlightOptions = {},\n): SheetsCellHighlightMap {\n const ttlMs = options.ttlMs ?? 2200;\n const screenId = options.screenId;\n const [, force] = useState(0);\n const hitsRef = useRef<\n Map<string, { event: AgentActivityEvent; expiresAt: number }>\n >(new Map());\n\n useEffect(() => {\n const off = onActivity((event) => {\n if (event.target?.kind !== \"sheet\") return;\n if (screenId && event.target.screenId && event.target.screenId !== screenId) return;\n const elementId = event.target.elementId;\n if (!elementId || !elementId.includes(\"!\")) return;\n hitsRef.current.set(elementId, { event, expiresAt: Date.now() + ttlMs });\n force((n) => n + 1);\n });\n return off;\n }, [screenId, ttlMs]);\n\n // Periodic GC — drop expired entries and force a re-render.\n useEffect(() => {\n const t = window.setInterval(() => {\n const now = Date.now();\n let dirty = false;\n for (const [k, v] of hitsRef.current) {\n if (v.expiresAt < now) {\n hitsRef.current.delete(k);\n dirty = true;\n }\n }\n if (dirty) force((n) => n + 1);\n }, 500);\n return () => window.clearInterval(t);\n }, []);\n\n // Re-derived on every render — the listener + GC timer above call\n // `force` so renders happen exactly when the map changes.\n const out: SheetsCellHighlightMap = {};\n for (const [elementId, { event }] of hitsRef.current) {\n const idx = elementId.indexOf(\"!\");\n const address = elementId.slice(idx + 1);\n if (!address) continue;\n const color = event.agentColor ?? \"#a855f7\";\n out[address] = {\n color,\n background: color + \"33\",\n label: event.agentName ?? event.agentId ?? \"agent\",\n };\n }\n return out;\n}\n"]}
@@ -0,0 +1,115 @@
1
+ import { SheetsBridgeAdapter } from './bridges/sheets.cjs';
2
+ import './tool-host-BQuUygLF.cjs';
3
+ import './types-aOQLTW0E.cjs';
4
+ import './types-CCSBGW9T.cjs';
5
+
6
+ /**
7
+ * Shared-session helpers for `@particle-academy/fancy-sheets`.
8
+ *
9
+ * fancy-sheets' `SheetWorkbook` is already controlled (`data` + `onChange`).
10
+ * The two missing pieces for a clean shared-session experience are:
11
+ *
12
+ * 1. an adapter object the host can hand to {@link registerSheetsBridge}
13
+ * without writing boilerplate, and
14
+ * 2. a derived `CellHighlightMap` so agent edits visibly pulse on the
15
+ * humans' screens — wired from the presence registry's per-bridge
16
+ * activity stream.
17
+ *
18
+ * These are kept as host-side hooks (not part of the bridge itself) so
19
+ * agent-integrations keeps zero hard deps on fancy-sheets. The host
20
+ * imports SheetWorkbook directly and feeds these hooks' outputs into
21
+ * its props.
22
+ *
23
+ * const wb = useSheetsAdapter(initial, { screenId: "deal-sheet" });
24
+ * const highlights = useSheetsActivityHighlights({ screenId: "deal-sheet" });
25
+ *
26
+ * useEffect(() => {
27
+ * const bridge = registerSheetsBridge(host, { adapter: wb.adapter });
28
+ * return bridge.dispose;
29
+ * }, [host, wb.adapter]);
30
+ *
31
+ * <SheetWorkbook
32
+ * data={wb.workbook}
33
+ * onChange={wb.setWorkbook}
34
+ * highlights={highlights}
35
+ * onActiveCellChange={wb.onActiveCellChange}
36
+ * />
37
+ */
38
+ type WorkbookLike = {
39
+ sheets: Array<{
40
+ id: string;
41
+ name: string;
42
+ [k: string]: unknown;
43
+ }>;
44
+ activeSheetId: string;
45
+ };
46
+ type SheetsAdapterOptions = {
47
+ /** Tags the bridge's screen id so presence events route correctly. */
48
+ screenId?: string;
49
+ };
50
+ type UseSheetsAdapterResult<W extends WorkbookLike> = {
51
+ /** Controlled workbook state. Wire to `<SheetWorkbook data={…} />`. */
52
+ workbook: W;
53
+ /** Setter for the controlled state. Wire to `<SheetWorkbook onChange={…} />`. */
54
+ setWorkbook: (next: W) => void;
55
+ /** Wire to `<SheetWorkbook onActiveCellChange={…} />` to track focus. */
56
+ onActiveCellChange: (address: string) => void;
57
+ /** Stable adapter to hand to `registerSheetsBridge({ adapter })`. */
58
+ adapter: SheetsBridgeAdapter;
59
+ /** Imperative: set the active sheet + cell. Mirrors the adapter's hook. */
60
+ setActiveCell: (sheetId: string, address: string) => void;
61
+ /** Read-only: the address last focused (any source). */
62
+ activeCell: string | null;
63
+ };
64
+ /**
65
+ * useSheetsAdapter — one-liner glue between fancy-sheets' SheetWorkbook
66
+ * and the sheets bridge.
67
+ *
68
+ * const wb = useSheetsAdapter(initialWorkbook, { screenId: "..." });
69
+ *
70
+ * useEffect(() => registerSheetsBridge(host, { adapter: wb.adapter }).dispose,
71
+ * [host, wb.adapter]);
72
+ *
73
+ * <SheetWorkbook
74
+ * data={wb.workbook}
75
+ * onChange={wb.setWorkbook}
76
+ * onActiveCellChange={wb.onActiveCellChange}
77
+ * />
78
+ */
79
+ declare function useSheetsAdapter<W extends WorkbookLike>(initial: W, options?: SheetsAdapterOptions): UseSheetsAdapterResult<W>;
80
+ /**
81
+ * Loose mirror of fancy-sheets' `CellHighlightMap`. Each key is a cell
82
+ * address (`"B12"`); each value is the visual treatment to apply.
83
+ */
84
+ type SheetsCellHighlight = {
85
+ color?: string;
86
+ /** Background tint; if omitted, derived from `color` at low alpha. */
87
+ background?: string;
88
+ /** Optional label rendered in a chip on the cell. */
89
+ label?: string;
90
+ /** Optional className appended to the cell. */
91
+ className?: string;
92
+ };
93
+ type SheetsCellHighlightMap = Record<string, SheetsCellHighlight>;
94
+ type SheetsHighlightOptions = {
95
+ /** Only include events for this screen (recommended). */
96
+ screenId?: string;
97
+ /** Highlight TTL in ms before a hit fades from the map. Default 2200. */
98
+ ttlMs?: number;
99
+ };
100
+ /**
101
+ * useSheetsActivityHighlights — subscribe to the presence registry,
102
+ * produce a CellHighlightMap reflecting recent sheet-bridge activity.
103
+ *
104
+ * Pass the result straight into `<SheetWorkbook highlights={…} />`. Each
105
+ * agent edit pulses in the agent's color for `ttlMs` then fades out.
106
+ *
107
+ * The bridge's target shape is `${sheetId}!${address}` — this hook
108
+ * filters for the currently-active sheet and exposes only its cells.
109
+ *
110
+ * const highlights = useSheetsActivityHighlights({ screenId: "deal-sheet" });
111
+ * <SheetWorkbook highlights={highlights} … />
112
+ */
113
+ declare function useSheetsActivityHighlights(options?: SheetsHighlightOptions): SheetsCellHighlightMap;
114
+
115
+ export { type SheetsAdapterOptions, type SheetsCellHighlight, type SheetsCellHighlightMap, type SheetsHighlightOptions, type UseSheetsAdapterResult, type WorkbookLike, useSheetsActivityHighlights, useSheetsAdapter };
@@ -0,0 +1,115 @@
1
+ import { SheetsBridgeAdapter } from './bridges/sheets.js';
2
+ import './tool-host-C8JMMGYq.js';
3
+ import './types-aOQLTW0E.js';
4
+ import './types-DIVNcIQO.js';
5
+
6
+ /**
7
+ * Shared-session helpers for `@particle-academy/fancy-sheets`.
8
+ *
9
+ * fancy-sheets' `SheetWorkbook` is already controlled (`data` + `onChange`).
10
+ * The two missing pieces for a clean shared-session experience are:
11
+ *
12
+ * 1. an adapter object the host can hand to {@link registerSheetsBridge}
13
+ * without writing boilerplate, and
14
+ * 2. a derived `CellHighlightMap` so agent edits visibly pulse on the
15
+ * humans' screens — wired from the presence registry's per-bridge
16
+ * activity stream.
17
+ *
18
+ * These are kept as host-side hooks (not part of the bridge itself) so
19
+ * agent-integrations keeps zero hard deps on fancy-sheets. The host
20
+ * imports SheetWorkbook directly and feeds these hooks' outputs into
21
+ * its props.
22
+ *
23
+ * const wb = useSheetsAdapter(initial, { screenId: "deal-sheet" });
24
+ * const highlights = useSheetsActivityHighlights({ screenId: "deal-sheet" });
25
+ *
26
+ * useEffect(() => {
27
+ * const bridge = registerSheetsBridge(host, { adapter: wb.adapter });
28
+ * return bridge.dispose;
29
+ * }, [host, wb.adapter]);
30
+ *
31
+ * <SheetWorkbook
32
+ * data={wb.workbook}
33
+ * onChange={wb.setWorkbook}
34
+ * highlights={highlights}
35
+ * onActiveCellChange={wb.onActiveCellChange}
36
+ * />
37
+ */
38
+ type WorkbookLike = {
39
+ sheets: Array<{
40
+ id: string;
41
+ name: string;
42
+ [k: string]: unknown;
43
+ }>;
44
+ activeSheetId: string;
45
+ };
46
+ type SheetsAdapterOptions = {
47
+ /** Tags the bridge's screen id so presence events route correctly. */
48
+ screenId?: string;
49
+ };
50
+ type UseSheetsAdapterResult<W extends WorkbookLike> = {
51
+ /** Controlled workbook state. Wire to `<SheetWorkbook data={…} />`. */
52
+ workbook: W;
53
+ /** Setter for the controlled state. Wire to `<SheetWorkbook onChange={…} />`. */
54
+ setWorkbook: (next: W) => void;
55
+ /** Wire to `<SheetWorkbook onActiveCellChange={…} />` to track focus. */
56
+ onActiveCellChange: (address: string) => void;
57
+ /** Stable adapter to hand to `registerSheetsBridge({ adapter })`. */
58
+ adapter: SheetsBridgeAdapter;
59
+ /** Imperative: set the active sheet + cell. Mirrors the adapter's hook. */
60
+ setActiveCell: (sheetId: string, address: string) => void;
61
+ /** Read-only: the address last focused (any source). */
62
+ activeCell: string | null;
63
+ };
64
+ /**
65
+ * useSheetsAdapter — one-liner glue between fancy-sheets' SheetWorkbook
66
+ * and the sheets bridge.
67
+ *
68
+ * const wb = useSheetsAdapter(initialWorkbook, { screenId: "..." });
69
+ *
70
+ * useEffect(() => registerSheetsBridge(host, { adapter: wb.adapter }).dispose,
71
+ * [host, wb.adapter]);
72
+ *
73
+ * <SheetWorkbook
74
+ * data={wb.workbook}
75
+ * onChange={wb.setWorkbook}
76
+ * onActiveCellChange={wb.onActiveCellChange}
77
+ * />
78
+ */
79
+ declare function useSheetsAdapter<W extends WorkbookLike>(initial: W, options?: SheetsAdapterOptions): UseSheetsAdapterResult<W>;
80
+ /**
81
+ * Loose mirror of fancy-sheets' `CellHighlightMap`. Each key is a cell
82
+ * address (`"B12"`); each value is the visual treatment to apply.
83
+ */
84
+ type SheetsCellHighlight = {
85
+ color?: string;
86
+ /** Background tint; if omitted, derived from `color` at low alpha. */
87
+ background?: string;
88
+ /** Optional label rendered in a chip on the cell. */
89
+ label?: string;
90
+ /** Optional className appended to the cell. */
91
+ className?: string;
92
+ };
93
+ type SheetsCellHighlightMap = Record<string, SheetsCellHighlight>;
94
+ type SheetsHighlightOptions = {
95
+ /** Only include events for this screen (recommended). */
96
+ screenId?: string;
97
+ /** Highlight TTL in ms before a hit fades from the map. Default 2200. */
98
+ ttlMs?: number;
99
+ };
100
+ /**
101
+ * useSheetsActivityHighlights — subscribe to the presence registry,
102
+ * produce a CellHighlightMap reflecting recent sheet-bridge activity.
103
+ *
104
+ * Pass the result straight into `<SheetWorkbook highlights={…} />`. Each
105
+ * agent edit pulses in the agent's color for `ttlMs` then fades out.
106
+ *
107
+ * The bridge's target shape is `${sheetId}!${address}` — this hook
108
+ * filters for the currently-active sheet and exposes only its cells.
109
+ *
110
+ * const highlights = useSheetsActivityHighlights({ screenId: "deal-sheet" });
111
+ * <SheetWorkbook highlights={highlights} … />
112
+ */
113
+ declare function useSheetsActivityHighlights(options?: SheetsHighlightOptions): SheetsCellHighlightMap;
114
+
115
+ export { type SheetsAdapterOptions, type SheetsCellHighlight, type SheetsCellHighlightMap, type SheetsHighlightOptions, type UseSheetsAdapterResult, type WorkbookLike, useSheetsActivityHighlights, useSheetsAdapter };
@@ -0,0 +1,4 @@
1
+ export { useSheetsActivityHighlights, useSheetsAdapter } from './chunk-E4AICMFZ.js';
2
+ import './chunk-JU2N4KK6.js';
3
+ //# sourceMappingURL=sheets-adapter.js.map
4
+ //# sourceMappingURL=sheets-adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"sheets-adapter.js"}
package/dist/styles.css CHANGED
@@ -328,4 +328,61 @@
328
328
  color: inherit;
329
329
  font-size: 12px;
330
330
  }
331
+ .agent-focused-element {
332
+ --agent-color: #a855f7;
333
+ position: relative;
334
+ outline: 2px solid var(--agent-color);
335
+ outline-offset: 2px;
336
+ border-radius: 4px;
337
+ animation: agent-focus-pulse 1500ms ease-out forwards;
338
+ }
339
+ @keyframes agent-focus-pulse {
340
+ 0% {
341
+ box-shadow: 0 0 0 0 var(--agent-color);
342
+ }
343
+ 40% {
344
+ box-shadow: 0 0 0 6px rgba(168, 85, 247, 0.18);
345
+ }
346
+ 100% {
347
+ box-shadow: 0 0 0 0 rgba(168, 85, 247, 0);
348
+ outline-color: transparent;
349
+ }
350
+ }
351
+ @media (prefers-reduced-motion: reduce) {
352
+ .agent-focused-element {
353
+ animation: none;
354
+ }
355
+ }
356
+ .agent-active-badge {
357
+ display: inline-flex;
358
+ align-items: center;
359
+ gap: 4px;
360
+ background: var(--agent-color, #a855f7);
361
+ color: white;
362
+ font-family:
363
+ ui-sans-serif,
364
+ system-ui,
365
+ sans-serif;
366
+ font-size: 11px;
367
+ font-weight: 500;
368
+ padding: 2px 6px;
369
+ border-radius: 999px;
370
+ vertical-align: middle;
371
+ }
372
+ .agent-active-badge::before {
373
+ content: "";
374
+ width: 6px;
375
+ height: 6px;
376
+ border-radius: 50%;
377
+ background: white;
378
+ animation: agent-active-blink 1.4s ease-in-out infinite;
379
+ }
380
+ @keyframes agent-active-blink {
381
+ 0%, 100% {
382
+ opacity: 0.4;
383
+ }
384
+ 50% {
385
+ opacity: 1;
386
+ }
387
+ }
331
388
  /*# sourceMappingURL=styles.css.map */
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/styles.css"],"sourcesContent":["/* @particle-academy/agent-integrations — minimal styles, host app provides tailwind/theme. */\n\n.fai-panel {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: white;\n color: #18181b;\n border-radius: 12px;\n border: 1px solid #e4e4e7;\n font-family: ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif;\n overflow: hidden;\n}\n@media (prefers-color-scheme: dark) {\n .fai-panel { background: #18181b; color: #fafafa; border-color: #3f3f46; }\n}\n.fai-panel__header {\n display: flex; align-items: center; gap: 10px;\n padding: 10px 12px; border-bottom: 1px solid #e4e4e7;\n}\n@media (prefers-color-scheme: dark) { .fai-panel__header { border-color: #3f3f46; } }\n.fai-panel__avatar {\n width: 32px; height: 32px; border-radius: 50%;\n display: inline-flex; align-items: center; justify-content: center;\n color: white; font-weight: 600; font-size: 14px;\n}\n.fai-panel__title { display: flex; flex-direction: column; line-height: 1.1; flex: 1; }\n.fai-panel__subtitle { font-size: 11px; opacity: 0.6; }\n.fai-panel__actions { display: flex; gap: 6px; }\n.fai-panel__stream {\n flex: 1; overflow: auto; padding: 8px 12px;\n display: flex; flex-direction: column; gap: 6px;\n}\n.fai-panel__empty { font-size: 12px; opacity: 0.5; margin: auto; }\n.fai-row {\n font-size: 12px;\n border-radius: 6px;\n padding: 6px 8px;\n background: rgba(244, 244, 245, 0.6);\n}\n@media (prefers-color-scheme: dark) { .fai-row { background: rgba(63, 63, 70, 0.4); } }\n.fai-row--tool { border-left: 3px solid #a855f7; }\n.fai-row--message { border-left: 3px solid #3b82f6; }\n.fai-row--info { border-left: 3px solid #71717a; }\n.fai-row--error { border-left: 3px solid #ef4444; background: rgba(254, 226, 226, 0.4); }\n.fai-row__meta { display: flex; justify-content: space-between; opacity: 0.6; font-size: 10px; }\n.fai-row__source { font-family: ui-monospace, monospace; }\n.fai-row__text { white-space: pre-wrap; }\n.fai-row__detail summary { cursor: pointer; opacity: 0.7; font-size: 11px; margin-top: 4px; }\n.fai-row__detail pre {\n font-size: 11px; margin: 4px 0 0; padding: 6px;\n background: rgba(0,0,0,0.06); border-radius: 4px; overflow: auto;\n max-height: 200px;\n}\n.fai-panel__composer {\n display: flex; gap: 6px; padding: 8px;\n border-top: 1px solid #e4e4e7;\n}\n@media (prefers-color-scheme: dark) { .fai-panel__composer { border-color: #3f3f46; } }\n.fai-panel__input {\n flex: 1;\n resize: none;\n border-radius: 6px;\n border: 1px solid #d4d4d8;\n padding: 6px 8px;\n font: inherit;\n background: transparent;\n color: inherit;\n}\n.fai-panel__input:focus { outline: 2px solid #a855f7; outline-offset: -1px; }\n.fai-panel__send {\n align-self: stretch;\n padding: 0 14px;\n border: 0; border-radius: 6px;\n background: #a855f7; color: white;\n cursor: pointer; font-weight: 500;\n}\n.fai-panel__send:disabled { opacity: 0.5; cursor: default; }\n\n/* Cursor */\n.fai-cursor {\n transition: left 500ms cubic-bezier(0.22, 1, 0.36, 1),\n top 500ms cubic-bezier(0.22, 1, 0.36, 1);\n}\n@media (prefers-reduced-motion: reduce) { .fai-cursor { transition: none; } }\n.fai-cursor__tag {\n position: absolute; left: 18px; top: 14px;\n color: white;\n padding: 2px 6px;\n border-radius: 4px;\n font-size: 11px; font-family: system-ui, sans-serif;\n white-space: nowrap;\n}\n.fai-cursor__status { font-style: normal; opacity: 0.85; }\n\n/* Highlight pulse */\n@keyframes fai-pulse {\n 0% { opacity: 0; transform: scale(0.96); }\n 20% { opacity: 1; transform: scale(1.03); }\n 100% { opacity: 0; transform: scale(1); }\n}\n\n/* ShareControls */\n.fai-share {\n border: 1px solid #e4e4e7; border-radius: 12px; padding: 12px;\n background: white; color: #18181b;\n font-family: ui-sans-serif, system-ui, sans-serif; font-size: 13px;\n}\n@media (prefers-color-scheme: dark) { .fai-share { background: #18181b; color: #fafafa; border-color: #3f3f46; } }\n.fai-share--idle { display: flex; flex-direction: column; gap: 6px; }\n.fai-share__start { align-self: flex-start; padding: 8px 14px; border: 0; border-radius: 6px; background: #a855f7; color: white; font-weight: 500; cursor: pointer; }\n.fai-share__hint { margin: 0; opacity: 0.7; font-size: 12px; }\n.fai-share__header { display: flex; justify-content: space-between; align-items: center; gap: 8px; margin-bottom: 8px; }\n.fai-share__id { display: block; opacity: 0.7; font-size: 11px; margin-top: 2px; }\n.fai-share__id code { font-family: ui-monospace, monospace; }\n.fai-share__header-actions { display: flex; gap: 8px; align-items: center; }\n.fai-share__status { font-size: 11px; padding: 2px 8px; border-radius: 999px; background: rgba(34, 197, 94, 0.15); color: #16a34a; }\n.fai-share__stop { border: 1px solid #d4d4d8; background: transparent; border-radius: 6px; padding: 4px 10px; cursor: pointer; font-size: 12px; color: inherit; }\n.fai-share__tabs { display: flex; gap: 4px; margin-bottom: 8px; }\n.fai-share__tab { border: 0; background: transparent; cursor: pointer; padding: 4px 10px; border-radius: 4px; color: inherit; font-size: 12px; }\n.fai-share__tab.is-active { background: rgba(168, 85, 247, 0.12); color: #a855f7; }\n.fai-share__panel-label { font-size: 11px; opacity: 0.7; margin-bottom: 4px; }\n.fai-share__copy { display: flex; gap: 6px; align-items: stretch; }\n.fai-share__pre { flex: 1; margin: 0; padding: 8px 10px; background: rgba(0,0,0,0.05); border-radius: 6px; font-family: ui-monospace, monospace; font-size: 11px; white-space: nowrap; overflow: auto; }\n.fai-share__pre.is-multi { white-space: pre; max-height: 240px; }\n.fai-share__copy-btn { border: 1px solid #d4d4d8; background: transparent; cursor: pointer; border-radius: 6px; padding: 0 12px; color: inherit; font-size: 12px; }\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/styles.css"],"sourcesContent":["/* @particle-academy/agent-integrations — minimal styles, host app provides tailwind/theme. */\n\n.fai-panel {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: white;\n color: #18181b;\n border-radius: 12px;\n border: 1px solid #e4e4e7;\n font-family: ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif;\n overflow: hidden;\n}\n@media (prefers-color-scheme: dark) {\n .fai-panel { background: #18181b; color: #fafafa; border-color: #3f3f46; }\n}\n.fai-panel__header {\n display: flex; align-items: center; gap: 10px;\n padding: 10px 12px; border-bottom: 1px solid #e4e4e7;\n}\n@media (prefers-color-scheme: dark) { .fai-panel__header { border-color: #3f3f46; } }\n.fai-panel__avatar {\n width: 32px; height: 32px; border-radius: 50%;\n display: inline-flex; align-items: center; justify-content: center;\n color: white; font-weight: 600; font-size: 14px;\n}\n.fai-panel__title { display: flex; flex-direction: column; line-height: 1.1; flex: 1; }\n.fai-panel__subtitle { font-size: 11px; opacity: 0.6; }\n.fai-panel__actions { display: flex; gap: 6px; }\n.fai-panel__stream {\n flex: 1; overflow: auto; padding: 8px 12px;\n display: flex; flex-direction: column; gap: 6px;\n}\n.fai-panel__empty { font-size: 12px; opacity: 0.5; margin: auto; }\n.fai-row {\n font-size: 12px;\n border-radius: 6px;\n padding: 6px 8px;\n background: rgba(244, 244, 245, 0.6);\n}\n@media (prefers-color-scheme: dark) { .fai-row { background: rgba(63, 63, 70, 0.4); } }\n.fai-row--tool { border-left: 3px solid #a855f7; }\n.fai-row--message { border-left: 3px solid #3b82f6; }\n.fai-row--info { border-left: 3px solid #71717a; }\n.fai-row--error { border-left: 3px solid #ef4444; background: rgba(254, 226, 226, 0.4); }\n.fai-row__meta { display: flex; justify-content: space-between; opacity: 0.6; font-size: 10px; }\n.fai-row__source { font-family: ui-monospace, monospace; }\n.fai-row__text { white-space: pre-wrap; }\n.fai-row__detail summary { cursor: pointer; opacity: 0.7; font-size: 11px; margin-top: 4px; }\n.fai-row__detail pre {\n font-size: 11px; margin: 4px 0 0; padding: 6px;\n background: rgba(0,0,0,0.06); border-radius: 4px; overflow: auto;\n max-height: 200px;\n}\n.fai-panel__composer {\n display: flex; gap: 6px; padding: 8px;\n border-top: 1px solid #e4e4e7;\n}\n@media (prefers-color-scheme: dark) { .fai-panel__composer { border-color: #3f3f46; } }\n.fai-panel__input {\n flex: 1;\n resize: none;\n border-radius: 6px;\n border: 1px solid #d4d4d8;\n padding: 6px 8px;\n font: inherit;\n background: transparent;\n color: inherit;\n}\n.fai-panel__input:focus { outline: 2px solid #a855f7; outline-offset: -1px; }\n.fai-panel__send {\n align-self: stretch;\n padding: 0 14px;\n border: 0; border-radius: 6px;\n background: #a855f7; color: white;\n cursor: pointer; font-weight: 500;\n}\n.fai-panel__send:disabled { opacity: 0.5; cursor: default; }\n\n/* Cursor */\n.fai-cursor {\n transition: left 500ms cubic-bezier(0.22, 1, 0.36, 1),\n top 500ms cubic-bezier(0.22, 1, 0.36, 1);\n}\n@media (prefers-reduced-motion: reduce) { .fai-cursor { transition: none; } }\n.fai-cursor__tag {\n position: absolute; left: 18px; top: 14px;\n color: white;\n padding: 2px 6px;\n border-radius: 4px;\n font-size: 11px; font-family: system-ui, sans-serif;\n white-space: nowrap;\n}\n.fai-cursor__status { font-style: normal; opacity: 0.85; }\n\n/* Highlight pulse */\n@keyframes fai-pulse {\n 0% { opacity: 0; transform: scale(0.96); }\n 20% { opacity: 1; transform: scale(1.03); }\n 100% { opacity: 0; transform: scale(1); }\n}\n\n/* ShareControls */\n.fai-share {\n border: 1px solid #e4e4e7; border-radius: 12px; padding: 12px;\n background: white; color: #18181b;\n font-family: ui-sans-serif, system-ui, sans-serif; font-size: 13px;\n}\n@media (prefers-color-scheme: dark) { .fai-share { background: #18181b; color: #fafafa; border-color: #3f3f46; } }\n.fai-share--idle { display: flex; flex-direction: column; gap: 6px; }\n.fai-share__start { align-self: flex-start; padding: 8px 14px; border: 0; border-radius: 6px; background: #a855f7; color: white; font-weight: 500; cursor: pointer; }\n.fai-share__hint { margin: 0; opacity: 0.7; font-size: 12px; }\n.fai-share__header { display: flex; justify-content: space-between; align-items: center; gap: 8px; margin-bottom: 8px; }\n.fai-share__id { display: block; opacity: 0.7; font-size: 11px; margin-top: 2px; }\n.fai-share__id code { font-family: ui-monospace, monospace; }\n.fai-share__header-actions { display: flex; gap: 8px; align-items: center; }\n.fai-share__status { font-size: 11px; padding: 2px 8px; border-radius: 999px; background: rgba(34, 197, 94, 0.15); color: #16a34a; }\n.fai-share__stop { border: 1px solid #d4d4d8; background: transparent; border-radius: 6px; padding: 4px 10px; cursor: pointer; font-size: 12px; color: inherit; }\n.fai-share__tabs { display: flex; gap: 4px; margin-bottom: 8px; }\n.fai-share__tab { border: 0; background: transparent; cursor: pointer; padding: 4px 10px; border-radius: 4px; color: inherit; font-size: 12px; }\n.fai-share__tab.is-active { background: rgba(168, 85, 247, 0.12); color: #a855f7; }\n.fai-share__panel-label { font-size: 11px; opacity: 0.7; margin-bottom: 4px; }\n.fai-share__copy { display: flex; gap: 6px; align-items: stretch; }\n.fai-share__pre { flex: 1; margin: 0; padding: 8px 10px; background: rgba(0,0,0,0.05); border-radius: 6px; font-family: ui-monospace, monospace; font-size: 11px; white-space: nowrap; overflow: auto; }\n.fai-share__pre.is-multi { white-space: pre; max-height: 240px; }\n.fai-share__copy-btn { border: 1px solid #d4d4d8; background: transparent; cursor: pointer; border-radius: 6px; padding: 0 12px; color: inherit; font-size: 12px; }\n\n/* === Cross-package agent presence === */\n/*\n * Apply `.agent-focused-element` to any DOM node that's currently being\n * touched by an agent. The CSS variables let hosts override the agent's\n * accent without touching the rule. Bridges typically apply this class\n * via a brief side-effect when their tools fire.\n */\n.agent-focused-element {\n --agent-color: #a855f7;\n position: relative;\n outline: 2px solid var(--agent-color);\n outline-offset: 2px;\n border-radius: 4px;\n animation: agent-focus-pulse 1500ms ease-out forwards;\n}\n@keyframes agent-focus-pulse {\n 0% { box-shadow: 0 0 0 0 var(--agent-color); }\n 40% { box-shadow: 0 0 0 6px rgba(168, 85, 247, 0.18); }\n 100% { box-shadow: 0 0 0 0 rgba(168, 85, 247, 0); outline-color: transparent; }\n}\n@media (prefers-reduced-motion: reduce) {\n .agent-focused-element { animation: none; }\n}\n\n/* Small badge hosts can drop next to an element to label \"Agent active\". */\n.agent-active-badge {\n display: inline-flex; align-items: center; gap: 4px;\n background: var(--agent-color, #a855f7);\n color: white;\n font-family: ui-sans-serif, system-ui, sans-serif;\n font-size: 11px; font-weight: 500;\n padding: 2px 6px; border-radius: 999px;\n vertical-align: middle;\n}\n.agent-active-badge::before {\n content: \"\";\n width: 6px; height: 6px;\n border-radius: 50%;\n background: white;\n animation: agent-active-blink 1.4s ease-in-out infinite;\n}\n@keyframes agent-active-blink {\n 0%, 100% { opacity: 0.4; }\n 50% { opacity: 1; }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,60 @@
1
+ import { T as ToolDefinition, b as ToolHandler, R as RegisteredTool, c as JsonObject, C as CallToolResult } from './types-aOQLTW0E.cjs';
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 };