agentic-comments 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -243,6 +243,34 @@ curl -H "authorization: Bearer $TOKEN" -H "content-type: application/json" \
243
243
  http://127.0.0.1:8787/api/v1/docs/d1/threads
244
244
  ```
245
245
 
246
+ ## HTTP client
247
+
248
+ `agentic-comments/client` is a typed client for the Worker routes. It uses only `fetch`, so it runs in browsers,
249
+ Workers, and Node.
250
+
251
+ ```ts
252
+ import { createClient, CommentsApiError } from "agentic-comments/client";
253
+
254
+ const comments = createClient({ baseUrl: "https://comments.example.com", token });
255
+ await comments.createDocument({ id: "d1", text: "The quick brown fox" });
256
+ const thread = await comments.addThread("d1", { anchor: { type: "text", quote: "fox" }, body: "Which fox?" });
257
+ await comments.reply("d1", thread.id, { body: "This one." });
258
+ try {
259
+ await comments.accept("d1", "missing");
260
+ } catch (error) {
261
+ if (error instanceof CommentsApiError) console.error(error.code, error.status);
262
+ }
263
+ ```
264
+
265
+ - The methods match the routes: `me`, `createDocument`, `getDocument`, `listThreads`, `getThread`, `addThread`, `reply`,
266
+ `resolve`, `reopen`, `relocate`, `listSuggestions`, `suggest`, `accept`, `reject`. Each takes an optional `{ signal }`.
267
+ - `baseUrl` is the Worker URL, `http` or `https`, with no query, hash, or credentials.
268
+ - The client never sends an author. The Worker stamps one from the token.
269
+ - A failed call throws `CommentsApiError`. `code` is the API error code, or `UNKNOWN` when the reply is not an API
270
+ error. `status` is the HTTP status. A `200` reply with the wrong shape also fails as `UNKNOWN`.
271
+ - The client never follows a redirect and never retries. A write that you abort after the client sends it can still apply:
272
+ read the thread before you try again.
273
+
246
274
  ## Live demo
247
275
 
248
276
  A demo Worker runs at <https://agentic-comments-demo.hello-fd1.workers.dev>. It is a staging site. Check it with
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  VERSION
3
- } from "./chunk-DRACF66Y.js";
3
+ } from "./chunk-WQHJNHAR.js";
4
4
  import {
5
5
  CommentsService
6
6
  } from "./chunk-NQWRFKJ6.js";
@@ -1,6 +1,6 @@
1
1
  // src/version.ts
2
2
  import { createRequire } from "module";
3
- var VERSION = true ? "0.4.0" : readPackageVersion();
3
+ var VERSION = true ? "0.5.0" : readPackageVersion();
4
4
 
5
5
  export {
6
6
  VERSION
package/dist/cli.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  } from "./chunk-HXXJNFEO.js";
7
7
  import {
8
8
  VERSION
9
- } from "./chunk-DRACF66Y.js";
9
+ } from "./chunk-WQHJNHAR.js";
10
10
  import {
11
11
  CommentsService,
12
12
  TEXT_ROOT
@@ -155,7 +155,7 @@ async function runCli(argv, io) {
155
155
  return 0;
156
156
  }
157
157
  if (p.command === "mcp") {
158
- const { startMcp } = await import("./mcp-ACC4QIEM.js");
158
+ const { startMcp } = await import("./mcp-JMEXA2RI.js");
159
159
  await startMcp(store, operatorActor(p.flags["--as"] ?? io.env.AGENTIC_COMMENTS_AS, io.env.AGENTIC_COMMENTS_SCOPES));
160
160
  return 0;
161
161
  }
@@ -0,0 +1,72 @@
1
+ import { L as ListFilter, A as AnchorSpec } from './service-hpWbnSmc.js';
2
+ import { C as CommentsErrorCode } from './errors-CNgWMVQT.js';
3
+ import { PrincipalKind } from 'agentic-identity';
4
+ export { PrincipalKind } from 'agentic-identity';
5
+ import { T as Thread, N as NewReply, M as Message, S as Suggestion } from './suggestions-CvJwo6r4.js';
6
+ import 'yjs';
7
+
8
+ /** Codes the comment rules return, plus the ones the Worker and its auth return. */
9
+ type ApiErrorCode = CommentsErrorCode | "UNAUTHENTICATED" | "NOT_FOUND" | "INTERNAL" | "UNKNOWN";
10
+ /** Thrown for every reply that is not a success. `code` is the API error code and `status` is the HTTP status. */
11
+ declare class CommentsApiError extends Error {
12
+ readonly code: ApiErrorCode;
13
+ readonly status: number;
14
+ constructor(code: ApiErrorCode, status: number, message: string);
15
+ }
16
+ interface ClientOptions {
17
+ /** The Worker origin, for example https://comments.example.com. The client adds /api/v1. */
18
+ baseUrl: string;
19
+ /** A bearer credential that the Worker's auth accepts. */
20
+ token: string;
21
+ fetch?: typeof fetch;
22
+ }
23
+ /**
24
+ * Every method takes an optional signal. An aborted call rejects with the signal reason, not a
25
+ * CommentsApiError. A write that was aborted after it was sent may still have applied: read the
26
+ * thread before you try again.
27
+ */
28
+ interface CallOptions {
29
+ signal?: AbortSignal;
30
+ }
31
+ interface Me {
32
+ kind: PrincipalKind;
33
+ name: string;
34
+ scopes: string[];
35
+ }
36
+ type ThreadFilter = ListFilter;
37
+ type SuggestionListFilter = Pick<ListFilter, "status" | "anchorState">;
38
+ interface CommentsClient {
39
+ me(options?: CallOptions): Promise<Me>;
40
+ createDocument(input: {
41
+ id: string;
42
+ text?: string;
43
+ }, options?: CallOptions): Promise<{
44
+ id: string;
45
+ }>;
46
+ getDocument(docId: string, options?: CallOptions): Promise<{
47
+ id: string;
48
+ text: string;
49
+ }>;
50
+ listThreads(docId: string, filter?: ThreadFilter, options?: CallOptions): Promise<Thread[]>;
51
+ getThread(docId: string, threadId: string, options?: CallOptions): Promise<Thread>;
52
+ addThread(docId: string, input: {
53
+ anchor: AnchorSpec;
54
+ body: string;
55
+ mentions?: string[];
56
+ }, options?: CallOptions): Promise<Thread>;
57
+ reply(docId: string, threadId: string, input: NewReply, options?: CallOptions): Promise<Message>;
58
+ resolve(docId: string, threadId: string, options?: CallOptions): Promise<Thread>;
59
+ reopen(docId: string, threadId: string, options?: CallOptions): Promise<Thread>;
60
+ relocate(docId: string, threadId: string, options?: CallOptions): Promise<Thread>;
61
+ listSuggestions(docId: string, filter?: SuggestionListFilter, options?: CallOptions): Promise<Suggestion[]>;
62
+ suggest(docId: string, input: {
63
+ anchor: AnchorSpec;
64
+ change: unknown;
65
+ summary?: string;
66
+ }, options?: CallOptions): Promise<Suggestion>;
67
+ accept(docId: string, suggestionId: string, options?: CallOptions): Promise<Suggestion>;
68
+ reject(docId: string, suggestionId: string, options?: CallOptions): Promise<Suggestion>;
69
+ }
70
+ declare function createClient(options: ClientOptions): CommentsClient;
71
+
72
+ export { AnchorSpec, type ApiErrorCode, type CallOptions, type ClientOptions, CommentsApiError, type CommentsClient, CommentsErrorCode, ListFilter, type Me, Message, NewReply, Suggestion, type SuggestionListFilter, Thread, type ThreadFilter, createClient };
package/dist/client.js ADDED
@@ -0,0 +1,135 @@
1
+ // src/client.ts
2
+ var CODES = {
3
+ INVALID_AUTHOR: true,
4
+ INVALID_INPUT: true,
5
+ INVALID_ANCHOR: true,
6
+ NO_THREAD: true,
7
+ THREAD_RESOLVED: true,
8
+ CORRUPT: true,
9
+ NO_SUGGESTION: true,
10
+ FORBIDDEN: true,
11
+ ALREADY_DECIDED: true,
12
+ APPLY_FAILED: true,
13
+ RELOCATE_FAILED: true,
14
+ STORE_MISSING: true,
15
+ STORE_EXISTS: true,
16
+ STORE_CORRUPT: true,
17
+ STORE_LOCKED: true,
18
+ STORE_FULL: true,
19
+ NO_DOCUMENT: true,
20
+ DOCUMENT_EXISTS: true,
21
+ CONFLICT: true,
22
+ MISCONFIGURED: true,
23
+ USAGE: true,
24
+ UNAUTHENTICATED: true,
25
+ NOT_FOUND: true,
26
+ INTERNAL: true,
27
+ UNKNOWN: true
28
+ };
29
+ var isKnownCode = (v) => typeof v === "string" && Object.hasOwn(CODES, v);
30
+ var CommentsApiError = class extends Error {
31
+ code;
32
+ status;
33
+ constructor(code, status, message) {
34
+ super(message);
35
+ this.name = "CommentsApiError";
36
+ this.code = code;
37
+ this.status = status;
38
+ }
39
+ };
40
+ var KINDS = { human: true, agent_of_human: true, agent_of_agent: true, agent: true, user_token: true };
41
+ var isObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
42
+ var isString = (v) => typeof v === "string";
43
+ var SHAPES = {
44
+ me: (v) => isObject(v) && (typeof v.kind === "string" && Object.hasOwn(KINDS, v.kind)) && isString(v.name) && Array.isArray(v.scopes),
45
+ created: (v) => isObject(v) && isString(v.id),
46
+ document: (v) => isObject(v) && isString(v.id) && isString(v.text),
47
+ thread: (v) => isObject(v) && isString(v.id) && isObject(v.anchor) && (v.status === "open" || v.status === "resolved") && Array.isArray(v.messages),
48
+ threads: (v) => Array.isArray(v) && v.every(SHAPES.thread),
49
+ message: (v) => isObject(v) && isString(v.id) && isString(v.body) && isObject(v.author),
50
+ suggestion: (v) => isObject(v) && isString(v.id) && isObject(v.anchor) && isString(v.status) && isObject(v.author),
51
+ suggestions: (v) => Array.isArray(v) && v.every(SHAPES.suggestion)
52
+ };
53
+ function createClient(options) {
54
+ if (typeof options.baseUrl !== "string" || options.baseUrl === "") throw new TypeError("createClient needs a baseUrl");
55
+ if (typeof options.token !== "string" || options.token === "") throw new TypeError("createClient needs a token");
56
+ let origin;
57
+ try {
58
+ origin = new URL(options.baseUrl);
59
+ } catch {
60
+ throw new TypeError("baseUrl must be an absolute URL");
61
+ }
62
+ if (origin.protocol !== "https:" && origin.protocol !== "http:") throw new TypeError("baseUrl must be an http or https URL");
63
+ if (origin.search !== "" || origin.hash !== "" || origin.username !== "" || origin.password !== "") {
64
+ throw new TypeError("baseUrl must have no query, hash, or credentials");
65
+ }
66
+ const root = `${origin.origin}${origin.pathname.replace(/\/+$/, "")}/api/v1`;
67
+ const token = options.token;
68
+ const send = (input, init) => {
69
+ const run = options.fetch ?? globalThis.fetch;
70
+ return run(input, init);
71
+ };
72
+ const seg = (value) => {
73
+ 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 ..");
74
+ return encodeURIComponent(value);
75
+ };
76
+ const query = (filter) => {
77
+ const params = new URLSearchParams();
78
+ for (const [key, value] of Object.entries(filter ?? {})) if (value !== void 0) params.set(key, String(value));
79
+ const text = params.toString();
80
+ return text === "" ? "" : `?${text}`;
81
+ };
82
+ async function call(method, path, body, callOptions, shape) {
83
+ const headers = { authorization: `Bearer ${token}` };
84
+ if (body !== void 0) headers["content-type"] = "application/json";
85
+ const response = await send(`${root}${path}`, {
86
+ method,
87
+ headers,
88
+ body: body === void 0 ? void 0 : JSON.stringify(body),
89
+ signal: callOptions?.signal,
90
+ // Never follow a redirect: it would send a write to another host.
91
+ redirect: "manual"
92
+ });
93
+ const text = await response.text();
94
+ let parsed;
95
+ try {
96
+ parsed = text === "" ? void 0 : JSON.parse(text);
97
+ } catch {
98
+ parsed = void 0;
99
+ }
100
+ if (!response.ok) {
101
+ const error = parsed?.error;
102
+ const code = isKnownCode(error?.code) ? error.code : "UNKNOWN";
103
+ const message = typeof error?.message === "string" ? error.message : `HTTP ${response.status}`;
104
+ throw new CommentsApiError(code, response.status, message);
105
+ }
106
+ if (!SHAPES[shape](parsed)) throw new CommentsApiError("UNKNOWN", response.status, `the reply is not a ${shape}`);
107
+ return parsed;
108
+ }
109
+ const doc = (id) => `/docs/${seg(id)}`;
110
+ const thread = (id, threadId) => `${doc(id)}/threads/${seg(threadId)}`;
111
+ const suggestion = (id, suggestionId) => `${doc(id)}/suggestions/${seg(suggestionId)}`;
112
+ const methods = {
113
+ me: (o) => call("GET", "/me", void 0, o, "me"),
114
+ createDocument: (input, o) => call("POST", "/docs", input, o, "created"),
115
+ getDocument: (id, o) => call("GET", doc(id), void 0, o, "document"),
116
+ listThreads: (id, filter, o) => call("GET", `${doc(id)}/threads${query(filter)}`, void 0, o, "threads"),
117
+ getThread: (id, threadId, o) => call("GET", thread(id, threadId), void 0, o, "thread"),
118
+ addThread: (id, input, o) => call("POST", `${doc(id)}/threads`, input, o, "thread"),
119
+ reply: (id, threadId, input, o) => call("POST", `${thread(id, threadId)}/replies`, input, o, "message"),
120
+ resolve: (id, threadId, o) => call("POST", `${thread(id, threadId)}/resolve`, void 0, o, "thread"),
121
+ reopen: (id, threadId, o) => call("POST", `${thread(id, threadId)}/reopen`, void 0, o, "thread"),
122
+ relocate: (id, threadId, o) => call("POST", `${thread(id, threadId)}/relocate`, void 0, o, "thread"),
123
+ listSuggestions: (id, filter, o) => call("GET", `${doc(id)}/suggestions${query(filter)}`, void 0, o, "suggestions"),
124
+ suggest: (id, input, o) => call("POST", `${doc(id)}/suggestions`, input, o, "suggestion"),
125
+ accept: (id, suggestionId, o) => call("POST", `${suggestion(id, suggestionId)}/accept`, void 0, o, "suggestion"),
126
+ reject: (id, suggestionId, o) => call("POST", `${suggestion(id, suggestionId)}/reject`, void 0, o, "suggestion")
127
+ };
128
+ return Object.fromEntries(
129
+ Object.entries(methods).map(([name, method]) => [name, async (...args) => method(...args)])
130
+ );
131
+ }
132
+ export {
133
+ CommentsApiError,
134
+ createClient
135
+ };
@@ -0,0 +1,7 @@
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 };
package/dist/index.d.ts CHANGED
@@ -1,16 +1,11 @@
1
- import { a as Author, C as Comments, b as Suggestions } from './suggestions-oj8fd-nG.js';
2
- export { c as ACCEPT_SCOPE, A as Actor, d as Anchor, e as AnchorKind, f as AnchorState, g as ApplyContext, h as CanvasAnchor, i as CellAnchor, j as CommentsOptions, M as Message, N as NewReply, k as NewSuggestion, l as NewThread, R as RelativePositionJson, m as RevertContext, n as SlideAnchor, o as StampInput, S as Suggestion, p as SuggestionFilter, q as SuggestionHost, r as TextAnchor, T as Thread, s as ThreadFilter, t as authorLabel, u as stamp, v as textAnchor, w as textOf } from './suggestions-oj8fd-nG.js';
1
+ import { a as Author, C as Comments, b as Suggestions } from './suggestions-CvJwo6r4.js';
2
+ export { c as ACCEPT_SCOPE, A as Actor, d as Anchor, e as AnchorKind, f as AnchorState, g as ApplyContext, h as CanvasAnchor, i as CellAnchor, j as CommentsOptions, M as Message, N as NewReply, k as NewSuggestion, l as NewThread, R as RelativePositionJson, m as RevertContext, n as SlideAnchor, o as StampInput, S as Suggestion, p as SuggestionFilter, q as SuggestionHost, r as TextAnchor, T as Thread, s as ThreadFilter, t as authorLabel, u as stamp, v as textAnchor, w as textOf } from './suggestions-CvJwo6r4.js';
3
+ export { a as CommentsError, C as CommentsErrorCode } from './errors-CNgWMVQT.js';
3
4
  export { PRINCIPAL_KINDS, PrincipalKind } from 'agentic-identity';
4
5
  import 'yjs';
5
6
 
6
7
  declare const VERSION: string;
7
8
 
8
- 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";
9
- declare class CommentsError extends Error {
10
- readonly code: CommentsErrorCode;
11
- constructor(code: CommentsErrorCode, message: string);
12
- }
13
-
14
9
  type CommentEventType = "thread.created" | "message.added" | "mention" | "thread.resolved" | "thread.reopened" | "suggestion.created" | "suggestion.accepted" | "suggestion.rejected";
15
10
  /**
16
11
  * One change, in terms a notifier can route. It holds ids and never the text of a comment, a suggestion, or
@@ -103,4 +98,4 @@ declare class Notifications {
103
98
  private decided;
104
99
  }
105
100
 
106
- export { Author, type CommentEvent, type CommentEventType, type CommentListener, Comments, CommentsError, type CommentsErrorCode, Notifications, type NotificationsOptions, Suggestions, VERSION };
101
+ export { Author, type CommentEvent, type CommentEventType, type CommentListener, Comments, Notifications, type NotificationsOptions, Suggestions, VERSION };
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  VERSION
3
- } from "./chunk-DRACF66Y.js";
3
+ } from "./chunk-WQHJNHAR.js";
4
4
  import {
5
5
  ACCEPT_SCOPE,
6
6
  Actor,
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-Dxw9TVxY.js';
3
- import { q as SuggestionHost, A as Actor } from './suggestions-oj8fd-nG.js';
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';
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. */
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  createMcpServer,
3
3
  startMcp
4
- } from "./chunk-R4LH36VG.js";
5
- import "./chunk-DRACF66Y.js";
4
+ } from "./chunk-ABXAPWXD.js";
5
+ import "./chunk-WQHJNHAR.js";
6
6
  import "./chunk-NQWRFKJ6.js";
7
7
  import "./chunk-BSNBDKR2.js";
8
8
  export {
package/dist/mcp-bin.js CHANGED
@@ -6,10 +6,10 @@ import {
6
6
  } from "./chunk-HXXJNFEO.js";
7
7
  import {
8
8
  startMcp
9
- } from "./chunk-R4LH36VG.js";
9
+ } from "./chunk-ABXAPWXD.js";
10
10
  import {
11
11
  VERSION
12
- } from "./chunk-DRACF66Y.js";
12
+ } from "./chunk-WQHJNHAR.js";
13
13
  import "./chunk-NQWRFKJ6.js";
14
14
  import "./chunk-BSNBDKR2.js";
15
15
 
@@ -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-oj8fd-nG.js';
2
+ import { A as Actor, T as Thread, M as Message, S as Suggestion } from './suggestions-CvJwo6r4.js';
3
3
 
4
4
  /** Where a comment or suggestion points, in terms a person or agent can write down. */
5
5
  type AnchorSpec = {
package/dist/worker.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { Hono } from 'hono';
2
2
  import { Auth, Store, PrincipalContext, Region } from 'agentic-identity';
3
3
  import { D1Like } from 'agentic-identity/cloudflare';
4
- import { A as Actor } from './suggestions-oj8fd-nG.js';
5
- import { C as CommentsService } from './service-Dxw9TVxY.js';
4
+ import { A as Actor } from './suggestions-CvJwo6r4.js';
5
+ import { C as CommentsService } from './service-hpWbnSmc.js';
6
6
  import 'yjs';
7
7
 
8
8
  interface WorkerEnv {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentic-comments",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Comment threads and suggestions anchored to any content, from people and agents.",
5
5
  "keywords": [
6
6
  "comments",
@@ -48,6 +48,10 @@
48
48
  "types": "./dist/anchoring.d.ts",
49
49
  "import": "./dist/anchoring.js"
50
50
  },
51
+ "./client": {
52
+ "types": "./dist/client.d.ts",
53
+ "import": "./dist/client.js"
54
+ },
51
55
  "./local": {
52
56
  "types": "./dist/local.d.ts",
53
57
  "import": "./dist/local.js"
@@ -1,6 +1,35 @@
1
1
  import * as Y from 'yjs';
2
2
  import { PrincipalKind } from 'agentic-identity';
3
3
 
4
+ /** What a thread keeps about who wrote a message. Scopes never reach storage. */
5
+ interface Author {
6
+ kind: PrincipalKind;
7
+ name: string;
8
+ }
9
+ interface StampInput {
10
+ kind: PrincipalKind;
11
+ name: string;
12
+ /** Rights the host granted this principal, such as `comments:accept`. Used to decide, never stored. */
13
+ scopes?: string[];
14
+ }
15
+ /**
16
+ * The only author a write accepts. The host makes one from the principal it authenticated, and an
17
+ * operation never reads an author from its input. An object that `stamp()` did not make is refused,
18
+ * and a stamped one cannot change.
19
+ */
20
+ declare class Actor implements Author {
21
+ readonly kind: PrincipalKind;
22
+ readonly name: string;
23
+ readonly scopes: readonly string[];
24
+ private constructor();
25
+ static of(input: StampInput): Actor;
26
+ static is(value: unknown): value is Actor;
27
+ }
28
+ /** Make the author for one authenticated principal. Throws when the kind or name is missing or wrong. */
29
+ declare const stamp: (input: StampInput) => Actor;
30
+ /** The label a UI shows. Agent work stays marked as machine-made. */
31
+ declare function authorLabel(author: Author): "person" | "person via agent" | "agent";
32
+
4
33
  /** A Yjs relative position as JSON. It names an item, not an index, so it follows its text. */
5
34
  interface RelativePositionJson {
6
35
  type?: {
@@ -66,35 +95,6 @@ declare function textOf(type: Y.Text | Y.XmlText): string;
66
95
  /** Pin a range of a Yjs text. Later edits by any replica do not move the anchor off its text. */
67
96
  declare function textAnchor(type: Y.Text | Y.XmlText, from: number, to: number): TextAnchor;
68
97
 
69
- /** What a thread keeps about who wrote a message. Scopes never reach storage. */
70
- interface Author {
71
- kind: PrincipalKind;
72
- name: string;
73
- }
74
- interface StampInput {
75
- kind: PrincipalKind;
76
- name: string;
77
- /** Rights the host granted this principal, such as `comments:accept`. Used to decide, never stored. */
78
- scopes?: string[];
79
- }
80
- /**
81
- * The only author a write accepts. The host makes one from the principal it authenticated, and an
82
- * operation never reads an author from its input. An object that `stamp()` did not make is refused,
83
- * and a stamped one cannot change.
84
- */
85
- declare class Actor implements Author {
86
- readonly kind: PrincipalKind;
87
- readonly name: string;
88
- readonly scopes: readonly string[];
89
- private constructor();
90
- static of(input: StampInput): Actor;
91
- static is(value: unknown): value is Actor;
92
- }
93
- /** Make the author for one authenticated principal. Throws when the kind or name is missing or wrong. */
94
- declare const stamp: (input: StampInput) => Actor;
95
- /** The label a UI shows. Agent work stays marked as machine-made. */
96
- declare function authorLabel(author: Author): "person" | "person via agent" | "agent";
97
-
98
98
  interface Message {
99
99
  id: string;
100
100
  at: string;