@agentshouse/mdruntime 0.1.0-ce531b51ff088 → 0.1.0-ce6bc9239a3ee
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 +25 -12
- package/dist/acceptance.d.ts +26 -0
- package/dist/acceptance.d.ts.map +1 -0
- package/dist/acceptance.js +384 -0
- package/dist/adapter.d.ts +218 -62
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js +1 -9
- package/dist/attached-model.d.ts +8 -0
- package/dist/attached-model.d.ts.map +1 -0
- package/dist/attached-model.js +46 -0
- package/dist/attempt.d.ts +16 -0
- package/dist/attempt.d.ts.map +1 -0
- package/dist/attempt.js +70 -0
- package/dist/binary.d.ts +18 -0
- package/dist/binary.d.ts.map +1 -0
- package/dist/binary.js +8 -0
- package/dist/bootstrap.d.ts +40 -6
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +336 -51
- package/dist/chain.d.ts +12 -0
- package/dist/chain.d.ts.map +1 -0
- package/dist/chain.js +64 -0
- package/dist/change-set.d.ts +9 -14
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +11 -56
- package/dist/change.d.ts +46 -0
- package/dist/change.d.ts.map +1 -1
- package/dist/change.js +34 -0
- package/dist/chat-types.d.ts +3 -0
- package/dist/chat-types.d.ts.map +1 -0
- package/dist/chat-types.js +2 -0
- package/dist/client.d.ts +50 -18
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +162 -30
- package/dist/collection.d.ts +13 -14
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +52 -56
- package/dist/compile.d.ts +16 -0
- package/dist/compile.d.ts.map +1 -0
- package/dist/compile.js +717 -0
- package/dist/connection.d.ts +63 -48
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +2487 -1575
- package/dist/context.d.ts +2 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +7 -0
- package/dist/conversation-row.d.ts +33 -0
- package/dist/conversation-row.d.ts.map +1 -0
- package/dist/conversation-row.js +47 -0
- package/dist/conversation.d.ts +72 -22
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +1008 -273
- package/dist/digest.d.ts +7 -0
- package/dist/digest.d.ts.map +1 -0
- package/dist/digest.js +113 -0
- package/dist/durability.d.ts +10 -1
- package/dist/durability.d.ts.map +1 -1
- package/dist/durability.js +5 -0
- package/dist/entry.d.ts +20 -0
- package/dist/entry.d.ts.map +1 -0
- package/dist/entry.js +161 -0
- package/dist/expectation.d.ts +21 -0
- package/dist/expectation.d.ts.map +1 -0
- package/dist/expectation.js +70 -0
- package/dist/failure.d.ts +13 -0
- package/dist/failure.d.ts.map +1 -0
- package/dist/failure.js +19 -0
- package/dist/figures.d.ts +48 -0
- package/dist/figures.d.ts.map +1 -0
- package/dist/figures.js +197 -0
- package/dist/file.d.ts +2 -19
- package/dist/file.d.ts.map +1 -1
- package/dist/file.js +7 -122
- package/dist/host.d.ts +18 -2
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +164 -50
- package/dist/identity.d.ts +3 -0
- package/dist/identity.d.ts.map +1 -1
- package/dist/identity.js +13 -1
- package/dist/index.d.ts +31 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -0
- package/dist/install.d.ts +86 -0
- package/dist/install.d.ts.map +1 -0
- package/dist/install.js +537 -0
- package/dist/job.d.ts +33 -0
- package/dist/job.d.ts.map +1 -0
- package/dist/job.js +54 -0
- package/dist/live.d.ts +23 -0
- package/dist/live.d.ts.map +1 -0
- package/dist/live.js +165 -0
- package/dist/log.d.ts +1 -0
- package/dist/log.d.ts.map +1 -1
- package/dist/media.d.ts +8 -0
- package/dist/media.d.ts.map +1 -0
- package/dist/media.js +218 -0
- package/dist/memory.d.ts +30 -9
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +337 -86
- package/dist/messages.d.ts +52 -0
- package/dist/messages.d.ts.map +1 -0
- package/dist/messages.js +168 -0
- package/dist/model.d.ts +17 -16
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +9 -9
- package/dist/name.d.ts +4 -0
- package/dist/name.d.ts.map +1 -0
- package/dist/name.js +122 -0
- package/dist/observation.d.ts +22 -2
- package/dist/observation.d.ts.map +1 -1
- package/dist/observation.js +133 -17
- package/dist/open.d.ts +2 -0
- package/dist/open.d.ts.map +1 -0
- package/dist/open.js +1 -0
- package/dist/opened.d.ts +22 -0
- package/dist/opened.d.ts.map +1 -0
- package/dist/opened.js +107 -0
- package/dist/outcome.d.ts +73 -5
- package/dist/outcome.d.ts.map +1 -1
- package/dist/outcome.js +84 -1
- package/dist/overlay.d.ts +16 -5
- package/dist/overlay.d.ts.map +1 -1
- package/dist/overlay.js +147 -180
- package/dist/parts.d.ts +59 -0
- package/dist/parts.d.ts.map +1 -0
- package/dist/parts.js +118 -0
- package/dist/paths.d.ts +1 -1
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +4 -16
- package/dist/query.d.ts +56 -180
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +3 -422
- package/dist/questions.d.ts +32 -0
- package/dist/questions.d.ts.map +1 -0
- package/dist/questions.js +118 -0
- package/dist/reach.d.ts +54 -0
- package/dist/reach.d.ts.map +1 -0
- package/dist/reach.js +494 -0
- package/dist/react.d.ts +46 -0
- package/dist/react.d.ts.map +1 -0
- package/dist/react.js +54 -0
- package/dist/read.d.ts +7 -0
- package/dist/read.d.ts.map +1 -0
- package/dist/read.js +17 -0
- package/dist/readiness.d.ts +50 -0
- package/dist/readiness.d.ts.map +1 -0
- package/dist/readiness.js +27 -0
- package/dist/reads.d.ts +36 -0
- package/dist/reads.d.ts.map +1 -0
- package/dist/reads.js +136 -0
- package/dist/recovery.d.ts +26 -0
- package/dist/recovery.d.ts.map +1 -0
- package/dist/recovery.js +232 -0
- package/dist/refresh.d.ts +21 -7
- package/dist/refresh.d.ts.map +1 -1
- package/dist/refresh.js +155 -55
- package/dist/replica.d.ts +58 -33
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +355 -158
- package/dist/room-draft.d.ts +62 -11
- package/dist/room-draft.d.ts.map +1 -1
- package/dist/room-draft.js +281 -83
- package/dist/room-drafts.d.ts +29 -0
- package/dist/room-drafts.d.ts.map +1 -0
- package/dist/room-drafts.js +216 -0
- package/dist/rows.d.ts +32 -34
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +65 -87
- package/dist/save.d.ts +74 -6
- package/dist/save.d.ts.map +1 -1
- package/dist/save.js +902 -182
- package/dist/saves.d.ts +96 -0
- package/dist/saves.d.ts.map +1 -0
- package/dist/saves.js +999 -0
- package/dist/search.d.ts +15 -54
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js +10 -121
- package/dist/seed.d.ts +30 -0
- package/dist/seed.d.ts.map +1 -0
- package/dist/seed.js +141 -0
- package/dist/shared.d.ts +15 -0
- package/dist/shared.d.ts.map +1 -0
- package/dist/shared.js +421 -0
- package/dist/socket/admission.d.ts +7 -0
- package/dist/socket/admission.d.ts.map +1 -0
- package/dist/socket/admission.js +39 -0
- package/dist/socket/answers.d.ts +6 -0
- package/dist/socket/answers.d.ts.map +1 -0
- package/dist/socket/answers.js +20 -0
- package/dist/socket/reads.d.ts +10 -0
- package/dist/socket/reads.d.ts.map +1 -0
- package/dist/socket/reads.js +31 -0
- package/dist/socket.d.ts +43 -0
- package/dist/socket.d.ts.map +1 -0
- package/dist/socket.js +96 -0
- package/dist/source.d.ts +61 -0
- package/dist/source.d.ts.map +1 -0
- package/dist/source.js +81 -0
- package/dist/status.d.ts +7 -4
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +3 -15
- package/dist/store/fences.d.ts +16 -0
- package/dist/store/fences.d.ts.map +1 -0
- package/dist/store/fences.js +26 -0
- package/dist/store/installs.d.ts +30 -0
- package/dist/store/installs.d.ts.map +1 -0
- package/dist/store/installs.js +34 -0
- package/dist/store/records.d.ts +212 -0
- package/dist/store/records.d.ts.map +1 -0
- package/dist/store/records.js +408 -0
- package/dist/store/room-held.d.ts +8 -0
- package/dist/store/room-held.d.ts.map +1 -0
- package/dist/store/room-held.js +17 -0
- package/dist/store/watchers.d.ts +15 -0
- package/dist/store/watchers.d.ts.map +1 -0
- package/dist/store/watchers.js +36 -0
- package/dist/store/writes.d.ts +24 -0
- package/dist/store/writes.d.ts.map +1 -0
- package/dist/store/writes.js +125 -0
- package/dist/store.d.ts +77 -94
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +118 -389
- package/dist/stored.d.ts +156 -0
- package/dist/stored.d.ts.map +1 -0
- package/dist/stored.js +637 -0
- package/dist/tables.d.ts +407 -0
- package/dist/tables.d.ts.map +1 -0
- package/dist/tables.js +724 -0
- package/dist/tallies.d.ts +18 -0
- package/dist/tallies.d.ts.map +1 -0
- package/dist/tallies.js +129 -0
- package/dist/transfer.d.ts +0 -5
- package/dist/transfer.d.ts.map +1 -1
- package/dist/turn.d.ts +9 -0
- package/dist/turn.d.ts.map +1 -0
- package/dist/turn.js +75 -0
- package/dist/validation.d.ts +29 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +104 -0
- package/dist/windows.d.ts +11 -0
- package/dist/windows.d.ts.map +1 -0
- package/dist/windows.js +16 -0
- package/dist/wire.d.ts +205 -19
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +95 -61
- package/package.json +33 -10
- package/AGENTS.md +0 -207
- package/dist/attachment.d.ts +0 -3
- package/dist/attachment.d.ts.map +0 -1
- package/dist/attachment.js +0 -8
- package/dist/read-side.d.ts +0 -14
- package/dist/read-side.d.ts.map +0 -1
- package/dist/read-side.js +0 -371
package/dist/memory.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { documentFacts, parse } from '@agentshouse/mdmodel';
|
|
2
|
+
import { NO_ROOM_TYPES, schemaTypes } from './attached-model.js';
|
|
3
3
|
import { sameChange } from './overlay.js';
|
|
4
|
+
const ATTACHMENTS = 'attachments';
|
|
5
|
+
function lastName(path) {
|
|
6
|
+
return path.slice(path.lastIndexOf('/') + 1);
|
|
7
|
+
}
|
|
4
8
|
const FIXTURE_RETENTION = {
|
|
5
9
|
history: { kind: 'duration', seconds: 1 },
|
|
6
|
-
outcome: 1,
|
|
7
10
|
refreshHorizon: 1,
|
|
8
11
|
};
|
|
9
12
|
function declaredRetention(value) {
|
|
@@ -15,6 +18,14 @@ function declaredRetention(value) {
|
|
|
15
18
|
}
|
|
16
19
|
throw new Error('history retention duration must be a finite positive number of seconds');
|
|
17
20
|
}
|
|
21
|
+
const MODEL_ABSENT = { kind: 'model-unavailable', reason: 'not-readable' };
|
|
22
|
+
function modelsOf(room) {
|
|
23
|
+
return { collection: room.model ?? MODEL_ABSENT };
|
|
24
|
+
}
|
|
25
|
+
function changedModels(room, from) {
|
|
26
|
+
const now = modelsOf(room);
|
|
27
|
+
return JSON.stringify(now) === JSON.stringify(modelsOf(from)) ? undefined : now;
|
|
28
|
+
}
|
|
18
29
|
function named(source, paths) {
|
|
19
30
|
const taken = {};
|
|
20
31
|
for (const path of paths) {
|
|
@@ -31,8 +42,6 @@ async function resolveScript(answer, fallback, request) {
|
|
|
31
42
|
}
|
|
32
43
|
export function createMemoryAdapter(options = {}) {
|
|
33
44
|
let rooms = options.rooms === undefined ? [] : [...options.rooms];
|
|
34
|
-
const mdModel = options.mdModel ?? MD_MODEL;
|
|
35
|
-
const supportedVersions = options.supportedVersions ?? [ROOM_CONTRACT_VERSION];
|
|
36
45
|
const capabilities = options.capabilities ?? [];
|
|
37
46
|
const providerLimits = options.providerLimits ?? [];
|
|
38
47
|
const retention = declaredRetention(options.retention);
|
|
@@ -44,11 +53,16 @@ export function createMemoryAdapter(options = {}) {
|
|
|
44
53
|
const listeners = new Set();
|
|
45
54
|
const modeObservers = new Set();
|
|
46
55
|
const scripted = new Map();
|
|
47
|
-
const
|
|
48
|
-
const sealed = new Set();
|
|
56
|
+
const sentTurns = new Map();
|
|
49
57
|
const historyPageSize = options.historyPageSize;
|
|
50
|
-
|
|
58
|
+
const attached = new Map();
|
|
59
|
+
const linked = new WeakMap();
|
|
60
|
+
const carried = new Map();
|
|
61
|
+
let expiring = [];
|
|
62
|
+
let openings = 0;
|
|
63
|
+
const declaredRooms = new Set();
|
|
51
64
|
let replicated = [];
|
|
65
|
+
let heldCursor = null;
|
|
52
66
|
let issued = 0;
|
|
53
67
|
function scriptConversations(entries) {
|
|
54
68
|
scripted.clear();
|
|
@@ -76,8 +90,48 @@ export function createMemoryAdapter(options = {}) {
|
|
|
76
90
|
const wanted = ordinals.get(`${locator}\u0000${target}`);
|
|
77
91
|
return held !== undefined && wanted !== undefined && held >= wanted;
|
|
78
92
|
}
|
|
93
|
+
function heldBlob(room, path) {
|
|
94
|
+
return attached.get(`${room.locator}\u0000${path}`) ?? room.blobs?.[path];
|
|
95
|
+
}
|
|
96
|
+
function freePath(room, name) {
|
|
97
|
+
const dot = name.lastIndexOf('.');
|
|
98
|
+
const stem = dot > 0 ? name.slice(0, dot) : name;
|
|
99
|
+
const extension = dot > 0 ? name.slice(dot) : '';
|
|
100
|
+
for (let count = 1;; count += 1) {
|
|
101
|
+
const path = `${ATTACHMENTS}/${count === 1 ? stem : `${stem}-${count}`}${extension}`;
|
|
102
|
+
if (heldBlob(room, path) === undefined && room.documents?.[path] === undefined)
|
|
103
|
+
return path;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
const saved = new WeakMap();
|
|
107
|
+
function factsAt(room, path, content) {
|
|
108
|
+
const types = schemaTypes(room.model?.kind === 'model' ? room.model.snapshot.schema : null, NO_ROOM_TYPES);
|
|
109
|
+
let held = saved.get(types);
|
|
110
|
+
if (held === undefined) {
|
|
111
|
+
held = new Map();
|
|
112
|
+
saved.set(types, held);
|
|
113
|
+
}
|
|
114
|
+
const key = `${path}\u0000${content}`;
|
|
115
|
+
if (!held.has(key)) {
|
|
116
|
+
const facts = documentFacts(path, parse(content, path), { types });
|
|
117
|
+
held.set(key, facts === null ? null : JSON.parse(JSON.stringify(facts)));
|
|
118
|
+
}
|
|
119
|
+
return held.get(key);
|
|
120
|
+
}
|
|
121
|
+
function factsIn(room, paths) {
|
|
122
|
+
const facts = {};
|
|
123
|
+
for (const path of paths) {
|
|
124
|
+
const content = room.documents?.[path];
|
|
125
|
+
const held = room.facts?.[path] ?? (content === undefined ? null : factsAt(room, path, content));
|
|
126
|
+
if (held !== null)
|
|
127
|
+
facts[path] = held;
|
|
128
|
+
}
|
|
129
|
+
return facts;
|
|
130
|
+
}
|
|
79
131
|
function retain(entries) {
|
|
80
132
|
for (const room of entries) {
|
|
133
|
+
for (const [path, content] of Object.entries(room.documents ?? {}))
|
|
134
|
+
factsAt(room, path, content);
|
|
81
135
|
note(room.locator, room);
|
|
82
136
|
const versions = served.get(room.locator) ?? new Map();
|
|
83
137
|
versions.set(room.roomVersion, room);
|
|
@@ -89,42 +143,64 @@ export function createMemoryAdapter(options = {}) {
|
|
|
89
143
|
return {
|
|
90
144
|
name: room.name,
|
|
91
145
|
locator: room.locator,
|
|
146
|
+
handle: room.handle,
|
|
92
147
|
roomVersion: room.roomVersion,
|
|
93
148
|
operations: room.operations,
|
|
94
149
|
replica: room.replica,
|
|
95
|
-
attachedModel: room.attachedModel,
|
|
96
150
|
...(room.typedSelection === undefined ? {} : { typedSelection: room.typedSelection }),
|
|
97
151
|
providerOwnedPaths: room.providerOwnedPaths,
|
|
98
152
|
};
|
|
99
153
|
}
|
|
100
|
-
function page(room,
|
|
154
|
+
function page(room, after) {
|
|
101
155
|
const paths = Object.keys(room.documents ?? {}).sort();
|
|
156
|
+
const opening = after === undefined;
|
|
157
|
+
const from = opening ? 0 : paths.filter((path) => path <= after).length;
|
|
102
158
|
const size = pageSize ?? paths.length;
|
|
103
159
|
const taken = paths.slice(from, from + size);
|
|
104
160
|
const next = from + taken.length;
|
|
105
|
-
const targets =
|
|
161
|
+
const targets = opening ? Object.keys(room.binaryFiles ?? {}) : [];
|
|
106
162
|
return {
|
|
107
163
|
kind: 'bootstrap',
|
|
108
164
|
roomVersion: room.roomVersion,
|
|
165
|
+
...(opening ? { models: modelsOf(room) } : {}),
|
|
109
166
|
documents: named(room.documents ?? {}, taken),
|
|
110
|
-
|
|
167
|
+
facts: factsIn(room, taken),
|
|
168
|
+
revisions: named(room.revisions ?? {}, [...taken, ...targets]),
|
|
169
|
+
...(room.fileIds === undefined
|
|
170
|
+
? {}
|
|
171
|
+
: { fileIds: named(room.fileIds, [...taken, ...targets]) }),
|
|
172
|
+
...(room.created === undefined
|
|
173
|
+
? {}
|
|
174
|
+
: { created: named(room.created, [...taken, ...targets]) }),
|
|
111
175
|
changes: named(room.changes ?? {}, [...taken, ...targets]),
|
|
112
|
-
|
|
113
|
-
...(
|
|
114
|
-
...(next < paths.length ? { pageToken:
|
|
176
|
+
binaryFiles: named(room.binaryFiles ?? {}, targets),
|
|
177
|
+
...(opening && room.folders !== undefined ? { folders: room.folders } : {}),
|
|
178
|
+
...(next < paths.length ? { pageToken: taken.at(-1) } : {}),
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
function foldersMoved(room, from) {
|
|
182
|
+
const now = new Set(room.folders ?? []);
|
|
183
|
+
const before = new Set(from.folders ?? []);
|
|
184
|
+
const folders = [...now].filter((folder) => !before.has(folder)).sort();
|
|
185
|
+
const folderRemovals = [...before].filter((folder) => !now.has(folder)).sort();
|
|
186
|
+
return {
|
|
187
|
+
...(folders.length === 0 ? {} : { folders }),
|
|
188
|
+
...(folderRemovals.length === 0 ? {} : { folderRemovals }),
|
|
115
189
|
};
|
|
116
190
|
}
|
|
117
|
-
function advancement(room, from, at) {
|
|
191
|
+
function advancement(room, from, at, whole = false) {
|
|
118
192
|
const current = room.documents ?? {};
|
|
119
193
|
const previous = from.documents ?? {};
|
|
120
|
-
const currentTargets = new Set(room.
|
|
121
|
-
const previousTargets = new Set(from.
|
|
194
|
+
const currentTargets = new Set(Object.keys(room.binaryFiles ?? {}));
|
|
195
|
+
const previousTargets = new Set(Object.keys(from.binaryFiles ?? {}));
|
|
122
196
|
const headMoved = (path) => room.revisions?.[path] !== from.revisions?.[path] ||
|
|
197
|
+
room.fileIds?.[path] !== from.fileIds?.[path] ||
|
|
198
|
+
room.created?.[path] !== from.created?.[path] ||
|
|
123
199
|
!sameChange(room.changes?.[path], from.changes?.[path]);
|
|
124
200
|
const changed = Object.keys(current)
|
|
125
201
|
.filter((path) => current[path] !== previous[path] || headMoved(path))
|
|
126
202
|
.sort();
|
|
127
|
-
const size = pageSize ?? changed.length;
|
|
203
|
+
const size = whole ? changed.length : (pageSize ?? changed.length);
|
|
128
204
|
const taken = changed.slice(at, at + size);
|
|
129
205
|
const next = at + taken.length;
|
|
130
206
|
const opening = at === 0;
|
|
@@ -134,25 +210,52 @@ export function createMemoryAdapter(options = {}) {
|
|
|
134
210
|
const targets = opening
|
|
135
211
|
? [...currentTargets].filter((target) => !previousTargets.has(target) || headMoved(target))
|
|
136
212
|
: [];
|
|
137
|
-
const
|
|
138
|
-
const declared = opening && JSON.stringify(attachments) !== JSON.stringify(from.attachments ?? []);
|
|
213
|
+
const models = opening ? changedModels(room, from) : undefined;
|
|
139
214
|
return {
|
|
140
215
|
kind: 'net-advancement',
|
|
141
216
|
roomVersion: room.roomVersion,
|
|
217
|
+
...(models === undefined ? {} : { models }),
|
|
142
218
|
documents: named(current, taken),
|
|
143
|
-
|
|
219
|
+
facts: factsIn(room, taken),
|
|
220
|
+
revisions: named(room.revisions ?? {}, [...taken, ...targets]),
|
|
221
|
+
...(room.fileIds === undefined
|
|
222
|
+
? {}
|
|
223
|
+
: { fileIds: named(room.fileIds, [...taken, ...targets]) }),
|
|
224
|
+
...(room.created === undefined
|
|
225
|
+
? {}
|
|
226
|
+
: { created: named(room.created, [...taken, ...targets]) }),
|
|
144
227
|
changes: named(room.changes ?? {}, [...taken, ...targets]),
|
|
145
|
-
|
|
228
|
+
binaryFiles: named(room.binaryFiles ?? {}, targets),
|
|
229
|
+
...(opening ? foldersMoved(room, from) : {}),
|
|
146
230
|
removals: opening
|
|
147
231
|
? [
|
|
148
232
|
...Object.keys(previous).filter((path) => !(path in current) && !currentTargets.has(path)),
|
|
149
233
|
...[...previousTargets].filter((target) => !currentTargets.has(target) && !(target in current)),
|
|
150
234
|
].sort()
|
|
151
235
|
: [],
|
|
152
|
-
...(declared ? { attachments } : {}),
|
|
153
236
|
...(pageToken === undefined ? {} : { pageToken }),
|
|
154
237
|
};
|
|
155
238
|
}
|
|
239
|
+
function carry(locator) {
|
|
240
|
+
const at = carried.get(locator);
|
|
241
|
+
const current = rooms.find((entry) => entry.locator === locator);
|
|
242
|
+
if (at === undefined || current === undefined || current.roomVersion === at)
|
|
243
|
+
return;
|
|
244
|
+
const from = served.get(locator)?.get(at);
|
|
245
|
+
carried.set(locator, current.roomVersion);
|
|
246
|
+
if (from === undefined)
|
|
247
|
+
return;
|
|
248
|
+
const page = advancement(current, from, 0, true);
|
|
249
|
+
for (const observer of observers) {
|
|
250
|
+
observer({
|
|
251
|
+
kind: 'room-change',
|
|
252
|
+
locator,
|
|
253
|
+
from: at,
|
|
254
|
+
roomVersion: current.roomVersion,
|
|
255
|
+
read: async () => page,
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
}
|
|
156
259
|
function pageOf(conversation, after) {
|
|
157
260
|
const messages = conversation.messages ?? [];
|
|
158
261
|
const first = typeof after === 'string' ? messages.findIndex((message) => message.position > after) : 0;
|
|
@@ -167,16 +270,13 @@ export function createMemoryAdapter(options = {}) {
|
|
|
167
270
|
: {}),
|
|
168
271
|
};
|
|
169
272
|
}
|
|
170
|
-
function historiesOf(
|
|
171
|
-
const asked = input?.conversations;
|
|
172
|
-
if (!Array.isArray(asked))
|
|
173
|
-
return { kind: 'rejected', code: 'invalid_request', fields: {} };
|
|
273
|
+
function historiesOf(request) {
|
|
174
274
|
const histories = [];
|
|
175
|
-
for (const entry of
|
|
176
|
-
const conversation =
|
|
275
|
+
for (const entry of request.conversations) {
|
|
276
|
+
const conversation = scripted.get(entry.conversation);
|
|
177
277
|
if (conversation === undefined)
|
|
178
278
|
continue;
|
|
179
|
-
const outcome = conversation.outcomes?.
|
|
279
|
+
const outcome = conversation.outcomes?.history;
|
|
180
280
|
if (outcome !== undefined)
|
|
181
281
|
return outcome;
|
|
182
282
|
histories.push(pageOf(conversation, entry.after));
|
|
@@ -187,52 +287,94 @@ export function createMemoryAdapter(options = {}) {
|
|
|
187
287
|
function conversationAction(request) {
|
|
188
288
|
const name = request.name;
|
|
189
289
|
const input = (request.input ?? {});
|
|
190
|
-
if (name === '
|
|
191
|
-
return historiesOf(request.input);
|
|
192
|
-
if (name === 'conversation.create') {
|
|
290
|
+
if (name === 'conversations/create') {
|
|
193
291
|
issued += 1;
|
|
194
292
|
const conversation = `conversation-${issued}`;
|
|
195
293
|
scripted.set(conversation, { conversation });
|
|
196
|
-
return { kind: 'action', result: { conversation } };
|
|
294
|
+
return { kind: 'action', result: { conversation_id: conversation } };
|
|
197
295
|
}
|
|
198
|
-
const named = typeof input.
|
|
296
|
+
const named = typeof input.conversation_id === 'string' ? input.conversation_id : '';
|
|
199
297
|
const conversation = scripted.get(named);
|
|
200
298
|
if (conversation === undefined) {
|
|
201
|
-
return {
|
|
299
|
+
return {
|
|
300
|
+
kind: 'rejected',
|
|
301
|
+
code: 'unknown-conversation',
|
|
302
|
+
fields: { conversation: named },
|
|
303
|
+
retryable: false,
|
|
304
|
+
};
|
|
202
305
|
}
|
|
203
306
|
const outcome = conversation.outcomes?.[name];
|
|
204
307
|
if (outcome !== undefined)
|
|
205
308
|
return outcome;
|
|
206
|
-
if (name === '
|
|
309
|
+
if (name === 'conversations/send') {
|
|
310
|
+
const operation = typeof input.operation_id === 'string' ? input.operation_id : null;
|
|
311
|
+
const answered = operation === null ? undefined : sentTurns.get(operation);
|
|
312
|
+
if (answered !== undefined)
|
|
313
|
+
return { kind: 'action', result: { message_id: answered } };
|
|
207
314
|
issued += 1;
|
|
208
315
|
const turn = `turn-${issued}`;
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
if (name === 'conversation.cancel') {
|
|
213
|
-
const turn = typeof input.turn === 'string' ? input.turn : '';
|
|
214
|
-
if (sealed.has(turn) || !unsealed.has(turn)) {
|
|
215
|
-
return { kind: 'rejected', code: 'turn-sealed', fields: { turn } };
|
|
216
|
-
}
|
|
217
|
-
unsealed.delete(turn);
|
|
218
|
-
return { kind: 'action', result: { turn, cancelled: true } };
|
|
316
|
+
if (operation !== null)
|
|
317
|
+
sentTurns.set(operation, turn);
|
|
318
|
+
return { kind: 'action', result: { message_id: turn } };
|
|
219
319
|
}
|
|
220
|
-
if (name === '
|
|
320
|
+
if (name === 'conversations/delete')
|
|
221
321
|
scripted.delete(named);
|
|
222
|
-
return { kind: 'action', result: {
|
|
322
|
+
return { kind: 'action', result: { conversation_id: named } };
|
|
323
|
+
}
|
|
324
|
+
function announceList() {
|
|
325
|
+
const entries = [...scripted.values()].flatMap(({ conversation, listed, messages = [] }) => listed === undefined
|
|
326
|
+
? []
|
|
327
|
+
: [{ conversation, ...listed, newest: messages.at(-1)?.position ?? null }]);
|
|
328
|
+
for (const listener of listeners) {
|
|
329
|
+
listener({ kind: 'list', entries, whole: true, more: false, cursor: null });
|
|
330
|
+
}
|
|
223
331
|
}
|
|
224
332
|
function discovery() {
|
|
225
333
|
return {
|
|
226
334
|
kind: 'discovery',
|
|
227
|
-
supportedVersions,
|
|
228
|
-
mdModel,
|
|
229
335
|
rooms: rooms.map(declared),
|
|
230
336
|
capabilities,
|
|
231
337
|
providerLimits,
|
|
232
338
|
retention,
|
|
339
|
+
...(options.member === undefined ? {} : { member: options.member }),
|
|
233
340
|
};
|
|
234
341
|
}
|
|
342
|
+
function discovered() {
|
|
343
|
+
return resolveScript(scripts.discover, discovery, undefined).then((answer) => {
|
|
344
|
+
if (answer.kind === 'discovery')
|
|
345
|
+
for (const room of answer.rooms)
|
|
346
|
+
note(room.locator, room);
|
|
347
|
+
return answer;
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
function opened(notify) {
|
|
351
|
+
const opening = ++openings;
|
|
352
|
+
notify({ kind: 'opened' });
|
|
353
|
+
void discovered()
|
|
354
|
+
.catch(() => ({ kind: 'transport-uncertainty' }))
|
|
355
|
+
.then((answer) => {
|
|
356
|
+
if (opening !== openings)
|
|
357
|
+
return;
|
|
358
|
+
if (answer.kind !== 'discovery') {
|
|
359
|
+
openings += 1;
|
|
360
|
+
carried.clear();
|
|
361
|
+
const revoked = answer.kind === 'revoked-authorization';
|
|
362
|
+
notify({ kind: 'closed', revoked, lost: !revoked, authorityChanged: false });
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
declaredRooms.clear();
|
|
366
|
+
for (const room of answer.rooms)
|
|
367
|
+
declaredRooms.add(room.locator);
|
|
368
|
+
notify({ kind: 'discovered', discovery: answer });
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
function emitAll(event) {
|
|
372
|
+
for (const observer of observers)
|
|
373
|
+
observer(event);
|
|
374
|
+
}
|
|
235
375
|
return {
|
|
376
|
+
...(options.roomTypes === undefined ? {} : { roomTypes: options.roomTypes }),
|
|
377
|
+
...(options.typedPaths === undefined ? {} : { typedPaths: options.typedPaths }),
|
|
236
378
|
setRooms(next) {
|
|
237
379
|
rooms = [...next];
|
|
238
380
|
retain(rooms);
|
|
@@ -240,15 +382,38 @@ export function createMemoryAdapter(options = {}) {
|
|
|
240
382
|
setConversations(next) {
|
|
241
383
|
scriptConversations(next);
|
|
242
384
|
},
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
385
|
+
open() {
|
|
386
|
+
carried.clear();
|
|
387
|
+
announceList();
|
|
388
|
+
opened(emitAll);
|
|
389
|
+
},
|
|
390
|
+
close({ revoked = false, lost = !revoked, authorityChanged = false } = {}) {
|
|
391
|
+
openings += 1;
|
|
392
|
+
carried.clear();
|
|
393
|
+
emitAll({ kind: 'closed', revoked, lost, authorityChanged });
|
|
394
|
+
},
|
|
395
|
+
expire(locators) {
|
|
396
|
+
expiring = [...locators];
|
|
397
|
+
},
|
|
398
|
+
notifyRoomVersion({ locator }) {
|
|
399
|
+
carry(locator);
|
|
400
|
+
},
|
|
401
|
+
notifyAuthorityChanged(locators) {
|
|
402
|
+
const named = new Set(locators ?? [...declaredRooms, ...rooms.map((room) => room.locator)]);
|
|
403
|
+
const listed = rooms.filter((room) => named.has(room.locator));
|
|
404
|
+
const left = [...named].filter((locator) => !listed.some((room) => room.locator === locator));
|
|
405
|
+
for (const room of listed)
|
|
406
|
+
declaredRooms.add(room.locator);
|
|
407
|
+
for (const locator of left)
|
|
408
|
+
declaredRooms.delete(locator);
|
|
409
|
+
emitAll({
|
|
410
|
+
kind: 'authority-changed',
|
|
411
|
+
rooms: listed.map(declared),
|
|
412
|
+
left,
|
|
413
|
+
...(options.member === undefined ? {} : { member: options.member }),
|
|
414
|
+
});
|
|
246
415
|
},
|
|
247
416
|
notifyConversation(event) {
|
|
248
|
-
if (event.kind === 'accepted-message') {
|
|
249
|
-
sealed.add(event.message.turn);
|
|
250
|
-
unsealed.delete(event.message.turn);
|
|
251
|
-
}
|
|
252
417
|
for (const listener of listeners)
|
|
253
418
|
listener(event);
|
|
254
419
|
},
|
|
@@ -256,22 +421,32 @@ export function createMemoryAdapter(options = {}) {
|
|
|
256
421
|
for (const observer of modeObservers)
|
|
257
422
|
observer(mode);
|
|
258
423
|
},
|
|
259
|
-
|
|
260
|
-
return
|
|
424
|
+
heldRooms() {
|
|
425
|
+
return [...carried.keys()].sort();
|
|
261
426
|
},
|
|
262
427
|
replicatedConversations() {
|
|
263
428
|
return replicated;
|
|
264
429
|
},
|
|
430
|
+
heldConversationCursor() {
|
|
431
|
+
return heldCursor;
|
|
432
|
+
},
|
|
265
433
|
conversations: {
|
|
266
434
|
replicate(next) {
|
|
267
435
|
replicated = [...next];
|
|
268
436
|
},
|
|
437
|
+
hold(cursor) {
|
|
438
|
+
heldCursor = cursor;
|
|
439
|
+
},
|
|
269
440
|
observe(notify) {
|
|
270
441
|
listeners.add(notify);
|
|
271
442
|
return () => {
|
|
272
443
|
listeners.delete(notify);
|
|
273
444
|
};
|
|
274
445
|
},
|
|
446
|
+
history: (request) => Promise.resolve(historiesOf(request)),
|
|
447
|
+
...(options.agents === undefined
|
|
448
|
+
? {}
|
|
449
|
+
: { agents: () => Promise.resolve(structuredClone(options.agents)) }),
|
|
275
450
|
},
|
|
276
451
|
serviceMode: {
|
|
277
452
|
observe(notify) {
|
|
@@ -281,12 +456,49 @@ export function createMemoryAdapter(options = {}) {
|
|
|
281
456
|
};
|
|
282
457
|
},
|
|
283
458
|
},
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
459
|
+
roomStream: {
|
|
460
|
+
hold(asked) {
|
|
461
|
+
const named = asked.map((room) => room.locator);
|
|
462
|
+
const unknown = asked.flatMap((room) => {
|
|
463
|
+
const current = rooms.find((entry) => entry.locator === room.locator);
|
|
464
|
+
const version = room.roomVersion;
|
|
465
|
+
return current === undefined ||
|
|
466
|
+
(version !== null &&
|
|
467
|
+
(current.roomVersion === version || served.get(room.locator)?.has(version) === true))
|
|
468
|
+
? []
|
|
469
|
+
: [room.locator];
|
|
470
|
+
});
|
|
471
|
+
const expired = [
|
|
472
|
+
...new Set([...expiring.filter((locator) => named.includes(locator)), ...unknown]),
|
|
473
|
+
];
|
|
474
|
+
expiring = expiring.filter((locator) => !named.includes(locator));
|
|
475
|
+
queueMicrotask(() => {
|
|
476
|
+
for (const room of asked) {
|
|
477
|
+
if (expired.includes(room.locator) || room.roomVersion === null)
|
|
478
|
+
carried.delete(room.locator);
|
|
479
|
+
else {
|
|
480
|
+
carried.set(room.locator, room.roomVersion);
|
|
481
|
+
carry(room.locator);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
emitAll({ kind: 'caught-up', expired });
|
|
485
|
+
});
|
|
287
486
|
},
|
|
288
487
|
observe(notify) {
|
|
289
488
|
observers.add(notify);
|
|
489
|
+
queueMicrotask(() => {
|
|
490
|
+
if (!observers.has(notify))
|
|
491
|
+
return;
|
|
492
|
+
if (options.unopened === true) {
|
|
493
|
+
notify({ kind: 'closed', revoked: false, lost: true, authorityChanged: false });
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
announceList();
|
|
497
|
+
opened((event) => {
|
|
498
|
+
if (observers.has(notify))
|
|
499
|
+
notify(event);
|
|
500
|
+
});
|
|
501
|
+
});
|
|
290
502
|
return () => {
|
|
291
503
|
observers.delete(notify);
|
|
292
504
|
};
|
|
@@ -298,32 +510,28 @@ export function createMemoryAdapter(options = {}) {
|
|
|
298
510
|
else
|
|
299
511
|
scripts[operation] = answer;
|
|
300
512
|
},
|
|
301
|
-
async model(_authorization, request) {
|
|
302
|
-
return resolveScript(scripts.model, () => {
|
|
303
|
-
const room = rooms.find((entry) => entry.locator === request.locator);
|
|
304
|
-
const answer = request.attached === 'collection' ? room?.model : room?.selectionModel;
|
|
305
|
-
return answer ?? { kind: 'model-unavailable', reason: 'not-readable' };
|
|
306
|
-
}, request);
|
|
307
|
-
},
|
|
308
513
|
async authorize() {
|
|
309
514
|
return resolveScript(scripts.authorize, () => ({}), undefined);
|
|
310
515
|
},
|
|
311
516
|
async discover(_authorization) {
|
|
312
|
-
return
|
|
313
|
-
if (answer.kind === 'discovery')
|
|
314
|
-
for (const room of answer.rooms)
|
|
315
|
-
note(room.locator, room);
|
|
316
|
-
return answer;
|
|
317
|
-
});
|
|
517
|
+
return discovered();
|
|
318
518
|
},
|
|
319
519
|
reached,
|
|
520
|
+
follows(locator, roomVersion, next) {
|
|
521
|
+
const scripted = scripts.follows;
|
|
522
|
+
if (typeof scripted === 'boolean')
|
|
523
|
+
return scripted;
|
|
524
|
+
if (scripted !== undefined)
|
|
525
|
+
return scripted(locator, roomVersion, next);
|
|
526
|
+
return true;
|
|
527
|
+
},
|
|
320
528
|
async bootstrap(_authorization, request) {
|
|
321
529
|
return resolveScript(scripts.bootstrap, () => {
|
|
322
530
|
const room = rooms.find((entry) => entry.locator === request.locator);
|
|
323
531
|
if (room === undefined) {
|
|
324
532
|
return { kind: 'room-not-readable', locator: request.locator };
|
|
325
533
|
}
|
|
326
|
-
return page(room, request.pageToken
|
|
534
|
+
return page(room, request.pageToken);
|
|
327
535
|
}, request).then((answer) => note(request.locator, answer));
|
|
328
536
|
},
|
|
329
537
|
async refresh(_authorization, request) {
|
|
@@ -341,10 +549,6 @@ export function createMemoryAdapter(options = {}) {
|
|
|
341
549
|
}
|
|
342
550
|
return advancement(continuation.room, continuation.from, continuation.at);
|
|
343
551
|
}
|
|
344
|
-
if (request.minPosition !== undefined &&
|
|
345
|
-
!reached(room.locator, room.roomVersion, request.minPosition)) {
|
|
346
|
-
return { kind: 'retryable-refusal', code: 'replica_behind', fields: {} };
|
|
347
|
-
}
|
|
348
552
|
if (request.fromRoomVersion === room.roomVersion) {
|
|
349
553
|
return { kind: 'still-current' };
|
|
350
554
|
}
|
|
@@ -360,8 +564,20 @@ export function createMemoryAdapter(options = {}) {
|
|
|
360
564
|
if (room === undefined) {
|
|
361
565
|
return { kind: 'room-not-readable', locator: request.locator };
|
|
362
566
|
}
|
|
363
|
-
return {
|
|
364
|
-
|
|
567
|
+
return {
|
|
568
|
+
kind: 'rejected',
|
|
569
|
+
code: 'unscripted-mutation',
|
|
570
|
+
fields: {},
|
|
571
|
+
retryable: false,
|
|
572
|
+
affected: {},
|
|
573
|
+
};
|
|
574
|
+
}, request).then((answer) => {
|
|
575
|
+
const destination = request.changeBatch.between?.destination.room;
|
|
576
|
+
if (answer.kind === 'accepted' && answer.destination !== undefined && destination) {
|
|
577
|
+
note(destination, answer.destination);
|
|
578
|
+
}
|
|
579
|
+
return note(request.locator, answer);
|
|
580
|
+
});
|
|
365
581
|
},
|
|
366
582
|
async history(_authorization, request) {
|
|
367
583
|
return resolveScript(scripts.history, () => {
|
|
@@ -375,11 +591,46 @@ export function createMemoryAdapter(options = {}) {
|
|
|
375
591
|
};
|
|
376
592
|
}, request);
|
|
377
593
|
},
|
|
594
|
+
async binary(_authorization, request) {
|
|
595
|
+
return resolveScript(scripts.binary, () => {
|
|
596
|
+
const room = rooms.find((entry) => entry.locator === request.locator);
|
|
597
|
+
if (room === undefined)
|
|
598
|
+
return { kind: 'room-not-readable', locator: request.locator };
|
|
599
|
+
const blob = heldBlob(room, request.path);
|
|
600
|
+
return blob === undefined
|
|
601
|
+
? { kind: 'rejected', code: 'file_not_found', fields: {}, retryable: false }
|
|
602
|
+
: { kind: 'binary', body: blob.stream(), mediaType: blob.type };
|
|
603
|
+
}, request);
|
|
604
|
+
},
|
|
605
|
+
async link(_authorization, request) {
|
|
606
|
+
return resolveScript(scripts.link, () => {
|
|
607
|
+
const room = rooms.find((entry) => entry.locator === request.locator);
|
|
608
|
+
if (room === undefined)
|
|
609
|
+
return { kind: 'room-not-readable', locator: request.locator };
|
|
610
|
+
const blob = heldBlob(room, request.path);
|
|
611
|
+
if (blob === undefined) {
|
|
612
|
+
return { kind: 'rejected', code: 'file_not_found', fields: {}, retryable: false };
|
|
613
|
+
}
|
|
614
|
+
const url = linked.get(blob) ?? URL.createObjectURL(blob);
|
|
615
|
+
linked.set(blob, url);
|
|
616
|
+
return { kind: 'link', url, name: lastName(request.path), mediaType: blob.type };
|
|
617
|
+
}, request);
|
|
618
|
+
},
|
|
619
|
+
async attach(_authorization, request) {
|
|
620
|
+
return resolveScript(scripts.attach, () => {
|
|
621
|
+
const room = rooms.find((entry) => entry.locator === request.locator);
|
|
622
|
+
if (room === undefined)
|
|
623
|
+
return { kind: 'room-not-readable', locator: request.locator };
|
|
624
|
+
const path = freePath(room, request.name);
|
|
625
|
+
attached.set(`${room.locator}\u0000${path}`, request.blob);
|
|
626
|
+
return { kind: 'attached', path, revision: `attached-${attached.size}` };
|
|
627
|
+
}, request);
|
|
628
|
+
},
|
|
378
629
|
async action(_authorization, request) {
|
|
379
630
|
return resolveScript(scripts.action, () => {
|
|
380
|
-
if (request.name.startsWith('
|
|
631
|
+
if (request.name.startsWith('conversations/'))
|
|
381
632
|
return conversationAction(request);
|
|
382
|
-
return { kind: 'rejected', code: 'unscripted-action', fields: {} };
|
|
633
|
+
return { kind: 'rejected', code: 'unscripted-action', fields: {}, retryable: false };
|
|
383
634
|
}, request).then((answer) => {
|
|
384
635
|
if (answer.kind === 'action' && answer.at !== undefined)
|
|
385
636
|
note(answer.at.locator, answer.at);
|