@cello-protocol/daemon 0.0.132 → 0.0.134
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/bin/cello-daemon.js +25 -0
- package/dist/bin/cello-daemon.js.map +1 -1
- package/dist/content-park.d.ts.map +1 -1
- package/dist/content-park.js +29 -11
- package/dist/content-park.js.map +1 -1
- package/dist/daemon.d.ts +11 -0
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +455 -17
- package/dist/daemon.js.map +1 -1
- package/dist/document-ack-inbound.d.ts +57 -0
- package/dist/document-ack-inbound.d.ts.map +1 -0
- package/dist/document-ack-inbound.js +174 -0
- package/dist/document-ack-inbound.js.map +1 -0
- package/dist/document-control-notifier.d.ts +61 -0
- package/dist/document-control-notifier.d.ts.map +1 -0
- package/dist/document-control-notifier.js +70 -0
- package/dist/document-control-notifier.js.map +1 -0
- package/dist/document-delivery-transport.d.ts +94 -0
- package/dist/document-delivery-transport.d.ts.map +1 -0
- package/dist/document-delivery-transport.js +179 -0
- package/dist/document-delivery-transport.js.map +1 -0
- package/dist/document-delivery.d.ts +181 -0
- package/dist/document-delivery.d.ts.map +1 -0
- package/dist/document-delivery.js +289 -0
- package/dist/document-delivery.js.map +1 -0
- package/dist/document-frame-router.d.ts +210 -0
- package/dist/document-frame-router.d.ts.map +1 -0
- package/dist/document-frame-router.js +396 -0
- package/dist/document-frame-router.js.map +1 -0
- package/dist/document-handlers.d.ts +47 -0
- package/dist/document-handlers.d.ts.map +1 -0
- package/dist/document-handlers.js +657 -0
- package/dist/document-handlers.js.map +1 -0
- package/dist/document-handshake.d.ts +156 -0
- package/dist/document-handshake.d.ts.map +1 -0
- package/dist/document-handshake.js +398 -0
- package/dist/document-handshake.js.map +1 -0
- package/dist/document-inbound.d.ts +91 -0
- package/dist/document-inbound.d.ts.map +1 -0
- package/dist/document-inbound.js +290 -0
- package/dist/document-inbound.js.map +1 -0
- package/dist/document-layer.d.ts +137 -0
- package/dist/document-layer.d.ts.map +1 -0
- package/dist/document-layer.js +255 -0
- package/dist/document-layer.js.map +1 -0
- package/dist/document-lifecycle.d.ts +125 -0
- package/dist/document-lifecycle.d.ts.map +1 -0
- package/dist/document-lifecycle.js +433 -0
- package/dist/document-lifecycle.js.map +1 -0
- package/dist/document-live-docs.d.ts +58 -0
- package/dist/document-live-docs.d.ts.map +1 -0
- package/dist/document-live-docs.js +126 -0
- package/dist/document-live-docs.js.map +1 -0
- package/dist/document-notify.d.ts +173 -0
- package/dist/document-notify.d.ts.map +1 -0
- package/dist/document-notify.js +438 -0
- package/dist/document-notify.js.map +1 -0
- package/dist/document-publish.d.ts +67 -0
- package/dist/document-publish.d.ts.map +1 -0
- package/dist/document-publish.js +149 -0
- package/dist/document-publish.js.map +1 -0
- package/dist/document-reachability.d.ts +42 -0
- package/dist/document-reachability.d.ts.map +1 -0
- package/dist/document-reachability.js +80 -0
- package/dist/document-reachability.js.map +1 -0
- package/dist/document-rejection.d.ts +240 -0
- package/dist/document-rejection.d.ts.map +1 -0
- package/dist/document-rejection.js +407 -0
- package/dist/document-rejection.js.map +1 -0
- package/dist/document-store.d.ts +154 -8
- package/dist/document-store.d.ts.map +1 -1
- package/dist/document-store.js +462 -4
- package/dist/document-store.js.map +1 -1
- package/dist/document-write-path.d.ts.map +1 -1
- package/dist/document-write-path.js +10 -43
- package/dist/document-write-path.js.map +1 -1
- package/dist/inbound-sessions.d.ts.map +1 -1
- package/dist/inbound-sessions.js +4 -0
- package/dist/inbound-sessions.js.map +1 -1
- package/dist/initiate-session-handler.d.ts +24 -1
- package/dist/initiate-session-handler.d.ts.map +1 -1
- package/dist/initiate-session-handler.js +35 -9
- package/dist/initiate-session-handler.js.map +1 -1
- package/dist/ipc-server.d.ts +11 -1
- package/dist/ipc-server.d.ts.map +1 -1
- package/dist/ipc-server.js +7 -1
- package/dist/ipc-server.js.map +1 -1
- package/dist/line-lcs.d.ts +51 -0
- package/dist/line-lcs.d.ts.map +1 -0
- package/dist/line-lcs.js +71 -0
- package/dist/line-lcs.js.map +1 -0
- package/dist/notification-handlers.d.ts.map +1 -1
- package/dist/notification-handlers.js +31 -14
- package/dist/notification-handlers.js.map +1 -1
- package/dist/outbound-sessions.d.ts +2 -0
- package/dist/outbound-sessions.d.ts.map +1 -1
- package/dist/outbound-sessions.js +11 -1
- package/dist/outbound-sessions.js.map +1 -1
- package/dist/session-content-handlers.d.ts.map +1 -1
- package/dist/session-content-handlers.js +3 -2
- package/dist/session-content-handlers.js.map +1 -1
- package/dist/session-node-manager.d.ts +26 -4
- package/dist/session-node-manager.d.ts.map +1 -1
- package/dist/session-node-manager.js +201 -15
- package/dist/session-node-manager.js.map +1 -1
- package/dist/session-read-handlers.js +1 -1
- package/dist/session-read-handlers.js.map +1 -1
- package/dist/types.d.ts +40 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/vocabulary.d.ts.map +1 -1
- package/dist/vocabulary.js +16 -0
- package/dist/vocabulary.js.map +1 -1
- package/dist/wire-content-hash.d.ts +27 -0
- package/dist/wire-content-hash.d.ts.map +1 -0
- package/dist/wire-content-hash.js +37 -0
- package/dist/wire-content-hash.js.map +1 -0
- package/package.json +5 -5
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DOD-DOC-NOTIFY-1 — passive notification and the two read calls (§16.5, §4.1).
|
|
3
|
+
*
|
|
4
|
+
* ── WHY NO DOORBELL ───────────────────────────────────────────────────────────────────────────
|
|
5
|
+
*
|
|
6
|
+
* A document update raises NO doorbell (§11.3 — doorbell-on-update is parked). The notice sits in
|
|
7
|
+
* the inbox aggregation and the agent finds it the next time it looks. That is the whole design:
|
|
8
|
+
* a collaborator typing produces a stream of updates, and a doorbell per update would interrupt
|
|
9
|
+
* the operator's agent continuously for something that has no deadline. The one-line summary the
|
|
10
|
+
* agent eventually reads carries the same information at none of the cost.
|
|
11
|
+
*
|
|
12
|
+
* Following the `contact_rename_notices` precedent: there is no inbox store — the inbox is computed
|
|
13
|
+
* per call — so a derived section needs its own table and its own getter.
|
|
14
|
+
*
|
|
15
|
+
* The notice carries `document_id` and a PENDING COUNT, and nothing else. Not a preview, not the
|
|
16
|
+
* first line, not the author's words. Content reaches the agent only through the two read calls
|
|
17
|
+
* below, which are screened; a notification that carried content would be an unscreened path into
|
|
18
|
+
* the agent's context, which is the whole prompt-injection surface §3.1 exists to close.
|
|
19
|
+
*
|
|
20
|
+
* ── THE TWO READ CALLS (§4.1) ─────────────────────────────────────────────────────────────────
|
|
21
|
+
*
|
|
22
|
+
* `diffStats` is STRUCTURAL ONLY — counts, ranges, and the overlap flag. It exists so an agent can
|
|
23
|
+
* decide whether to read without reading, which is exactly what a cautious agent should be able to
|
|
24
|
+
* do. `diff` is the git-like diff itself and is an ordinary screened read.
|
|
25
|
+
*
|
|
26
|
+
* ── SUPPORTED TYPES FOR `diff`, DECIDED IN-UNIT ───────────────────────────────────────────────
|
|
27
|
+
*
|
|
28
|
+
* Markdown, plain text, and JSON. Markdown and plain text share one line-diff implementation (a
|
|
29
|
+
* markdown file IS lines); JSON gets a key-path diff because a line diff over re-serialized JSON
|
|
30
|
+
* reports the whole document as changed whenever a formatter touches it. Anything else returns a
|
|
31
|
+
* REFUSAL naming the type rather than a line diff over bytes that are not lines — a diff that is
|
|
32
|
+
* wrong is worse than no diff, because the agent cannot tell.
|
|
33
|
+
*/
|
|
34
|
+
import type { DocumentStore } from "./document-store.js";
|
|
35
|
+
import type { Logger } from "./types.js";
|
|
36
|
+
/** Types `diff` can render. Decided in-unit; see the header for why the list is closed. */
|
|
37
|
+
export declare const DIFFABLE_DOCUMENT_TYPES: readonly ["markdown", "text", "json"];
|
|
38
|
+
export type DiffableDocumentType = (typeof DIFFABLE_DOCUMENT_TYPES)[number];
|
|
39
|
+
export interface DocumentNotice {
|
|
40
|
+
documentId: string;
|
|
41
|
+
pending: number;
|
|
42
|
+
noticedAtMs: number;
|
|
43
|
+
}
|
|
44
|
+
export interface DiffStats {
|
|
45
|
+
documentId: string;
|
|
46
|
+
linesAdded: number;
|
|
47
|
+
linesRemoved: number;
|
|
48
|
+
/** 1-based inclusive line ranges the change touches. Ranges, never the lines themselves. */
|
|
49
|
+
ranges: Array<{
|
|
50
|
+
start: number;
|
|
51
|
+
end: number;
|
|
52
|
+
}>;
|
|
53
|
+
/** For JSON: the key paths touched. Empty for line-oriented types. */
|
|
54
|
+
keyPaths: string[];
|
|
55
|
+
/**
|
|
56
|
+
* Whether the peer's change touches a region this operator also edited. The one field here that
|
|
57
|
+
* is a judgement rather than a count, and the reason an agent reads stats before deciding.
|
|
58
|
+
*/
|
|
59
|
+
overlap: boolean | null;
|
|
60
|
+
/** Whether `keyPaths` was computed at all — an empty list is otherwise read as "none changed". */
|
|
61
|
+
keyPathsComputed: boolean;
|
|
62
|
+
/** The document exceeded the LCS limit, so the numbers above are bounds, not a measurement. */
|
|
63
|
+
truncated: boolean;
|
|
64
|
+
}
|
|
65
|
+
export type DiffResult = {
|
|
66
|
+
ok: true;
|
|
67
|
+
documentType: DiffableDocumentType;
|
|
68
|
+
diff: string;
|
|
69
|
+
/**
|
|
70
|
+
* Set when the renderer could not do what the type asked for. A notice inside the diff STRING
|
|
71
|
+
* is readable by a human and invisible to a caller branching on the result — and it travels
|
|
72
|
+
* as screened content, which is the wrong channel for a fact about the renderer.
|
|
73
|
+
*/
|
|
74
|
+
fallback?: "unparseable_json_line_diff";
|
|
75
|
+
} | {
|
|
76
|
+
ok: false;
|
|
77
|
+
reason: string;
|
|
78
|
+
detail: string;
|
|
79
|
+
};
|
|
80
|
+
export declare class DocumentNotifications {
|
|
81
|
+
#private;
|
|
82
|
+
constructor(store: DocumentStore, logger: Logger);
|
|
83
|
+
/**
|
|
84
|
+
* Record what this agent last SAW, so a later diff has a "before".
|
|
85
|
+
*
|
|
86
|
+
* REPLACES. A read mark is a bookmark, not a history — keeping every read would make the table
|
|
87
|
+
* grow with attention rather than with content, and no caller wants the second-most-recent read.
|
|
88
|
+
*/
|
|
89
|
+
markRead(agentId: string, documentId: string, text: string, nowMs: number): void;
|
|
90
|
+
/**
|
|
91
|
+
* Record the text WE just wrote, against the current read mark.
|
|
92
|
+
*
|
|
93
|
+
* No-ops when there is no read mark: without a baseline there is nothing to measure our edit
|
|
94
|
+
* against, and inventing one would make the first diff after a write report the whole document as
|
|
95
|
+
* a conflict.
|
|
96
|
+
*/
|
|
97
|
+
markWritten(agentId: string, documentId: string, text: string): void;
|
|
98
|
+
/**
|
|
99
|
+
* The lines WE edited since the last read, 1-based, in the baseline's coordinate space — or null
|
|
100
|
+
* when we have not written since, which is "not computed" and must stay distinguishable from "no
|
|
101
|
+
* conflict". `diffStats` requires the distinction for exactly that reason.
|
|
102
|
+
*/
|
|
103
|
+
/**
|
|
104
|
+
* WHY THIS EXISTS, stated where the reader will need it:
|
|
105
|
+
*
|
|
106
|
+
* At diff time the two texts in hand are the baseline and the current document, and no amount of
|
|
107
|
+
* comparing them says which of the changes were the PEER's. So `overlap` was hardcoded `null`
|
|
108
|
+
* while three shipped instruction sheets told agents to branch on it — and `null` is falsy in
|
|
109
|
+
* JSON, so every one of them got the reassuring answer on every call. That is the precise trap
|
|
110
|
+
* `diffStats` made its `myEdits` parameter required to prevent, defeated by the one caller.
|
|
111
|
+
*
|
|
112
|
+
* Diffing the baseline against what WE last wrote gives our own edited lines in the baseline's
|
|
113
|
+
* coordinate space, which is the space the diff reports its ranges in. Exact, not inferred.
|
|
114
|
+
*/
|
|
115
|
+
myEditedLines(agentId: string, documentId: string): number[] | null;
|
|
116
|
+
/**
|
|
117
|
+
* The text this agent last saw, or null if it has never read this document.
|
|
118
|
+
*
|
|
119
|
+
* NULL IS NOT THE EMPTY STRING, and callers must not conflate them. Never-read means the whole
|
|
120
|
+
* document is new to this agent; last-saw-empty means they read it when it was empty and
|
|
121
|
+
* everything since is a change they can be shown against that. Returning "" for both would render
|
|
122
|
+
* a first read of a long document as an enormous diff the agent then treats as "what just
|
|
123
|
+
* changed".
|
|
124
|
+
*/
|
|
125
|
+
lastSeen(agentId: string, documentId: string): string | null;
|
|
126
|
+
/**
|
|
127
|
+
* Note that a document has unread updates. Passive — this never pushes anything.
|
|
128
|
+
*
|
|
129
|
+
* The count is REPLACED rather than incremented, and is passed in by the caller from the log,
|
|
130
|
+
* so the notice cannot drift from the document. An incremented counter is a second tally of a
|
|
131
|
+
* fact the log already holds, and the two disagree the first time a notice write is missed.
|
|
132
|
+
*/
|
|
133
|
+
notice(agentId: string, documentId: string, pending: number, nowMs: number): void;
|
|
134
|
+
/** The derived inbox section. Read-only — fetching is what clears, and that is explicit. */
|
|
135
|
+
pending(agentId: string): DocumentNotice[];
|
|
136
|
+
/**
|
|
137
|
+
* Cleared by the agent's EXPLICIT fetch, never by the aggregation that displays it.
|
|
138
|
+
*
|
|
139
|
+
* Reading the inbox and clearing the notice are different acts: an inbox render that cleared
|
|
140
|
+
* would drop the notice for an agent that merely glanced at a summary, and the update would then
|
|
141
|
+
* be unread and unannounced — invisible in both directions.
|
|
142
|
+
*/
|
|
143
|
+
clear(agentId: string, documentId: string): boolean;
|
|
144
|
+
/**
|
|
145
|
+
* Structural counts and ranges — NO content. Lets an agent decide whether to read.
|
|
146
|
+
*
|
|
147
|
+
* `before` and `after` are the materialized text of the document at the two points; the caller
|
|
148
|
+
* holds the engine and therefore the materialization.
|
|
149
|
+
*
|
|
150
|
+
* Uses the SHARED line LCS, not a positional walk. The first version compared line i to line i,
|
|
151
|
+
* which is correct only while the line count is unchanged: inserting one line at the top of a
|
|
152
|
+
* three-line file reported `+4 -3` with a single range covering the whole document. The counts
|
|
153
|
+
* being wrong is the smaller half — `overlap` is derived from those ranges, so it became
|
|
154
|
+
* permanently true, and a flag that always says yes trains the agent to ignore it. Same defect
|
|
155
|
+
* class WRITE-1 measured, which is why there is now one LCS rather than two.
|
|
156
|
+
*/
|
|
157
|
+
diffStats(documentId: string, before: string, after: string,
|
|
158
|
+
/**
|
|
159
|
+
* The operator's own edited line numbers. REQUIRED — pass `null` to say "not computed".
|
|
160
|
+
* Defaulting to `[]` made a caller that simply forgot receive `overlap: false`, the reassuring
|
|
161
|
+
* answer, indistinguishable from "checked, and there is no conflict". This is the one
|
|
162
|
+
* judgement field in an otherwise structural result.
|
|
163
|
+
*/
|
|
164
|
+
myEdits: readonly number[] | null, documentType?: string): DiffStats;
|
|
165
|
+
/**
|
|
166
|
+
* The git-like diff. An ordinary screened read — the caller screens it; this renders it.
|
|
167
|
+
*
|
|
168
|
+
* An unsupported type is REFUSED by name rather than line-diffed anyway. A diff that is silently
|
|
169
|
+
* wrong is worse than no diff, because the agent has no way to tell and will act on it.
|
|
170
|
+
*/
|
|
171
|
+
diff(documentType: string, before: string, after: string, documentId: string): DiffResult;
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=document-notify.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"document-notify.d.ts","sourceRoot":"","sources":["../src/document-notify.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DOD-DOC-NOTIFY-1 — passive notification and the two read calls (§16.5, §4.1).
|
|
3
|
+
*
|
|
4
|
+
* ── WHY NO DOORBELL ───────────────────────────────────────────────────────────────────────────
|
|
5
|
+
*
|
|
6
|
+
* A document update raises NO doorbell (§11.3 — doorbell-on-update is parked). The notice sits in
|
|
7
|
+
* the inbox aggregation and the agent finds it the next time it looks. That is the whole design:
|
|
8
|
+
* a collaborator typing produces a stream of updates, and a doorbell per update would interrupt
|
|
9
|
+
* the operator's agent continuously for something that has no deadline. The one-line summary the
|
|
10
|
+
* agent eventually reads carries the same information at none of the cost.
|
|
11
|
+
*
|
|
12
|
+
* Following the `contact_rename_notices` precedent: there is no inbox store — the inbox is computed
|
|
13
|
+
* per call — so a derived section needs its own table and its own getter.
|
|
14
|
+
*
|
|
15
|
+
* The notice carries `document_id` and a PENDING COUNT, and nothing else. Not a preview, not the
|
|
16
|
+
* first line, not the author's words. Content reaches the agent only through the two read calls
|
|
17
|
+
* below, which are screened; a notification that carried content would be an unscreened path into
|
|
18
|
+
* the agent's context, which is the whole prompt-injection surface §3.1 exists to close.
|
|
19
|
+
*
|
|
20
|
+
* ── THE TWO READ CALLS (§4.1) ─────────────────────────────────────────────────────────────────
|
|
21
|
+
*
|
|
22
|
+
* `diffStats` is STRUCTURAL ONLY — counts, ranges, and the overlap flag. It exists so an agent can
|
|
23
|
+
* decide whether to read without reading, which is exactly what a cautious agent should be able to
|
|
24
|
+
* do. `diff` is the git-like diff itself and is an ordinary screened read.
|
|
25
|
+
*
|
|
26
|
+
* ── SUPPORTED TYPES FOR `diff`, DECIDED IN-UNIT ───────────────────────────────────────────────
|
|
27
|
+
*
|
|
28
|
+
* Markdown, plain text, and JSON. Markdown and plain text share one line-diff implementation (a
|
|
29
|
+
* markdown file IS lines); JSON gets a key-path diff because a line diff over re-serialized JSON
|
|
30
|
+
* reports the whole document as changed whenever a formatter touches it. Anything else returns a
|
|
31
|
+
* REFUSAL naming the type rather than a line diff over bytes that are not lines — a diff that is
|
|
32
|
+
* wrong is worse than no diff, because the agent cannot tell.
|
|
33
|
+
*/
|
|
34
|
+
import { lineRuns, toChunks } from "./line-lcs.js";
|
|
35
|
+
const CREATE_NOTICES_SQL = `
|
|
36
|
+
CREATE TABLE IF NOT EXISTS document_notices (
|
|
37
|
+
agent_id TEXT NOT NULL,
|
|
38
|
+
document_id TEXT NOT NULL,
|
|
39
|
+
-- The count, not the content. See the header.
|
|
40
|
+
pending INTEGER NOT NULL,
|
|
41
|
+
noticed_at INTEGER NOT NULL,
|
|
42
|
+
PRIMARY KEY (agent_id, document_id)
|
|
43
|
+
);
|
|
44
|
+
`;
|
|
45
|
+
/**
|
|
46
|
+
* WHAT THE OPERATOR LAST SAW, so `diff` can answer "what changed since I looked".
|
|
47
|
+
*
|
|
48
|
+
* The text itself, not a state vector. A state vector says which OPERATIONS this side has seen,
|
|
49
|
+
* which is a fact about the CRDT — and the question an agent is asking before it builds on a shared
|
|
50
|
+
* document is a fact about the TEXT: what words are different from the ones I read. Reconstructing
|
|
51
|
+
* the old text from a vector means replaying the log to a point, which is both expensive and a
|
|
52
|
+
* second answer to a question this column answers directly.
|
|
53
|
+
*
|
|
54
|
+
* Written by READ, never by write or by an arriving update. It marks what a human or an agent
|
|
55
|
+
* actually looked at; moving it on an inbound update would erase the very change the diff exists to
|
|
56
|
+
* show, silently, at the moment it arrived.
|
|
57
|
+
*/
|
|
58
|
+
const CREATE_READ_MARKS_SQL = `
|
|
59
|
+
CREATE TABLE IF NOT EXISTS document_read_marks (
|
|
60
|
+
agent_id TEXT NOT NULL,
|
|
61
|
+
document_id TEXT NOT NULL,
|
|
62
|
+
seen_text TEXT NOT NULL,
|
|
63
|
+
seen_at INTEGER NOT NULL,
|
|
64
|
+
-- The last text WE wrote since that read, or NULL if we have not written since. See the
|
|
65
|
+
-- myEditedLines comment below for why it exists: it is what makes the overlap flag a computed
|
|
66
|
+
-- answer rather than a hardcoded null.
|
|
67
|
+
--
|
|
68
|
+
-- No backticks in this string. It is a JS template literal, so one would terminate it — which
|
|
69
|
+
-- is exactly how this shipped broken for a minute.
|
|
70
|
+
my_text TEXT,
|
|
71
|
+
PRIMARY KEY (agent_id, document_id)
|
|
72
|
+
);
|
|
73
|
+
`;
|
|
74
|
+
/** Born on an existing table. See `PEER_DECISION_COLUMNS` in document-handshake.ts for the pattern. */
|
|
75
|
+
const READ_MARK_COLUMNS = ["ALTER TABLE document_read_marks ADD COLUMN my_text TEXT"];
|
|
76
|
+
/** Types `diff` can render. Decided in-unit; see the header for why the list is closed. */
|
|
77
|
+
export const DIFFABLE_DOCUMENT_TYPES = ["markdown", "text", "json"];
|
|
78
|
+
export class DocumentNotifications {
|
|
79
|
+
#store;
|
|
80
|
+
#logger;
|
|
81
|
+
constructor(store, logger) {
|
|
82
|
+
this.#store = store;
|
|
83
|
+
this.#logger = logger;
|
|
84
|
+
this.#store.rawDb.exec(CREATE_NOTICES_SQL);
|
|
85
|
+
this.#store.rawDb.exec(CREATE_READ_MARKS_SQL);
|
|
86
|
+
for (const sql of READ_MARK_COLUMNS) {
|
|
87
|
+
// Birth-gated so a daemon that already holds read marks gains the column instead of losing
|
|
88
|
+
// them. A failure here is either "already present" — the ordinary case — or a locked
|
|
89
|
+
// database, and the latter surfaces loudly on the very next SELECT rather than degrading.
|
|
90
|
+
const has = this.#store.rawDb.prepare("PRAGMA table_info(document_read_marks)").all().some((c) => c.name === "my_text");
|
|
91
|
+
if (!has)
|
|
92
|
+
this.#store.rawDb.exec(sql);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Record what this agent last SAW, so a later diff has a "before".
|
|
97
|
+
*
|
|
98
|
+
* REPLACES. A read mark is a bookmark, not a history — keeping every read would make the table
|
|
99
|
+
* grow with attention rather than with content, and no caller wants the second-most-recent read.
|
|
100
|
+
*/
|
|
101
|
+
markRead(agentId, documentId, text, nowMs) {
|
|
102
|
+
this.#store.rawDb
|
|
103
|
+
.prepare(`INSERT INTO document_read_marks (agent_id, document_id, seen_text, seen_at, my_text)
|
|
104
|
+
VALUES (?, ?, ?, ?, NULL)
|
|
105
|
+
ON CONFLICT (agent_id, document_id) DO UPDATE SET
|
|
106
|
+
seen_text = excluded.seen_text, seen_at = excluded.seen_at,
|
|
107
|
+
-- CLEARED. A read establishes a new baseline, so edits made before it are no longer
|
|
108
|
+
-- "mine since I looked" — carrying them forward would report an overlap against work
|
|
109
|
+
-- the operator has already seen merged.
|
|
110
|
+
my_text = NULL`)
|
|
111
|
+
.run(agentId, documentId, text, nowMs);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Record the text WE just wrote, against the current read mark.
|
|
115
|
+
*
|
|
116
|
+
* No-ops when there is no read mark: without a baseline there is nothing to measure our edit
|
|
117
|
+
* against, and inventing one would make the first diff after a write report the whole document as
|
|
118
|
+
* a conflict.
|
|
119
|
+
*/
|
|
120
|
+
markWritten(agentId, documentId, text) {
|
|
121
|
+
this.#store.rawDb
|
|
122
|
+
.prepare("UPDATE document_read_marks SET my_text = ? WHERE agent_id = ? AND document_id = ?")
|
|
123
|
+
.run(text, agentId, documentId);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* The lines WE edited since the last read, 1-based, in the baseline's coordinate space — or null
|
|
127
|
+
* when we have not written since, which is "not computed" and must stay distinguishable from "no
|
|
128
|
+
* conflict". `diffStats` requires the distinction for exactly that reason.
|
|
129
|
+
*/
|
|
130
|
+
/**
|
|
131
|
+
* WHY THIS EXISTS, stated where the reader will need it:
|
|
132
|
+
*
|
|
133
|
+
* At diff time the two texts in hand are the baseline and the current document, and no amount of
|
|
134
|
+
* comparing them says which of the changes were the PEER's. So `overlap` was hardcoded `null`
|
|
135
|
+
* while three shipped instruction sheets told agents to branch on it — and `null` is falsy in
|
|
136
|
+
* JSON, so every one of them got the reassuring answer on every call. That is the precise trap
|
|
137
|
+
* `diffStats` made its `myEdits` parameter required to prevent, defeated by the one caller.
|
|
138
|
+
*
|
|
139
|
+
* Diffing the baseline against what WE last wrote gives our own edited lines in the baseline's
|
|
140
|
+
* coordinate space, which is the space the diff reports its ranges in. Exact, not inferred.
|
|
141
|
+
*/
|
|
142
|
+
myEditedLines(agentId, documentId) {
|
|
143
|
+
const row = this.#store.rawDb
|
|
144
|
+
.prepare("SELECT seen_text, my_text FROM document_read_marks WHERE agent_id = ? AND document_id = ?")
|
|
145
|
+
.get(agentId, documentId);
|
|
146
|
+
if (!row || row.my_text === null)
|
|
147
|
+
return null;
|
|
148
|
+
const lines = [];
|
|
149
|
+
const runs = lineRuns(toChunks(row.seen_text), toChunks(row.my_text));
|
|
150
|
+
if (runs === null) {
|
|
151
|
+
// Above the LCS limit. Said as "not computed" rather than degraded into a whole-file range,
|
|
152
|
+
// which would report every diff as an overlap and train the agent to ignore the field.
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
for (const hunk of runs) {
|
|
156
|
+
// The hunk's BEFORE range, which is what the diff reports its own ranges in. A pure insertion
|
|
157
|
+
// has an empty before-range and is attributed to the line it precedes, matching `diffStats`.
|
|
158
|
+
const start = hunk.aStart + 1;
|
|
159
|
+
const end = hunk.aEnd === hunk.aStart ? hunk.aStart + 1 : hunk.aEnd;
|
|
160
|
+
for (let n = start; n <= end; n++)
|
|
161
|
+
lines.push(n);
|
|
162
|
+
}
|
|
163
|
+
return lines;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* The text this agent last saw, or null if it has never read this document.
|
|
167
|
+
*
|
|
168
|
+
* NULL IS NOT THE EMPTY STRING, and callers must not conflate them. Never-read means the whole
|
|
169
|
+
* document is new to this agent; last-saw-empty means they read it when it was empty and
|
|
170
|
+
* everything since is a change they can be shown against that. Returning "" for both would render
|
|
171
|
+
* a first read of a long document as an enormous diff the agent then treats as "what just
|
|
172
|
+
* changed".
|
|
173
|
+
*/
|
|
174
|
+
lastSeen(agentId, documentId) {
|
|
175
|
+
const row = this.#store.rawDb
|
|
176
|
+
.prepare("SELECT seen_text FROM document_read_marks WHERE agent_id = ? AND document_id = ?")
|
|
177
|
+
.get(agentId, documentId);
|
|
178
|
+
return row === undefined ? null : row.seen_text;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Note that a document has unread updates. Passive — this never pushes anything.
|
|
182
|
+
*
|
|
183
|
+
* The count is REPLACED rather than incremented, and is passed in by the caller from the log,
|
|
184
|
+
* so the notice cannot drift from the document. An incremented counter is a second tally of a
|
|
185
|
+
* fact the log already holds, and the two disagree the first time a notice write is missed.
|
|
186
|
+
*/
|
|
187
|
+
notice(agentId, documentId, pending, nowMs) {
|
|
188
|
+
if (pending <= 0) {
|
|
189
|
+
// Nothing pending is not a notice with zero in it — it is the absence of a notice. A row
|
|
190
|
+
// saying "0 unread" is an inbox entry the agent must read to discover it has nothing to read.
|
|
191
|
+
//
|
|
192
|
+
// Deleted DIRECTLY rather than through `clear()`: that method's event means "the agent
|
|
193
|
+
// fetched", and firing it because a count fell to zero reports a fetch that never happened.
|
|
194
|
+
this.#store.rawDb
|
|
195
|
+
.prepare("DELETE FROM document_notices WHERE agent_id = ? AND document_id = ?")
|
|
196
|
+
.run(agentId, documentId);
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
this.#store.rawDb
|
|
200
|
+
.prepare(`INSERT INTO document_notices (agent_id, document_id, pending, noticed_at)
|
|
201
|
+
VALUES (?, ?, ?, ?)
|
|
202
|
+
ON CONFLICT (agent_id, document_id)
|
|
203
|
+
DO UPDATE SET pending = excluded.pending, noticed_at = excluded.noticed_at`)
|
|
204
|
+
.run(agentId, documentId, pending, nowMs);
|
|
205
|
+
}
|
|
206
|
+
/** The derived inbox section. Read-only — fetching is what clears, and that is explicit. */
|
|
207
|
+
pending(agentId) {
|
|
208
|
+
const rows = this.#store.rawDb
|
|
209
|
+
.prepare(`SELECT document_id, pending, noticed_at FROM document_notices
|
|
210
|
+
WHERE agent_id = ? ORDER BY noticed_at ASC, document_id ASC`)
|
|
211
|
+
.all(agentId);
|
|
212
|
+
return rows.map((r) => ({
|
|
213
|
+
documentId: r["document_id"],
|
|
214
|
+
pending: r["pending"],
|
|
215
|
+
noticedAtMs: r["noticed_at"],
|
|
216
|
+
}));
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Cleared by the agent's EXPLICIT fetch, never by the aggregation that displays it.
|
|
220
|
+
*
|
|
221
|
+
* Reading the inbox and clearing the notice are different acts: an inbox render that cleared
|
|
222
|
+
* would drop the notice for an agent that merely glanced at a summary, and the update would then
|
|
223
|
+
* be unread and unannounced — invisible in both directions.
|
|
224
|
+
*/
|
|
225
|
+
clear(agentId, documentId) {
|
|
226
|
+
const info = this.#store.rawDb
|
|
227
|
+
.prepare("DELETE FROM document_notices WHERE agent_id = ? AND document_id = ?")
|
|
228
|
+
.run(agentId, documentId);
|
|
229
|
+
const cleared = Number(info.changes) > 0;
|
|
230
|
+
// agentId included: this daemon attends several agents, and an event that names only the
|
|
231
|
+
// document cannot be attributed to one of them.
|
|
232
|
+
if (cleared)
|
|
233
|
+
this.#logger.info("document.notice.cleared", { agentId, documentId });
|
|
234
|
+
return cleared;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Structural counts and ranges — NO content. Lets an agent decide whether to read.
|
|
238
|
+
*
|
|
239
|
+
* `before` and `after` are the materialized text of the document at the two points; the caller
|
|
240
|
+
* holds the engine and therefore the materialization.
|
|
241
|
+
*
|
|
242
|
+
* Uses the SHARED line LCS, not a positional walk. The first version compared line i to line i,
|
|
243
|
+
* which is correct only while the line count is unchanged: inserting one line at the top of a
|
|
244
|
+
* three-line file reported `+4 -3` with a single range covering the whole document. The counts
|
|
245
|
+
* being wrong is the smaller half — `overlap` is derived from those ranges, so it became
|
|
246
|
+
* permanently true, and a flag that always says yes trains the agent to ignore it. Same defect
|
|
247
|
+
* class WRITE-1 measured, which is why there is now one LCS rather than two.
|
|
248
|
+
*/
|
|
249
|
+
diffStats(documentId, before, after,
|
|
250
|
+
/**
|
|
251
|
+
* The operator's own edited line numbers. REQUIRED — pass `null` to say "not computed".
|
|
252
|
+
* Defaulting to `[]` made a caller that simply forgot receive `overlap: false`, the reassuring
|
|
253
|
+
* answer, indistinguishable from "checked, and there is no conflict". This is the one
|
|
254
|
+
* judgement field in an otherwise structural result.
|
|
255
|
+
*/
|
|
256
|
+
myEdits, documentType) {
|
|
257
|
+
const a = toChunks(before);
|
|
258
|
+
const b = toChunks(after);
|
|
259
|
+
const runs = lineRuns(a, b);
|
|
260
|
+
if (runs === null) {
|
|
261
|
+
// Above the LCS limit. Said plainly rather than degraded silently into a whole-file range
|
|
262
|
+
// that would read as a measurement.
|
|
263
|
+
return {
|
|
264
|
+
documentId,
|
|
265
|
+
linesAdded: b.length,
|
|
266
|
+
linesRemoved: a.length,
|
|
267
|
+
ranges: [{ start: 1, end: Math.max(a.length, b.length) }],
|
|
268
|
+
keyPaths: [],
|
|
269
|
+
keyPathsComputed: false,
|
|
270
|
+
overlap: myEdits === null ? null : myEdits.length > 0,
|
|
271
|
+
truncated: true,
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
// G3: for a JSON document the DoD asks for KEY ranges, not line ranges. Produced here rather
|
|
275
|
+
// than left as a permanently-empty array, which reads as "no key paths changed" instead of
|
|
276
|
+
// "not computed" — the same absent-is-not-fine trap as the overlap default above.
|
|
277
|
+
let keyPaths = [];
|
|
278
|
+
let keyPathsComputed = false;
|
|
279
|
+
if (documentType === "json") {
|
|
280
|
+
const paths = changedKeyPaths(before, after);
|
|
281
|
+
if (paths !== null) {
|
|
282
|
+
keyPaths = paths;
|
|
283
|
+
keyPathsComputed = true;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
let added = 0;
|
|
287
|
+
let removed = 0;
|
|
288
|
+
const ranges = [];
|
|
289
|
+
for (const r of runs) {
|
|
290
|
+
added += r.bEnd - r.bStart;
|
|
291
|
+
removed += r.aEnd - r.aStart;
|
|
292
|
+
// 1-based inclusive, over the BEFORE text — which is the text the operator's own edit line
|
|
293
|
+
// numbers refer to, and therefore the only coordinate space in which the overlap comparison
|
|
294
|
+
// means anything. A pure insertion has an empty before-range, so it is reported at the line
|
|
295
|
+
// it precedes rather than as a zero-width range nobody can act on.
|
|
296
|
+
ranges.push({ start: r.aStart + 1, end: r.aEnd === r.aStart ? r.aStart + 1 : r.aEnd });
|
|
297
|
+
}
|
|
298
|
+
const touched = new Set();
|
|
299
|
+
for (const r of ranges)
|
|
300
|
+
for (let n = r.start; n <= r.end; n++)
|
|
301
|
+
touched.add(n);
|
|
302
|
+
return {
|
|
303
|
+
documentId,
|
|
304
|
+
linesAdded: added,
|
|
305
|
+
linesRemoved: removed,
|
|
306
|
+
ranges,
|
|
307
|
+
keyPaths,
|
|
308
|
+
keyPathsComputed,
|
|
309
|
+
overlap: myEdits === null ? null : myEdits.some((line) => touched.has(line)),
|
|
310
|
+
truncated: false,
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* The git-like diff. An ordinary screened read — the caller screens it; this renders it.
|
|
315
|
+
*
|
|
316
|
+
* An unsupported type is REFUSED by name rather than line-diffed anyway. A diff that is silently
|
|
317
|
+
* wrong is worse than no diff, because the agent has no way to tell and will act on it.
|
|
318
|
+
*/
|
|
319
|
+
diff(documentType, before, after, documentId) {
|
|
320
|
+
if (!DIFFABLE_DOCUMENT_TYPES.includes(documentType)) {
|
|
321
|
+
return {
|
|
322
|
+
ok: false,
|
|
323
|
+
reason: "document_type_not_diffable",
|
|
324
|
+
detail: `this build renders diffs for ${DIFFABLE_DOCUMENT_TYPES.join(", ")} and this document is ` +
|
|
325
|
+
`"${documentType}" — read the document instead of a diff that would be guesswork`,
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
void documentId;
|
|
329
|
+
if (documentType === "json") {
|
|
330
|
+
// A key-path diff, not a line diff. Re-serialized JSON changes shape whenever a formatter
|
|
331
|
+
// touches it, so a line diff reports the entire document as rewritten for a change of one
|
|
332
|
+
// value — which is the false "everything changed" alarm that makes an agent stop reading
|
|
333
|
+
// diffs at all.
|
|
334
|
+
const parsedPaths = changedKeyPaths(before, after);
|
|
335
|
+
if (parsedPaths === null) {
|
|
336
|
+
return {
|
|
337
|
+
ok: true,
|
|
338
|
+
documentType: "json",
|
|
339
|
+
diff: `! this document does not parse as JSON; showing a line diff instead\n${lineDiff(before, after)}`,
|
|
340
|
+
fallback: "unparseable_json_line_diff",
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
return { ok: true, documentType: "json", diff: jsonDiff(before, after) };
|
|
344
|
+
}
|
|
345
|
+
return { ok: true, documentType: documentType, diff: lineDiff(before, after) };
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
function lineDiff(before, after) {
|
|
349
|
+
// The SHARED LCS, same as diffStats. This renderer kept the positional walk after diffStats moved
|
|
350
|
+
// off it, which is the worse half to leave behind: inserting one line into a ten-line file
|
|
351
|
+
// rendered eighteen +/- lines, reporting the entire remainder as rewritten. That is the false
|
|
352
|
+
// "everything changed" alarm that makes an agent stop reading diffs at all — the exact reasoning
|
|
353
|
+
// that gave JSON a key-path diff, applied to markdown at last.
|
|
354
|
+
const a = toChunks(before);
|
|
355
|
+
const b = toChunks(after);
|
|
356
|
+
const runs = lineRuns(a, b);
|
|
357
|
+
if (runs === null)
|
|
358
|
+
return "! this document is too large to diff line-by-line";
|
|
359
|
+
const out = [];
|
|
360
|
+
let ai = 0;
|
|
361
|
+
for (const r of runs) {
|
|
362
|
+
for (; ai < r.aStart; ai++)
|
|
363
|
+
out.push(` ${strip(a[ai])}`);
|
|
364
|
+
for (let i = r.aStart; i < r.aEnd; i++)
|
|
365
|
+
out.push(`- ${strip(a[i])}`);
|
|
366
|
+
for (let j = r.bStart; j < r.bEnd; j++)
|
|
367
|
+
out.push(`+ ${strip(b[j])}`);
|
|
368
|
+
ai = r.aEnd;
|
|
369
|
+
}
|
|
370
|
+
for (; ai < a.length; ai++)
|
|
371
|
+
out.push(` ${strip(a[ai])}`);
|
|
372
|
+
return out.join("\n");
|
|
373
|
+
}
|
|
374
|
+
/** Chunks carry their own trailing newline; the rendered diff supplies its own line breaks. */
|
|
375
|
+
function strip(chunk) {
|
|
376
|
+
return chunk.endsWith("\n") ? chunk.slice(0, -1) : chunk;
|
|
377
|
+
}
|
|
378
|
+
function flatten(value, prefix = "") {
|
|
379
|
+
const out = new Map();
|
|
380
|
+
if (value === null || typeof value !== "object") {
|
|
381
|
+
out.set(prefix || "$", JSON.stringify(value));
|
|
382
|
+
return out;
|
|
383
|
+
}
|
|
384
|
+
for (const [k, v] of Object.entries(value)) {
|
|
385
|
+
// The separator is ESCAPED in the key. Unescaped, {"a.b": 1} and {"a": {"b": 1}} flatten to the
|
|
386
|
+
// same path, so a document using dotted keys reports no change where there was one — silently,
|
|
387
|
+
// which is the exact behaviour this unit refuses everywhere else.
|
|
388
|
+
const path = prefix ? `${prefix}.${k.replace(/\\/g, "\\\\").replace(/\./g, "\\.")}` : k.replace(/\\/g, "\\\\").replace(/\./g, "\\.");
|
|
389
|
+
for (const [p, s] of flatten(v, path))
|
|
390
|
+
out.set(p, s);
|
|
391
|
+
}
|
|
392
|
+
return out;
|
|
393
|
+
}
|
|
394
|
+
/** The changed key paths, or null when either side does not parse. */
|
|
395
|
+
function changedKeyPaths(before, after) {
|
|
396
|
+
let a;
|
|
397
|
+
let b;
|
|
398
|
+
try {
|
|
399
|
+
a = flatten(JSON.parse(before));
|
|
400
|
+
b = flatten(JSON.parse(after));
|
|
401
|
+
}
|
|
402
|
+
catch {
|
|
403
|
+
return null;
|
|
404
|
+
}
|
|
405
|
+
const paths = new Set();
|
|
406
|
+
for (const [path, value] of a)
|
|
407
|
+
if (!b.has(path) || b.get(path) !== value)
|
|
408
|
+
paths.add(path);
|
|
409
|
+
for (const [path] of b)
|
|
410
|
+
if (!a.has(path))
|
|
411
|
+
paths.add(path);
|
|
412
|
+
return [...paths].sort();
|
|
413
|
+
}
|
|
414
|
+
function jsonDiff(before, after) {
|
|
415
|
+
let a;
|
|
416
|
+
let b;
|
|
417
|
+
try {
|
|
418
|
+
a = flatten(JSON.parse(before));
|
|
419
|
+
b = flatten(JSON.parse(after));
|
|
420
|
+
}
|
|
421
|
+
catch {
|
|
422
|
+
// Deliberately explicit: unparseable JSON gets a stated fallback, not a silent line diff
|
|
423
|
+
// pretending to be a key diff. The caller sees which it got.
|
|
424
|
+
return `! this document does not parse as JSON; showing a line diff instead\n${lineDiff(before, after)}`;
|
|
425
|
+
}
|
|
426
|
+
const out = [];
|
|
427
|
+
for (const [path, value] of a) {
|
|
428
|
+
if (!b.has(path))
|
|
429
|
+
out.push(`- ${path}: ${value}`);
|
|
430
|
+
else if (b.get(path) !== value)
|
|
431
|
+
out.push(`~ ${path}: ${value} -> ${b.get(path)}`);
|
|
432
|
+
}
|
|
433
|
+
for (const [path, value] of b)
|
|
434
|
+
if (!a.has(path))
|
|
435
|
+
out.push(`+ ${path}: ${value}`);
|
|
436
|
+
return out.join("\n");
|
|
437
|
+
}
|
|
438
|
+
//# sourceMappingURL=document-notify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"document-notify.js","sourceRoot":"","sources":["../src/document-notify.ts"],"names":[],"mappings":"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"}
|