agentic-comments 0.5.1 → 0.6.1

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/dist/index.js CHANGED
@@ -1,11 +1,14 @@
1
1
  import {
2
2
  VERSION
3
- } from "./chunk-REFQNIEP.js";
3
+ } from "./chunk-H6JK62SP.js";
4
4
  import {
5
5
  ACCEPT_SCOPE,
6
6
  Actor,
7
7
  Comments,
8
8
  CommentsError,
9
+ ERASED_BODY,
10
+ ERASED_NAME,
11
+ MODERATE_SCOPE,
9
12
  PRINCIPAL_KINDS,
10
13
  Suggestions,
11
14
  authorLabel,
@@ -14,7 +17,7 @@ import {
14
17
  textAnchor,
15
18
  textOf,
16
19
  validDate
17
- } from "./chunk-BSNBDKR2.js";
20
+ } from "./chunk-VAVKBAHM.js";
18
21
 
19
22
  // src/notifications.ts
20
23
  import * as Y from "yjs";
@@ -134,7 +137,16 @@ var Notifications = class {
134
137
  if (change) this.statusChanged(String(path[0]), change.oldValue, source);
135
138
  } else if (path.length === 2 && path[1] === "messages" && event.target instanceof Y.Array) {
136
139
  const ids = /* @__PURE__ */ new Set();
137
- for (const item of event.changes.added) for (const raw of item.content.getContent()) ids.add(raw?.id);
140
+ const changes = event.changes;
141
+ const replaced = /* @__PURE__ */ new Set();
142
+ for (const item of changes.deleted) for (const raw of item.content.getContent()) replaced.add(raw?.id);
143
+ const seen = /* @__PURE__ */ new Set();
144
+ for (const raw of event.target.toArray()) {
145
+ const id = raw?.id;
146
+ if (seen.has(id)) replaced.add(id);
147
+ seen.add(id);
148
+ }
149
+ for (const item of changes.added) for (const raw of item.content.getContent()) if (!replaced.has(raw?.id)) ids.add(raw?.id);
138
150
  this.messagesAdded(String(path[0]), ids, source);
139
151
  }
140
152
  });
@@ -212,6 +224,9 @@ export {
212
224
  Actor,
213
225
  Comments,
214
226
  CommentsError,
227
+ ERASED_BODY,
228
+ ERASED_NAME,
229
+ MODERATE_SCOPE,
215
230
  Notifications,
216
231
  PRINCIPAL_KINDS,
217
232
  Suggestions,
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-hpWbnSmc.js';
3
- import { q as SuggestionHost, A as Actor } from './suggestions-CvJwo6r4.js';
2
+ export { A as AnchorSpec, C as CommentsService, L as ListFilter } from './service-BvIE6t5n.js';
3
+ import { u as SuggestionHost, A as Actor } from './suggestions-CnkEcTuY.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-QJXI3P5D.js";
6
+ } from "./chunk-6N53KQVU.js";
7
7
  import {
8
8
  CommentsService,
9
9
  TEXT_ROOT,
10
10
  VALUES_ROOT,
11
11
  localHost
12
- } from "./chunk-NQWRFKJ6.js";
13
- import "./chunk-BSNBDKR2.js";
12
+ } from "./chunk-T77DPNIV.js";
13
+ import "./chunk-VAVKBAHM.js";
14
14
  export {
15
15
  CommentsService,
16
16
  FileStore,
@@ -0,0 +1,11 @@
1
+ import {
2
+ createMcpServer,
3
+ startMcp
4
+ } from "./chunk-UNJL3N2L.js";
5
+ import "./chunk-H6JK62SP.js";
6
+ import "./chunk-T77DPNIV.js";
7
+ import "./chunk-VAVKBAHM.js";
8
+ export {
9
+ createMcpServer,
10
+ startMcp
11
+ };
package/dist/mcp-bin.js CHANGED
@@ -3,15 +3,15 @@ import {
3
3
  FileStore,
4
4
  lockTimeoutFrom,
5
5
  operatorActor
6
- } from "./chunk-QJXI3P5D.js";
6
+ } from "./chunk-6N53KQVU.js";
7
7
  import {
8
8
  startMcp
9
- } from "./chunk-TFRLOBJW.js";
9
+ } from "./chunk-UNJL3N2L.js";
10
10
  import {
11
11
  VERSION
12
- } from "./chunk-REFQNIEP.js";
13
- import "./chunk-NQWRFKJ6.js";
14
- import "./chunk-BSNBDKR2.js";
12
+ } from "./chunk-H6JK62SP.js";
13
+ import "./chunk-T77DPNIV.js";
14
+ import "./chunk-VAVKBAHM.js";
15
15
 
