@eigenpal/docx-editor-agents 0.1.0 → 0.2.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 (45) hide show
  1. package/README.md +69 -16
  2. package/dist/ai-sdk/react.d.mts +67 -0
  3. package/dist/ai-sdk/react.d.ts +67 -0
  4. package/dist/ai-sdk/react.js +1 -0
  5. package/dist/ai-sdk/react.mjs +1 -0
  6. package/dist/ai-sdk/server.d.mts +42 -0
  7. package/dist/ai-sdk/server.d.ts +42 -0
  8. package/dist/ai-sdk/server.js +1 -0
  9. package/dist/ai-sdk/server.mjs +1 -0
  10. package/dist/bridge.d.mts +1 -11
  11. package/dist/bridge.d.ts +1 -11
  12. package/dist/bridge.js +1 -1
  13. package/dist/bridge.mjs +1 -1
  14. package/dist/{chunk-W2QSJSB7.mjs → chunk-25L7AEDN.mjs} +11 -11
  15. package/dist/chunk-2MJYWSD3.js +2 -0
  16. package/dist/chunk-4OJA3FMM.mjs +5 -0
  17. package/dist/chunk-AITWKLUF.mjs +2 -0
  18. package/dist/chunk-ALS6DG4A.js +1 -0
  19. package/dist/chunk-CWKXKM35.mjs +1 -0
  20. package/dist/chunk-DOKVZKIP.js +5 -0
  21. package/dist/chunk-GZG3RJXW.js +8 -0
  22. package/dist/chunk-S2PFUY23.mjs +8 -0
  23. package/dist/{chunk-G5XAABHF.js → chunk-VF25WARH.js} +11 -11
  24. package/dist/headless-B7TGKW6I.js +1 -0
  25. package/dist/{headless-I5CZ42MD.mjs → headless-PZWHORT6.mjs} +1 -1
  26. package/dist/index.d.mts +118 -1997
  27. package/dist/index.d.ts +118 -1997
  28. package/dist/index.js +1 -5
  29. package/dist/index.mjs +1 -5
  30. package/dist/mcp.d.mts +180 -0
  31. package/dist/mcp.d.ts +180 -0
  32. package/dist/mcp.js +4 -0
  33. package/dist/mcp.mjs +4 -0
  34. package/dist/react.d.mts +78 -0
  35. package/dist/react.d.ts +78 -0
  36. package/dist/react.js +1 -0
  37. package/dist/react.mjs +1 -0
  38. package/dist/server-B7dGWiLh.d.mts +2395 -0
  39. package/dist/server-B7dGWiLh.d.ts +2395 -0
  40. package/dist/server.d.mts +1 -0
  41. package/dist/server.d.ts +1 -0
  42. package/dist/server.js +1 -0
  43. package/dist/server.mjs +1 -0
  44. package/package.json +52 -1
  45. package/dist/headless-DGAHMYIU.js +0 -1
package/README.md CHANGED
@@ -2,40 +2,93 @@
2
2
 
