agentic-comments 0.6.1 → 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.
@@ -1,6 +1,6 @@
1
1
  // src/version.ts
2
2
  import { createRequire } from "module";
3
- var VERSION = true ? "0.6.1" : readPackageVersion();
3
+ var VERSION = true ? "0.8.0" : readPackageVersion();
4
4
 
5
5
  export {
6
6
  VERSION
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  VERSION
3
- } from "./chunk-H6JK62SP.js";
3
+ } from "./chunk-6UQ6CDLJ.js";
4
4
  import {
5
5
  CommentsService
6
- } from "./chunk-T77DPNIV.js";
6
+ } from "./chunk-TTNT27PG.js";
7
7
  import {
8
8
  CommentsError
9
- } from "./chunk-VAVKBAHM.js";
9
+ } from "./chunk-4G5BDTBA.js";
10
10
 
11
11
  // src/mcp.ts
12
12
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -2,7 +2,7 @@ import {
2
2
  CommentsError,
3
3
  PRINCIPAL_KINDS,
4
4
  stamp
5
- } from "./chunk-VAVKBAHM.js";
5
+ } from "./chunk-4G5BDTBA.js";
6
6
 
7
7
  // src/file-store.ts
8
8
  import { createHash } from "crypto";
@@ -7,7 +7,7 @@ import {
7
7
  textAnchor,
8
8
  textOf,
9
9
  textRange
10
- } from "./chunk-VAVKBAHM.js";
10
+ } from "./chunk-4G5BDTBA.js";
11
11
 
12
12
  // src/local-host.ts
13
13
  import * as Y from "yjs";
