@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.
- package/README.md +20 -0
- package/dist/bridges/charts.d.cts +39 -0
- package/dist/bridges/charts.d.ts +39 -0
- package/dist/bridges/code.d.cts +47 -0
- package/dist/bridges/code.d.ts +47 -0
- package/dist/bridges/flow.d.cts +4 -3
- package/dist/bridges/flow.d.ts +4 -3
- package/dist/bridges/forms.d.cts +76 -0
- package/dist/bridges/forms.d.ts +76 -0
- package/dist/bridges/scene.d.cts +54 -0
- package/dist/bridges/scene.d.ts +54 -0
- package/dist/bridges/screens.d.cts +78 -0
- package/dist/bridges/screens.d.ts +78 -0
- package/dist/bridges/sheets.d.cts +62 -0
- package/dist/bridges/sheets.d.ts +62 -0
- package/dist/bridges/whiteboard.d.cts +4 -3
- package/dist/bridges/whiteboard.d.ts +4 -3
- package/dist/bridges-charts.cjs +167 -0
- package/dist/bridges-charts.cjs.map +1 -0
- package/dist/bridges-charts.js +6 -0
- package/dist/bridges-charts.js.map +1 -0
- package/dist/bridges-code.cjs +219 -0
- package/dist/bridges-code.cjs.map +1 -0
- package/dist/bridges-code.js +6 -0
- package/dist/bridges-code.js.map +1 -0
- package/dist/bridges-flow.cjs +78 -19
- package/dist/bridges-flow.cjs.map +1 -1
- package/dist/bridges-flow.js +4 -2
- package/dist/bridges-forms.cjs +205 -0
- package/dist/bridges-forms.cjs.map +1 -0
- package/dist/bridges-forms.js +6 -0
- package/dist/bridges-forms.js.map +1 -0
- package/dist/bridges-scene.cjs +250 -0
- package/dist/bridges-scene.cjs.map +1 -0
- package/dist/bridges-scene.js +6 -0
- package/dist/bridges-scene.js.map +1 -0
- package/dist/bridges-screens.cjs +227 -0
- package/dist/bridges-screens.cjs.map +1 -0
- package/dist/bridges-screens.js +6 -0
- package/dist/bridges-screens.js.map +1 -0
- package/dist/bridges-sheets.cjs +327 -0
- package/dist/bridges-sheets.cjs.map +1 -0
- package/dist/bridges-sheets.js +6 -0
- package/dist/bridges-sheets.js.map +1 -0
- package/dist/bridges-whiteboard.cjs +226 -40
- package/dist/bridges-whiteboard.cjs.map +1 -1
- package/dist/bridges-whiteboard.js +5 -2
- package/dist/{chunk-5ZUHNNLR.js → chunk-3KSZNGNW.js} +81 -43
- package/dist/chunk-3KSZNGNW.js.map +1 -0
- package/dist/chunk-4BL5M3U3.js +158 -0
- package/dist/chunk-4BL5M3U3.js.map +1 -0
- package/dist/{chunk-QGCF7YKW.js → chunk-4KAIV6OD.js} +40 -12
- package/dist/chunk-4KAIV6OD.js.map +1 -0
- package/dist/chunk-52S7XYZK.js +38 -0
- package/dist/chunk-52S7XYZK.js.map +1 -0
- package/dist/chunk-57ZDHD53.js +180 -0
- package/dist/chunk-57ZDHD53.js.map +1 -0
- package/dist/chunk-E4AICMFZ.js +83 -0
- package/dist/chunk-E4AICMFZ.js.map +1 -0
- package/dist/chunk-GQ7XXK7G.js +124 -0
- package/dist/chunk-GQ7XXK7G.js.map +1 -0
- package/dist/chunk-HSTW7ZNO.js +172 -0
- package/dist/chunk-HSTW7ZNO.js.map +1 -0
- package/dist/chunk-IANI25IT.js +280 -0
- package/dist/chunk-IANI25IT.js.map +1 -0
- package/dist/{chunk-FLEOQUKF.js → chunk-JMYPUAFH.js} +17 -2
- package/dist/chunk-JMYPUAFH.js.map +1 -0
- package/dist/chunk-JU2N4KK6.js +34 -0
- package/dist/chunk-JU2N4KK6.js.map +1 -0
- package/dist/{chunk-2VOQJKSU.js → chunk-N3H4DXY5.js} +44 -22
- package/dist/chunk-N3H4DXY5.js.map +1 -0
- package/dist/chunk-NTDZWGYB.js +120 -0
- package/dist/chunk-NTDZWGYB.js.map +1 -0
- package/dist/chunk-RGO42EQ6.js +25 -0
- package/dist/chunk-RGO42EQ6.js.map +1 -0
- package/dist/chunk-X66JWQBB.js +37 -0
- package/dist/chunk-X66JWQBB.js.map +1 -0
- package/dist/chunk-XRAJSOPS.js +203 -0
- package/dist/chunk-XRAJSOPS.js.map +1 -0
- package/dist/index.cjs +1766 -127
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +99 -3
- package/dist/index.d.ts +99 -3
- package/dist/index.js +115 -9
- package/dist/index.js.map +1 -1
- package/dist/mcp/index.d.cts +5 -2
- package/dist/mcp/index.d.ts +5 -2
- package/dist/mcp.cjs +37 -9
- package/dist/mcp.cjs.map +1 -1
- package/dist/mcp.js +1 -1
- package/dist/presence/index.d.cts +136 -0
- package/dist/presence/index.d.ts +136 -0
- package/dist/presence.cjs +107 -0
- package/dist/presence.cjs.map +1 -0
- package/dist/presence.js +5 -0
- package/dist/presence.js.map +1 -0
- package/dist/registry-2DRURS6U.js +3 -0
- package/dist/registry-2DRURS6U.js.map +1 -0
- package/dist/server-BsSwfemr.d.cts +63 -0
- package/dist/server-Du3-IGqM.d.ts +63 -0
- package/dist/sharing/index.d.cts +3 -1
- package/dist/sharing/index.d.ts +3 -1
- package/dist/sharing.cjs +68 -0
- package/dist/sharing.cjs.map +1 -1
- package/dist/sharing.js +1 -1
- package/dist/sheets-adapter.cjs +96 -0
- package/dist/sheets-adapter.cjs.map +1 -0
- package/dist/sheets-adapter.d.cts +115 -0
- package/dist/sheets-adapter.d.ts +115 -0
- package/dist/sheets-adapter.js +4 -0
- package/dist/sheets-adapter.js.map +1 -0
- package/dist/styles.css +57 -0
- package/dist/styles.css.map +1 -1
- package/dist/tool-host-BQuUygLF.d.cts +60 -0
- package/dist/tool-host-C8JMMGYq.d.ts +60 -0
- package/dist/{types-CRPA_D0z.d.ts → types-CCSBGW9T.d.cts} +2 -2
- package/dist/{types-DR5AS6Rd.d.cts → types-DIVNcIQO.d.ts} +2 -2
- package/dist/types-aOQLTW0E.d.cts +112 -0
- package/dist/types-aOQLTW0E.d.ts +112 -0
- package/dist/undo/index.d.cts +69 -0
- package/dist/undo/index.d.ts +69 -0
- package/dist/undo.cjs +163 -0
- package/dist/undo.cjs.map +1 -0
- package/dist/undo.js +5 -0
- package/dist/undo.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-2VOQJKSU.js.map +0 -1
- package/dist/chunk-5ZUHNNLR.js.map +0 -1
- package/dist/chunk-FLEOQUKF.js.map +0 -1
- package/dist/chunk-QGCF7YKW.js.map +0 -1
- package/dist/server-Bv985us3.d.cts +0 -173
- package/dist/server-Bv985us3.d.ts +0 -173
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { onActivity } from './chunk-JU2N4KK6.js';
|
|
2
|
+
import { useState, useRef, useCallback, useMemo, useEffect } from 'react';
|
|
3
|
+
|
|
4
|
+
function useSheetsAdapter(initial, options = {}) {
|
|
5
|
+
const [workbook, setWorkbook] = useState(initial);
|
|
6
|
+
const [activeCell, setActiveCellState] = useState(null);
|
|
7
|
+
const workbookRef = useRef(workbook);
|
|
8
|
+
workbookRef.current = workbook;
|
|
9
|
+
const setActiveCell = useCallback((sheetId, address) => {
|
|
10
|
+
setWorkbook((cur) => cur.activeSheetId === sheetId ? cur : { ...cur, activeSheetId: sheetId });
|
|
11
|
+
setActiveCellState(address);
|
|
12
|
+
}, []);
|
|
13
|
+
const onActiveCellChange = useCallback((address) => {
|
|
14
|
+
setActiveCellState(address);
|
|
15
|
+
}, []);
|
|
16
|
+
const setWorkbookRef = useRef(setWorkbook);
|
|
17
|
+
setWorkbookRef.current = setWorkbook;
|
|
18
|
+
const adapter = useMemo(
|
|
19
|
+
() => ({
|
|
20
|
+
screenId: options.screenId,
|
|
21
|
+
getWorkbook: () => workbookRef.current,
|
|
22
|
+
setWorkbook: (next) => setWorkbookRef.current(next),
|
|
23
|
+
setActiveCell
|
|
24
|
+
}),
|
|
25
|
+
[options.screenId, setActiveCell]
|
|
26
|
+
);
|
|
27
|
+
return {
|
|
28
|
+
workbook,
|
|
29
|
+
setWorkbook,
|
|
30
|
+
onActiveCellChange,
|
|
31
|
+
adapter,
|
|
32
|
+
setActiveCell,
|
|
33
|
+
activeCell
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function useSheetsActivityHighlights(options = {}) {
|
|
37
|
+
const ttlMs = options.ttlMs ?? 2200;
|
|
38
|
+
const screenId = options.screenId;
|
|
39
|
+
const [, force] = useState(0);
|
|
40
|
+
const hitsRef = useRef(/* @__PURE__ */ new Map());
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
const off = onActivity((event) => {
|
|
43
|
+
if (event.target?.kind !== "sheet") return;
|
|
44
|
+
if (screenId && event.target.screenId && event.target.screenId !== screenId) return;
|
|
45
|
+
const elementId = event.target.elementId;
|
|
46
|
+
if (!elementId || !elementId.includes("!")) return;
|
|
47
|
+
hitsRef.current.set(elementId, { event, expiresAt: Date.now() + ttlMs });
|
|
48
|
+
force((n) => n + 1);
|
|
49
|
+
});
|
|
50
|
+
return off;
|
|
51
|
+
}, [screenId, ttlMs]);
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
const t = window.setInterval(() => {
|
|
54
|
+
const now = Date.now();
|
|
55
|
+
let dirty = false;
|
|
56
|
+
for (const [k, v] of hitsRef.current) {
|
|
57
|
+
if (v.expiresAt < now) {
|
|
58
|
+
hitsRef.current.delete(k);
|
|
59
|
+
dirty = true;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (dirty) force((n) => n + 1);
|
|
63
|
+
}, 500);
|
|
64
|
+
return () => window.clearInterval(t);
|
|
65
|
+
}, []);
|
|
66
|
+
const out = {};
|
|
67
|
+
for (const [elementId, { event }] of hitsRef.current) {
|
|
68
|
+
const idx = elementId.indexOf("!");
|
|
69
|
+
const address = elementId.slice(idx + 1);
|
|
70
|
+
if (!address) continue;
|
|
71
|
+
const color = event.agentColor ?? "#a855f7";
|
|
72
|
+
out[address] = {
|
|
73
|
+
color,
|
|
74
|
+
background: color + "33",
|
|
75
|
+
label: event.agentName ?? event.agentId ?? "agent"
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
return out;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export { useSheetsActivityHighlights, useSheetsAdapter };
|
|
82
|
+
//# sourceMappingURL=chunk-E4AICMFZ.js.map
|
|
83
|
+
//# sourceMappingURL=chunk-E4AICMFZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/sheets-adapter.ts"],"names":[],"mappings":"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","file":"chunk-E4AICMFZ.js","sourcesContent":["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,124 @@
|
|
|
1
|
+
import { errorResult, textResult } from './chunk-4KAIV6OD.js';
|
|
2
|
+
|
|
3
|
+
// src/undo/undo-stack.ts
|
|
4
|
+
var stacks = /* @__PURE__ */ new Map();
|
|
5
|
+
var CAP = 200;
|
|
6
|
+
function getStack(agentId) {
|
|
7
|
+
let s = stacks.get(agentId);
|
|
8
|
+
if (!s) {
|
|
9
|
+
s = { past: [], future: [] };
|
|
10
|
+
stacks.set(agentId, s);
|
|
11
|
+
}
|
|
12
|
+
return s;
|
|
13
|
+
}
|
|
14
|
+
function pushUndoEntry(agentId, entry) {
|
|
15
|
+
const s = getStack(agentId);
|
|
16
|
+
s.past.push(entry);
|
|
17
|
+
if (s.past.length > CAP) s.past.splice(0, s.past.length - CAP);
|
|
18
|
+
s.future.length = 0;
|
|
19
|
+
}
|
|
20
|
+
async function undoOne(agentId) {
|
|
21
|
+
const s = getStack(agentId);
|
|
22
|
+
const entry = s.past.pop();
|
|
23
|
+
if (!entry) return null;
|
|
24
|
+
await entry.undo();
|
|
25
|
+
s.future.push(entry);
|
|
26
|
+
return entry;
|
|
27
|
+
}
|
|
28
|
+
async function redoOne(agentId) {
|
|
29
|
+
const s = getStack(agentId);
|
|
30
|
+
const entry = s.future.pop();
|
|
31
|
+
if (!entry) return null;
|
|
32
|
+
await entry.redo();
|
|
33
|
+
s.past.push(entry);
|
|
34
|
+
return entry;
|
|
35
|
+
}
|
|
36
|
+
function readHistory(agentId) {
|
|
37
|
+
return getStack(agentId).past.slice();
|
|
38
|
+
}
|
|
39
|
+
function clearStack(agentId) {
|
|
40
|
+
stacks.delete(agentId);
|
|
41
|
+
}
|
|
42
|
+
function resetAllUndoStacks() {
|
|
43
|
+
stacks.clear();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// src/undo/undo-tools.ts
|
|
47
|
+
var installedHosts = /* @__PURE__ */ new WeakSet();
|
|
48
|
+
function ensureUndoToolsRegistered(host, options = {}) {
|
|
49
|
+
if (installedHosts.has(host)) return;
|
|
50
|
+
installedHosts.add(host);
|
|
51
|
+
registerUndoTools(host, options);
|
|
52
|
+
}
|
|
53
|
+
function registerUndoTools(host, options = {}) {
|
|
54
|
+
const defaultAgent = options.defaultAgentId ?? "agent";
|
|
55
|
+
const disposers = [];
|
|
56
|
+
const agentOf = (args) => typeof args?.agentId === "string" ? args.agentId : defaultAgent;
|
|
57
|
+
disposers.push(
|
|
58
|
+
host.registerTool(
|
|
59
|
+
{
|
|
60
|
+
name: "agent_undo",
|
|
61
|
+
description: "Undo the most recent action on the agent's stack. Optional agentId targets a specific agent.",
|
|
62
|
+
inputSchema: {
|
|
63
|
+
type: "object",
|
|
64
|
+
properties: { agentId: { type: "string" } },
|
|
65
|
+
additionalProperties: false
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
async (args) => {
|
|
69
|
+
const entry = await undoOne(agentOf(args));
|
|
70
|
+
if (!entry) return errorResult("Nothing to undo.");
|
|
71
|
+
return textResult(`Undid: ${entry.label}`, { entry: serialize(entry) });
|
|
72
|
+
}
|
|
73
|
+
)
|
|
74
|
+
);
|
|
75
|
+
disposers.push(
|
|
76
|
+
host.registerTool(
|
|
77
|
+
{
|
|
78
|
+
name: "agent_redo",
|
|
79
|
+
description: "Redo the most recently undone action.",
|
|
80
|
+
inputSchema: {
|
|
81
|
+
type: "object",
|
|
82
|
+
properties: { agentId: { type: "string" } },
|
|
83
|
+
additionalProperties: false
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
async (args) => {
|
|
87
|
+
const entry = await redoOne(agentOf(args));
|
|
88
|
+
if (!entry) return errorResult("Nothing to redo.");
|
|
89
|
+
return textResult(`Redid: ${entry.label}`, { entry: serialize(entry) });
|
|
90
|
+
}
|
|
91
|
+
)
|
|
92
|
+
);
|
|
93
|
+
disposers.push(
|
|
94
|
+
host.registerTool(
|
|
95
|
+
{
|
|
96
|
+
name: "agent_history",
|
|
97
|
+
description: "List the agent's undo stack (oldest first). Useful for understanding what's reversible.",
|
|
98
|
+
inputSchema: {
|
|
99
|
+
type: "object",
|
|
100
|
+
properties: { agentId: { type: "string" } },
|
|
101
|
+
additionalProperties: false
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
async (args) => {
|
|
105
|
+
const history = readHistory(agentOf(args)).map(serialize);
|
|
106
|
+
const text = history.map((e) => `${new Date(e.timestamp).toISOString()} ${e.bridgeId} ${e.action}: ${e.label}`).join("\n");
|
|
107
|
+
return textResult(text || "(empty)", history);
|
|
108
|
+
}
|
|
109
|
+
)
|
|
110
|
+
);
|
|
111
|
+
return () => disposers.forEach((d) => d());
|
|
112
|
+
}
|
|
113
|
+
function serialize(entry) {
|
|
114
|
+
return {
|
|
115
|
+
timestamp: entry.timestamp,
|
|
116
|
+
bridgeId: entry.bridgeId,
|
|
117
|
+
action: entry.action,
|
|
118
|
+
label: entry.label
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export { clearStack, ensureUndoToolsRegistered, pushUndoEntry, readHistory, redoOne, registerUndoTools, resetAllUndoStacks, undoOne };
|
|
123
|
+
//# sourceMappingURL=chunk-GQ7XXK7G.js.map
|
|
124
|
+
//# sourceMappingURL=chunk-GQ7XXK7G.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/undo/undo-stack.ts","../src/undo/undo-tools.ts"],"names":[],"mappings":"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","file":"chunk-GQ7XXK7G.js","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 { 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"]}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { wrapToolWithActivity } from './chunk-52S7XYZK.js';
|
|
2
|
+
import { textResult, errorResult } from './chunk-4KAIV6OD.js';
|
|
3
|
+
|
|
4
|
+
// src/bridges/code.ts
|
|
5
|
+
var DEFAULT_AGENT = { id: "agent", name: "Agent", color: "#a855f7" };
|
|
6
|
+
function registerCodeBridge(host, options) {
|
|
7
|
+
const { adapter } = options;
|
|
8
|
+
const agent = { ...DEFAULT_AGENT, ...options.agent ?? {} };
|
|
9
|
+
const disposers = [];
|
|
10
|
+
const target = {
|
|
11
|
+
kind: "code",
|
|
12
|
+
screenId: adapter.screenId,
|
|
13
|
+
elementId: adapter.id,
|
|
14
|
+
label: adapter.title ?? adapter.id
|
|
15
|
+
};
|
|
16
|
+
const reg = (name, description, properties, required, handler, isMutation) => {
|
|
17
|
+
const wrapped = async (args) => {
|
|
18
|
+
try {
|
|
19
|
+
return await handler(args);
|
|
20
|
+
} catch (e) {
|
|
21
|
+
return errorResult(e instanceof Error ? e.message : String(e));
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
const final = isMutation ? wrapToolWithActivity(wrapped, {
|
|
25
|
+
toolName: name,
|
|
26
|
+
agent,
|
|
27
|
+
kind: "code",
|
|
28
|
+
screenId: adapter.screenId,
|
|
29
|
+
resolveTarget: () => target
|
|
30
|
+
}) : wrapped;
|
|
31
|
+
disposers.push(
|
|
32
|
+
host.registerTool(
|
|
33
|
+
{
|
|
34
|
+
name,
|
|
35
|
+
description,
|
|
36
|
+
inputSchema: { type: "object", properties, required, additionalProperties: false }
|
|
37
|
+
},
|
|
38
|
+
final
|
|
39
|
+
)
|
|
40
|
+
);
|
|
41
|
+
};
|
|
42
|
+
reg(
|
|
43
|
+
"code_describe",
|
|
44
|
+
`Describe the editor "${adapter.id}" \u2014 language + length + has-selection.`,
|
|
45
|
+
{},
|
|
46
|
+
[],
|
|
47
|
+
() => {
|
|
48
|
+
const value = adapter.getValue();
|
|
49
|
+
const language = adapter.getLanguage?.() ?? "unknown";
|
|
50
|
+
const summary = { id: adapter.id, language, length: value.length, lines: value.split("\n").length };
|
|
51
|
+
return textResult(JSON.stringify(summary), summary);
|
|
52
|
+
},
|
|
53
|
+
false
|
|
54
|
+
);
|
|
55
|
+
reg(
|
|
56
|
+
"code_get_value",
|
|
57
|
+
"Read the full document text.",
|
|
58
|
+
{},
|
|
59
|
+
[],
|
|
60
|
+
() => {
|
|
61
|
+
const value = adapter.getValue();
|
|
62
|
+
return textResult(value, { value });
|
|
63
|
+
},
|
|
64
|
+
false
|
|
65
|
+
);
|
|
66
|
+
reg(
|
|
67
|
+
"code_get_selection",
|
|
68
|
+
"Read the currently-selected text (empty string if no selection).",
|
|
69
|
+
{},
|
|
70
|
+
[],
|
|
71
|
+
() => {
|
|
72
|
+
if (!adapter.getSelection) return errorResult("Host did not provide getSelection.");
|
|
73
|
+
const value = adapter.getSelection();
|
|
74
|
+
return textResult(value, { value });
|
|
75
|
+
},
|
|
76
|
+
false
|
|
77
|
+
);
|
|
78
|
+
reg(
|
|
79
|
+
"code_set_value",
|
|
80
|
+
"Replace the entire document.",
|
|
81
|
+
{ value: { type: "string" } },
|
|
82
|
+
["value"],
|
|
83
|
+
(args) => {
|
|
84
|
+
const value = String(args.value ?? "");
|
|
85
|
+
adapter.setValue(value);
|
|
86
|
+
return textResult(`Replaced document (${value.length} chars)`, { length: value.length });
|
|
87
|
+
},
|
|
88
|
+
true
|
|
89
|
+
);
|
|
90
|
+
reg(
|
|
91
|
+
"code_append",
|
|
92
|
+
"Append text to the end of the document.",
|
|
93
|
+
{ text: { type: "string" } },
|
|
94
|
+
["text"],
|
|
95
|
+
(args) => {
|
|
96
|
+
const text = String(args.text ?? "");
|
|
97
|
+
const next = adapter.getValue() + text;
|
|
98
|
+
adapter.setValue(next);
|
|
99
|
+
return textResult(`Appended ${text.length} chars`, { length: next.length });
|
|
100
|
+
},
|
|
101
|
+
true
|
|
102
|
+
);
|
|
103
|
+
reg(
|
|
104
|
+
"code_stream_append",
|
|
105
|
+
"Type characters into the document one at a time so the human can read it forming. Returns when streaming finishes.",
|
|
106
|
+
{
|
|
107
|
+
text: { type: "string" },
|
|
108
|
+
cps: { type: "number", description: "Characters per second. Default 25." }
|
|
109
|
+
},
|
|
110
|
+
["text"],
|
|
111
|
+
async (args) => {
|
|
112
|
+
const text = String(args.text ?? "");
|
|
113
|
+
const cps = Math.max(1, Number(args.cps ?? 25));
|
|
114
|
+
const interval = Math.max(8, Math.round(1e3 / cps));
|
|
115
|
+
const start = adapter.getValue();
|
|
116
|
+
for (let i = 1; i <= text.length; i++) {
|
|
117
|
+
adapter.setValue(start + text.slice(0, i));
|
|
118
|
+
if (i < text.length) await new Promise((r) => setTimeout(r, interval));
|
|
119
|
+
}
|
|
120
|
+
return textResult(`Streamed ${text.length} chars`, { length: text.length });
|
|
121
|
+
},
|
|
122
|
+
true
|
|
123
|
+
);
|
|
124
|
+
reg(
|
|
125
|
+
"code_replace_selection",
|
|
126
|
+
"Replace the currently-selected text with the supplied text.",
|
|
127
|
+
{ text: { type: "string" } },
|
|
128
|
+
["text"],
|
|
129
|
+
(args) => {
|
|
130
|
+
if (!adapter.replaceSelection) return errorResult("Host did not provide replaceSelection.");
|
|
131
|
+
adapter.replaceSelection(String(args.text ?? ""));
|
|
132
|
+
return textResult("Selection replaced", {});
|
|
133
|
+
},
|
|
134
|
+
true
|
|
135
|
+
);
|
|
136
|
+
reg(
|
|
137
|
+
"code_set_language",
|
|
138
|
+
"Switch the active syntax highlighting / formatter.",
|
|
139
|
+
{ language: { type: "string", description: "e.g. 'javascript', 'php', 'sql'." } },
|
|
140
|
+
["language"],
|
|
141
|
+
(args) => {
|
|
142
|
+
if (!adapter.setLanguage) return errorResult("Host did not provide setLanguage.");
|
|
143
|
+
const lang = String(args.language ?? "");
|
|
144
|
+
adapter.setLanguage(lang);
|
|
145
|
+
return textResult(`Language \u2192 ${lang}`, { language: lang });
|
|
146
|
+
},
|
|
147
|
+
true
|
|
148
|
+
);
|
|
149
|
+
reg(
|
|
150
|
+
"code_focus",
|
|
151
|
+
"Move browser focus to the editor.",
|
|
152
|
+
{},
|
|
153
|
+
[],
|
|
154
|
+
() => {
|
|
155
|
+
if (!adapter.focus) return errorResult("Host did not provide focus.");
|
|
156
|
+
adapter.focus();
|
|
157
|
+
return textResult("Focused", {});
|
|
158
|
+
},
|
|
159
|
+
true
|
|
160
|
+
);
|
|
161
|
+
return {
|
|
162
|
+
id: `code:${adapter.id}`,
|
|
163
|
+
title: adapter.title ?? adapter.id,
|
|
164
|
+
dispose: () => {
|
|
165
|
+
for (const d of disposers) d();
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export { registerCodeBridge };
|
|
171
|
+
//# sourceMappingURL=chunk-HSTW7ZNO.js.map
|
|
172
|
+
//# sourceMappingURL=chunk-HSTW7ZNO.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/bridges/code.ts"],"names":[],"mappings":"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","file":"chunk-HSTW7ZNO.js","sourcesContent":["import { textResult, errorResult } from \"../mcp/server\";\nimport type { ToolHost } from \"../mcp/tool-host\";\nimport type { JsonObject } from \"../mcp/types\";\nimport type { Bridge } from \"./types\";\nimport { wrapToolWithActivity } from \"../presence/wrap-tool-with-activity\";\nimport type { AgentTarget } from \"../presence/types\";\n\n/**\n * Adapter the host wires to a fancy-code CodeEditor (typically via the\n * useCodeEditor hook's context value).\n */\nexport type CodeBridgeAdapter = {\n /** Stable id for this editor instance. */\n id: string;\n /** Display label. */\n title?: string;\n /** Optional fancy-screens screen id. */\n screenId?: string;\n /** Read the current document text. */\n getValue: () => string;\n /** Replace the document. */\n setValue: (value: string) => void;\n /** Read the current selection text (empty string if none). */\n getSelection?: () => string;\n /** Replace the current selection with text. */\n replaceSelection?: (text: string) => void;\n /** Programmatic focus. */\n focus?: () => void;\n /** Read / set the active language. */\n getLanguage?: () => string;\n /** Set the active language. */\n setLanguage?: (lang: string) => void;\n};\n\nexport type CodeBridgeOptions = {\n adapter: CodeBridgeAdapter;\n agent?: { id: string; name?: string; color?: string };\n};\n\nconst DEFAULT_AGENT = { id: \"agent\", name: \"Agent\", color: \"#a855f7\" };\n\n/**\n * registerCodeBridge — schema-aware MCP access to a single CodeEditor.\n * Tools cover read, full replace, append, selection replace, language\n * switch, and a streaming append helper for \"type characters into the\n * editor over time\" UX.\n */\nexport function registerCodeBridge(\n host: ToolHost,\n options: CodeBridgeOptions,\n): Bridge {\n const { adapter } = options;\n const agent = { ...DEFAULT_AGENT, ...(options.agent ?? {}) };\n const disposers: Array<() => void> = [];\n\n const target: AgentTarget = {\n kind: \"code\",\n screenId: adapter.screenId,\n elementId: adapter.id,\n label: adapter.title ?? adapter.id,\n };\n\n const reg = (\n name: string,\n description: string,\n properties: Record<string, unknown>,\n required: string[],\n handler: (args: JsonObject) => Promise<any> | any,\n isMutation: boolean,\n ) => {\n const wrapped = async (args: JsonObject) => {\n try {\n return await handler(args);\n } catch (e) {\n return errorResult(e instanceof Error ? e.message : String(e));\n }\n };\n const final = isMutation\n ? wrapToolWithActivity(wrapped, {\n toolName: name,\n agent,\n kind: \"code\",\n screenId: adapter.screenId,\n resolveTarget: () => target,\n })\n : wrapped;\n disposers.push(\n host.registerTool(\n {\n name,\n description,\n inputSchema: { type: \"object\", properties: properties as any, required, additionalProperties: false },\n },\n final as any,\n ),\n );\n };\n\n reg(\n \"code_describe\",\n `Describe the editor \"${adapter.id}\" — language + length + has-selection.`,\n {},\n [],\n () => {\n const value = adapter.getValue();\n const language = adapter.getLanguage?.() ?? \"unknown\";\n const summary = { id: adapter.id, language, length: value.length, lines: value.split(\"\\n\").length };\n return textResult(JSON.stringify(summary), summary);\n },\n false,\n );\n\n reg(\n \"code_get_value\",\n \"Read the full document text.\",\n {},\n [],\n () => {\n const value = adapter.getValue();\n return textResult(value, { value });\n },\n false,\n );\n\n reg(\n \"code_get_selection\",\n \"Read the currently-selected text (empty string if no selection).\",\n {},\n [],\n () => {\n if (!adapter.getSelection) return errorResult(\"Host did not provide getSelection.\");\n const value = adapter.getSelection();\n return textResult(value, { value });\n },\n false,\n );\n\n reg(\n \"code_set_value\",\n \"Replace the entire document.\",\n { value: { type: \"string\" } },\n [\"value\"],\n (args) => {\n const value = String(args.value ?? \"\");\n adapter.setValue(value);\n return textResult(`Replaced document (${value.length} chars)`, { length: value.length });\n },\n true,\n );\n\n reg(\n \"code_append\",\n \"Append text to the end of the document.\",\n { text: { type: \"string\" } },\n [\"text\"],\n (args) => {\n const text = String(args.text ?? \"\");\n const next = adapter.getValue() + text;\n adapter.setValue(next);\n return textResult(`Appended ${text.length} chars`, { length: next.length });\n },\n true,\n );\n\n reg(\n \"code_stream_append\",\n \"Type characters into the document one at a time so the human can read it forming. Returns when streaming finishes.\",\n {\n text: { type: \"string\" },\n cps: { type: \"number\", description: \"Characters per second. Default 25.\" },\n },\n [\"text\"],\n async (args) => {\n const text = String(args.text ?? \"\");\n const cps = Math.max(1, Number(args.cps ?? 25));\n const interval = Math.max(8, Math.round(1000 / cps));\n const start = adapter.getValue();\n for (let i = 1; i <= text.length; i++) {\n adapter.setValue(start + text.slice(0, i));\n if (i < text.length) await new Promise((r) => setTimeout(r, interval));\n }\n return textResult(`Streamed ${text.length} chars`, { length: text.length });\n },\n true,\n );\n\n reg(\n \"code_replace_selection\",\n \"Replace the currently-selected text with the supplied text.\",\n { text: { type: \"string\" } },\n [\"text\"],\n (args) => {\n if (!adapter.replaceSelection) return errorResult(\"Host did not provide replaceSelection.\");\n adapter.replaceSelection(String(args.text ?? \"\"));\n return textResult(\"Selection replaced\", { });\n },\n true,\n );\n\n reg(\n \"code_set_language\",\n \"Switch the active syntax highlighting / formatter.\",\n { language: { type: \"string\", description: \"e.g. 'javascript', 'php', 'sql'.\" } },\n [\"language\"],\n (args) => {\n if (!adapter.setLanguage) return errorResult(\"Host did not provide setLanguage.\");\n const lang = String(args.language ?? \"\");\n adapter.setLanguage(lang);\n return textResult(`Language → ${lang}`, { language: lang });\n },\n true,\n );\n\n reg(\n \"code_focus\",\n \"Move browser focus to the editor.\",\n {},\n [],\n () => {\n if (!adapter.focus) return errorResult(\"Host did not provide focus.\");\n adapter.focus();\n return textResult(\"Focused\", { });\n },\n true,\n );\n\n return {\n id: `code:${adapter.id}`,\n title: adapter.title ?? adapter.id,\n dispose: () => {\n for (const d of disposers) d();\n },\n };\n}\n"]}
|