16
16
  // src/mcp-bin.ts
17
17
  var MCP_HELP = `agentic-comments-mcp ${VERSION}
@@ -27,7 +27,7 @@ Usage: agentic-comments-mcp [--help | --version]`;
27
27
  var args = process.argv.slice(2);
28
28
  if (args.includes("--help") || args.includes("-h")) {
29
29
  console.log(MCP_HELP);
30
- } else if (args.includes("--version") || args.includes("-v")) {
30
+ } else if (args.includes("--version") || args.includes("-V") || args.includes("-v")) {
31
31
  console.log(VERSION);
32
32
  } else {
33
33
  try {
@@ -1,5 +1,5 @@
1
1
  import * as Y from 'yjs';
2
- import { A as Actor, T as Thread, M as Message, S as Suggestion } from './suggestions-CvJwo6r4.js';
2
+ import { A as Actor, T as Thread, M as Message, P as PersonExport, a as PersonSuggestions, S as Suggestion } from './suggestions-CnkEcTuY.js';
3
3
 
4
4
  /** Where a comment or suggestion points, in terms a person or agent can write down. */
5
5
  type AnchorSpec = {
@@ -51,6 +51,29 @@ declare class CommentsService {
51
51
  resolve(threadId: string): Thread;
52
52
  reopen(threadId: string): Thread;
53
53
  relocate(threadId: string): Thread;
54
+ editMessage(threadId: string, messageId: string, input: {
55
+ body: string;
56
+ mentions?: string[];
57
+ }): Message;
58
+ deleteMessage(threadId: string, messageId: string): {
59
+ threadId: string;
60
+ messageId: string;
61
+ };
62
+ deleteThread(threadId: string): {
63
+ threadId: string;
64
+ };
65
+ /** Everything the store holds that this person wrote: messages, status changes, and suggestions. */
66
+ exportPerson(person: string): PersonExport & {
67
+ suggestions: PersonSuggestions;
68
+ };
69
+ /** Tombstone everything this person wrote. Both parts are checked before either is written. */
70
+ erasePerson(person: string): {
71
+ messages: number;
72
+ statuses: number;
73
+ edits: number;
74
+ suggestions: number;
75
+ decisions: number;
76
+ };
54
77
  suggestions(filter?: ListFilter): Suggestion[];
55
78
  suggest(input: {
56
79
  anchor: AnchorSpec;
@@ -1,6 +1,9 @@
1
1
  import * as Y from 'yjs';
2
2
  import { PrincipalKind } from 'agentic-identity';
3
3
 
4
+ /** The name an erased author shows. No principal may have it, so a tombstone belongs to no one. */
5
+ declare const ERASED_NAME = "erased";
6
+
4
7
  /** What a thread keeps about who wrote a message. Scopes never reach storage. */
5
8
  interface Author {
6
9
  kind: PrincipalKind;
@@ -102,6 +105,11 @@ interface Message {
102
105
  body: string;
103
106
  /** Handles named in the body or listed on the input, without the `@`. */
104
107
  mentions: string[];
108
+ /** Set when the message was edited. A message that was never edited has none. */
109
+ editedAt?: string;
110
+ editedBy?: Author;
111
+ /** True for a message that `erasePerson` replaced with a tombstone. */
112
+ erased?: true;
105
113
  }
106
114
  interface Thread {
107
115
  id: string;
@@ -138,6 +146,34 @@ interface NewReply {
138
146
  body: string;
139
147
  mentions?: string[];
140
148
  }
149
+ interface EditMessage {
150
+ body: string;
151
+ mentions?: string[];
152
+ }
153
+ /** What the store holds about one person in one document. It lists only that person's own words and acts. */
154
+ interface PersonExport {
155
+ person: string;
156
+ exportedAt: string;
157
+ threads: {
158
+ threadId: string;
159
+ anchor: Anchor;
160
+ messages: Message[];
161
+ /** Set when this person made the status change that stands now. */
162
+ statusChange?: {
163
+ status: Thread["status"];
164
+ at: string;
165
+ by: Author;
166
+ };
167
+ }[];
168
+ /** Messages by other people that this person edited. */
169
+ edits: {
170
+ threadId: string;
171
+ messageId: string;
172
+ at: string;
173
+ }[];
174
+ /** Stored entries with this person's name that the export cannot show. Not 0 means the export is not complete. */
175
+ unread: number;
176
+ }
141
177
  interface ThreadFilter {
142
178
  status?: Thread["status"];
143
179
  anchorType?: string;
@@ -145,6 +181,11 @@ interface ThreadFilter {
145
181
  mentioning?: string;
146
182
  anchorState?: AnchorState["state"];
147
183
  }
184
+ /** A scope that lets a principal edit, delete, export, or erase what another person wrote. A host gives it to an owner or admin. */
185
+ declare const MODERATE_SCOPE = "comments:moderate";
186
+ /** What an erased message shows in place of its body. The author shows `ERASED_NAME`. */
187
+ declare const ERASED_BODY = "[erased]";
188
+
148
189
  /** Comment threads on a Yjs document. Replies and status merge across replicas. */
149
190
  declare class Comments {
150
191
  readonly ydoc: Y.Doc;
@@ -188,6 +229,62 @@ declare class Comments {
188
229
  relocate(threadId: string, actor: Actor): Thread;
189
230
  resolve(threadId: string, actor: Actor): Thread;
190
231
  reopen(threadId: string, actor: Actor): Thread;
232
+ /** A principal may change what it wrote. A principal with the moderate scope may change what anyone wrote. A tombstone is nobody's. */
233
+ private mayChange;
234
+ private forbid;
235
+ /** Replace every stored copy of a message with `next`, or remove it when `next` is undefined. */
236
+ private rewrite;
237
+ private find;
238
+ /** Change the body of a message. Its author and time stay, and it records who edited it and when. */
239
+ editMessage(threadId: string, messageId: string, input: EditMessage, actor: Actor): Message;
240
+ private readEdited;
241
+ /**
242
+ * Remove one message, also from a resolved thread. The last message of a thread stays: delete the thread instead. Replicas that delete
243
+ * two messages at once can leave a thread with none. A moderator can still delete that thread.
244
+ */
245
+ deleteMessage(threadId: string, messageId: string, actor: Actor): {
246
+ threadId: string;
247
+ messageId: string;
248
+ };
249
+ /**
250
+ * Remove a thread and all its messages. Its author may do it when every message is theirs. Once someone
251
+ * else replied, a message is erased, or a stored message cannot be read, it needs the moderate scope. A
252
+ * moderator can also remove a thread that cannot be read at all.
253
+ */
254
+ deleteThread(threadId: string, actor: Actor): {
255
+ threadId: string;
256
+ };
257
+ /**
258
+ * Check who an export or erase is for, and that the actor may. A person may act on their own data: the same
259
+ * kind and name. A principal with the moderate scope may act on anyone's, and then every kind with that name is
260
+ * included. Returns the name and a test for an author. Used by `Suggestions`.
261
+ */
262
+ checkPerson(person: unknown, actor: Actor): {
263
+ name: string;
264
+ match: (author: Author) => boolean;
265
+ };
266
+ /** The raw stored threads, for the code that must see data a read hides. `list` is missing when the message field is bad. */
267
+ private stored;
268
+ /**
269
+ * What a person wrote in this document: their messages, the status change of theirs that stands, and the
270
+ * edits they made to other people's messages. `unread` counts stored entries with their name that this read
271
+ * cannot show: a bad message, a thread that cannot be read, a message past the read limit, or a copy that
272
+ * lost a merge. When it is not 0 the export is not complete. A person may export their own data.
273
+ */
274
+ exportPerson(person: string, actor: Actor): PersonExport;
275
+ /**
276
+ * Replace every message a person wrote with a tombstone: the body becomes "[erased]", the author name
277
+ * becomes "erased", and the mentions go. The author kind stays, so machine-made work stays marked. Thread
278
+ * shape, order, and times stay. A status change of theirs, and their name as the editor of another person's
279
+ * message, get the tombstone name. It works on the stored entries, so it reaches a bad message, a message past
280
+ * the read limit, a thread that cannot be read, and every copy that a merge hid. Names inside other people's
281
+ * messages are not touched. Returns how many messages, status changes, and edit stamps it erased.
282
+ */
283
+ erasePerson(person: string, actor: Actor): {
284
+ messages: number;
285
+ statuses: number;
286
+ edits: number;
287
+ };
191
288
  get(threadId: string): Thread;
192
289
  /** Threads in the order they were started. A malformed thread is skipped; `problems()` names it. */
193
290
  list(filter?: ThreadFilter): Thread[];
@@ -215,6 +312,17 @@ interface Suggestion {
215
312
  decidedBy?: Author;
216
313
  decidedAt?: string;
217
314
  }
315
+ /** What a person wrote and decided among the suggestions of one document. */
316
+ interface PersonSuggestions {
317
+ authored: Suggestion[];
318
+ decided: {
319
+ id: string;
320
+ status: "accepted" | "rejected";
321
+ at: string;
322
+ }[];
323
+ /** Stored entries with this person's name that the export cannot show. Not 0 means it is not complete. */
324
+ unread: number;
325
+ }
218
326
  interface ApplyContext {
219
327
  suggestionId: string;
220
328
  anchor: Anchor;
@@ -302,6 +410,25 @@ declare class Suggestions {
302
410
  private index;
303
411
  private read;
304
412
  private fields;
413
+ /**
414
+ * The suggestions a person wrote, and the decisions they made, in this document. `unread` counts stored
415
+ * suggestions and decision records with their name that this export cannot show: a suggestion that cannot
416
+ * be read, a decision that lost a merge, a refused decision, or a decision with no suggestion. When it is not
417
+ * 0 the export is not complete. A person may export their own data.
418
+ */
419
+ exportPerson(person: string, actor: Actor): PersonSuggestions;
420
+ private named;
421
+ /**
422
+ * Give every suggestion a person wrote a tombstone author and drop its summary. An accepted one keeps its
423
+ * change, because that change is already part of the content. A pending or rejected one loses its change, and
424
+ * can no longer be accepted. A decision they made gets a tombstone decider. A decision that a read refuses (a
425
+ * principal that decided its own work) is deleted, so erase never turns it into a valid one. Returns how many
426
+ * suggestions and decisions it changed.
427
+ */
428
+ erasePerson(person: string, actor: Actor): {
429
+ suggestions: number;
430
+ decisions: number;
431
+ };
305
432
  /** Propose a change. Anyone can suggest. The host stamps the author. */
306
433
  add(input: NewSuggestion, actor: Actor): Suggestion;
307
434
  /** A person decides. An agent needs the accept scope, and never decides its own suggestion. */
@@ -330,4 +457,4 @@ declare class Suggestions {
330
457
  settle(): void;
331
458
  }
332
459
 
333
- export { Actor as A, Comments as C, type Message as M, type NewReply as N, type RelativePositionJson as R, type Suggestion as S, type Thread as T, type Author as a, Suggestions as b, ACCEPT_SCOPE as c, type Anchor as d, type AnchorKind as e, type AnchorState as f, type ApplyContext as g, type CanvasAnchor as h, type CellAnchor as i, type CommentsOptions as j, type NewSuggestion as k, type NewThread as l, type RevertContext as m, type SlideAnchor as n, type StampInput as o, type SuggestionFilter as p, type SuggestionHost as q, type TextAnchor as r, type ThreadFilter as s, authorLabel as t, stamp as u, textAnchor as v, textOf as w };
460
+ export { Actor as A, textOf as B, Comments as C, ERASED_BODY as E, type Message as M, type NewReply as N, type PersonExport as P, type RelativePositionJson as R, type Suggestion as S, type Thread as T, type PersonSuggestions as a, type Author as b, Suggestions as c, ACCEPT_SCOPE as d, type Anchor as e, type AnchorKind as f, type AnchorState as g, type ApplyContext as h, type CanvasAnchor as i, type CellAnchor as j, type CommentsOptions as k, ERASED_NAME as l, type EditMessage as m, MODERATE_SCOPE as n, type NewSuggestion as o, type NewThread as p, type RevertContext as q, type SlideAnchor as r, type StampInput as s, type SuggestionFilter as t, type SuggestionHost as u, type TextAnchor as v, type ThreadFilter as w, authorLabel as x, stamp as y, textAnchor as z };
package/dist/worker.d.ts CHANGED
@@ -2,8 +2,8 @@ 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 { A as Actor } from './suggestions-CvJwo6r4.js';
6
- import { C as CommentsService } from './service-hpWbnSmc.js';
5
+ import { A as Actor } from './suggestions-CnkEcTuY.js';
6
+ import { C as CommentsService } from './service-BvIE6t5n.js';
7
7
  import 'yjs';
8
8
 
9
9
  interface WorkerEnv {
@@ -13,8 +13,10 @@ interface WorkerEnv {
13
13
  AGENTIC_COMMENTS_UNSAFE_REGION?: string;
14
14
  /** D1 database: each document, with its threads, as a Yjs update. */
15
15
  DB?: D1Like;
16
+ /** Set to "1" to read `AGENTIC_COMMENTS_TOKENS`. Without it the list never signs anyone in, and a request gets 503 when the list is set. A dev fallback: never set it in production. */
17
+ AGENTIC_COMMENTS_UNSAFE_STATIC_TOKENS?: string;
16
18
  /**
17
- * JSON object: bearer token (32 to 512 bytes of A-Z a-z 0-9 _ -) to `kind:name` (the name uses A-Z a-z 0-9 . _ -), or to `{ "as": "kind:name", "scopes": ["comments:write"] }`.
19
+ * Dev fallback, read only when `AGENTIC_COMMENTS_UNSAFE_STATIC_TOKENS` is "1". It is tried after the identity credentials. JSON object: bearer token (32 to 512 bytes of A-Z a-z 0-9 _ -) to `kind:name` (the name uses A-Z a-z 0-9 . _ -), or to `{ "as": "kind:name", "scopes": ["comments:write"] }`.
18
20
  * Set it as a secret. A short token or a bad entry refuses the whole config.
19
21
  */
20
22
  AGENTIC_COMMENTS_TOKENS?: string;
@@ -26,7 +28,7 @@ interface WorkerEnv {
26
28
  declare function createTokenAuth(raw: string | undefined): Auth;
27
29
  /** The documents, on the `Store` port. Each write loads the document, runs the change, and saves in one transaction. */
28
30
  declare class CommentsDocs {
29
- private readonly store;
31
+ readonly store: Store;
30
32
  constructor(store: Store);
31
33
  private id;
32
34
  private load;
@@ -38,7 +40,10 @@ declare class CommentsDocs {
38
40
  /** The docs, or a function that makes them from the Worker bindings. */
39
41
  type DocsSource = CommentsDocs | ((env: WorkerEnv) => Promise<CommentsDocs>);
40
42
  interface AppOptions {
41
- /** The credential check. The default reads `AGENTIC_COMMENTS_TOKENS`. Pass any `Auth` from agentic-identity to replace it. */
43
+ /**
44
+ * The credential check. The default reads credentials and principals from the Store of the documents (`createStoreAuth`),
45
+ * then the static list when `AGENTIC_COMMENTS_UNSAFE_STATIC_TOKENS=1`. Pass any `Auth` from agentic-identity to replace it.
46
+ */
42
47
  auth?: Auth | ((env: WorkerEnv) => Auth);
43
48
  /** Origins that may send a write with a session cookie. A bearer token needs none. Passed to `requirePrincipal`. */
44
49
  origins?: RequirePrincipalOptions["origins"];
package/dist/worker.js CHANGED
@@ -1,18 +1,19 @@
1
1
  import {
2
2
  CommentsService,
3
3
  TEXT_ROOT
4
- } from "./chunk-NQWRFKJ6.js";
4
+ } from "./chunk-T77DPNIV.js";
5
5
  import {
6
6
  ACCEPT_SCOPE,
7
7
  CommentsError,
8
+ MODERATE_SCOPE,
8
9
  exact,
9
10
  invalid,
10
11
  stamp
11
- } from "./chunk-BSNBDKR2.js";
12
+ } from "./chunk-VAVKBAHM.js";
12
13
 
13
14
  // src/worker.ts
14
15
  import { Hono } from "hono";
15
- import { StoreConflictError, assertRegion, isScope, parseRegion, hasScope, parsePrincipal } from "agentic-identity";
16
+ import { StoreConflictError, assertRegion, createStoreAuth, isScope, parseRegion, hasScope, parsePrincipal } from "agentic-identity";
16
17
  import { requirePrincipal } from "agentic-identity/http";
17
18
  import { D1_SCHEMA, createD1Store } from "agentic-identity/cloudflare";
18
19
  import * as Y from "yjs";
@@ -86,7 +87,7 @@ function createTokenAuth(raw) {
86
87
  };
87
88
  }
88
89
  function actorOf(context) {
89
- const scopes = hasScope(context.scopes, ADMIN) ? [ACCEPT_SCOPE] : [];
90
+ const scopes = hasScope(context.scopes, ADMIN) ? [ACCEPT_SCOPE, MODERATE_SCOPE] : [];
90
91
  return stamp({ kind: context.kind, name: context.principalId, scopes });
91
92
  }
92
93
  var DOCS = "comments_docs";
@@ -155,6 +156,7 @@ var STATUS = {
155
156
  INVALID_INPUT: 400,
156
157
  INVALID_ANCHOR: 400,
157
158
  NO_THREAD: 404,
159
+ NO_MESSAGE: 404,
158
160
  NO_SUGGESTION: 404,
159
161
  NO_DOCUMENT: 404,
160
162
  THREAD_RESOLVED: 409,
@@ -191,8 +193,28 @@ function createApp(source, options = {}) {
191
193
  });
192
194
  app2.get("/health", (c) => c.json({ ok: true, pack: "agentic-comments" }));
193
195
  app2.use("/api/v1/*", async (c, next) => {
194
- const auth = typeof options.auth === "function" ? options.auth(c.env ?? {}) : options.auth ?? createTokenAuth(c.env?.AGENTIC_COMMENTS_TOKENS);
195
- const context = await requirePrincipal(c.req.raw, auth, { origins: options.origins });
196
+ let auth;
197
+ if (typeof options.auth === "function") auth = options.auth(c.env ?? {});
198
+ else if (options.auth) auth = options.auth;
199
+ else {
200
+ const staticOn = c.env?.AGENTIC_COMMENTS_UNSAFE_STATIC_TOKENS === "1";
201
+ if (c.env?.AGENTIC_COMMENTS_TOKENS && !staticOn) {
202
+ return fail("NOT_CONFIGURED", "AGENTIC_COMMENTS_TOKENS is a dev fallback. Set AGENTIC_COMMENTS_UNSAFE_STATIC_TOKENS=1 to use it, or remove it and issue agentic-identity credentials", 503);
203
+ }
204
+ const store = (await docsOf(c)).store;
205
+ const auths = [createStoreAuth(store)];
206
+ if (staticOn) auths.push(createTokenAuth(c.env?.AGENTIC_COMMENTS_TOKENS));
207
+ auth = {
208
+ async authenticate(request) {
209
+ for (const each of auths) {
210
+ const found = await each.authenticate(request);
211
+ if (found) return found;
212
+ }
213
+ return null;
214
+ }
215
+ };
216
+ }
217
+ const context = await requirePrincipal(c.req.raw, auth, { origins: options.origins, scope: READ });
196
218
  if (context instanceof Response) {
197
219
  if (context.status === 401) return Response.json({ error: { code: "UNAUTHENTICATED", message: "send a valid bearer token" } }, { status: 401, headers: { "www-authenticate": "Bearer" } });
198
220
  if (context.status === 403) return fail("FORBIDDEN", "this credential may not do this", 403);
@@ -237,6 +259,14 @@ function createApp(source, options = {}) {
237
259
  for (const action of ["resolve", "reopen"]) {
238
260
  app2.post(`${doc}/threads/:id/${action}`, (c) => act(c, COMMENT, true, (s) => s[action](c.req.param("id"))));
239
261
  }
262
+ app2.patch(`${doc}/threads/:id/messages/:message`, async (c) => {
263
+ const input = await body(c);
264
+ return act(c, COMMENT, true, (s) => s.editMessage(c.req.param("id"), c.req.param("message"), input));
265
+ });
266
+ app2.delete(`${doc}/threads/:id/messages/:message`, (c) => act(c, COMMENT, true, (s) => s.deleteMessage(c.req.param("id"), c.req.param("message"))));
267
+ app2.delete(`${doc}/threads/:id`, (c) => act(c, COMMENT, true, (s) => s.deleteThread(c.req.param("id"))));
268
+ app2.get(`${doc}/people/:name/export`, (c) => act(c, READ, false, (s) => s.exportPerson(c.req.param("name"))));
269
+ app2.delete(`${doc}/people/:name`, (c) => act(c, COMMENT, true, (s) => s.erasePerson(c.req.param("name"))));
240
270
  app2.post(`${doc}/threads/:id/relocate`, (c) => act(c, WRITE, true, (s) => s.relocate(c.req.param("id"))));
241
271
  app2.get(`${doc}/suggestions`, (c) => act(c, READ, false, (s) => s.suggestions(c.req.query())));
242
272
  app2.post(`${doc}/suggestions`, async (c) => {
package/llms.txt CHANGED
@@ -7,6 +7,7 @@ Keywords: comments,threads,mentions,suggestions,annotations,collaboration,agents
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
9
  - Threads live on a Yjs document and merge across replicas.
10
+ - 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.
10
11
  - Events: new Notifications(comments, suggestions).subscribe(listener). Ids only, never text.
11
12
  - CLI: agentic-comments <command>. MCP: agentic-comments mcp. Both use a local store file.
12
13
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentic-comments",
3
- "version": "0.5.1",
3
+ "version": "0.6.1",
4
4
  "description": "Comment threads and suggestions anchored to any content, from people and agents.",
5
5
  "keywords": [
6
6
  "comments",
@@ -76,9 +76,9 @@
76
76
  },
77
77
  "dependencies": {
78
78
  "@modelcontextprotocol/sdk": "^1.32.2",
79
- "agentic-identity": "^0.6.0",
79
+ "agentic-identity": "^0.8.0",
80
80
  "hono": "^4.13.14",
81
- "zod": "^3.25.76"
81
+ "zod": "^4.6.7"
82
82
  },
83
83
  "peerDependencies": {
84
84
  "yjs": "^13.6.0"
@@ -1,7 +0,0 @@
1
- type CommentsErrorCode = "INVALID_AUTHOR" | "INVALID_INPUT" | "INVALID_ANCHOR" | "NO_THREAD" | "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
- declare class CommentsError extends Error {
3
- readonly code: CommentsErrorCode;
4
- constructor(code: CommentsErrorCode, message: string);
5
- }
6
-
7
- export { type CommentsErrorCode as C, CommentsError as a };
@@ -1,11 +0,0 @@
1
- import {
2
- createMcpServer,
3
- startMcp
4
- } from "./chunk-TFRLOBJW.js";
5
- import "./chunk-REFQNIEP.js";
6
- import "./chunk-NQWRFKJ6.js";
7
- import "./chunk-BSNBDKR2.js";
8
- export {
9
- createMcpServer,
10
- startMcp
11
- };