agentic-comments 0.7.0 → 0.8.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 +30 -0
- package/dist/account.d.ts +62 -0
- package/dist/account.js +114 -0
- package/dist/{chunk-6MS4LASK.js → chunk-4G5BDTBA.js} +5 -5
- package/dist/{chunk-EFO5PSJL.js → chunk-6UQ6CDLJ.js} +1 -1
- package/dist/{chunk-OV2GGL3T.js → chunk-J6JGWDUP.js} +3 -3
- package/dist/{chunk-M77Q5CWI.js → chunk-LFYTCHXS.js} +1 -1
- package/dist/{chunk-B7LTOLUO.js → chunk-TTNT27PG.js} +1 -1
- package/dist/cli.js +5 -5
- package/dist/client.d.ts +3 -3
- package/dist/{errors-C-xvp24-.d.ts → errors-Cin-719h.d.ts} +3 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/local.d.ts +2 -2
- package/dist/local.js +3 -3
- package/dist/mcp-IFQQHOA2.js +11 -0
- package/dist/mcp-bin.js +5 -5
- package/dist/{service-BvIE6t5n.d.ts → service-ZdZPWrwI.d.ts} +1 -1
- package/dist/{suggestions-CnkEcTuY.d.ts → suggestions-G1seGf5d.d.ts} +5 -2
- package/dist/ui.d.ts +3 -3
- package/dist/worker.d.ts +26 -2
- package/dist/worker.js +91 -3
- package/llms.txt +1 -0
- package/package.json +5 -1
- package/dist/mcp-LP3X55C2.js +0 -11
package/README.md
CHANGED
|
@@ -86,6 +86,36 @@ erased message or a thread that holds one.
|
|
|
86
86
|
Replicas that edit one message at once leave a copy each. When the copies agree on the author kind and time,
|
|
87
87
|
the read keeps the erased copy, or else the latest edit. An edit and a delete made at once keep the edit.
|
|
88
88
|
|
|
89
|
+
## Account export and erase
|
|
90
|
+
|
|
91
|
+
`import { commentsAccount } from "agentic-comments/account"` lets a host export and erase one person's data
|
|
92
|
+
without reading the storage layout. Pass the subject (`{ kind, name }`) and `{ documents, actor }`. Each
|
|
93
|
+
document is `{ id, comments, suggestions }`. The actor is made with `stamp()` and must name the subject.
|
|
94
|
+
`exportAccount` returns JSON records in `messages`, `statusChanges`, `edits`, `suggestions`, `decisions`,
|
|
95
|
+
`documents`, and `unread`. `eraseAccount` tombstones the same data as `erasePerson` and returns counts. A second
|
|
96
|
+
erase counts 0, except `unread`: see below.
|
|
97
|
+
|
|
98
|
+
The package keeps no grants or audit rows. A host that runs the HTTP worker passes `worker: docs` (the
|
|
99
|
+
`CommentsDocs` of the worker). Then the run also covers every stored document: the subject's messages and
|
|
100
|
+
suggestions in it, and the creator that the worker keeps on each row. The creators are listed under `documents`
|
|
101
|
+
and get the tombstone name on erase. Without `worker`, that data stays. A stored document is erased in one
|
|
102
|
+
transaction. A stored document that `documents` also lists is refused, before any write. The check compares ids
|
|
103
|
+
only. A row whose creator cannot be read shows in `unread` of the export, and in the `unread` count of the erase,
|
|
104
|
+
which leaves the row in place. `unread` counts every row it cannot read, whoever made it, so it can stay above 0 on a repeat. A store that loses a race gives `CONFLICT`: run the erase again.
|
|
105
|
+
|
|
106
|
+
A run covers only the subject's own kind: `human:ada` does not reach `agent_of_human:ada`. Names and mentions of
|
|
107
|
+
the subject inside other people's messages are neither exported nor erased. Records can name other people, such
|
|
108
|
+
as the moderator in `editedBy` or the person in `decidedBy`. A record in `unread` means the export is not
|
|
109
|
+
complete: the store holds entries of the subject that a read cannot show. A missing `documents` list is an
|
|
110
|
+
error, not an empty run. Erase goes document by document. If one fails, the error code tells why, the message names the document when the store allows, and the earlier
|
|
111
|
+
documents stay erased. A failed document in `documents` may be partly erased (its threads and its suggestions
|
|
112
|
+
are two steps). A repeat counts only what is left. The counts of a failed run are lost.
|
|
113
|
+
|
|
114
|
+
The ids in `documents` and the ids of the worker share one namespace. Erase can make a state a little larger, so a stored
|
|
115
|
+
document near the size limit may stop the run with `STORE_FULL`: the host must shrink or delete that document, and
|
|
116
|
+
run the erase again. The run reads the list of stored documents once. Block the subject before the erase, so no
|
|
117
|
+
document is made during it. A repeat erase finishes what a race left.
|
|
118
|
+
|
|
89
119
|
## Anchors for plain text
|
|
90
120
|
|
|
91
121
|
`agentic-comments/anchor` pins a range of a plain string. It needs no Yjs and no framework.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { C as Comments, c as Suggestions, b as Author, A as Actor } from './suggestions-G1seGf5d.js';
|
|
2
|
+
import 'yjs';
|
|
3
|
+
import 'agentic-identity';
|
|
4
|
+
|
|
5
|
+
/** One document the host holds comments on. The host loads it; the package never lists documents. */
|
|
6
|
+
interface CommentsAccountDocument {
|
|
7
|
+
/** A stable id for the document. It goes in each exported record. */
|
|
8
|
+
id: string;
|
|
9
|
+
comments: Comments;
|
|
10
|
+
suggestions: Suggestions;
|
|
11
|
+
}
|
|
12
|
+
/** What `CommentsDocs` offers the plug-in. */
|
|
13
|
+
interface AccountWorker {
|
|
14
|
+
/** The id of each stored document. */
|
|
15
|
+
accountDocumentIds(): Promise<string[]>;
|
|
16
|
+
/** Runs `fn` on each stored document. With `save`, the state is written back in the same transaction. A document deleted meanwhile is skipped. `committed` is called with the id after its transaction commits. */
|
|
17
|
+
eachAccountDocument(fn: (doc: CommentsAccountDocument) => void, save: boolean, committed?: (id: string) => void): Promise<void>;
|
|
18
|
+
/** The documents the subject created. `unread` names the rows whose creator cannot be read, so the export is not complete for them. */
|
|
19
|
+
exportCreated(author: Author): Promise<{
|
|
20
|
+
documents: {
|
|
21
|
+
documentId: string;
|
|
22
|
+
createdAt: unknown;
|
|
23
|
+
}[];
|
|
24
|
+
unread: string[];
|
|
25
|
+
}>;
|
|
26
|
+
/** `count` is the creators it tombstoned. `unread` names the rows whose creator cannot be read, which it leaves in place. */
|
|
27
|
+
eraseCreated(author: Author): Promise<{
|
|
28
|
+
count: number;
|
|
29
|
+
unread: string[];
|
|
30
|
+
}>;
|
|
31
|
+
}
|
|
32
|
+
interface CommentsAccountDeps {
|
|
33
|
+
/** Every document that may hold data of the subject. */
|
|
34
|
+
documents: Iterable<CommentsAccountDocument>;
|
|
35
|
+
/** Made with `stamp()`. It must name the subject: the same kind and name. A moderate scope does not widen the run. */
|
|
36
|
+
actor: Actor;
|
|
37
|
+
/**
|
|
38
|
+
* The document store of the HTTP worker (`CommentsDocs`), when the host runs it. Its rows hold the threads of each
|
|
39
|
+
* document and the creator of each document, outside what `documents` can reach. Pass it, and the run covers every
|
|
40
|
+
* stored document. Without it, a worker host leaves that data in place.
|
|
41
|
+
*/
|
|
42
|
+
worker?: AccountWorker;
|
|
43
|
+
}
|
|
44
|
+
type JsonRecord = Record<string, unknown>;
|
|
45
|
+
interface CommentsAccount {
|
|
46
|
+
id: "comments";
|
|
47
|
+
/**
|
|
48
|
+
* Collections, each record tagged with `documentId`: `messages` (with the thread anchor), `statusChanges`,
|
|
49
|
+
* `edits` (the subject's edits of other people's messages), `suggestions` (the ones the subject wrote),
|
|
50
|
+
* `decisions` (accepts and rejects by the subject), `documents` (the worker documents the subject created, only
|
|
51
|
+
* when `deps.worker` is set), and `unread` (stored entries of the subject that a read
|
|
52
|
+
* cannot show; the export is not complete when this has a record). The package keeps no grants or audit rows. Its
|
|
53
|
+
* one owner-like record is the creator of a worker document, which `deps.worker` covers. A host that runs the
|
|
54
|
+
* worker and omits `worker` leaves that creator in place.
|
|
55
|
+
*/
|
|
56
|
+
exportAccount(subject: Author, deps: CommentsAccountDeps): Promise<Record<"messages" | "statusChanges" | "edits" | "suggestions" | "decisions" | "documents" | "unread", JsonRecord[]>>;
|
|
57
|
+
/** Replaces what the subject wrote with a tombstone (see `Comments.erasePerson`). Counts by kind, summed over documents. The thread shape stays. A repeat finds nothing and counts 0. A decision that the subject made on their own suggestion is deleted and not counted, while `unread` in the export counts it. */
|
|
58
|
+
eraseAccount(subject: Author, deps: CommentsAccountDeps): Promise<Record<"messages" | "statuses" | "edits" | "suggestions" | "decisions" | "documents" | "unread", number>>;
|
|
59
|
+
}
|
|
60
|
+
declare const commentsAccount: CommentsAccount;
|
|
61
|
+
|
|
62
|
+
export { type AccountWorker, type CommentsAccount, type CommentsAccountDeps, type CommentsAccountDocument, commentsAccount };
|
package/dist/account.js
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Actor,
|
|
3
|
+
Comments,
|
|
4
|
+
CommentsError,
|
|
5
|
+
Suggestions,
|
|
6
|
+
assertActor,
|
|
7
|
+
readAuthor,
|
|
8
|
+
shown
|
|
9
|
+
} from "./chunk-4G5BDTBA.js";
|
|
10
|
+
|
|
11
|
+
// src/account.ts
|
|
12
|
+
import { StoreConflictError } from "agentic-identity";
|
|
13
|
+
var plain = (value) => JSON.parse(JSON.stringify(value));
|
|
14
|
+
function check(subject, deps) {
|
|
15
|
+
const who = readAuthor(subject);
|
|
16
|
+
if (!who) throw new CommentsError("INVALID_INPUT", "subject needs a known kind and a name");
|
|
17
|
+
const actor = assertActor(deps?.actor);
|
|
18
|
+
if (actor.kind !== who.kind || actor.name !== who.name) throw new CommentsError("FORBIDDEN", "actor does not match the subject");
|
|
19
|
+
if (typeof deps?.documents?.[Symbol.iterator] !== "function") throw new CommentsError("INVALID_INPUT", "deps.documents must be a list of documents");
|
|
20
|
+
const documents = [...deps.documents];
|
|
21
|
+
const ids = /* @__PURE__ */ new Set();
|
|
22
|
+
const seen = /* @__PURE__ */ new Set();
|
|
23
|
+
const bad = (why) => {
|
|
24
|
+
throw new CommentsError("INVALID_INPUT", `deps: ${why}`);
|
|
25
|
+
};
|
|
26
|
+
if (deps.worker !== void 0 && ["accountDocumentIds", "eachAccountDocument", "exportCreated", "eraseCreated"].some((m) => typeof deps.worker?.[m] !== "function")) bad("worker needs accountDocumentIds, eachAccountDocument, exportCreated, and eraseCreated");
|
|
27
|
+
for (const d of documents) {
|
|
28
|
+
if (typeof d?.id !== "string" || d.id === "" || ids.has(d.id)) bad("each document needs a unique id");
|
|
29
|
+
if (!(d.comments instanceof Comments) || !(d.suggestions instanceof Suggestions)) bad("each document needs Comments and Suggestions made by this package");
|
|
30
|
+
if (d.suggestions.comments.ydoc !== d.comments.ydoc) bad("suggestions must be made on the same comments");
|
|
31
|
+
if (seen.has(d.comments.ydoc)) bad("two documents share one Yjs document");
|
|
32
|
+
ids.add(d.id);
|
|
33
|
+
seen.add(d.comments.ydoc);
|
|
34
|
+
}
|
|
35
|
+
return { documents, own: Actor.of(who) };
|
|
36
|
+
}
|
|
37
|
+
function collect({ id: documentId, comments, suggestions }, own, out) {
|
|
38
|
+
const c = comments.exportPerson(own.name, own);
|
|
39
|
+
const s = suggestions.exportPerson(own.name, own);
|
|
40
|
+
for (const t of c.threads) {
|
|
41
|
+
for (const message of t.messages) out.messages.push({ ...message, documentId, threadId: t.threadId, anchor: t.anchor });
|
|
42
|
+
if (t.statusChange) out.statusChanges.push({ ...t.statusChange, documentId, threadId: t.threadId });
|
|
43
|
+
}
|
|
44
|
+
for (const e of c.edits) out.edits.push({ ...e, documentId });
|
|
45
|
+
for (const x of s.authored) out.suggestions.push({ ...x, documentId });
|
|
46
|
+
for (const d of s.decided) out.decisions.push({ documentId, suggestionId: d.id, status: d.status, at: d.at });
|
|
47
|
+
const unread = c.unread + s.unread;
|
|
48
|
+
if (unread > 0) out.unread.push({ documentId, count: unread });
|
|
49
|
+
}
|
|
50
|
+
async function stopAt(where, run) {
|
|
51
|
+
try {
|
|
52
|
+
return await run();
|
|
53
|
+
} catch (cause) {
|
|
54
|
+
const known = cause instanceof CommentsError;
|
|
55
|
+
const reason = cause instanceof Error ? cause.message : "unknown error";
|
|
56
|
+
if (!known && !(cause instanceof StoreConflictError)) throw cause;
|
|
57
|
+
throw new CommentsError(known ? cause.code : "CONFLICT", `erase stopped at ${where}: ${reason}`, { cause });
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
async function refuseOverlap(documents, worker) {
|
|
61
|
+
const listed = new Set(documents.map((d) => d.id));
|
|
62
|
+
if ((await worker.accountDocumentIds()).some((id) => listed.has(id))) throw new CommentsError("INVALID_INPUT", "deps.worker holds a document that deps.documents also lists");
|
|
63
|
+
}
|
|
64
|
+
function wipe({ comments, suggestions }, own, counts) {
|
|
65
|
+
const c = comments.erasePerson(own.name, own);
|
|
66
|
+
const s = suggestions.erasePerson(own.name, own);
|
|
67
|
+
counts.messages += c.messages;
|
|
68
|
+
counts.statuses += c.statuses;
|
|
69
|
+
counts.edits += c.edits;
|
|
70
|
+
counts.suggestions += s.suggestions;
|
|
71
|
+
counts.decisions += s.decisions;
|
|
72
|
+
}
|
|
73
|
+
var commentsAccount = {
|
|
74
|
+
id: "comments",
|
|
75
|
+
async exportAccount(subject, deps) {
|
|
76
|
+
const { documents, own } = check(subject, deps);
|
|
77
|
+
const out = { messages: [], statusChanges: [], edits: [], suggestions: [], decisions: [], documents: [], unread: [] };
|
|
78
|
+
if (deps.worker) await refuseOverlap(documents, deps.worker);
|
|
79
|
+
for (const doc of documents) collect(doc, own, out);
|
|
80
|
+
if (deps.worker) {
|
|
81
|
+
await deps.worker.eachAccountDocument((doc) => collect(doc, own, out), false);
|
|
82
|
+
const created = await deps.worker.exportCreated(own);
|
|
83
|
+
out.documents.push(...created.documents);
|
|
84
|
+
for (const documentId of created.unread) out.unread.push({ documentId, count: 1 });
|
|
85
|
+
}
|
|
86
|
+
return plain(out);
|
|
87
|
+
},
|
|
88
|
+
async eraseAccount(subject, deps) {
|
|
89
|
+
const { documents, own } = check(subject, deps);
|
|
90
|
+
if (deps.worker) await refuseOverlap(documents, deps.worker);
|
|
91
|
+
const counts = { messages: 0, statuses: 0, edits: 0, suggestions: 0, decisions: 0, documents: 0, unread: 0 };
|
|
92
|
+
for (const doc of documents) await stopAt(`document ${shown(doc.id)}`, () => wipe(doc, own, counts));
|
|
93
|
+
if (deps.worker) {
|
|
94
|
+
const each = /* @__PURE__ */ new Map();
|
|
95
|
+
const done = /* @__PURE__ */ new Set();
|
|
96
|
+
await stopAt(
|
|
97
|
+
"a worker document",
|
|
98
|
+
() => deps.worker.eachAccountDocument((doc) => {
|
|
99
|
+
const one = { messages: 0, statuses: 0, edits: 0, suggestions: 0, decisions: 0, documents: 0, unread: 0 };
|
|
100
|
+
wipe(doc, own, one);
|
|
101
|
+
each.set(doc.id, one);
|
|
102
|
+
}, true, (id) => void done.add(id))
|
|
103
|
+
);
|
|
104
|
+
for (const [id, c] of each) if (done.has(id)) for (const k of Object.keys(counts)) counts[k] += c[k];
|
|
105
|
+
const creators = await stopAt("the worker creators", () => deps.worker.eraseCreated(own));
|
|
106
|
+
counts.documents = creators.count;
|
|
107
|
+
counts.unread = creators.unread.length;
|
|
108
|
+
}
|
|
109
|
+
return counts;
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
export {
|
|
113
|
+
commentsAccount
|
|
114
|
+
};
|
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
// src/errors.ts
|
|
2
2
|
var CommentsError = class extends Error {
|
|
3
|
-
constructor(code, message) {
|
|
4
|
-
super(message);
|
|
3
|
+
constructor(code, message, options) {
|
|
4
|
+
super(message, options);
|
|
5
5
|
this.code = code;
|
|
6
6
|
this.name = "CommentsError";
|
|
7
7
|
}
|
|
8
8
|
code;
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
-
// src/anchor.ts
|
|
12
|
-
import * as Y from "yjs";
|
|
13
|
-
|
|
14
11
|
// src/text.ts
|
|
15
12
|
var HIDDEN = /[\p{Cc}\p{Cf}]/u;
|
|
16
13
|
var ID = /^[\w-]{1,64}$/;
|
|
@@ -21,6 +18,7 @@ var shown = (value) => JSON.stringify(safeId(value));
|
|
|
21
18
|
var safeText = (value) => String(value).replace(/[^\p{L}\p{N}\p{P}\p{S} ]/gu, "?").slice(0, 200);
|
|
22
19
|
|
|
23
20
|
// src/anchor.ts
|
|
21
|
+
import * as Y from "yjs";
|
|
24
22
|
var MAX_QUOTE = 2e3;
|
|
25
23
|
var MAX_ANCHOR = 4096;
|
|
26
24
|
var MAX_KEY = 256;
|
|
@@ -1020,6 +1018,7 @@ var Suggestions = class {
|
|
|
1020
1018
|
|
|
1021
1019
|
export {
|
|
1022
1020
|
CommentsError,
|
|
1021
|
+
shown,
|
|
1023
1022
|
textOf,
|
|
1024
1023
|
textAnchor,
|
|
1025
1024
|
textRange,
|
|
@@ -1027,6 +1026,7 @@ export {
|
|
|
1027
1026
|
ERASED_NAME,
|
|
1028
1027
|
Actor,
|
|
1029
1028
|
stamp,
|
|
1029
|
+
assertActor,
|
|
1030
1030
|
readAuthor,
|
|
1031
1031
|
authorLabel,
|
|
1032
1032
|
invalid,
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
VERSION
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-6UQ6CDLJ.js";
|
|
4
4
|
import {
|
|
5
5
|
CommentsService
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-TTNT27PG.js";
|
|
7
7
|
import {
|
|
8
8
|
CommentsError
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-4G5BDTBA.js";
|
|
10
10
|
|
|
11
11
|
// src/mcp.ts
|
|
12
12
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
package/dist/cli.js
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
VERSION
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-6UQ6CDLJ.js";
|
|
5
5
|
import {
|
|
6
6
|
FileStore,
|
|
7
7
|
lockTimeoutFrom,
|
|
8
8
|
operatorActor
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-LFYTCHXS.js";
|
|
10
10
|
import {
|
|
11
11
|
CommentsService,
|
|
12
12
|
TEXT_ROOT
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-TTNT27PG.js";
|
|
14
14
|
import {
|
|
15
15
|
CommentsError
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-4G5BDTBA.js";
|
|
17
17
|
|
|
18
18
|
// src/cli.ts
|
|
19
19
|
import { readFileSync, realpathSync } from "fs";
|
|
@@ -178,7 +178,7 @@ async function runCli(argv, io) {
|
|
|
178
178
|
return 0;
|
|
179
179
|
}
|
|
180
180
|
if (p.command === "mcp") {
|
|
181
|
-
const { startMcp } = await import("./mcp-
|
|
181
|
+
const { startMcp } = await import("./mcp-IFQQHOA2.js");
|
|
182
182
|
await startMcp(store, operatorActor(p.flags["--as"] ?? io.env.AGENTIC_COMMENTS_AS, io.env.AGENTIC_COMMENTS_SCOPES));
|
|
183
183
|
return 0;
|
|
184
184
|
}
|
package/dist/client.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { L as ListFilter, A as AnchorSpec } from './service-
|
|
2
|
-
import { C as CommentsErrorCode } from './errors-
|
|
1
|
+
import { L as ListFilter, A as AnchorSpec } from './service-ZdZPWrwI.js';
|
|
2
|
+
import { C as CommentsErrorCode } from './errors-Cin-719h.js';
|
|
3
3
|
import { PrincipalKind } from 'agentic-identity';
|
|
4
4
|
export { PrincipalKind } from 'agentic-identity';
|
|
5
|
-
import { T as Thread, N as NewReply, M as Message, P as PersonExport, a as PersonSuggestions, S as Suggestion } from './suggestions-
|
|
5
|
+
import { T as Thread, N as NewReply, M as Message, P as PersonExport, a as PersonSuggestions, S as Suggestion } from './suggestions-G1seGf5d.js';
|
|
6
6
|
import 'yjs';
|
|
7
7
|
|
|
8
8
|
/** Codes the comment rules return, plus the ones the Worker and its auth return. */
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
type CommentsErrorCode = "INVALID_AUTHOR" | "INVALID_INPUT" | "INVALID_ANCHOR" | "NO_THREAD" | "NO_MESSAGE" | "THREAD_RESOLVED" | "CORRUPT" | "NO_SUGGESTION" | "FORBIDDEN" | "ALREADY_DECIDED" | "APPLY_FAILED" | "RELOCATE_FAILED" | "STORE_MISSING" | "STORE_EXISTS" | "STORE_CORRUPT" | "STORE_LOCKED" | "STORE_FULL" | "NO_DOCUMENT" | "DOCUMENT_EXISTS" | "CONFLICT" | "MISCONFIGURED" | "USAGE";
|
|
2
2
|
declare class CommentsError extends Error {
|
|
3
3
|
readonly code: CommentsErrorCode;
|
|
4
|
-
constructor(code: CommentsErrorCode, message: string
|
|
4
|
+
constructor(code: CommentsErrorCode, message: string, options?: {
|
|
5
|
+
cause?: unknown;
|
|
6
|
+
});
|
|
5
7
|
}
|
|
6
8
|
|
|
7
9
|
export { type CommentsErrorCode as C, CommentsError as a };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { b as Author, C as Comments, c as Suggestions } from './suggestions-
|
|
2
|
-
export { d as ACCEPT_SCOPE, A as Actor, e as Anchor, f as AnchorKind, g as AnchorState, h as ApplyContext, i as CanvasAnchor, j as CellAnchor, k as CommentsOptions, E as ERASED_BODY, l as ERASED_NAME, m as EditMessage, n as MODERATE_SCOPE, M as Message, N as NewReply, o as NewSuggestion, p as NewThread, P as PersonExport, R as RelativePositionJson, q as RevertContext, r as SlideAnchor, s as StampInput, S as Suggestion, t as SuggestionFilter, u as SuggestionHost, v as TextAnchor, T as Thread, w as ThreadFilter, x as authorLabel, y as stamp, z as textAnchor, B as textOf } from './suggestions-
|
|
3
|
-
export { a as CommentsError, C as CommentsErrorCode } from './errors-
|
|
1
|
+
import { b as Author, C as Comments, c as Suggestions } from './suggestions-G1seGf5d.js';
|
|
2
|
+
export { d as ACCEPT_SCOPE, A as Actor, e as Anchor, f as AnchorKind, g as AnchorState, h as ApplyContext, i as CanvasAnchor, j as CellAnchor, k as CommentsOptions, E as ERASED_BODY, l as ERASED_NAME, m as EditMessage, n as MODERATE_SCOPE, M as Message, N as NewReply, o as NewSuggestion, p as NewThread, P as PersonExport, R as RelativePositionJson, q as RevertContext, r as SlideAnchor, s as StampInput, S as Suggestion, t as SuggestionFilter, u as SuggestionHost, v as TextAnchor, T as Thread, w as ThreadFilter, x as authorLabel, y as stamp, z as textAnchor, B as textOf } from './suggestions-G1seGf5d.js';
|
|
3
|
+
export { a as CommentsError, C as CommentsErrorCode } from './errors-Cin-719h.js';
|
|
4
4
|
export { PRINCIPAL_KINDS, PrincipalKind } from 'agentic-identity';
|
|
5
5
|
import 'yjs';
|
|
6
6
|
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
VERSION
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-6UQ6CDLJ.js";
|
|
4
4
|
import {
|
|
5
5
|
ACCEPT_SCOPE,
|
|
6
6
|
Actor,
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
textAnchor,
|
|
18
18
|
textOf,
|
|
19
19
|
validDate
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-4G5BDTBA.js";
|
|
21
21
|
|
|
22
22
|
// src/notifications.ts
|
|
23
23
|
import * as Y from "yjs";
|
package/dist/local.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as Y from 'yjs';
|
|
2
|
-
export { A as AnchorSpec, C as CommentsService, L as ListFilter } from './service-
|
|
3
|
-
import { u as SuggestionHost, A as Actor } from './suggestions-
|
|
2
|
+
export { A as AnchorSpec, C as CommentsService, L as ListFilter } from './service-ZdZPWrwI.js';
|
|
3
|
+
import { u as SuggestionHost, A as Actor } from './suggestions-G1seGf5d.js';
|
|
4
4
|
import 'agentic-identity';
|
|
5
5
|
|
|
6
6
|
/** The most bytes a saved document holds. A save over this is refused, so a file never grows past what a load accepts. */
|
package/dist/local.js
CHANGED
|
@@ -3,14 +3,14 @@ import {
|
|
|
3
3
|
MAX_FILE,
|
|
4
4
|
lockTimeoutFrom,
|
|
5
5
|
operatorActor
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-LFYTCHXS.js";
|
|
7
7
|
import {
|
|
8
8
|
CommentsService,
|
|
9
9
|
TEXT_ROOT,
|
|
10
10
|
VALUES_ROOT,
|
|
11
11
|
localHost
|
|
12
|
-
} from "./chunk-
|
|
13
|
-
import "./chunk-
|
|
12
|
+
} from "./chunk-TTNT27PG.js";
|
|
13
|
+
import "./chunk-4G5BDTBA.js";
|
|
14
14
|
export {
|
|
15
15
|
CommentsService,
|
|
16
16
|
FileStore,
|
package/dist/mcp-bin.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
startMcp
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-J6JGWDUP.js";
|
|
5
5
|
import {
|
|
6
6
|
VERSION
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-6UQ6CDLJ.js";
|
|
8
8
|
import {
|
|
9
9
|
FileStore,
|
|
10
10
|
lockTimeoutFrom,
|
|
11
11
|
operatorActor
|
|
12
|
-
} from "./chunk-
|
|
13
|
-
import "./chunk-
|
|
14
|
-
import "./chunk-
|
|
12
|
+
} from "./chunk-LFYTCHXS.js";
|
|
13
|
+
import "./chunk-TTNT27PG.js";
|
|
14
|
+
import "./chunk-4G5BDTBA.js";
|
|
15
15
|
|
|
16
16
|
// src/mcp-bin.ts
|
|
17
17
|
var MCP_HELP = `agentic-comments-mcp ${VERSION}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as Y from 'yjs';
|
|
2
|
-
import { A as Actor, T as Thread, M as Message, P as PersonExport, a as PersonSuggestions, S as Suggestion } from './suggestions-
|
|
2
|
+
import { A as Actor, T as Thread, M as Message, P as PersonExport, a as PersonSuggestions, S as Suggestion } from './suggestions-G1seGf5d.js';
|
|
3
3
|
|
|
4
4
|
/** Where a comment or suggestion points, in terms a person or agent can write down. */
|
|
5
5
|
type AnchorSpec = {
|
|
@@ -383,11 +383,14 @@ interface SuggestionFilter {
|
|
|
383
383
|
* with the accept scope that did not write it, accepts. Accept and reject are safe across replicas.
|
|
384
384
|
*/
|
|
385
385
|
declare class Suggestions {
|
|
386
|
-
|
|
386
|
+
/** The threads these suggestions anchor to. */
|
|
387
|
+
readonly comments: Comments;
|
|
387
388
|
private readonly host;
|
|
388
389
|
private readonly onTransaction;
|
|
389
390
|
private failures;
|
|
390
|
-
constructor(
|
|
391
|
+
constructor(
|
|
392
|
+
/** The threads these suggestions anchor to. */
|
|
393
|
+
comments: Comments, host: SuggestionHost);
|
|
391
394
|
/** Stop listening to the document. */
|
|
392
395
|
close(): void;
|
|
393
396
|
private get ydoc();
|
package/dist/ui.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { CommentsClient, Me } from './client.js';
|
|
3
3
|
export { createClient } from './client.js';
|
|
4
|
-
import { b as Author, S as Suggestion } from './suggestions-
|
|
5
|
-
import './service-
|
|
4
|
+
import { b as Author, S as Suggestion } from './suggestions-G1seGf5d.js';
|
|
5
|
+
import './service-ZdZPWrwI.js';
|
|
6
6
|
import 'yjs';
|
|
7
|
-
import './errors-
|
|
7
|
+
import './errors-Cin-719h.js';
|
|
8
8
|
import 'agentic-identity';
|
|
9
9
|
|
|
10
10
|
/** The calls the panel makes. A `CommentsClient` fits, and so does any object with these methods. */
|
package/dist/worker.d.ts
CHANGED
|
@@ -2,8 +2,9 @@ import { Hono } from 'hono';
|
|
|
2
2
|
import { Auth, Store, PrincipalContext, Region } from 'agentic-identity';
|
|
3
3
|
import { RequirePrincipalOptions } from 'agentic-identity/http';
|
|
4
4
|
import { D1Like } from 'agentic-identity/cloudflare';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { CommentsAccountDocument } from './account.js';
|
|
6
|
+
import { A as Actor, b as Author } from './suggestions-G1seGf5d.js';
|
|
7
|
+
import { C as CommentsService } from './service-ZdZPWrwI.js';
|
|
7
8
|
import 'yjs';
|
|
8
9
|
|
|
9
10
|
interface WorkerEnv {
|
|
@@ -36,6 +37,29 @@ declare class CommentsDocs {
|
|
|
36
37
|
create(id: string, text: string, actor: Actor): Promise<void>;
|
|
37
38
|
read<T>(id: string, actor: Actor, run: (service: CommentsService) => T): Promise<T>;
|
|
38
39
|
write<T>(id: string, actor: Actor, run: (service: CommentsService) => T): Promise<T>;
|
|
40
|
+
/** For the account plug-in: the id of each stored document. */
|
|
41
|
+
accountDocumentIds(): Promise<string[]>;
|
|
42
|
+
private ids;
|
|
43
|
+
/**
|
|
44
|
+
* For the account plug-in: runs `fn` on each stored document, with its `Comments` and `Suggestions`. With `save`,
|
|
45
|
+
* each document runs in one transaction and the state is written back there. A throw leaves it as it was. A document deleted since the list was read is skipped.
|
|
46
|
+
* A damaged document stops the run with CORRUPT.
|
|
47
|
+
*/
|
|
48
|
+
eachAccountDocument(fn: (doc: CommentsAccountDocument) => void, save: boolean, committed?: (id: string) => void): Promise<void>;
|
|
49
|
+
private scan;
|
|
50
|
+
/** The documents this subject created: the creator is kept on the row. For the account plug-in. */
|
|
51
|
+
exportCreated(author: Author): Promise<{
|
|
52
|
+
documents: {
|
|
53
|
+
documentId: string;
|
|
54
|
+
createdAt: unknown;
|
|
55
|
+
}[];
|
|
56
|
+
unread: string[];
|
|
57
|
+
}>;
|
|
58
|
+
/** Give the creator of each document this subject made the tombstone name. Returns how many, and the rows it could not read. A repeat finds none. */
|
|
59
|
+
eraseCreated(author: Author): Promise<{
|
|
60
|
+
count: number;
|
|
61
|
+
unread: string[];
|
|
62
|
+
}>;
|
|
39
63
|
}
|
|
40
64
|
/** The docs, or a function that makes them from the Worker bindings. */
|
|
41
65
|
type DocsSource = CommentsDocs | ((env: WorkerEnv) => Promise<CommentsDocs>);
|
package/dist/worker.js
CHANGED
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CommentsService,
|
|
3
|
-
TEXT_ROOT
|
|
4
|
-
|
|
3
|
+
TEXT_ROOT,
|
|
4
|
+
localHost
|
|
5
|
+
} from "./chunk-TTNT27PG.js";
|
|
5
6
|
import {
|
|
6
7
|
ACCEPT_SCOPE,
|
|
8
|
+
Comments,
|
|
7
9
|
CommentsError,
|
|
10
|
+
ERASED_NAME,
|
|
8
11
|
MODERATE_SCOPE,
|
|
12
|
+
Suggestions,
|
|
9
13
|
exact,
|
|
10
14
|
invalid,
|
|
15
|
+
readAuthor,
|
|
11
16
|
stamp
|
|
12
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-4G5BDTBA.js";
|
|
13
18
|
|
|
14
19
|
// src/worker.ts
|
|
15
20
|
import { Hono } from "hono";
|
|
@@ -150,6 +155,89 @@ var CommentsDocs = class {
|
|
|
150
155
|
return result;
|
|
151
156
|
});
|
|
152
157
|
}
|
|
158
|
+
/** For the account plug-in: the id of each stored document. */
|
|
159
|
+
async accountDocumentIds() {
|
|
160
|
+
return this.ids();
|
|
161
|
+
}
|
|
162
|
+
async ids() {
|
|
163
|
+
const ids = [];
|
|
164
|
+
let cursor;
|
|
165
|
+
do {
|
|
166
|
+
const page = await this.store.query(DOCS, { limit: 100, ...cursor ? { cursor } : {} });
|
|
167
|
+
for (const { id } of page.items) ids.push(id);
|
|
168
|
+
cursor = page.nextCursor;
|
|
169
|
+
} while (cursor);
|
|
170
|
+
return ids;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* For the account plug-in: runs `fn` on each stored document, with its `Comments` and `Suggestions`. With `save`,
|
|
174
|
+
* each document runs in one transaction and the state is written back there. A throw leaves it as it was. A document deleted since the list was read is skipped.
|
|
175
|
+
* A damaged document stops the run with CORRUPT.
|
|
176
|
+
*/
|
|
177
|
+
async eachAccountDocument(fn, save, committed) {
|
|
178
|
+
const open = (id, row) => {
|
|
179
|
+
const ydoc = this.load(row, id);
|
|
180
|
+
ydoc.getText(TEXT_ROOT);
|
|
181
|
+
const comments = new Comments(ydoc);
|
|
182
|
+
return { ydoc, doc: { id, comments, suggestions: new Suggestions(comments, localHost) } };
|
|
183
|
+
};
|
|
184
|
+
for (const id of await this.ids()) {
|
|
185
|
+
if (!save) {
|
|
186
|
+
const row = await this.store.get(DOCS, id);
|
|
187
|
+
if (row !== null) fn(open(id, row).doc);
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
const wrote = await this.store.transaction(async (tx) => {
|
|
191
|
+
const row = await tx.get(DOCS, id);
|
|
192
|
+
if (row === null) return false;
|
|
193
|
+
const { ydoc, doc } = open(id, row);
|
|
194
|
+
fn(doc);
|
|
195
|
+
const state = Y.encodeStateAsUpdate(ydoc);
|
|
196
|
+
if (state.length > MAX_STATE) throw new CommentsError("STORE_FULL", `document "${id}" would pass ${MAX_STATE} bytes of state`);
|
|
197
|
+
await tx.put(DOCS, id, { state: toBase64(state), createdBy: row?.createdBy ?? null, createdAt: row?.createdAt ?? null });
|
|
198
|
+
return true;
|
|
199
|
+
});
|
|
200
|
+
if (wrote) committed?.(id);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
async scan(author, each, unread) {
|
|
204
|
+
let cursor;
|
|
205
|
+
do {
|
|
206
|
+
const page = await this.store.query(DOCS, { limit: 100, ...cursor ? { cursor } : {} });
|
|
207
|
+
for (const { id, data } of page.items) {
|
|
208
|
+
const raw = data?.createdBy;
|
|
209
|
+
const by = readAuthor(raw);
|
|
210
|
+
if (!by && raw !== null && raw !== void 0) unread?.push(id);
|
|
211
|
+
else if (by && by.kind === author.kind && by.name === author.name) await each(id, data);
|
|
212
|
+
}
|
|
213
|
+
cursor = page.nextCursor;
|
|
214
|
+
} while (cursor);
|
|
215
|
+
}
|
|
216
|
+
/** The documents this subject created: the creator is kept on the row. For the account plug-in. */
|
|
217
|
+
async exportCreated(author) {
|
|
218
|
+
const documents = [];
|
|
219
|
+
const unread = [];
|
|
220
|
+
await this.scan(author, async (id, row) => void documents.push({ documentId: id, createdAt: row.createdAt ?? null }), unread);
|
|
221
|
+
return { documents, unread };
|
|
222
|
+
}
|
|
223
|
+
/** Give the creator of each document this subject made the tombstone name. Returns how many, and the rows it could not read. A repeat finds none. */
|
|
224
|
+
async eraseCreated(author) {
|
|
225
|
+
const ids = [];
|
|
226
|
+
const unread = [];
|
|
227
|
+
await this.scan(author, async (id) => void ids.push(id), unread);
|
|
228
|
+
let count = 0;
|
|
229
|
+
for (const id of ids) {
|
|
230
|
+
const changed = await this.store.transaction(async (tx) => {
|
|
231
|
+
const row = await tx.get(DOCS, id);
|
|
232
|
+
const by = readAuthor(row?.createdBy);
|
|
233
|
+
if (!row || !by || by.kind !== author.kind || by.name !== author.name) return false;
|
|
234
|
+
await tx.put(DOCS, id, { ...row, createdBy: { kind: by.kind, name: ERASED_NAME } });
|
|
235
|
+
return true;
|
|
236
|
+
});
|
|
237
|
+
if (changed) count++;
|
|
238
|
+
}
|
|
239
|
+
return { count, unread };
|
|
240
|
+
}
|
|
153
241
|
};
|
|
154
242
|
var STATUS = {
|
|
155
243
|
INVALID_AUTHOR: 400,
|
package/llms.txt
CHANGED
|
@@ -6,6 +6,7 @@ Keywords: comments,threads,mentions,suggestions,annotations,collaboration,agents
|
|
|
6
6
|
|
|
7
7
|
- SDK: import { Comments, Suggestions, textAnchor } from "agentic-comments". The host calls stamp(); an agent never does.
|
|
8
8
|
- Plain text anchors: import { createAnchor, resolveAnchor, mapRange } from "agentic-comments/anchor". No Yjs. resolveAnchor maps through edits, then falls back to quote + context, else orphaned.
|
|
9
|
+
- Account export and erase: import { commentsAccount } from "agentic-comments/account". exportAccount(subject, { documents, actor }) returns JSON records of one subject (pass worker: docs to cover every stored worker document and its creator). eraseAccount tombstones them and returns counts. The actor is made with stamp() and names the subject.
|
|
9
10
|
- Threads live on a Yjs document and merge across replicas.
|
|
10
11
|
- Lifecycle: comments.editMessage, deleteMessage, deleteThread, exportPerson, erasePerson (and the same on the CLI, MCP, HTTP API, and client). Only the author may change their own messages; comments:moderate may change anyone's.
|
|
11
12
|
- React panel: import { CommentsPanel, createClient, mayDecide } from "agentic-comments/ui" and "agentic-comments/ui.css" (with agentic-design/styles.css). Threads, quotes, replies, resolve and reopen, agent badge, accept and reject. An agent never decides its own suggestion. Peer: react, react-dom.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentic-comments",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Comment threads and suggestions anchored to any content, from people and agents.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"comments",
|
|
@@ -44,6 +44,10 @@
|
|
|
44
44
|
"types": "./dist/index.d.ts",
|
|
45
45
|
"import": "./dist/index.js"
|
|
46
46
|
},
|
|
47
|
+
"./account": {
|
|
48
|
+
"types": "./dist/account.d.ts",
|
|
49
|
+
"import": "./dist/account.js"
|
|
50
|
+
},
|
|
47
51
|
"./anchor": {
|
|
48
52
|
"types": "./dist/anchoring.d.ts",
|
|
49
53
|
"import": "./dist/anchoring.js"
|