@@ -0,0 +1,148 @@
1
+ // src/client.ts
2
+ var CODES = {
3
+ INVALID_AUTHOR: true,
4
+ INVALID_INPUT: true,
5
+ INVALID_ANCHOR: true,
6
+ NO_THREAD: true,
7
+ NO_MESSAGE: true,
8
+ THREAD_RESOLVED: true,
9
+ CORRUPT: true,
10
+ NO_SUGGESTION: true,
11
+ FORBIDDEN: true,
12
+ ALREADY_DECIDED: true,
13
+ APPLY_FAILED: true,
14
+ RELOCATE_FAILED: true,
15
+ STORE_MISSING: true,
16
+ STORE_EXISTS: true,
17
+ STORE_CORRUPT: true,
18
+ STORE_LOCKED: true,
19
+ STORE_FULL: true,
20
+ NO_DOCUMENT: true,
21
+ DOCUMENT_EXISTS: true,
22
+ CONFLICT: true,
23
+ MISCONFIGURED: true,
24
+ USAGE: true,
25
+ UNAUTHENTICATED: true,
26
+ NOT_FOUND: true,
27
+ INTERNAL: true,
28
+ UNKNOWN: true
29
+ };
30
+ var isKnownCode = (v) => typeof v === "string" && Object.hasOwn(CODES, v);
31
+ var CommentsApiError = class extends Error {
32
+ code;
33
+ status;
34
+ constructor(code, status, message) {
35
+ super(message);
36
+ this.name = "CommentsApiError";
37
+ this.code = code;
38
+ this.status = status;
39
+ }
40
+ };
41
+ var KINDS = { human: true, agent_of_human: true, agent_of_agent: true, agent: true, user_token: true };
42
+ var isObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
43
+ var isString = (v) => typeof v === "string";
44
+ var isAuthor = (v) => isObject(v) && typeof v.kind === "string" && Object.hasOwn(KINDS, v.kind) && isString(v.name);
45
+ var personThread = (v) => isObject(v) && isString(v.threadId) && isObject(v.anchor) && Array.isArray(v.messages) && v.messages.every(SHAPES.message) && (v.statusChange === void 0 || isObject(v.statusChange) && (v.statusChange.status === "open" || v.statusChange.status === "resolved") && isString(v.statusChange.at) && isAuthor(v.statusChange.by));
46
+ var SHAPES = {
47
+ me: (v) => isObject(v) && (typeof v.kind === "string" && Object.hasOwn(KINDS, v.kind)) && isString(v.name) && Array.isArray(v.scopes) && v.scopes.every(isString),
48
+ created: (v) => isObject(v) && isString(v.id),
49
+ document: (v) => isObject(v) && isString(v.id) && isString(v.text),
50
+ thread: (v) => isObject(v) && isString(v.id) && isObject(v.anchor) && isObject(v.anchorState) && isString(v.anchorState.state) && (v.status === "open" || v.status === "resolved") && isAuthor(v.statusBy) && isString(v.statusAt) && Array.isArray(v.messages) && v.messages.every(SHAPES.message) && typeof v.skipped === "number",
51
+ threads: (v) => Array.isArray(v) && v.every(SHAPES.thread),
52
+ message: (v) => isObject(v) && isString(v.id) && isString(v.at) && isString(v.body) && Array.isArray(v.mentions) && v.mentions.every(isString) && isAuthor(v.author) && (v.editedAt === void 0 || isString(v.editedAt)) && (v.editedBy === void 0 || isAuthor(v.editedBy)),
53
+ suggestion: (v) => isObject(v) && isString(v.id) && isObject(v.anchor) && isObject(v.anchorState) && isString(v.anchorState.state) && (v.status === "pending" || v.status === "accepted" || v.status === "rejected") && isAuthor(v.author) && isString(v.createdAt) && "change" in v && (v.summary === void 0 || isString(v.summary)) && (v.decidedBy === void 0 || isAuthor(v.decidedBy)) && (v.decidedAt === void 0 || isString(v.decidedAt)),
54
+ suggestions: (v) => Array.isArray(v) && v.every(SHAPES.suggestion),
55
+ deletedMessage: (v) => isObject(v) && isString(v.threadId) && isString(v.messageId),
56
+ deletedThread: (v) => isObject(v) && isString(v.threadId),
57
+ person: (v) => isObject(v) && isString(v.person) && isString(v.exportedAt) && Array.isArray(v.threads) && v.threads.every(personThread) && Array.isArray(v.edits) && v.edits.every((e) => isObject(e) && isString(e.threadId) && isString(e.messageId) && isString(e.at)) && typeof v.unread === "number" && isObject(v.suggestions) && Array.isArray(v.suggestions.authored) && v.suggestions.authored.every(SHAPES.suggestion) && Array.isArray(v.suggestions.decided) && v.suggestions.decided.every((d) => isObject(d) && isString(d.id) && (d.status === "accepted" || d.status === "rejected") && isString(d.at)) && typeof v.suggestions.unread === "number",
58
+ erased: (v) => isObject(v) && ["messages", "statuses", "edits", "suggestions", "decisions"].every((k) => typeof v[k] === "number")
59
+ };
60
+ function createClient(options) {
61
+ if (typeof options.baseUrl !== "string" || options.baseUrl === "") throw new TypeError("createClient needs a baseUrl");
62
+ if (typeof options.token !== "string" || options.token === "") throw new TypeError("createClient needs a token");
63
+ let origin;
64
+ try {
65
+ origin = new URL(options.baseUrl);
66
+ } catch {
67
+ throw new TypeError("baseUrl must be an absolute URL");
68
+ }
69
+ if (origin.protocol !== "https:" && origin.protocol !== "http:") throw new TypeError("baseUrl must be an http or https URL");
70
+ if (origin.search !== "" || origin.hash !== "" || origin.username !== "" || origin.password !== "") {
71
+ throw new TypeError("baseUrl must have no query, hash, or credentials");
72
+ }
73
+ const root = `${origin.origin}${origin.pathname.replace(/\/+$/, "")}/api/v1`;
74
+ const token = options.token;
75
+ const send = (input, init) => {
76
+ const run = options.fetch ?? globalThis.fetch;
77
+ return run(input, init);
78
+ };
79
+ const seg = (value) => {
80
+ if (typeof value !== "string" || /^(?:\.|%2e){0,2}$/i.test(value)) throw new TypeError("an id must be a non-empty string that is not . or ..");
81
+ return encodeURIComponent(value);
82
+ };
83
+ const query = (filter) => {
84
+ const params = new URLSearchParams();
85
+ for (const [key, value] of Object.entries(filter ?? {})) if (value !== void 0) params.set(key, String(value));
86
+ const text = params.toString();
87
+ return text === "" ? "" : `?${text}`;
88
+ };
89
+ async function call(method, path, body, callOptions, shape) {
90
+ const headers = { authorization: `Bearer ${token}` };
91
+ if (body !== void 0) headers["content-type"] = "application/json";
92
+ const response = await send(`${root}${path}`, {
93
+ method,
94
+ headers,
95
+ body: body === void 0 ? void 0 : JSON.stringify(body),
96
+ signal: callOptions?.signal,
97
+ // Never follow a redirect: it would send a write to another host.
98
+ redirect: "manual"
99
+ });
100
+ const text = await response.text();
101
+ let parsed;
102
+ try {
103
+ parsed = text === "" ? void 0 : JSON.parse(text);
104
+ } catch {
105
+ parsed = void 0;
106
+ }
107
+ if (!response.ok) {
108
+ const error = parsed?.error;
109
+ const code = isKnownCode(error?.code) ? error.code : "UNKNOWN";
110
+ const message = typeof error?.message === "string" ? error.message : `HTTP ${response.status}`;
111
+ throw new CommentsApiError(code, response.status, message);
112
+ }
113
+ if (!SHAPES[shape](parsed)) throw new CommentsApiError("UNKNOWN", response.status, `the reply is not a ${shape}`);
114
+ return parsed;
115
+ }
116
+ const doc = (id) => `/docs/${seg(id)}`;
117
+ const thread = (id, threadId) => `${doc(id)}/threads/${seg(threadId)}`;
118
+ const suggestion = (id, suggestionId) => `${doc(id)}/suggestions/${seg(suggestionId)}`;
119
+ const methods = {
120
+ me: (o) => call("GET", "/me", void 0, o, "me"),
121
+ createDocument: (input, o) => call("POST", "/docs", input, o, "created"),
122
+ getDocument: (id, o) => call("GET", doc(id), void 0, o, "document"),
123
+ listThreads: (id, filter, o) => call("GET", `${doc(id)}/threads${query(filter)}`, void 0, o, "threads"),
124
+ getThread: (id, threadId, o) => call("GET", thread(id, threadId), void 0, o, "thread"),
125
+ addThread: (id, input, o) => call("POST", `${doc(id)}/threads`, input, o, "thread"),
126
+ reply: (id, threadId, input, o) => call("POST", `${thread(id, threadId)}/replies`, input, o, "message"),
127
+ resolve: (id, threadId, o) => call("POST", `${thread(id, threadId)}/resolve`, void 0, o, "thread"),
128
+ reopen: (id, threadId, o) => call("POST", `${thread(id, threadId)}/reopen`, void 0, o, "thread"),
129
+ relocate: (id, threadId, o) => call("POST", `${thread(id, threadId)}/relocate`, void 0, o, "thread"),
130
+ editMessage: (id, threadId, messageId, input, o) => call("PATCH", `${thread(id, threadId)}/messages/${seg(messageId)}`, input, o, "message"),
131
+ deleteMessage: (id, threadId, messageId, o) => call("DELETE", `${thread(id, threadId)}/messages/${seg(messageId)}`, void 0, o, "deletedMessage"),
132
+ deleteThread: (id, threadId, o) => call("DELETE", thread(id, threadId), void 0, o, "deletedThread"),
133
+ exportPerson: (id, person, o) => call("GET", `${doc(id)}/people/${seg(person)}/export`, void 0, o, "person"),
134
+ erasePerson: (id, person, o) => call("DELETE", `${doc(id)}/people/${seg(person)}`, void 0, o, "erased"),
135
+ listSuggestions: (id, filter, o) => call("GET", `${doc(id)}/suggestions${query(filter)}`, void 0, o, "suggestions"),
136
+ suggest: (id, input, o) => call("POST", `${doc(id)}/suggestions`, input, o, "suggestion"),
137
+ accept: (id, suggestionId, o) => call("POST", `${suggestion(id, suggestionId)}/accept`, void 0, o, "suggestion"),
138
+ reject: (id, suggestionId, o) => call("POST", `${suggestion(id, suggestionId)}/reject`, void 0, o, "suggestion")
139
+ };
140
+ return Object.fromEntries(
141
+ Object.entries(methods).map(([name, method]) => [name, async (...args) => method(...args)])
142
+ );
143
+ }
144
+
145
+ export {
146
+ CommentsApiError,
147
+ createClient
148
+ };
package/dist/cli.js CHANGED
@@ -1,19 +1,19 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ VERSION
4
+ } from "./chunk-6UQ6CDLJ.js";
2
5
  import {
3
6
  FileStore,
4
7
  lockTimeoutFrom,
5
8
  operatorActor
6
- } from "./chunk-6N53KQVU.js";
7
- import {
8
- VERSION
9
- } from "./chunk-H6JK62SP.js";
9
+ } from "./chunk-LFYTCHXS.js";
10
10
  import {
11
11
  CommentsService,
12
12
  TEXT_ROOT
13
- } from "./chunk-T77DPNIV.js";
13
+ } from "./chunk-TTNT27PG.js";
14
14
  import {
15
15
  CommentsError
16
- } from "./chunk-VAVKBAHM.js";
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-WBAHKMF4.js");
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-BvIE6t5n.js';
2
- import { C as CommentsErrorCode } from './errors-C-xvp24-.js';
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-CnkEcTuY.js';
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. */
package/dist/client.js CHANGED
@@ -1,146 +1,7 @@
1
- // src/client.ts
2
- var CODES = {
3
- INVALID_AUTHOR: true,
4
- INVALID_INPUT: true,
5
- INVALID_ANCHOR: true,
6
- NO_THREAD: true,
7
- NO_MESSAGE: true,
8
- THREAD_RESOLVED: true,
9
- CORRUPT: true,
10
- NO_SUGGESTION: true,
11
- FORBIDDEN: true,
12
- ALREADY_DECIDED: true,
13
- APPLY_FAILED: true,
14
- RELOCATE_FAILED: true,
15
- STORE_MISSING: true,
16
- STORE_EXISTS: true,
17
- STORE_CORRUPT: true,
18
- STORE_LOCKED: true,
19
- STORE_FULL: true,
20
- NO_DOCUMENT: true,
21
- DOCUMENT_EXISTS: true,
22
- CONFLICT: true,
23
- MISCONFIGURED: true,
24
- USAGE: true,
25
- UNAUTHENTICATED: true,
26
- NOT_FOUND: true,
27
- INTERNAL: true,
28
- UNKNOWN: true
29
- };
30
- var isKnownCode = (v) => typeof v === "string" && Object.hasOwn(CODES, v);
31
- var CommentsApiError = class extends Error {
32
- code;
33
- status;
34
- constructor(code, status, message) {
35
- super(message);
36
- this.name = "CommentsApiError";
37
- this.code = code;
38
- this.status = status;
39
- }
40
- };
41
- var KINDS = { human: true, agent_of_human: true, agent_of_agent: true, agent: true, user_token: true };
42
- var isObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
43
- var isString = (v) => typeof v === "string";
44
- var isAuthor = (v) => isObject(v) && typeof v.kind === "string" && Object.hasOwn(KINDS, v.kind) && isString(v.name);
45
- var personThread = (v) => isObject(v) && isString(v.threadId) && isObject(v.anchor) && Array.isArray(v.messages) && v.messages.every(SHAPES.message) && (v.statusChange === void 0 || isObject(v.statusChange) && (v.statusChange.status === "open" || v.statusChange.status === "resolved") && isString(v.statusChange.at) && isAuthor(v.statusChange.by));
46
- var SHAPES = {
47
- me: (v) => isObject(v) && (typeof v.kind === "string" && Object.hasOwn(KINDS, v.kind)) && isString(v.name) && Array.isArray(v.scopes) && v.scopes.every(isString),
48
- created: (v) => isObject(v) && isString(v.id),
49
- document: (v) => isObject(v) && isString(v.id) && isString(v.text),
50
- thread: (v) => isObject(v) && isString(v.id) && isObject(v.anchor) && isObject(v.anchorState) && isString(v.anchorState.state) && (v.status === "open" || v.status === "resolved") && isAuthor(v.statusBy) && isString(v.statusAt) && Array.isArray(v.messages) && v.messages.every(SHAPES.message) && typeof v.skipped === "number",
51
- threads: (v) => Array.isArray(v) && v.every(SHAPES.thread),
52
- message: (v) => isObject(v) && isString(v.id) && isString(v.at) && isString(v.body) && Array.isArray(v.mentions) && v.mentions.every(isString) && isAuthor(v.author) && (v.editedAt === void 0 || isString(v.editedAt)) && (v.editedBy === void 0 || isAuthor(v.editedBy)),
53
- suggestion: (v) => isObject(v) && isString(v.id) && isObject(v.anchor) && isObject(v.anchorState) && isString(v.anchorState.state) && (v.status === "pending" || v.status === "accepted" || v.status === "rejected") && isAuthor(v.author) && isString(v.createdAt) && "change" in v && (v.summary === void 0 || isString(v.summary)) && (v.decidedBy === void 0 || isAuthor(v.decidedBy)) && (v.decidedAt === void 0 || isString(v.decidedAt)),
54
- suggestions: (v) => Array.isArray(v) && v.every(SHAPES.suggestion),
55
- deletedMessage: (v) => isObject(v) && isString(v.threadId) && isString(v.messageId),
56
- deletedThread: (v) => isObject(v) && isString(v.threadId),
57
- person: (v) => isObject(v) && isString(v.person) && isString(v.exportedAt) && Array.isArray(v.threads) && v.threads.every(personThread) && Array.isArray(v.edits) && v.edits.every((e) => isObject(e) && isString(e.threadId) && isString(e.messageId) && isString(e.at)) && typeof v.unread === "number" && isObject(v.suggestions) && Array.isArray(v.suggestions.authored) && v.suggestions.authored.every(SHAPES.suggestion) && Array.isArray(v.suggestions.decided) && v.suggestions.decided.every((d) => isObject(d) && isString(d.id) && (d.status === "accepted" || d.status === "rejected") && isString(d.at)) && typeof v.suggestions.unread === "number",
58
- erased: (v) => isObject(v) && ["messages", "statuses", "edits", "suggestions", "decisions"].every((k) => typeof v[k] === "number")
59
- };
60
- function createClient(options) {
61
- if (typeof options.baseUrl !== "string" || options.baseUrl === "") throw new TypeError("createClient needs a baseUrl");
62
- if (typeof options.token !== "string" || options.token === "") throw new TypeError("createClient needs a token");
63
- let origin;
64
- try {
65
- origin = new URL(options.baseUrl);
66
- } catch {
67
- throw new TypeError("baseUrl must be an absolute URL");
68
- }
69
- if (origin.protocol !== "https:" && origin.protocol !== "http:") throw new TypeError("baseUrl must be an http or https URL");
70
- if (origin.search !== "" || origin.hash !== "" || origin.username !== "" || origin.password !== "") {
71
- throw new TypeError("baseUrl must have no query, hash, or credentials");
72
- }
73
- const root = `${origin.origin}${origin.pathname.replace(/\/+$/, "")}/api/v1`;
74
- const token = options.token;
75
- const send = (input, init) => {
76
- const run = options.fetch ?? globalThis.fetch;
77
- return run(input, init);
78
- };
79
- const seg = (value) => {
80
- if (typeof value !== "string" || /^(?:\.|%2e){0,2}$/i.test(value)) throw new TypeError("an id must be a non-empty string that is not . or ..");
81
- return encodeURIComponent(value);
82
- };
83
- const query = (filter) => {
84
- const params = new URLSearchParams();
85
- for (const [key, value] of Object.entries(filter ?? {})) if (value !== void 0) params.set(key, String(value));
86
- const text = params.toString();
87
- return text === "" ? "" : `?${text}`;
88
- };
89
- async function call(method, path, body, callOptions, shape) {
90
- const headers = { authorization: `Bearer ${token}` };
91
- if (body !== void 0) headers["content-type"] = "application/json";
92
- const response = await send(`${root}${path}`, {
93
- method,
94
- headers,
95
- body: body === void 0 ? void 0 : JSON.stringify(body),
96
- signal: callOptions?.signal,
97
- // Never follow a redirect: it would send a write to another host.
98
- redirect: "manual"
99
- });
100
- const text = await response.text();
101
- let parsed;
102
- try {
103
- parsed = text === "" ? void 0 : JSON.parse(text);
104
- } catch {
105
- parsed = void 0;
106
- }
107
- if (!response.ok) {
108
- const error = parsed?.error;
109
- const code = isKnownCode(error?.code) ? error.code : "UNKNOWN";
110
- const message = typeof error?.message === "string" ? error.message : `HTTP ${response.status}`;
111
- throw new CommentsApiError(code, response.status, message);
112
- }
113
- if (!SHAPES[shape](parsed)) throw new CommentsApiError("UNKNOWN", response.status, `the reply is not a ${shape}`);
114
- return parsed;
115
- }
116
- const doc = (id) => `/docs/${seg(id)}`;
117
- const thread = (id, threadId) => `${doc(id)}/threads/${seg(threadId)}`;
118
- const suggestion = (id, suggestionId) => `${doc(id)}/suggestions/${seg(suggestionId)}`;
119
- const methods = {
120
- me: (o) => call("GET", "/me", void 0, o, "me"),
121
- createDocument: (input, o) => call("POST", "/docs", input, o, "created"),
122
- getDocument: (id, o) => call("GET", doc(id), void 0, o, "document"),
123
- listThreads: (id, filter, o) => call("GET", `${doc(id)}/threads${query(filter)}`, void 0, o, "threads"),
124
- getThread: (id, threadId, o) => call("GET", thread(id, threadId), void 0, o, "thread"),
125
- addThread: (id, input, o) => call("POST", `${doc(id)}/threads`, input, o, "thread"),
126
- reply: (id, threadId, input, o) => call("POST", `${thread(id, threadId)}/replies`, input, o, "message"),
127
- resolve: (id, threadId, o) => call("POST", `${thread(id, threadId)}/resolve`, void 0, o, "thread"),
128
- reopen: (id, threadId, o) => call("POST", `${thread(id, threadId)}/reopen`, void 0, o, "thread"),
129
- relocate: (id, threadId, o) => call("POST", `${thread(id, threadId)}/relocate`, void 0, o, "thread"),
130
- editMessage: (id, threadId, messageId, input, o) => call("PATCH", `${thread(id, threadId)}/messages/${seg(messageId)}`, input, o, "message"),
131
- deleteMessage: (id, threadId, messageId, o) => call("DELETE", `${thread(id, threadId)}/messages/${seg(messageId)}`, void 0, o, "deletedMessage"),
132
- deleteThread: (id, threadId, o) => call("DELETE", thread(id, threadId), void 0, o, "deletedThread"),
133
- exportPerson: (id, person, o) => call("GET", `${doc(id)}/people/${seg(person)}/export`, void 0, o, "person"),
134
- erasePerson: (id, person, o) => call("DELETE", `${doc(id)}/people/${seg(person)}`, void 0, o, "erased"),
135
- listSuggestions: (id, filter, o) => call("GET", `${doc(id)}/suggestions${query(filter)}`, void 0, o, "suggestions"),
136
- suggest: (id, input, o) => call("POST", `${doc(id)}/suggestions`, input, o, "suggestion"),
137
- accept: (id, suggestionId, o) => call("POST", `${suggestion(id, suggestionId)}/accept`, void 0, o, "suggestion"),
138
- reject: (id, suggestionId, o) => call("POST", `${suggestion(id, suggestionId)}/reject`, void 0, o, "suggestion")
139
- };
140
- return Object.fromEntries(
141
- Object.entries(methods).map(([name, method]) => [name, async (...args) => method(...args)])
142
- );
143
- }
1
+ import {
2
+ CommentsApiError,
3
+ createClient
4
+ } from "./chunk-UEQL27KF.js";
144
5
  export {
145
6
  CommentsApiError,
146
7
  createClient
@@ -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-CnkEcTuY.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-CnkEcTuY.js';
3
- export { a as CommentsError, C as CommentsErrorCode } from './errors-C-xvp24-.js';
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-H6JK62SP.js";
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-VAVKBAHM.js";
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-BvIE6t5n.js';
3
- import { u as SuggestionHost, A as Actor } from './suggestions-CnkEcTuY.js';
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-6N53KQVU.js";
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-T77DPNIV.js";
13
- import "./chunk-VAVKBAHM.js";
12
+ } from "./chunk-TTNT27PG.js";
13
+ import "./chunk-4G5BDTBA.js";
14
14
  export {
15
15
  CommentsService,
16
16
  FileStore,
@@ -0,0 +1,11 @@
1
+ import {
2
+ createMcpServer,
3
+ startMcp
4
+ } from "./chunk-J6JGWDUP.js";
5
+ import "./chunk-6UQ6CDLJ.js";
6
+ import "./chunk-TTNT27PG.js";
7
+ import "./chunk-4G5BDTBA.js";
8
+ export {
9
+ createMcpServer,
10
+ startMcp
11
+ };
package/dist/mcp-bin.js CHANGED
@@ -1,17 +1,17 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- FileStore,
4
- lockTimeoutFrom,
5
- operatorActor
6
- } from "./chunk-6N53KQVU.js";
7
2
  import {
8
3
  startMcp
9
- } from "./chunk-UNJL3N2L.js";
4
+ } from "./chunk-J6JGWDUP.js";
10
5
  import {
11
6
  VERSION
12
- } from "./chunk-H6JK62SP.js";
13
- import "./chunk-T77DPNIV.js";
14
- import "./chunk-VAVKBAHM.js";
7
+ } from "./chunk-6UQ6CDLJ.js";
8
+ import {
9
+ FileStore,
10
+ lockTimeoutFrom,
11
+ operatorActor
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-CnkEcTuY.js';
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
- private readonly comments;
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(comments: Comments, host: SuggestionHost);
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.css ADDED
@@ -0,0 +1,54 @@
1
+ /* The comments panel. It uses the agentic-design tokens and its own agd-comments classes. */
2
+ .agd-comments {
3
+ display: grid;
4
+ grid-template-columns: minmax(0, 1fr);
5
+ gap: var(--agd-space-16);
6
+ max-width: 44rem;
7
+ margin-inline: auto;
8
+ padding: var(--agd-space-16);
9
+ color: var(--agd-color-text);
10
+ font-family: var(--agd-font-family-sans);
11
+ font-size: var(--agd-font-size-md);
12
+ line-height: var(--agd-line-height-normal);
13
+ }
14
+ .agd-comments__top { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--agd-space-8); }
15
+ .agd-comments__title { margin: 0; font-size: var(--agd-font-size-xl); font-weight: var(--agd-font-weight-semibold); line-height: var(--agd-line-height-tight); }
16
+ .agd-comments__me { display: flex; align-items: center; gap: var(--agd-space-8); margin: 0; color: var(--agd-color-text-muted); font-size: var(--agd-font-size-sm); }
17
+ .agd-comments__section { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--agd-space-12, 12px); }
18
+ .agd-comments__heading { display: flex; align-items: center; gap: var(--agd-space-8); margin: 0; font-size: var(--agd-font-size-lg); font-weight: var(--agd-font-weight-semibold); }
19
+ .agd-comments__count { min-width: 1.5rem; padding: 0 var(--agd-space-6); border-radius: var(--agd-radius-full); background: var(--agd-color-surface-sunken); color: var(--agd-color-text-muted); font-size: var(--agd-font-size-sm); font-weight: var(--agd-font-weight-medium); text-align: center; }
20
+ .agd-comments__views { display: flex; flex-wrap: wrap; gap: var(--agd-space-8); }
21
+ .agd-comments__list, .agd-comments__messages { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--agd-space-12, 12px); margin: 0; padding: 0; list-style: none; }
22
+ .agd-comments__thread { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--agd-space-8); padding: var(--agd-space-16); border: 1px solid var(--agd-color-border); border-radius: var(--agd-radius-lg); background: var(--agd-color-surface); }
23
+ .agd-comments__thread > article { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--agd-space-8); }
24
+ .agd-comments__item-top { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--agd-space-8); }
25
+ .agd-comments__item-title { display: flex; flex-wrap: wrap; align-items: center; gap: var(--agd-space-8); margin: 0; font-size: var(--agd-font-size-md); font-weight: var(--agd-font-weight-semibold); overflow-wrap: anywhere; }
26
+ .agd-comments__status { font-size: var(--agd-font-size-xs); }
27
+ .agd-comments__status--resolved, .agd-comments__status--accepted { background: var(--agd-color-success-subtle); color: var(--agd-color-success-text); }
28
+ .agd-comments__status--rejected { background: var(--agd-color-danger-subtle); color: var(--agd-color-danger-text); }
29
+ .agd-comments__status--pending, .agd-comments__status--open { background: var(--agd-color-accent-subtle); color: var(--agd-color-accent-text); }
30
+ .agd-comments__anchor { margin: 0; }
31
+ .agd-comments__label { color: var(--agd-color-text-muted); font-size: var(--agd-font-size-xs); font-weight: var(--agd-font-weight-medium); letter-spacing: 0.04em; text-transform: uppercase; }
32
+ .agd-comments__quote { margin: var(--agd-space-4) 0 0; padding: var(--agd-space-4) var(--agd-space-12, 12px); border-inline-start: 3px solid var(--agd-color-border-strong); background: var(--agd-color-surface-sunken); border-radius: var(--agd-radius-sm); overflow-wrap: anywhere; }
33
+ .agd-comments__state { margin: 0; padding: var(--agd-space-8) var(--agd-space-12, 12px); border-radius: var(--agd-radius-md); background: var(--agd-color-warning-subtle); color: var(--agd-color-warning-text); font-size: var(--agd-font-size-sm); overflow-wrap: anywhere; }
34
+ .agd-comments__message { display: grid; gap: var(--agd-space-4); padding-top: var(--agd-space-8); border-top: 1px solid var(--agd-color-border); }
35
+ .agd-comments__meta { display: flex; flex-wrap: wrap; align-items: center; gap: var(--agd-space-8); }
36
+ .agd-comments__who { display: inline-flex; align-items: center; gap: var(--agd-space-8); }
37
+ .agd-comments__name { font-weight: var(--agd-font-weight-semibold); overflow-wrap: anywhere; }
38
+ .agd-comments__time { color: var(--agd-color-text-muted); font-size: var(--agd-font-size-sm); }
39
+ .agd-comments__body { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; }
40
+ .agd-comments__change { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--agd-space-4); margin: 0; }
41
+ .agd-comments__code { padding: var(--agd-space-4) var(--agd-space-8); border-radius: var(--agd-radius-sm); background: var(--agd-color-surface-sunken); font-family: var(--agd-font-family-mono); font-size: var(--agd-font-size-sm); white-space: pre-wrap; overflow-wrap: anywhere; }
42
+ .agd-comments__note, .agd-comments__empty { margin: 0; color: var(--agd-color-text-muted); font-size: var(--agd-font-size-sm); }
43
+ .agd-comments__reply, .agd-comments__new { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--agd-space-8); }
44
+ .agd-comments__new { padding: var(--agd-space-16); border: 1px solid var(--agd-color-border); border-radius: var(--agd-radius-lg); background: var(--agd-color-surface-raised); }
45
+ .agd-comments__reply .agd-button, .agd-comments__new .agd-button { justify-self: start; }
46
+ .agd-comments__actions { display: flex; flex-wrap: wrap; gap: var(--agd-space-8); }
47
+ .agd-comments__error { margin: 0; padding: var(--agd-space-8) var(--agd-space-12, 12px); border: 1px solid var(--agd-color-danger); border-radius: var(--agd-radius-md); background: var(--agd-color-danger-subtle); color: var(--agd-color-danger-text); overflow-wrap: anywhere; }
48
+ @media (max-width: 480px) {
49
+ .agd-comments { padding: var(--agd-space-12, 12px); }
50
+ .agd-comments__thread { padding: var(--agd-space-12, 12px); }
51
+ }
52
+ /* On a narrow screen the badge wraps, so its words are never cut off. */
53
+ .agd-comments .agd-agent-badge { flex-wrap: wrap; row-gap: 0; }
54
+ .agd-comments .agd-agent-badge__label, .agd-comments .agd-agent-badge__meta { white-space: normal; overflow: visible; overflow-wrap: anywhere; }
package/dist/ui.d.ts ADDED
@@ -0,0 +1,46 @@
1
+ import * as react from 'react';
2
+ import { CommentsClient, Me } from './client.js';
3
+ export { createClient } from './client.js';
4
+ import { b as Author, S as Suggestion } from './suggestions-G1seGf5d.js';
5
+ import './service-ZdZPWrwI.js';
6
+ import 'yjs';
7
+ import './errors-Cin-719h.js';
8
+ import 'agentic-identity';
9
+
10
+ /** The calls the panel makes. A `CommentsClient` fits, and so does any object with these methods. */
11
+ type PanelClient = Pick<CommentsClient, "me" | "listThreads" | "addThread" | "reply" | "resolve" | "reopen" | "listSuggestions" | "accept" | "reject">;
12
+ interface CommentsPanelProps {
13
+ client: PanelClient;
14
+ /** The document whose threads the panel shows. */
15
+ docId: string;
16
+ /** The heading. Defaults to "Comments". */
17
+ title?: string;
18
+ /** Show the form that starts a thread on a quote. On by default. A page that makes its own anchors turns it off. */
19
+ newThread?: boolean;
20
+ /** Name the person an agent acts for, when the host knows. The badge says "for unknown" without it. */
21
+ actingFor?: (author: Author) => string | undefined;
22
+ }
23
+ /**
24
+ * A threaded comments panel: thread list, reply box, resolve and reopen, the quote each thread points at, an agent or
25
+ * person mark on every message, and accept and reject buttons for suggestions. The buttons follow the product rule: an
26
+ * agent never decides its own suggestion. The server still checks every call, so a hidden button is a help and not a lock.
27
+ * It needs the `agentic-design/styles.css` and `agentic-comments/ui.css` styles on the page.
28
+ */
29
+ declare function CommentsPanel({ client, docId, title, newThread, actingFor }: CommentsPanelProps): react.JSX.Element;
30
+
31
+ type Need = "read" | "comment" | "write";
32
+ /** Whether the principal holds a scope at least as strong as `need`. */
33
+ declare function holds(me: Pick<Me, "scopes"> | undefined, need: Need): boolean;
34
+ interface Decision {
35
+ allowed: boolean;
36
+ /** Plain words that say why not. Set when `allowed` is false. */
37
+ reason?: string;
38
+ }
39
+ /**
40
+ * Whether the signed-in principal may accept a suggestion, and whether it may reject one. The Worker checks the
41
+ * same rule, so this only decides what the page offers. A person may decide any suggestion. An agent needs the
42
+ * `comments:admin` scope or the `comments:accept` right, and never decides its own suggestion.
43
+ */
44
+ declare function mayDecide(me: Me | undefined, suggestion: Pick<Suggestion, "author" | "status">): Decision;
45
+
46
+ export { CommentsClient, CommentsPanel, type CommentsPanelProps, type Decision, Me, type Need, type PanelClient, holds, mayDecide };