3
3
  [![License: AGPL-3.0](https://img.shields.io/badge/License-AGPL--3.0-blue.svg)](./LICENSE)
4
4
 
5
- Word-like API for AI document review. Add comments, suggest replacements, accept/reject tracked changes all headless, no DOM required.
6
-
7
- ## Install
5
+ Word-like API for AI agents to review DOCX documents. Read, comment, suggest tracked changes, accept/reject. Headless. Server-friendly. Browser-friendly. **The library you build your AI document features on top of.**
8
6
 
9
7
  ```bash
10
8
  npm install @eigenpal/docx-editor-agents
11
9
  ```
12
10
 
13
- ## Usage
11
+ ## Three ways to use this package
12
+
13
+ ### 1. Static review (`DocxReviewer`) — single function call against a parsed DOCX
14
14
 
15
15
  ```ts
16
16
  import { DocxReviewer } from '@eigenpal/docx-editor-agents';
17
17
 
18
18
  const reviewer = await DocxReviewer.fromBuffer(buffer, 'AI Reviewer');
19
+ reviewer.addComment(5, 'This cap seems too low.');
20
+ reviewer.replace(5, '$50k', '$500k');
21
+ const output = await reviewer.toBuffer();
22
+ ```
19
23
 
20
- // Read
21
- const text = reviewer.getContentAsText();
24
+ Drop into a CI bot, a queue worker, a Lambda. No editor needed. ~50 KB.
22
25
 
23
- // Comment
24
- reviewer.addComment(5, 'This cap seems too low.');
26
+ ### 2. Live editor bridge (`createEditorBridge`) — wire AI tools into a running `<DocxEditor>` instance
25
27
 
26
- // Replace (tracked change)
27
- reviewer.replace(5, '$50k', '$500k');
28
+ ```ts
29
+ import { useAgentChat } from '@eigenpal/docx-editor-agents/bridge';
30
+
31
+ const { executeToolCall, toolSchemas } = useAgentChat({ editorRef, author: 'Assistant' });
32
+ ```
33
+
34
+ The agent's `add_comment`, `suggest_change`, `find_text` etc. show up live in the user's editor. Used by Eigenpal's chat panel; published for anyone building an AI document UX.
35
+
36
+ ### 3. Build your own MCP server (`McpServer` + `createReviewerBridge`) — the SaaS path
37
+
38
+ This is the one most teams want. The published library exposes a transport-agnostic MCP server core. **You wrap it inside your own auth, storage, and transport layer.** Stdio, HTTP-SSE, WebSocket, queue-worker — your call.
39
+
40
+ ```ts
41
+ // Your /api/mcp/sse route — Express, Hono, Next.js, whatever
42
+ import { McpServer, createReviewerBridge, DocxReviewer } from '@eigenpal/docx-editor-agents';
28
43
 
29
- // Batch from LLM response
30
- reviewer.applyReview({
31
- comments: [{ paragraphIndex: 5, text: 'Too low.' }],
32
- proposals: [{ paragraphIndex: 5, search: '$50k', replaceWith: '$500k' }],
44
+ app.post('/api/mcp', requireAuth, async (req, res) => {
45
+ // 1. Pull the DOCX from your storage (S3, Postgres bytea, etc.)
46
+ const buffer = await loadDocxForUser(req.user, req.params.docId);
47
+
48
+ // 2. Wire it through the bridge
49
+ const reviewer = await DocxReviewer.fromBuffer(buffer, req.user.name);
50
+ const bridge = createReviewerBridge(reviewer);
51
+ const server = new McpServer(bridge, {
52
+ name: 'acme-contract-review',
53
+ version: '1.0.0',
54
+ });
55
+
56
+ // 3. Drive MCP messages over your transport. server.handle() is sync,
57
+ // transport-free, and never throws.
58
+ const reply = server.handle(JSON.parse(req.body));
59
+ res.json(reply);
60
+
61
+ // 4. After the agent's done, persist the modified DOCX back to your storage.
62
+ await saveDocxForUser(req.user, req.params.docId, await reviewer.toBuffer());
33
63
  });
64
+ ```
34
65
 
35
- // Export
36
- const output = await reviewer.toBuffer();
66
+ That's the whole shape. Ten built-in agent tools (`read_document`, `find_text`, `add_comment`, `suggest_change`, `read_comments`, `read_changes`, `reply_comment`, `resolve_comment`, `read_selection`, `scroll`) are exposed automatically through MCP `tools/list` and `tools/call`. MCP spec version: `2025-06-18`.
67
+
68
+ #### Why server-side, why not a local stdio bin?
69
+
70
+ A local-installed stdio MCP server only works for one document per config — Claude Desktop loads its MCP server list at startup. That's a useless shape for a contract-review product where users have many documents. The right deployment is **a hosted MCP server you operate**, with your own auth and storage. The library gives you the engine; you bring the chassis.
71
+
72
+ ## Word JS API parity
73
+
74
+ The bridge mirrors the Office.js Word API pattern — locate a stable handle (`paraId`) first, then mutate. The parity contract is enforced at compile time:
75
+
76
+ ```ts
77
+ import type { WordCompatBridge } from '@eigenpal/docx-editor-agents';
37
78
  ```
38
79
 
80
+ `WordCompatBridge` is a TypeScript interface that `EditorBridge` is statically required to satisfy. If we ever drop a method that maps to a Word API call, typecheck breaks.
81
+
82
+ ## What's in the package
83
+
84
+ | Subpath | What | Use when |
85
+ | ------------------------------------- | ----------------------------------------------------------------- | --------------------------------------------------------- |
86
+ | `@eigenpal/docx-editor-agents` | `DocxReviewer`, `createReviewerBridge`, agent tool catalog, types | Server-side review, building your own MCP server |
87
+ | `@eigenpal/docx-editor-agents/bridge` | `useAgentChat`, `createEditorBridge`, `EditorBridge` interface | Wiring AI tools into a live `<DocxEditor>` in the browser |
88
+ | `@eigenpal/docx-editor-agents/mcp` | `McpServer`, JSON-RPC types, stdio adapter | Building an MCP server (any transport) |
89
+
90
+ Zero new runtime dependencies. Tree-shakes cleanly per subpath.
91
+
39
92
  ## License
40
93
 
41
94
  [AGPL-3.0](./LICENSE) — free to use and modify, but you must open-source your code. For commercial licensing without AGPL obligations, contact [founders@eigenpal.com](mailto:founders@eigenpal.com).
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Vercel AI SDK adapter (React side) — opt-in.
3
+ *
4
+ * Use this only if you're driving the chat with `useChat` from
5
+ * `@ai-sdk/react`. The library's `<AgentChatLog>` consumes a flat
6
+ * `AgentMessage[]` shape; AI SDK's `useChat` produces `UIMessage[]`
7
+ * with structured `parts`. `toAgentMessages()` is the bridge.
8
+ */
9
+ /**
10
+ * Local mirror of `AgentMessage` / `AgentToolCall` from
11
+ * `@eigenpal/docx-js-editor`. Inlined here so this subpath has zero
12
+ * runtime/type coupling to the editor package — the shapes are
13
+ * structurally identical, so values flow either way without casting.
14
+ */
15
+ interface AgentToolCall {
16
+ id: string;
17
+ name: string;
18
+ input?: unknown;
19
+ result?: string;
20
+ error?: string;
21
+ status: 'running' | 'done' | 'error';
22
+ }
23
+ interface AgentMessage {
24
+ id: string;
25
+ role: 'user' | 'assistant';
26
+ text: string;
27
+ toolCalls?: AgentToolCall[];
28
+ status?: 'streaming' | 'done';
29
+ }
30
+ /**
31
+ * Minimal structural shape of a Vercel AI SDK `UIMessage` — keeps the
32
+ * `ai` package as a peer dep, not a runtime dep.
33
+ */
34
+ interface AiSdkUIMessage {
35
+ id: string;
36
+ role: 'user' | 'assistant' | 'system';
37
+ parts?: ReadonlyArray<{
38
+ type: string;
39
+ text?: string;
40
+ toolCallId?: string;
41
+ state?: string;
42
+ input?: unknown;
43
+ output?: unknown;
44
+ errorText?: string;
45
+ }>;
46
+ }
47
+ /**
48
+ * Adapt AI SDK's `UIMessage[]` (from `useChat`) to the `AgentMessage[]`
49
+ * shape `<AgentChatLog>` consumes.
50
+ *
51
+ * @param uiMessages - the `messages` array from `useChat`
52
+ * @param status - the `status` from `useChat`. The last assistant
53
+ * message is marked `streaming` while the chat is still in flight.
54
+ *
55
+ * @example
56
+ * ```tsx
57
+ * const chat = useChat({ ... });
58
+ * const messages = useMemo(
59
+ * () => toAgentMessages(chat.messages, chat.status),
60
+ * [chat.messages, chat.status]
61
+ * );
62
+ * return <AgentChatLog messages={messages} />;
63
+ * ```
64
+ */
65
+ declare function toAgentMessages(uiMessages: ReadonlyArray<AiSdkUIMessage>, status: string): AgentMessage[];
66
+
67
+ export { type AgentMessage, type AgentToolCall, toAgentMessages };
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Vercel AI SDK adapter (React side) — opt-in.
3
+ *
4
+ * Use this only if you're driving the chat with `useChat` from
5
+ * `@ai-sdk/react`. The library's `<AgentChatLog>` consumes a flat
6
+ * `AgentMessage[]` shape; AI SDK's `useChat` produces `UIMessage[]`
7
+ * with structured `parts`. `toAgentMessages()` is the bridge.
8
+ */
9
+ /**
10
+ * Local mirror of `AgentMessage` / `AgentToolCall` from
11
+ * `@eigenpal/docx-js-editor`. Inlined here so this subpath has zero
12
+ * runtime/type coupling to the editor package — the shapes are
13
+ * structurally identical, so values flow either way without casting.
14
+ */
15
+ interface AgentToolCall {
16
+ id: string;
17
+ name: string;
18
+ input?: unknown;
19
+ result?: string;
20
+ error?: string;
21
+ status: 'running' | 'done' | 'error';
22
+ }
23
+ interface AgentMessage {
24
+ id: string;
25
+ role: 'user' | 'assistant';
26
+ text: string;
27
+ toolCalls?: AgentToolCall[];
28
+ status?: 'streaming' | 'done';
29
+ }
30
+ /**
31
+ * Minimal structural shape of a Vercel AI SDK `UIMessage` — keeps the
32
+ * `ai` package as a peer dep, not a runtime dep.
33
+ */
34
+ interface AiSdkUIMessage {
35
+ id: string;
36
+ role: 'user' | 'assistant' | 'system';
37
+ parts?: ReadonlyArray<{
38
+ type: string;
39
+ text?: string;
40
+ toolCallId?: string;
41
+ state?: string;
42
+ input?: unknown;
43
+ output?: unknown;
44
+ errorText?: string;
45
+ }>;
46
+ }
47
+ /**
48
+ * Adapt AI SDK's `UIMessage[]` (from `useChat`) to the `AgentMessage[]`
49
+ * shape `<AgentChatLog>` consumes.
50
+ *
51
+ * @param uiMessages - the `messages` array from `useChat`
52
+ * @param status - the `status` from `useChat`. The last assistant
53
+ * message is marked `streaming` while the chat is still in flight.
54
+ *
55
+ * @example
56
+ * ```tsx
57
+ * const chat = useChat({ ... });
58
+ * const messages = useMemo(
59
+ * () => toAgentMessages(chat.messages, chat.status),
60
+ * [chat.messages, chat.status]
61
+ * );
62
+ * return <AgentChatLog messages={messages} />;
63
+ * ```
64
+ */
65
+ declare function toAgentMessages(uiMessages: ReadonlyArray<AiSdkUIMessage>, status: string): AgentMessage[];
66
+
67
+ export { type AgentMessage, type AgentToolCall, toAgentMessages };
@@ -0,0 +1 @@
1
+ 'use strict';function g(s,r){return s.map((e,a)=>{let o="",n=[];for(let t of e.parts??[])if(t.type==="text")o+=t.text??"";else if(t.type.startsWith("tool-")){let u=t.state==="output-available"?"done":t.state==="output-error"?"error":"running";n.push({id:t.toolCallId??`${e.id}-tc-${n.length}`,name:t.type.slice(5),input:t.input,result:typeof t.output=="string"?t.output:void 0,error:t.errorText,status:u});}let i=a===s.length-1,l=e.role==="assistant"&&i&&(r==="streaming"||r==="submitted");return {id:e.id,role:e.role==="user"?"user":"assistant",text:o,toolCalls:n.length>0?n:void 0,status:l?"streaming":"done"}})}exports.toAgentMessages=g;
@@ -0,0 +1 @@
1
+ function g(s,r){return s.map((e,a)=>{let o="",n=[];for(let t of e.parts??[])if(t.type==="text")o+=t.text??"";else if(t.type.startsWith("tool-")){let u=t.state==="output-available"?"done":t.state==="output-error"?"error":"running";n.push({id:t.toolCallId??`${e.id}-tc-${n.length}`,name:t.type.slice(5),input:t.input,result:typeof t.output=="string"?t.output:void 0,error:t.errorText,status:u});}let i=a===s.length-1,l=e.role==="assistant"&&i&&(r==="streaming"||r==="submitted");return {id:e.id,role:e.role==="user"?"user":"assistant",text:o,toolCalls:n.length>0?n:void 0,status:l?"streaming":"done"}})}export{g as toAgentMessages};
@@ -0,0 +1,42 @@
1
+ import { Tool } from 'ai';
2
+
3
+ /**
4
+ * Vercel AI SDK adapter (server side) — opt-in.
5
+ *
6
+ * The core toolkit is runtime-agnostic. Use this entry only if you're
7
+ * wiring `streamText` / `generateText` from `ai` in your route handler.
8
+ * For LangChain / Anthropic SDK / OpenAI direct, import
9
+ * `getToolSchemas` from `@eigenpal/docx-editor-agents/server` and
10
+ * shape it however your runtime expects.
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * import { getAiSdkTools } from '@eigenpal/docx-editor-agents/ai-sdk/server';
15
+ * import { streamText, stepCountIs, convertToModelMessages } from 'ai';
16
+ *
17
+ * const tools = getAiSdkTools();
18
+ *
19
+ * export async function POST(req: Request) {
20
+ * const { messages } = await req.json();
21
+ * const result = streamText({
22
+ * model: 'openai/gpt-4o',
23
+ * messages: await convertToModelMessages(messages),
24
+ * tools,
25
+ * stopWhen: stepCountIs(12),
26
+ * });
27
+ * return result.toUIMessageStreamResponse();
28
+ * }
29
+ * ```
30
+ */
31
+
32
+ /**
33
+ * Get tool schemas in Vercel AI SDK shape (`{ [name]: Tool }`). Pass
34
+ * directly to `streamText({ tools })`. No `execute` is set, so AI SDK
35
+ * forwards each tool call to the client's `useChat({ onToolCall })`
36
+ * handler — wire that to `useDocxAgentTools().executeToolCall` from
37
+ * `@eigenpal/docx-editor-agents/ai-sdk/react` or
38
+ * `@eigenpal/docx-editor-agents/react`.
39
+ */
40
+ declare function getAiSdkTools(): Record<string, Tool>;
41
+
42
+ export { getAiSdkTools };
@@ -0,0 +1,42 @@
1
+ import { Tool } from 'ai';
2
+
3
+ /**
4
+ * Vercel AI SDK adapter (server side) — opt-in.
5
+ *
6
+ * The core toolkit is runtime-agnostic. Use this entry only if you're
7
+ * wiring `streamText` / `generateText` from `ai` in your route handler.
8
+ * For LangChain / Anthropic SDK / OpenAI direct, import
9
+ * `getToolSchemas` from `@eigenpal/docx-editor-agents/server` and
10
+ * shape it however your runtime expects.
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * import { getAiSdkTools } from '@eigenpal/docx-editor-agents/ai-sdk/server';
15
+ * import { streamText, stepCountIs, convertToModelMessages } from 'ai';
16
+ *
17
+ * const tools = getAiSdkTools();
18
+ *
19
+ * export async function POST(req: Request) {
20
+ * const { messages } = await req.json();
21
+ * const result = streamText({
22
+ * model: 'openai/gpt-4o',
23
+ * messages: await convertToModelMessages(messages),
24
+ * tools,
25
+ * stopWhen: stepCountIs(12),
26
+ * });
27
+ * return result.toUIMessageStreamResponse();
28
+ * }
29
+ * ```
30
+ */
31
+
32
+ /**
33
+ * Get tool schemas in Vercel AI SDK shape (`{ [name]: Tool }`). Pass
34
+ * directly to `streamText({ tools })`. No `execute` is set, so AI SDK
35
+ * forwards each tool call to the client's `useChat({ onToolCall })`
36
+ * handler — wire that to `useDocxAgentTools().executeToolCall` from
37
+ * `@eigenpal/docx-editor-agents/ai-sdk/react` or
38
+ * `@eigenpal/docx-editor-agents/react`.
39
+ */
40
+ declare function getAiSdkTools(): Record<string, Tool>;
41
+
42
+ export { getAiSdkTools };
@@ -0,0 +1 @@
1
+ 'use strict';var chunkGZG3RJXW_js=require('../chunk-GZG3RJXW.js'),ai=require('ai');function n(){return Object.fromEntries(chunkGZG3RJXW_js.a.map(o=>[o.name,{description:o.description,inputSchema:ai.jsonSchema(o.inputSchema)}]))}exports.getAiSdkTools=n;
@@ -0,0 +1 @@
1
+ import {a}from'../chunk-S2PFUY23.mjs';import {jsonSchema}from'ai';function n(){return Object.fromEntries(a.map(o=>[o.name,{description:o.description,inputSchema:jsonSchema(o.inputSchema)}]))}export{n as getAiSdkTools};
package/dist/bridge.d.mts CHANGED
@@ -1,11 +1 @@
1
- /**
2
- * Editor ref bridge — optional client-side integration.
3
- *
4
- * Separate entry point: import from '@eigenpal/docx-editor-agents/bridge'
5
- * This file may import React/ProseMirror — NOT included in the main headless bundle.
6
- *
7
- * TODO: Implement in task 9.1
8
- */
9
- declare function createReviewBridge(_editorRef: unknown): Record<string, unknown>;
10
-
11
- export { createReviewBridge };
1
+ export { A as AgentToolDefinition, a as AgentToolResult, n as ContentChangeEvent, v as EditorBridge, E as EditorRefLike, o as SelectionChangeEvent, U as UseAgentChatOptions, c as UseAgentChatReturn, r as agentTools, w as createEditorBridge, s as createReviewerBridge, t as executeToolCall, g as getToolSchemas, u as useAgentChat } from './server-B7dGWiLh.mjs';
package/dist/bridge.d.ts CHANGED
@@ -1,11 +1 @@
1
- /**
2
- * Editor ref bridge — optional client-side integration.
3
- *
4
- * Separate entry point: import from '@eigenpal/docx-editor-agents/bridge'
5
- * This file may import React/ProseMirror — NOT included in the main headless bundle.
6
- *
7
- * TODO: Implement in task 9.1
8
- */
9
- declare function createReviewBridge(_editorRef: unknown): Record<string, unknown>;
10
-
11
- export { createReviewBridge };
1
+ export { A as AgentToolDefinition, a as AgentToolResult, n as ContentChangeEvent, v as EditorBridge, E as EditorRefLike, o as SelectionChangeEvent, U as UseAgentChatOptions, c as UseAgentChatReturn, r as agentTools, w as createEditorBridge, s as createReviewerBridge, t as executeToolCall, g as getToolSchemas, u as useAgentChat } from './server-B7dGWiLh.js';
package/dist/bridge.js CHANGED
@@ -1 +1 @@
1
- 'use strict';function n(e){throw new Error("createReviewBridge is not yet implemented")}exports.createReviewBridge=n;
1
+ 'use strict';var chunk2MJYWSD3_js=require('./chunk-2MJYWSD3.js'),chunkDOKVZKIP_js=require('./chunk-DOKVZKIP.js'),chunkGZG3RJXW_js=require('./chunk-GZG3RJXW.js');Object.defineProperty(exports,"createEditorBridge",{enumerable:true,get:function(){return chunk2MJYWSD3_js.b}});Object.defineProperty(exports,"useAgentChat",{enumerable:true,get:function(){return chunk2MJYWSD3_js.a}});Object.defineProperty(exports,"createReviewerBridge",{enumerable:true,get:function(){return chunkDOKVZKIP_js.m}});Object.defineProperty(exports,"agentTools",{enumerable:true,get:function(){return chunkGZG3RJXW_js.a}});Object.defineProperty(exports,"executeToolCall",{enumerable:true,get:function(){return chunkGZG3RJXW_js.b}});Object.defineProperty(exports,"getToolSchemas",{enumerable:true,get:function(){return chunkGZG3RJXW_js.d}});
package/dist/bridge.mjs CHANGED
@@ -1 +1 @@
1
- function n(e){throw new Error("createReviewBridge is not yet implemented")}export{n as createReviewBridge};
1
+ export{b as createEditorBridge,a as useAgentChat}from'./chunk-AITWKLUF.mjs';export{m as createReviewerBridge}from'./chunk-4OJA3FMM.mjs';export{a as agentTools,b as executeToolCall,d as getToolSchemas}from'./chunk-S2PFUY23.mjs';