agentic-documents 0.3.0 → 0.5.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/CHANGELOG.md +48 -0
- package/README.md +143 -5
- package/dist/{chunk-YBBQNCMC.js → chunk-6QOYS63L.js} +14 -189
- package/dist/{chunk-TFCRPEEM.js → chunk-7R7ZPPRF.js} +1 -1
- package/dist/{chunk-R5XUTBC3.js → chunk-E7B6T5WN.js} +29 -8
- package/dist/{chunk-IU3YIQ4Y.js → chunk-FX755I7V.js} +30 -8
- package/dist/{chunk-GYQFTF2E.js → chunk-L6W2AQUF.js} +15 -146
- package/dist/chunk-S6MQDWFK.js +150 -0
- package/dist/chunk-UPEGODUV.js +195 -0
- package/dist/{chunk-FZA5LQV3.js → chunk-YNHZCIGO.js} +86 -11
- package/dist/cli.js +8 -6
- package/dist/client.d.ts +188 -0
- package/dist/client.js +121 -0
- package/dist/{service-BKkxEnaJ.d.ts → doc-mJPajXEQ.d.ts} +5 -56
- package/dist/export-Cwx276UP.d.ts +17 -0
- package/dist/formats.d.ts +49 -0
- package/dist/formats.js +234 -0
- package/dist/index.d.ts +7 -99
- package/dist/index.js +11 -7
- package/dist/live-worker.d.ts +2 -1
- package/dist/live-worker.js +6 -4
- package/dist/live.d.ts +6 -1
- package/dist/live.js +4 -3
- package/dist/{mcp-P2C75IIG.js → mcp-ESSEG46C.js} +4 -2
- package/dist/review-Hgq8OgvD.d.ts +85 -0
- package/dist/service-CqKE9DKz.d.ts +91 -0
- package/dist/ui/_headers +4 -0
- package/dist/ui/app.css +1 -0
- package/dist/ui/app.js +270 -0
- package/dist/ui/assets/inter-latin-ext-wght-normal-HA22NDSG.woff2 +0 -0
- package/dist/ui/assets/inter-latin-wght-normal-NRMW37G5.woff2 +0 -0
- package/dist/ui/assets/jetbrains-mono-latin-ext-wght-normal-TSLVDSQR.woff2 +0 -0
- package/dist/ui/assets/jetbrains-mono-latin-wght-normal-VBS632QH.woff2 +0 -0
- package/dist/ui/index.html +13 -0
- package/dist/worker.d.ts +2 -1
- package/dist/worker.js +6 -4
- package/llms.txt +2 -0
- package/package.json +23 -3
package/dist/formats.js
ADDED
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DATA_IMAGE,
|
|
3
|
+
exportDoc,
|
|
4
|
+
fromBase64,
|
|
5
|
+
safeHref,
|
|
6
|
+
toBase64
|
|
7
|
+
} from "./chunk-L6W2AQUF.js";
|
|
8
|
+
import {
|
|
9
|
+
AgenticDoc,
|
|
10
|
+
DocError,
|
|
11
|
+
checkAuthor
|
|
12
|
+
} from "./chunk-6QOYS63L.js";
|
|
13
|
+
|
|
14
|
+
// src/formats.ts
|
|
15
|
+
import { DEFAULT_LIMITS, FormatError, readDocx, readOdt, writeDocx, writeOdt } from "agentic-formats";
|
|
16
|
+
var IMPORT_FORMATS = ["md", "odt", "docx"];
|
|
17
|
+
var FILE_FORMATS = ["md", "html", "odt", "docx"];
|
|
18
|
+
var DOCX_MIME = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
|
|
19
|
+
var CONTENT_TYPES = {
|
|
20
|
+
md: "text/markdown; charset=utf-8",
|
|
21
|
+
html: "text/html; charset=utf-8",
|
|
22
|
+
odt: "application/vnd.oasis.opendocument.text",
|
|
23
|
+
docx: DOCX_MIME
|
|
24
|
+
};
|
|
25
|
+
var PM_MARKS = { bold: "bold", italic: "italic", strike: "strike", code: "code" };
|
|
26
|
+
var MODEL_IMAGE = /^image\/(png|jpeg|gif)$/;
|
|
27
|
+
var TASK = ["\u2610 ", "\u2611 "];
|
|
28
|
+
var warner = () => {
|
|
29
|
+
const seen = /* @__PURE__ */ new Set();
|
|
30
|
+
return { warn: (line) => void seen.add(line), list: () => [...seen] };
|
|
31
|
+
};
|
|
32
|
+
function toTextDoc(blocks, title) {
|
|
33
|
+
const { warn, list } = warner();
|
|
34
|
+
const inlines = (nodes = [], extra = []) => nodes.flatMap((node) => {
|
|
35
|
+
if (node.type === "hardBreak") return [{ type: "break" }];
|
|
36
|
+
if (node.type === "image") {
|
|
37
|
+
const alt = typeof node.attrs?.alt === "string" ? node.attrs.alt : "";
|
|
38
|
+
const found = DATA_IMAGE.exec(String(node.attrs?.src ?? "").trim());
|
|
39
|
+
if (found && MODEL_IMAGE.test(found[1].toLowerCase())) {
|
|
40
|
+
try {
|
|
41
|
+
return [{ type: "image", data: fromBase64(found[2]), mime: found[1].toLowerCase(), ...alt ? { alt } : {} }];
|
|
42
|
+
} catch {
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
warn("an image that is not an embedded PNG, JPEG, or GIF is kept as its alt text");
|
|
46
|
+
return [{ type: "text", text: `[image${alt ? `: ${alt}` : ""}]` }];
|
|
47
|
+
}
|
|
48
|
+
if (!node.text) return [];
|
|
49
|
+
const marks = [...extra, ...(node.marks ?? []).flatMap((m) => PM_MARKS[m.type] ? [PM_MARKS[m.type]] : [])];
|
|
50
|
+
const href = safeHref((node.marks ?? []).find((m) => m.type === "link")?.attrs?.href);
|
|
51
|
+
return [{ type: "text", text: node.text, ...marks.length ? { marks } : {}, ...href ? { href } : {} }];
|
|
52
|
+
});
|
|
53
|
+
const listOf = (node) => {
|
|
54
|
+
const items = (node.content ?? []).map((item) => {
|
|
55
|
+
const [first, ...rest] = item.content ?? [];
|
|
56
|
+
const content = inlines(first?.content);
|
|
57
|
+
if (item.type === "taskItem") content.unshift({ type: "text", text: TASK[item.attrs?.checked ? 1 : 0] });
|
|
58
|
+
let sublist;
|
|
59
|
+
for (const child of rest) {
|
|
60
|
+
if (!sublist && /^(bulletList|orderedList|taskList)$/.test(child.type)) sublist = listOf(child);
|
|
61
|
+
else {
|
|
62
|
+
warn("a list item with more than one paragraph is kept as one paragraph with line breaks");
|
|
63
|
+
for (const b of toBlocks(child)) content.push({ type: "break" }, ...textOfBlock(b));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return { content, ...sublist ? { sublist } : {} };
|
|
67
|
+
});
|
|
68
|
+
if (node.type === "orderedList" && Number(node.attrs?.start ?? 1) !== 1) warn("a numbered list that does not start at 1 starts at 1");
|
|
69
|
+
return { type: "list", ordered: node.type === "orderedList", items };
|
|
70
|
+
};
|
|
71
|
+
const textOfBlock = (b) => b.type === "list" ? b.items.flatMap((i, n) => [...n ? [{ type: "break" }] : [], ...i.content]) : b.type === "table" ? b.rows.flat(2).flatMap(textOfBlock) : b.content;
|
|
72
|
+
const toBlocks = (node) => {
|
|
73
|
+
switch (node.type) {
|
|
74
|
+
case "paragraph":
|
|
75
|
+
return [{ type: "paragraph", content: inlines(node.content) }];
|
|
76
|
+
case "heading": {
|
|
77
|
+
const level = Math.min(6, Math.max(1, Math.trunc(Number(node.attrs?.level)) || 1));
|
|
78
|
+
return [{ type: "heading", level, content: inlines(node.content) }];
|
|
79
|
+
}
|
|
80
|
+
case "blockquote":
|
|
81
|
+
warn("a quote is kept as plain paragraphs");
|
|
82
|
+
return (node.content ?? []).flatMap(toBlocks);
|
|
83
|
+
case "codeBlock": {
|
|
84
|
+
warn("a code block is kept as a paragraph of code text");
|
|
85
|
+
const lines = (node.content ?? []).map((t) => t.text ?? "").join("").split("\n");
|
|
86
|
+
return [{ type: "paragraph", content: lines.flatMap((line, i) => [...i ? [{ type: "break" }] : [], ...line ? [{ type: "text", text: line, marks: ["code"] }] : []]) }];
|
|
87
|
+
}
|
|
88
|
+
case "horizontalRule":
|
|
89
|
+
warn("a horizontal rule is left out");
|
|
90
|
+
return [];
|
|
91
|
+
case "bulletList":
|
|
92
|
+
case "orderedList":
|
|
93
|
+
case "taskList":
|
|
94
|
+
return [listOf(node)];
|
|
95
|
+
case "table": {
|
|
96
|
+
const rows = node.content ?? [];
|
|
97
|
+
const header = rows.length > 0 && (rows[0].content ?? []).length > 0 && (rows[0].content ?? []).every((c) => c.type === "tableHeader");
|
|
98
|
+
return [{ type: "table", ...header ? { header: true } : {}, rows: rows.map((r) => (r.content ?? []).map((c) => (c.content ?? []).flatMap(toBlocks))) }];
|
|
99
|
+
}
|
|
100
|
+
default:
|
|
101
|
+
warn(`a ${node.type} block is left out`);
|
|
102
|
+
return [];
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
const model = { ...title ? { title } : {}, blocks: blocks.flatMap(toBlocks) };
|
|
106
|
+
return { model, warnings: list() };
|
|
107
|
+
}
|
|
108
|
+
function fromTextDoc(model) {
|
|
109
|
+
const { warn, list } = warner();
|
|
110
|
+
const text = (value, marks = []) => value ? [{ type: "text", text: value, ...marks.length ? { marks } : {} }] : [];
|
|
111
|
+
const inlines = (nodes) => nodes.flatMap((node) => {
|
|
112
|
+
switch (node.type) {
|
|
113
|
+
case "break":
|
|
114
|
+
return [{ type: "hardBreak" }];
|
|
115
|
+
case "image":
|
|
116
|
+
if (!MODEL_IMAGE.test(node.mime)) {
|
|
117
|
+
warn("an image that is not a PNG, JPEG, or GIF is kept as its alt text");
|
|
118
|
+
return text(`[image${node.alt ? `: ${node.alt}` : ""}]`);
|
|
119
|
+
}
|
|
120
|
+
return [{ type: "image", attrs: { src: `data:${node.mime};base64,${toBase64(node.data)}`, alt: node.alt ?? null, title: null } }];
|
|
121
|
+
case "footnote":
|
|
122
|
+
warn("a footnote is kept as text in brackets");
|
|
123
|
+
return [...text(" ["), ...inlines(node.content), ...text("]")];
|
|
124
|
+
case "text": {
|
|
125
|
+
const marks = [];
|
|
126
|
+
for (const mark of node.marks ?? []) {
|
|
127
|
+
if (mark === "bold" || mark === "italic" || mark === "strike" || mark === "code") marks.push({ type: mark });
|
|
128
|
+
else warn(`the ${mark} mark is left out; the text stays`);
|
|
129
|
+
}
|
|
130
|
+
const href = node.href === void 0 ? void 0 : safeHref(node.href);
|
|
131
|
+
if (node.href !== void 0 && !href) warn("a link that is not a web, mail, or relative address is left out; the text stays");
|
|
132
|
+
if (href) marks.push({ type: "link", attrs: { href } });
|
|
133
|
+
if (marks.length > 1 && marks.some((m) => m.type === "code")) {
|
|
134
|
+
warn("a mark or a link on code text is left out; the text stays as code");
|
|
135
|
+
return text(node.text, [{ type: "code" }]);
|
|
136
|
+
}
|
|
137
|
+
return text(node.text, marks);
|
|
138
|
+
}
|
|
139
|
+
default:
|
|
140
|
+
return [];
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
const paragraph = (content) => ({ type: "paragraph", ...content.length ? { content: inlines(content) } : {} });
|
|
144
|
+
const toNodes = (block) => {
|
|
145
|
+
switch (block.type) {
|
|
146
|
+
case "heading":
|
|
147
|
+
return [{ type: "heading", attrs: { level: block.level }, content: inlines(block.content) }];
|
|
148
|
+
case "paragraph":
|
|
149
|
+
return [paragraph(block.content)];
|
|
150
|
+
case "list":
|
|
151
|
+
return [
|
|
152
|
+
{
|
|
153
|
+
type: block.ordered ? "orderedList" : "bulletList",
|
|
154
|
+
...block.ordered ? { attrs: { start: 1, type: null } } : {},
|
|
155
|
+
content: block.items.map((item) => ({ type: "listItem", content: [paragraph(item.content), ...item.sublist ? toNodes(item.sublist) : []] }))
|
|
156
|
+
}
|
|
157
|
+
];
|
|
158
|
+
case "table": {
|
|
159
|
+
const rows = block.rows.filter((r) => r.length > 0);
|
|
160
|
+
if (rows.length === 0) return [];
|
|
161
|
+
return [
|
|
162
|
+
{
|
|
163
|
+
type: "table",
|
|
164
|
+
content: rows.map((row, i) => ({
|
|
165
|
+
type: "tableRow",
|
|
166
|
+
content: row.map((cell) => {
|
|
167
|
+
const content = cell.flatMap(toNodes);
|
|
168
|
+
return { type: block.header && i === 0 ? "tableHeader" : "tableCell", attrs: { colspan: 1, rowspan: 1, colwidth: null }, content: content.length ? content : [{ type: "paragraph" }] };
|
|
169
|
+
})
|
|
170
|
+
}))
|
|
171
|
+
}
|
|
172
|
+
];
|
|
173
|
+
}
|
|
174
|
+
default:
|
|
175
|
+
return [];
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
const prune = (node) => node.content ? { ...node, content: node.content.filter((n) => n.type !== "text" || n.text).map(prune) } : node;
|
|
179
|
+
const blocks = model.blocks.flatMap(toNodes).map(prune);
|
|
180
|
+
return { blocks, ...model.title ? { title: model.title } : {}, warnings: list() };
|
|
181
|
+
}
|
|
182
|
+
var formatFailure = (error) => {
|
|
183
|
+
if (error instanceof FormatError) throw new DocError("INVALID_INPUT", `the file was refused (${error.code}): ${error.message}`);
|
|
184
|
+
throw error;
|
|
185
|
+
};
|
|
186
|
+
function importFile(input, format, author, options = {}) {
|
|
187
|
+
checkAuthor(author);
|
|
188
|
+
if (!IMPORT_FORMATS.includes(format)) throw new DocError("INVALID_INPUT", `import format must be one of ${IMPORT_FORMATS.join(", ")}`);
|
|
189
|
+
const maxBytes = options.limits?.maxInputBytes ?? DEFAULT_LIMITS.maxInputBytes;
|
|
190
|
+
if (format === "md") {
|
|
191
|
+
let markdown;
|
|
192
|
+
try {
|
|
193
|
+
markdown = typeof input === "string" ? input : new TextDecoder("utf-8", { fatal: true }).decode(input);
|
|
194
|
+
} catch {
|
|
195
|
+
throw new DocError("INVALID_INPUT", "a md file must be UTF-8 text");
|
|
196
|
+
}
|
|
197
|
+
if (new TextEncoder().encode(markdown).length > maxBytes) throw new DocError("INVALID_INPUT", `the file is larger than ${maxBytes} bytes`);
|
|
198
|
+
return { doc: AgenticDoc.fromMarkdown(markdown, author, options.doc), warnings: [] };
|
|
199
|
+
}
|
|
200
|
+
if (!(input instanceof Uint8Array)) throw new DocError("INVALID_INPUT", `a ${format} file must be bytes, not text`);
|
|
201
|
+
let read;
|
|
202
|
+
try {
|
|
203
|
+
read = format === "odt" ? readOdt(input, options.limits) : readDocx(input, options.limits);
|
|
204
|
+
} catch (error) {
|
|
205
|
+
return formatFailure(error);
|
|
206
|
+
}
|
|
207
|
+
const mapped = fromTextDoc(read.model);
|
|
208
|
+
return {
|
|
209
|
+
doc: AgenticDoc.fromBlocks(mapped.blocks, author, options.doc),
|
|
210
|
+
...mapped.title ? { title: mapped.title } : {},
|
|
211
|
+
warnings: [...read.warnings, ...mapped.warnings]
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
function exportFile(doc, format, options = {}) {
|
|
215
|
+
if (!FILE_FORMATS.includes(format)) throw new DocError("INVALID_INPUT", `export format must be one of ${FILE_FORMATS.join(", ")}`);
|
|
216
|
+
if (format === "md" || format === "html") return { format, contentType: CONTENT_TYPES[format], data: exportDoc(doc, format, options).data, warnings: [] };
|
|
217
|
+
const { model, warnings } = toTextDoc(doc.toJSON().content ?? [], options.title);
|
|
218
|
+
let written;
|
|
219
|
+
try {
|
|
220
|
+
written = format === "odt" ? writeOdt(model) : writeDocx(model);
|
|
221
|
+
} catch (error) {
|
|
222
|
+
return formatFailure(error);
|
|
223
|
+
}
|
|
224
|
+
return { format, contentType: CONTENT_TYPES[format], data: written.bytes, warnings: [...warnings, ...written.warnings] };
|
|
225
|
+
}
|
|
226
|
+
export {
|
|
227
|
+
DOCX_MIME,
|
|
228
|
+
FILE_FORMATS,
|
|
229
|
+
IMPORT_FORMATS,
|
|
230
|
+
exportFile,
|
|
231
|
+
fromTextDoc,
|
|
232
|
+
importFile,
|
|
233
|
+
toTextDoc
|
|
234
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,94 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export {
|
|
1
|
+
import { c as Principal, b as PmNode } from './doc-mJPajXEQ.js';
|
|
2
|
+
export { a as AgenticDoc, A as Anchor, d as AnchorTarget, D as DocOptions, E as EditEntry, e as PRINCIPAL_KINDS, P as PrincipalKind, S as Section, f as SectionTarget, T as Target, g as TextTarget, h as blocksToMarkdown, m as markdownToBlocks, p as parseAuthor } from './doc-mJPajXEQ.js';
|
|
3
3
|
import { Schema } from 'prosemirror-model';
|
|
4
|
+
export { a as DocError, D as DocErrorCode, M as Message, S as Suggestion, b as Suggestions, T as Thread, c as Threads } from './review-Hgq8OgvD.js';
|
|
4
5
|
import { z } from 'zod';
|
|
6
|
+
import { a as DocAccess } from './service-CqKE9DKz.js';
|
|
7
|
+
export { b as DocInfo, c as DocsPorts, d as DocsService } from './service-CqKE9DKz.js';
|
|
8
|
+
export { b as EXPORT_FORMATS, E as ExportFormat, a as ExportOptions, c as ExportResult, e as exportDoc } from './export-Cwx276UP.js';
|
|
5
9
|
import { AdapterOptions, Blobs, Store } from 'agentic-identity';
|
|
6
10
|
import 'yjs';
|
|
7
11
|
|
|
8
|
-
type DocErrorCode = "NO_MATCH" | "AMBIGUOUS_MATCH" | "STALE_ANCHOR" | "NO_SECTION" | "INVALID_AUTHOR" | "INVALID_INPUT" | "NO_THREAD" | "NO_SUGGESTION" | "ALREADY_DECIDED" | "FORBIDDEN" | "NO_DOCUMENT" | "DOCUMENT_EXISTS" | "LOCKED" | "CONFLICT" | "MISCONFIGURED";
|
|
9
|
-
declare class DocError extends Error {
|
|
10
|
-
readonly code: DocErrorCode;
|
|
11
|
-
readonly matches: {
|
|
12
|
-
occurrence: number;
|
|
13
|
-
context: string;
|
|
14
|
-
}[];
|
|
15
|
-
constructor(code: DocErrorCode, message: string, matches?: {
|
|
16
|
-
occurrence: number;
|
|
17
|
-
context: string;
|
|
18
|
-
}[]);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
12
|
declare const schema: Schema<string, string>;
|
|
22
13
|
|
|
23
|
-
interface Message {
|
|
24
|
-
id: string;
|
|
25
|
-
at: string;
|
|
26
|
-
author: Principal;
|
|
27
|
-
body: string;
|
|
28
|
-
}
|
|
29
|
-
interface Thread {
|
|
30
|
-
id: string;
|
|
31
|
-
anchor: Anchor;
|
|
32
|
-
status: "open" | "resolved";
|
|
33
|
-
resolvedBy?: Principal;
|
|
34
|
-
resolvedAt?: string;
|
|
35
|
-
messages: Message[];
|
|
36
|
-
/** The anchored text no longer matches what the comment was made on. */
|
|
37
|
-
stale: boolean;
|
|
38
|
-
/** The text now under the anchor, or null when it is gone. */
|
|
39
|
-
currentText: string | null;
|
|
40
|
-
}
|
|
41
|
-
interface Suggestion {
|
|
42
|
-
id: string;
|
|
43
|
-
kind: "insert" | "replace" | "delete";
|
|
44
|
-
at?: "before" | "after";
|
|
45
|
-
anchor: Anchor;
|
|
46
|
-
replacement: string;
|
|
47
|
-
author: Principal;
|
|
48
|
-
createdAt: string;
|
|
49
|
-
status: "pending" | "accepted" | "rejected";
|
|
50
|
-
decidedBy?: Principal;
|
|
51
|
-
decidedAt?: string;
|
|
52
|
-
stale: boolean;
|
|
53
|
-
}
|
|
54
|
-
/** Comment threads anchored to text. Replies and status merge across replicas. */
|
|
55
|
-
declare class Threads {
|
|
56
|
-
private readonly doc;
|
|
57
|
-
constructor(doc: AgenticDoc);
|
|
58
|
-
private get map();
|
|
59
|
-
private get;
|
|
60
|
-
private message;
|
|
61
|
-
add(target: TextTarget | AnchorTarget, body: string, author: Principal): string;
|
|
62
|
-
reply(id: string, body: string, author: Principal): void;
|
|
63
|
-
private setStatus;
|
|
64
|
-
resolve(id: string, author: Principal): void;
|
|
65
|
-
reopen(id: string, author: Principal): void;
|
|
66
|
-
list(filter?: {
|
|
67
|
-
status?: Thread["status"];
|
|
68
|
-
}): Thread[];
|
|
69
|
-
}
|
|
70
|
-
/** Suggested edits. Nothing changes in the text until a person, or an agent with the accept scope, accepts one. */
|
|
71
|
-
declare class Suggestions {
|
|
72
|
-
private readonly doc;
|
|
73
|
-
constructor(doc: AgenticDoc);
|
|
74
|
-
private get map();
|
|
75
|
-
private get;
|
|
76
|
-
private read;
|
|
77
|
-
/** An `at` on the target means insert. An empty replacement means delete. */
|
|
78
|
-
add(target: TextTarget | AnchorTarget, replacement: string, author: Principal): string;
|
|
79
|
-
private decide;
|
|
80
|
-
/**
|
|
81
|
-
* Apply the edit as the accepting author. The check and the edit share one transaction. Accepting
|
|
82
|
-
* an accepted suggestion changes nothing. A stale anchor throws and the suggestion stays pending.
|
|
83
|
-
*/
|
|
84
|
-
accept(id: string, author: Principal): void;
|
|
85
|
-
/** Same right as accept. A person can withdraw their own suggestion. An agent can never decide its own. */
|
|
86
|
-
reject(id: string, author: Principal): void;
|
|
87
|
-
list(filter?: {
|
|
88
|
-
status?: Suggestion["status"];
|
|
89
|
-
}): Suggestion[];
|
|
90
|
-
}
|
|
91
|
-
|
|
92
14
|
/** One table drives the CLI, the MCP server, and the HTTP API, so they stay the same. */
|
|
93
15
|
interface Command {
|
|
94
16
|
name: string;
|
|
@@ -99,20 +21,6 @@ interface Command {
|
|
|
99
21
|
}
|
|
100
22
|
declare const commands: Command[];
|
|
101
23
|
|
|
102
|
-
declare const EXPORT_FORMATS: readonly ["md", "html", "odt"];
|
|
103
|
-
type ExportFormat = (typeof EXPORT_FORMATS)[number];
|
|
104
|
-
interface ExportOptions {
|
|
105
|
-
/** Keep `<img>` for web and relative sources. Opening the page then asks those hosts for the image. Off by default. */
|
|
106
|
-
remoteImages?: boolean;
|
|
107
|
-
}
|
|
108
|
-
interface ExportResult {
|
|
109
|
-
format: ExportFormat;
|
|
110
|
-
contentType: string;
|
|
111
|
-
encoding: "utf8" | "base64";
|
|
112
|
-
data: string;
|
|
113
|
-
}
|
|
114
|
-
declare function exportDoc(doc: AgenticDoc, format: ExportFormat, options?: ExportOptions): ExportResult;
|
|
115
|
-
|
|
116
24
|
interface FileBlobsOptions extends AdapterOptions {
|
|
117
25
|
/** A lock older than this is taken over. Default 10 seconds. A save takes milliseconds. */
|
|
118
26
|
lockStaleMs?: number;
|
|
@@ -142,4 +50,4 @@ declare const ODT_MIME = "application/vnd.oasis.opendocument.text";
|
|
|
142
50
|
/** Write blocks as an OpenDocument text file (.odt): content, styles, and images as embedded files. */
|
|
143
51
|
declare function exportOdt(blocks: PmNode[]): Uint8Array;
|
|
144
52
|
|
|
145
|
-
export {
|
|
53
|
+
export { type Command, type FileBlobsOptions, ODT_MIME, PmNode, Principal, commands, createFileBlobs, createFileStore, exportOdt, schema };
|
package/dist/index.js
CHANGED
|
@@ -1,28 +1,32 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createFileBlobs,
|
|
3
3
|
createFileStore
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-7R7ZPPRF.js";
|
|
5
5
|
import {
|
|
6
6
|
DocsService
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-YNHZCIGO.js";
|
|
8
|
+
import {
|
|
9
|
+
commands
|
|
10
|
+
} from "./chunk-S6MQDWFK.js";
|
|
8
11
|
import {
|
|
9
12
|
EXPORT_FORMATS,
|
|
10
13
|
ODT_MIME,
|
|
11
|
-
commands,
|
|
12
14
|
exportDoc,
|
|
13
15
|
exportOdt
|
|
14
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-L6W2AQUF.js";
|
|
17
|
+
import {
|
|
18
|
+
Suggestions,
|
|
19
|
+
Threads
|
|
20
|
+
} from "./chunk-UPEGODUV.js";
|
|
15
21
|
import {
|
|
16
22
|
AgenticDoc,
|
|
17
23
|
DocError,
|
|
18
24
|
PRINCIPAL_KINDS,
|
|
19
|
-
Suggestions,
|
|
20
|
-
Threads,
|
|
21
25
|
blocksToMarkdown,
|
|
22
26
|
markdownToBlocks,
|
|
23
27
|
parseAuthor,
|
|
24
28
|
schema
|
|
25
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-6QOYS63L.js";
|
|
26
30
|
export {
|
|
27
31
|
AgenticDoc,
|
|
28
32
|
DocError,
|
package/dist/live-worker.d.ts
CHANGED
|
@@ -5,8 +5,9 @@ import 'hono';
|
|
|
5
5
|
import 'agentic-identity';
|
|
6
6
|
import 'agentic-identity/cloudflare';
|
|
7
7
|
import 'agentic-realtime';
|
|
8
|
-
import './
|
|
8
|
+
import './doc-mJPajXEQ.js';
|
|
9
9
|
import 'yjs';
|
|
10
|
+
import './service-CqKE9DKz.js';
|
|
10
11
|
|
|
11
12
|
type Namespace = Parameters<typeof createDurableObjectRealtime>[0];
|
|
12
13
|
interface LiveEnv extends WorkerEnv {
|
package/dist/live-worker.js
CHANGED
|
@@ -2,12 +2,14 @@ import {
|
|
|
2
2
|
createApp,
|
|
3
3
|
regionOf,
|
|
4
4
|
serviceFor
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import "./chunk-
|
|
7
|
-
import "./chunk-
|
|
5
|
+
} from "./chunk-FX755I7V.js";
|
|
6
|
+
import "./chunk-YNHZCIGO.js";
|
|
7
|
+
import "./chunk-S6MQDWFK.js";
|
|
8
|
+
import "./chunk-L6W2AQUF.js";
|
|
9
|
+
import "./chunk-UPEGODUV.js";
|
|
8
10
|
import {
|
|
9
11
|
DocError
|
|
10
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-6QOYS63L.js";
|
|
11
13
|
|
|
12
14
|
// src/live-worker.ts
|
|
13
15
|
import { createDurableObjectRealtime } from "agentic-realtime/worker";
|
package/dist/live.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Peer } from 'agentic-realtime/client';
|
|
2
|
-
import { a as AgenticDoc,
|
|
2
|
+
import { a as AgenticDoc, c as Principal, D as DocOptions } from './doc-mJPajXEQ.js';
|
|
3
|
+
import { a as DocAccess, D as DocSummary } from './service-CqKE9DKz.js';
|
|
3
4
|
import 'yjs';
|
|
4
5
|
import 'agentic-identity';
|
|
5
6
|
|
|
@@ -63,8 +64,12 @@ declare class LiveService implements DocAccess {
|
|
|
63
64
|
private readonly options;
|
|
64
65
|
constructor(options: Pick<LiveOptions, "server" | "token" | "name" | "doc">);
|
|
65
66
|
private join;
|
|
67
|
+
private call;
|
|
66
68
|
/** The room is only a copy, so a new document is made in the store. A room seeds from it on the next join. */
|
|
67
69
|
create(id: string, markdown: string): Promise<void>;
|
|
70
|
+
list(): Promise<DocSummary[]>;
|
|
71
|
+
rename(id: string, title: string): Promise<void>;
|
|
72
|
+
remove(id: string): Promise<void>;
|
|
68
73
|
read<T>(id: string, run: (doc: AgenticDoc) => T): Promise<T>;
|
|
69
74
|
write<T>(id: string, run: (doc: AgenticDoc) => T): Promise<T>;
|
|
70
75
|
private exists;
|
package/dist/live.js
CHANGED
|
@@ -2,9 +2,10 @@ import {
|
|
|
2
2
|
LiveDoc,
|
|
3
3
|
LiveService,
|
|
4
4
|
whoAmI
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import "./chunk-
|
|
7
|
-
import "./chunk-
|
|
5
|
+
} from "./chunk-E7B6T5WN.js";
|
|
6
|
+
import "./chunk-YNHZCIGO.js";
|
|
7
|
+
import "./chunk-UPEGODUV.js";
|
|
8
|
+
import "./chunk-6QOYS63L.js";
|
|
8
9
|
export {
|
|
9
10
|
LiveDoc,
|
|
10
11
|
LiveService,
|
|
@@ -3,10 +3,12 @@ import {
|
|
|
3
3
|
} from "./chunk-5Y66UCTE.js";
|
|
4
4
|
import {
|
|
5
5
|
commands
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-S6MQDWFK.js";
|
|
7
|
+
import "./chunk-L6W2AQUF.js";
|
|
8
|
+
import "./chunk-UPEGODUV.js";
|
|
7
9
|
import {
|
|
8
10
|
DocError
|
|
9
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-6QOYS63L.js";
|
|
10
12
|
|
|
11
13
|
// src/mcp.ts
|
|
12
14
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { A as Anchor, c as Principal, a as AgenticDoc, g as TextTarget, d as AnchorTarget } from './doc-mJPajXEQ.js';
|
|
2
|
+
|
|
3
|
+
type DocErrorCode = "NO_MATCH" | "AMBIGUOUS_MATCH" | "STALE_ANCHOR" | "NO_SECTION" | "INVALID_AUTHOR" | "INVALID_INPUT" | "NO_THREAD" | "NO_SUGGESTION" | "ALREADY_DECIDED" | "FORBIDDEN" | "NO_DOCUMENT" | "DOCUMENT_EXISTS" | "LOCKED" | "CONFLICT" | "MISCONFIGURED";
|
|
4
|
+
declare class DocError extends Error {
|
|
5
|
+
readonly code: DocErrorCode;
|
|
6
|
+
readonly matches: {
|
|
7
|
+
occurrence: number;
|
|
8
|
+
context: string;
|
|
9
|
+
}[];
|
|
10
|
+
constructor(code: DocErrorCode, message: string, matches?: {
|
|
11
|
+
occurrence: number;
|
|
12
|
+
context: string;
|
|
13
|
+
}[]);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface Message {
|
|
17
|
+
id: string;
|
|
18
|
+
at: string;
|
|
19
|
+
author: Principal;
|
|
20
|
+
body: string;
|
|
21
|
+
}
|
|
22
|
+
interface Thread {
|
|
23
|
+
id: string;
|
|
24
|
+
anchor: Anchor;
|
|
25
|
+
status: "open" | "resolved";
|
|
26
|
+
resolvedBy?: Principal;
|
|
27
|
+
resolvedAt?: string;
|
|
28
|
+
messages: Message[];
|
|
29
|
+
/** The anchored text no longer matches what the comment was made on. */
|
|
30
|
+
stale: boolean;
|
|
31
|
+
/** The text now under the anchor, or null when it is gone. */
|
|
32
|
+
currentText: string | null;
|
|
33
|
+
}
|
|
34
|
+
interface Suggestion {
|
|
35
|
+
id: string;
|
|
36
|
+
kind: "insert" | "replace" | "delete";
|
|
37
|
+
at?: "before" | "after";
|
|
38
|
+
anchor: Anchor;
|
|
39
|
+
replacement: string;
|
|
40
|
+
author: Principal;
|
|
41
|
+
createdAt: string;
|
|
42
|
+
status: "pending" | "accepted" | "rejected";
|
|
43
|
+
decidedBy?: Principal;
|
|
44
|
+
decidedAt?: string;
|
|
45
|
+
stale: boolean;
|
|
46
|
+
}
|
|
47
|
+
/** Comment threads anchored to text. Replies and status merge across replicas. */
|
|
48
|
+
declare class Threads {
|
|
49
|
+
private readonly doc;
|
|
50
|
+
constructor(doc: AgenticDoc);
|
|
51
|
+
private get map();
|
|
52
|
+
private get;
|
|
53
|
+
private message;
|
|
54
|
+
add(target: TextTarget | AnchorTarget, body: string, author: Principal): string;
|
|
55
|
+
reply(id: string, body: string, author: Principal): void;
|
|
56
|
+
private setStatus;
|
|
57
|
+
resolve(id: string, author: Principal): void;
|
|
58
|
+
reopen(id: string, author: Principal): void;
|
|
59
|
+
list(filter?: {
|
|
60
|
+
status?: Thread["status"];
|
|
61
|
+
}): Thread[];
|
|
62
|
+
}
|
|
63
|
+
/** Suggested edits. Nothing changes in the text until a person, or an agent with the accept scope, accepts one. */
|
|
64
|
+
declare class Suggestions {
|
|
65
|
+
private readonly doc;
|
|
66
|
+
constructor(doc: AgenticDoc);
|
|
67
|
+
private get map();
|
|
68
|
+
private get;
|
|
69
|
+
private read;
|
|
70
|
+
/** An `at` on the target means insert. An empty replacement means delete. */
|
|
71
|
+
add(target: TextTarget | AnchorTarget, replacement: string, author: Principal): string;
|
|
72
|
+
private decide;
|
|
73
|
+
/**
|
|
74
|
+
* Apply the edit as the accepting author. The check and the edit share one transaction. Accepting
|
|
75
|
+
* an accepted suggestion changes nothing. A stale anchor throws and the suggestion stays pending.
|
|
76
|
+
*/
|
|
77
|
+
accept(id: string, author: Principal): void;
|
|
78
|
+
/** Same right as accept. A person can withdraw their own suggestion. An agent can never decide its own. */
|
|
79
|
+
reject(id: string, author: Principal): void;
|
|
80
|
+
list(filter?: {
|
|
81
|
+
status?: Suggestion["status"];
|
|
82
|
+
}): Suggestion[];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export { type DocErrorCode as D, type Message as M, type Suggestion as S, type Thread as T, DocError as a, Suggestions as b, Threads as c };
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { P as PrincipalKind, c as Principal, a as AgenticDoc, D as DocOptions } from './doc-mJPajXEQ.js';
|
|
2
|
+
import { Blobs, Store } from 'agentic-identity';
|
|
3
|
+
|
|
4
|
+
/** What the catalog keeps for each document. The author kind stays, so machine-made work stays labeled. */
|
|
5
|
+
interface DocInfo {
|
|
6
|
+
createdBy: {
|
|
7
|
+
kind: PrincipalKind;
|
|
8
|
+
name: string;
|
|
9
|
+
};
|
|
10
|
+
createdAt: number;
|
|
11
|
+
/** The name a person gave the document. Without one, a list shows the id. */
|
|
12
|
+
title?: string;
|
|
13
|
+
/** The last save, and who made it. They start as the creation. */
|
|
14
|
+
updatedAt?: number;
|
|
15
|
+
updatedBy?: {
|
|
16
|
+
kind: PrincipalKind;
|
|
17
|
+
name: string;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/** One row of the document list. */
|
|
21
|
+
interface DocSummary {
|
|
22
|
+
id: string;
|
|
23
|
+
title: string;
|
|
24
|
+
createdBy: DocInfo["createdBy"];
|
|
25
|
+
createdAt: number;
|
|
26
|
+
updatedAt: number;
|
|
27
|
+
updatedBy: DocInfo["createdBy"];
|
|
28
|
+
}
|
|
29
|
+
interface DocsPorts {
|
|
30
|
+
/** Holds each document as a Yjs update. Saves are conditional on the version that was read. */
|
|
31
|
+
blobs: Blobs;
|
|
32
|
+
/** Holds the catalog: who created each document, and when. */
|
|
33
|
+
store: Store;
|
|
34
|
+
}
|
|
35
|
+
/** What every command needs: make a document, read it, and change it. A live room can stand behind it as well as the ports. */
|
|
36
|
+
interface DocAccess {
|
|
37
|
+
create(id: string, markdown: string, author: Principal): Promise<void>;
|
|
38
|
+
read<T>(id: string, run: (doc: AgenticDoc) => T): Promise<T>;
|
|
39
|
+
write<T>(id: string, run: (doc: AgenticDoc) => T, author?: Principal): Promise<T>;
|
|
40
|
+
list(): Promise<DocSummary[]>;
|
|
41
|
+
rename(id: string, title: string, author: Principal): Promise<void>;
|
|
42
|
+
remove(id: string, author: Principal): Promise<void>;
|
|
43
|
+
}
|
|
44
|
+
/** Loads and saves documents as Yjs updates in a Blobs port. The CLI, MCP server, and API share it. */
|
|
45
|
+
declare class DocsService implements DocAccess {
|
|
46
|
+
private readonly options;
|
|
47
|
+
private readonly blobs;
|
|
48
|
+
private readonly store;
|
|
49
|
+
constructor(ports: DocsPorts, options?: DocOptions);
|
|
50
|
+
private key;
|
|
51
|
+
create(id: string, markdown: string, author: Principal): Promise<void>;
|
|
52
|
+
/** The stored document as a Yjs update. A live client seeds its room from it. */
|
|
53
|
+
state(id: string): Promise<Uint8Array>;
|
|
54
|
+
/**
|
|
55
|
+
* Merge a Yjs update from a live room into the stored document, and create the document when it
|
|
56
|
+
* has none. A merge is safe to repeat and to run in any order. The save is conditional, so two
|
|
57
|
+
* checkpoints at once both land.
|
|
58
|
+
*/
|
|
59
|
+
checkpoint(id: string, update: Uint8Array, author: Principal, options?: {
|
|
60
|
+
create?: boolean;
|
|
61
|
+
}): Promise<void>;
|
|
62
|
+
/**
|
|
63
|
+
* The encoded document, once it reads back and every stored entry has the shape the commands rely on. Yjs does not
|
|
64
|
+
* check types, so an update can put text where the editor expects blocks, or a thread with no messages, and then a
|
|
65
|
+
* command fails on every later call.
|
|
66
|
+
*/
|
|
67
|
+
private checked;
|
|
68
|
+
private fits;
|
|
69
|
+
private catalog;
|
|
70
|
+
/**
|
|
71
|
+
* Record a save. The catalog is a side record that follows the blob, so a failure here never fails a save that worked:
|
|
72
|
+
* the caller would repeat a change that is not safe to repeat. A document with no catalog entry stays without one.
|
|
73
|
+
*/
|
|
74
|
+
private touch;
|
|
75
|
+
/** Every cataloged document, newest first, up to ${MAX_LISTED}. A damaged entry is skipped, so one bad entry never hides the list. */
|
|
76
|
+
list(): Promise<DocSummary[]>;
|
|
77
|
+
/** Give the document a name. It does not change the text. */
|
|
78
|
+
rename(id: string, title: string, author: Principal): Promise<void>;
|
|
79
|
+
/** Delete the document and its catalog entry. An agent needs the remove scope. A live room keeps its own copy of the text. */
|
|
80
|
+
remove(id: string, author: Principal): Promise<void>;
|
|
81
|
+
/** Who created the document and when, or null when it has no catalog entry (made before the catalog, or a failed catalog write). */
|
|
82
|
+
info(id: string): Promise<DocInfo | null>;
|
|
83
|
+
read<T>(id: string, run: (doc: AgenticDoc) => T): Promise<T>;
|
|
84
|
+
/**
|
|
85
|
+
* Load, run the change, and save only if nobody saved in between. When someone did, load again and
|
|
86
|
+
* run the change again, so a check such as "is it still pending" sees the new state.
|
|
87
|
+
*/
|
|
88
|
+
write<T>(id: string, run: (doc: AgenticDoc) => T, author?: Principal): Promise<T>;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export { type DocSummary as D, type DocAccess as a, type DocInfo as b, type DocsPorts as c, DocsService as d };
|
package/dist/ui/_headers
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Content-Security-Policy: default-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; connect-src 'self'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'
|
|
3
|
+
X-Content-Type-Options: nosniff
|
|
4
|
+
Referrer-Policy: no-referrer
|