@agentshouse/mdruntime-adapter 0.1.0-ceda7e87cc6e8 → 0.1.0-cedb8e57837a7
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/AGENTS.md +281 -276
- package/dist/actions.d.ts +23 -0
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +54 -35
- package/dist/adapter.d.ts +1 -0
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js +258 -73
- package/dist/discovery.d.ts +5 -3
- package/dist/discovery.d.ts.map +1 -1
- package/dist/discovery.js +29 -10
- package/dist/gather.d.ts +1 -1
- package/dist/gather.d.ts.map +1 -1
- package/dist/gather.js +4 -14
- package/dist/handed.d.ts +6 -6
- package/dist/handed.d.ts.map +1 -1
- package/dist/handed.js +36 -29
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/model.d.ts +0 -1
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +0 -3
- package/dist/read.d.ts +44 -7
- package/dist/read.d.ts.map +1 -1
- package/dist/read.js +177 -59
- package/dist/refusal.d.ts.map +1 -1
- package/dist/refusal.js +2 -1
- package/dist/site.d.ts +1 -1
- package/dist/site.d.ts.map +1 -1
- package/dist/site.js +159 -62
- package/dist/stream.d.ts +4 -4
- package/dist/stream.d.ts.map +1 -1
- package/dist/stream.js +334 -43
- package/dist/transport.d.ts +49 -4
- package/dist/transport.d.ts.map +1 -1
- package/dist/version.d.ts +1 -0
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +1 -1
- package/dist/write.d.ts +12 -1
- package/dist/write.d.ts.map +1 -1
- package/dist/write.js +85 -40
- package/package.json +2 -3
package/dist/adapter.js
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
|
+
import { sortedKeys, stepped } from '@agentshouse/mdruntime';
|
|
1
2
|
import { DISCOVER_PREFIXES, capabilities, discoveredRoom, mapDiscovery, parseDiscovery, trackedFrom, } from './discovery.js';
|
|
2
3
|
import { partModels, streamedModels, viewModels } from './model.js';
|
|
3
4
|
import { gathered, gatheredEach, selections } from './gather.js';
|
|
4
5
|
import { createSiteAdapter } from './site.js';
|
|
5
|
-
import {
|
|
6
|
+
import { authorityRoot, bootstrapping, matchesScope, parseEnumeration, parseEnvelope, parseGrant, refreshFrom, viewOf, wholeView, } from './read.js';
|
|
7
|
+
import { sha256Hex } from './digest.js';
|
|
6
8
|
import { acceptedHanded, enumeratedHanded, heldHanded, scopeHanded, streamedHanded, } from './handed.js';
|
|
7
9
|
import { houseAction } from './actions.js';
|
|
8
10
|
import { classifyDoor, classifyRead, discoveryFailure, doorRefusal } from './refusal.js';
|
|
9
11
|
import { houseNotifications } from './stream.js';
|
|
10
12
|
import { DOOR_BATCH, DOOR_BATCH_UPLOAD, DOOR_BOOTSTRAP, DOOR_DISCOVER, DOOR_ENUMERATE, DOOR_FILES_GET, DOOR_REVISIONS, } from './transport.js';
|
|
11
|
-
import { batchPaths, deltaBody, deltaBytes, deltaDigest, historyOutcome, houseChanges, mutationOutcome, operationHead, rejectedMapping, revisionBody, uploadable, } from './write.js';
|
|
13
|
+
import { batchPaths, deltaBody, deltaBytes, deltaDigest, historyOutcome, houseChanges, houseMove, mutationOutcome, operationHead, rejectedMapping, revisionBody, uploadable, } from './write.js';
|
|
12
14
|
import { houseReferrer } from './referrer.js';
|
|
13
15
|
import { followsVersion, logPosition, presented, reachedVersion, roomVersion, } from './version.js';
|
|
14
16
|
const UNCERTAIN = { kind: 'transport-uncertainty' };
|
|
@@ -16,9 +18,56 @@ const UNCERTAIN_READ = { kind: 'transport-uncertainty' };
|
|
|
16
18
|
function lapsedGrant(outcome) {
|
|
17
19
|
return outcome.kind === 'rejected' && outcome.code === 'upload_unavailable';
|
|
18
20
|
}
|
|
21
|
+
const heldPaths = new WeakMap();
|
|
22
|
+
async function sortedHeld(held) {
|
|
23
|
+
const known = heldPaths.get(held.documents);
|
|
24
|
+
if (known !== undefined)
|
|
25
|
+
return known;
|
|
26
|
+
const sorted = await stepped(sortedKeys(held.documents));
|
|
27
|
+
heldPaths.set(held.documents, sorted);
|
|
28
|
+
return sorted;
|
|
29
|
+
}
|
|
30
|
+
function heldFiles(authority, held, paths, after, bound) {
|
|
31
|
+
let low = 0;
|
|
32
|
+
for (let high = paths.length; low < high;) {
|
|
33
|
+
const middle = (low + high) >>> 1;
|
|
34
|
+
if (after !== null && paths[middle] <= after)
|
|
35
|
+
low = middle + 1;
|
|
36
|
+
else
|
|
37
|
+
high = middle;
|
|
38
|
+
}
|
|
39
|
+
const files = [];
|
|
40
|
+
let through = null;
|
|
41
|
+
for (let at = low; at < paths.length && through === null; at += 1) {
|
|
42
|
+
const revision = held.revisions[paths[at]];
|
|
43
|
+
if (revision === undefined)
|
|
44
|
+
continue;
|
|
45
|
+
if (files.length === bound)
|
|
46
|
+
through = files.at(-1).path;
|
|
47
|
+
else
|
|
48
|
+
files.push({ path: paths[at], revision });
|
|
49
|
+
}
|
|
50
|
+
return files.length === 0 ? null : { room: authority, through, files };
|
|
51
|
+
}
|
|
52
|
+
async function reused(scope, held) {
|
|
53
|
+
const sent = new Set(scope.contents.map((entry) => entry.path));
|
|
54
|
+
const contents = [...scope.contents];
|
|
55
|
+
for (const file of scope.files) {
|
|
56
|
+
if (file.binary !== undefined || sent.has(file.path))
|
|
57
|
+
continue;
|
|
58
|
+
const content = held?.documents[file.path];
|
|
59
|
+
if (content === undefined ||
|
|
60
|
+
held?.revisions[file.path] !== file.revision ||
|
|
61
|
+
(await sha256Hex(content)) !== file.sha256) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
contents.push({ path: file.path, content });
|
|
65
|
+
}
|
|
66
|
+
return { ...scope, contents };
|
|
67
|
+
}
|
|
19
68
|
export function createAgentsHouseAdapter(options) {
|
|
20
69
|
if ('site' in options)
|
|
21
|
-
return createSiteAdapter(options.site);
|
|
70
|
+
return createSiteAdapter(options.site, options.snapshot === true);
|
|
22
71
|
const transport = options.transport;
|
|
23
72
|
const notifications = houseNotifications(transport, 'credential');
|
|
24
73
|
const tracked = new Map();
|
|
@@ -29,12 +78,36 @@ export function createAgentsHouseAdapter(options) {
|
|
|
29
78
|
throw new Error('no Discovery declared the bounds');
|
|
30
79
|
return bounds;
|
|
31
80
|
}
|
|
32
|
-
|
|
33
|
-
|
|
81
|
+
function hand(locator, steps) {
|
|
82
|
+
const held = handed.get(locator);
|
|
83
|
+
const next = stepped(steps);
|
|
84
|
+
if (!(next instanceof Promise)) {
|
|
85
|
+
handed.set(locator, next);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
return next.then((room) => {
|
|
89
|
+
if (handed.get(locator) === held)
|
|
90
|
+
handed.set(locator, room);
|
|
91
|
+
else
|
|
92
|
+
handed.delete(locator);
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
async function bootstrappedRoom(envelope, sent, asked, continued) {
|
|
96
|
+
const { entry } = asked;
|
|
97
|
+
if (sent === undefined)
|
|
34
98
|
return { kind: 'room-not-readable', locator: entry.locator };
|
|
35
|
-
const
|
|
99
|
+
const scope = await reused(sent, asked.held);
|
|
100
|
+
if (scope === null) {
|
|
101
|
+
return asked.held === undefined
|
|
102
|
+
? UNCERTAIN_READ
|
|
103
|
+
: continued === null
|
|
104
|
+
? bootstrapTogether({ entry, held: undefined })
|
|
105
|
+
: continuedBootstrap({ entry, held: undefined }, continued);
|
|
106
|
+
}
|
|
107
|
+
const cursor = envelope.next?.room === entry.authority ? envelope.next : undefined;
|
|
108
|
+
const next = cursor?.path ?? null;
|
|
36
109
|
if (continued === null && next === null)
|
|
37
|
-
|
|
110
|
+
await hand(entry.locator, scopeHanded(scope));
|
|
38
111
|
else
|
|
39
112
|
handed.delete(entry.locator);
|
|
40
113
|
notifications.reach(entry.authority, scope);
|
|
@@ -43,22 +116,45 @@ export function createAgentsHouseAdapter(options) {
|
|
|
43
116
|
...entry.schemas.filter((schema) => scope.files.some((file) => file.path === schema)),
|
|
44
117
|
];
|
|
45
118
|
const models = await partModels(scope, entry, new Set(seen), next === null);
|
|
46
|
-
const continuation = next === null
|
|
119
|
+
const continuation = next === null
|
|
120
|
+
? null
|
|
121
|
+
: { after: next, seen, ...(cursor?.cut === undefined ? {} : { cut: cursor.cut }) };
|
|
47
122
|
return {
|
|
48
|
-
...
|
|
123
|
+
...(await stepped(bootstrapping(scope))),
|
|
49
124
|
...(models === undefined ? {} : { models }),
|
|
50
125
|
...(continuation === null ? {} : { pageToken: JSON.stringify(continuation) }),
|
|
51
126
|
};
|
|
52
127
|
}
|
|
128
|
+
async function enumeratedPage(ask, page) {
|
|
129
|
+
const answer = await transport.door(DOOR_ENUMERATE, {
|
|
130
|
+
prefixes: [authorityRoot(ask.entry.handle)],
|
|
131
|
+
positions: [
|
|
132
|
+
{
|
|
133
|
+
...presented(ask.entry.authority, { position: page.after, logEpoch: ask.from.logEpoch }),
|
|
134
|
+
page: { through: page.through, read: page.read },
|
|
135
|
+
},
|
|
136
|
+
],
|
|
137
|
+
});
|
|
138
|
+
const failure = classifyRead(answer.status, answer.body, ask.entry.locator);
|
|
139
|
+
if (failure !== null)
|
|
140
|
+
return failure;
|
|
141
|
+
const enumeration = parseEnumeration(answer.body, 'credential');
|
|
142
|
+
if (enumeration === null)
|
|
143
|
+
return UNCERTAIN_READ;
|
|
144
|
+
return (viewOf(enumeration, ask.entry) ?? { kind: 'room-not-readable', locator: ask.entry.locator });
|
|
145
|
+
}
|
|
53
146
|
async function enumeratedRoom(enumeration, ask) {
|
|
54
|
-
const
|
|
55
|
-
if (
|
|
147
|
+
const first = viewOf(enumeration, ask.entry);
|
|
148
|
+
if (first === undefined)
|
|
56
149
|
return { kind: 'room-not-readable', locator: ask.entry.locator };
|
|
150
|
+
const view = await wholeView(first, (page) => enumeratedPage(ask, page));
|
|
151
|
+
if ('kind' in view)
|
|
152
|
+
return view;
|
|
57
153
|
notifications.reach(ask.entry.authority, view);
|
|
58
|
-
const answer = refreshFrom(view, ask.fromRoomVersion);
|
|
154
|
+
const answer = await refreshFrom(view, ask.fromRoomVersion);
|
|
59
155
|
if (answer.kind !== 'net-advancement')
|
|
60
156
|
return answer;
|
|
61
|
-
|
|
157
|
+
await hand(ask.entry.locator, enumeratedHanded(view, handed.get(ask.entry.locator)));
|
|
62
158
|
const models = await viewModels(view, ask.entry);
|
|
63
159
|
return models === undefined ? answer : { ...answer, models };
|
|
64
160
|
}
|
|
@@ -66,10 +162,10 @@ export function createAgentsHouseAdapter(options) {
|
|
|
66
162
|
const held = handed.get(entry.locator);
|
|
67
163
|
if (held?.roomVersion !== request.fromRoomVersion)
|
|
68
164
|
return null;
|
|
69
|
-
const advanced = notifications.advance(entry, from, (path, revision) => held.revisions
|
|
165
|
+
const advanced = await stepped(notifications.advance(entry, from, (path, revision) => held.revisions[path] === revision));
|
|
70
166
|
if (advanced === null)
|
|
71
167
|
return null;
|
|
72
|
-
|
|
168
|
+
await hand(entry.locator, streamedHanded(held, advanced));
|
|
73
169
|
const models = await streamedModels(advanced.schemas, entry, 'credential');
|
|
74
170
|
return models === undefined ? advanced.page : { ...advanced.page, models };
|
|
75
171
|
}
|
|
@@ -85,14 +181,20 @@ export function createAgentsHouseAdapter(options) {
|
|
|
85
181
|
});
|
|
86
182
|
notifications.send(kept);
|
|
87
183
|
}
|
|
88
|
-
function
|
|
184
|
+
async function adoptedMove(entry, into, outcome) {
|
|
185
|
+
if (outcome.kind === 'accepted' && outcome.destination !== undefined) {
|
|
186
|
+
await adopted(into, { kind: 'accepted', ...outcome.destination });
|
|
187
|
+
}
|
|
188
|
+
return adopted(entry, outcome);
|
|
189
|
+
}
|
|
190
|
+
async function adopted(entry, outcome) {
|
|
89
191
|
if (outcome.kind !== 'accepted')
|
|
90
192
|
return outcome;
|
|
91
193
|
const held = handed.get(entry.locator);
|
|
92
194
|
if (held === undefined)
|
|
93
195
|
return outcome;
|
|
94
196
|
const follows = followsVersion(held.roomVersion, outcome.roomVersion);
|
|
95
|
-
|
|
197
|
+
await hand(entry.locator, acceptedHanded(held, outcome, follows ? outcome.roomVersion : held.roomVersion));
|
|
96
198
|
const at = logPosition(outcome.roomVersion);
|
|
97
199
|
if (follows && at !== null)
|
|
98
200
|
notifications.reach(entry.authority, at);
|
|
@@ -115,19 +217,31 @@ export function createAgentsHouseAdapter(options) {
|
|
|
115
217
|
return result;
|
|
116
218
|
return { ...result, at: { locator, roomVersion: roomVersion({ position, logEpoch }) } };
|
|
117
219
|
}
|
|
118
|
-
function
|
|
119
|
-
|
|
220
|
+
async function holding(asks, after) {
|
|
221
|
+
const bound = Math.floor(declared().max_held_files / Math.max(1, asks.filter((ask) => ask.held !== undefined).length));
|
|
222
|
+
const held = [];
|
|
223
|
+
for (const ask of asks) {
|
|
224
|
+
if (ask.held === undefined || bound === 0)
|
|
225
|
+
continue;
|
|
226
|
+
const paths = await sortedHeld(ask.held);
|
|
227
|
+
const files = heldFiles(ask.entry.authority, ask.held, paths, after, bound);
|
|
228
|
+
if (files !== null)
|
|
229
|
+
held.push(files);
|
|
230
|
+
}
|
|
231
|
+
return held;
|
|
120
232
|
}
|
|
121
|
-
async function bootstrapped(
|
|
122
|
-
let open =
|
|
233
|
+
async function bootstrapped(asks, answer) {
|
|
234
|
+
let open = asks.map((asked, ask) => ({ entry: asked.entry, asked, ask }));
|
|
123
235
|
while (open.length > 0) {
|
|
124
236
|
const unreached = [];
|
|
125
|
-
for (const part of selections(open,
|
|
237
|
+
for (const part of selections(open, declared().max_prefixes)) {
|
|
238
|
+
const held = await holding(part.map(({ asked }) => asked), null);
|
|
126
239
|
const answered = await transport.door(DOOR_BOOTSTRAP, {
|
|
127
|
-
prefixes: part.
|
|
240
|
+
prefixes: part.map(({ entry }) => authorityRoot(entry.handle)),
|
|
241
|
+
...(held.length === 0 ? {} : { held }),
|
|
128
242
|
});
|
|
129
243
|
const classified = part.map(({ entry }) => classifyDoor(answered.status, answered.body, entry.locator));
|
|
130
|
-
const envelope = classified[0] === null ? parseEnvelope(answered.body) : null;
|
|
244
|
+
const envelope = classified[0] === null ? await stepped(parseEnvelope(answered.body)) : null;
|
|
131
245
|
for (const [index, asked] of part.entries()) {
|
|
132
246
|
const failure = classified[index];
|
|
133
247
|
if (failure !== null && failure !== undefined) {
|
|
@@ -143,71 +257,79 @@ export function createAgentsHouseAdapter(options) {
|
|
|
143
257
|
unreached.push(asked);
|
|
144
258
|
continue;
|
|
145
259
|
}
|
|
146
|
-
answer(asked.ask, await bootstrappedRoom(envelope, scope, asked.
|
|
260
|
+
answer(asked.ask, await bootstrappedRoom(envelope, scope, asked.asked, null));
|
|
147
261
|
}
|
|
148
262
|
}
|
|
149
263
|
open = unreached;
|
|
150
264
|
}
|
|
151
265
|
}
|
|
152
|
-
async function continuedBootstrap(
|
|
153
|
-
const
|
|
266
|
+
async function continuedBootstrap(asked, continued) {
|
|
267
|
+
const { entry } = asked;
|
|
268
|
+
const after = {
|
|
269
|
+
room: entry.authority,
|
|
270
|
+
path: continued.after,
|
|
271
|
+
...(continued.cut === undefined ? {} : { cut: continued.cut }),
|
|
272
|
+
};
|
|
273
|
+
const held = await holding([asked], continued.after);
|
|
154
274
|
const answered = await transport.door(DOOR_BOOTSTRAP, {
|
|
155
|
-
prefixes:
|
|
275
|
+
prefixes: [authorityRoot(entry.handle)],
|
|
156
276
|
after,
|
|
277
|
+
...(held.length === 0 ? {} : { held }),
|
|
157
278
|
});
|
|
158
|
-
const failure =
|
|
279
|
+
const failure = classifyRead(answered.status, answered.body, entry.locator);
|
|
159
280
|
if (failure !== null)
|
|
160
281
|
return failure;
|
|
161
|
-
const envelope = parseEnvelope(answered.body);
|
|
282
|
+
const envelope = await stepped(parseEnvelope(answered.body));
|
|
162
283
|
if (envelope === null)
|
|
163
284
|
return UNCERTAIN_READ;
|
|
164
285
|
const scope = envelope.scopes.find((candidate) => matchesScope(candidate, entry));
|
|
165
|
-
return bootstrappedRoom(envelope, scope,
|
|
286
|
+
return bootstrappedRoom(envelope, scope, asked, continued);
|
|
166
287
|
}
|
|
167
288
|
function layered(asks) {
|
|
168
289
|
const depths = new Map();
|
|
169
290
|
const layers = [];
|
|
170
291
|
for (const ask of asks) {
|
|
171
|
-
const depth = depths.get(ask.entry.authority) ?? 0;
|
|
172
|
-
depths.set(ask.entry.authority, depth + 1);
|
|
292
|
+
const depth = depths.get(ask.ask.entry.authority) ?? 0;
|
|
293
|
+
depths.set(ask.ask.entry.authority, depth + 1);
|
|
173
294
|
(layers[depth] ??= []).push(ask);
|
|
174
295
|
}
|
|
175
296
|
return layers;
|
|
176
297
|
}
|
|
177
298
|
async function enumerated(asks) {
|
|
178
|
-
const answers =
|
|
179
|
-
const parts = layered(asks
|
|
299
|
+
const answers = [];
|
|
300
|
+
const parts = layered(asks.map((ask, index) => ({ ask, index }))).flatMap((layer) => selections(layer, declared().max_prefixes));
|
|
180
301
|
for (const part of parts) {
|
|
181
302
|
let open = [...part];
|
|
182
303
|
while (open.length > 0) {
|
|
183
304
|
const answer = await transport.door(DOOR_ENUMERATE, {
|
|
184
|
-
prefixes: open.
|
|
185
|
-
positions: open.map((ask) => presented(ask.entry.authority, ask.from)),
|
|
305
|
+
prefixes: open.map(({ ask }) => authorityRoot(ask.entry.handle)),
|
|
306
|
+
positions: open.map(({ ask }) => presented(ask.entry.authority, ask.from)),
|
|
186
307
|
});
|
|
187
|
-
const classified = open.map((ask) => classifyRead(answer.status, answer.body, ask.entry.locator));
|
|
308
|
+
const classified = open.map(({ ask }) => classifyRead(answer.status, answer.body, ask.entry.locator));
|
|
188
309
|
if (classified[0]?.kind === 'beyond-horizon') {
|
|
189
310
|
const named = doorRefusal(answer.body)?.fields.authority;
|
|
190
|
-
const expired = open.filter((ask) => ask.entry.authority === named);
|
|
311
|
+
const expired = open.filter(({ ask }) => ask.entry.authority === named);
|
|
191
312
|
const answered = expired.length === 0 ? UNCERTAIN_READ : { kind: 'beyond-horizon' };
|
|
192
313
|
const gone = expired.length === 0 ? open : expired;
|
|
193
314
|
for (const ask of gone)
|
|
194
|
-
answers.
|
|
315
|
+
answers[ask.index] = answered;
|
|
195
316
|
open = open.filter((ask) => !gone.includes(ask));
|
|
196
317
|
continue;
|
|
197
318
|
}
|
|
198
319
|
const enumeration = classified[0] === null ? parseEnumeration(answer.body, 'credential') : null;
|
|
199
|
-
for (const [
|
|
200
|
-
const failure = classified[
|
|
320
|
+
for (const [answerIndex, { ask, index }] of open.entries()) {
|
|
321
|
+
const failure = classified[answerIndex];
|
|
201
322
|
if (failure !== null && failure !== undefined) {
|
|
202
|
-
answers
|
|
323
|
+
answers[index] = failure;
|
|
203
324
|
continue;
|
|
204
325
|
}
|
|
205
|
-
answers
|
|
326
|
+
answers[index] =
|
|
327
|
+
enumeration === null ? UNCERTAIN_READ : await enumeratedRoom(enumeration, ask);
|
|
206
328
|
}
|
|
207
329
|
open = [];
|
|
208
330
|
}
|
|
209
331
|
}
|
|
210
|
-
return
|
|
332
|
+
return answers;
|
|
211
333
|
}
|
|
212
334
|
const bootstrapTogether = gatheredEach(bootstrapped);
|
|
213
335
|
const enumerateTogether = gathered(enumerated);
|
|
@@ -219,7 +341,9 @@ export function createAgentsHouseAdapter(options) {
|
|
|
219
341
|
}
|
|
220
342
|
const minted = await transport.door(DOOR_BATCH_UPLOAD, { ...head, paths, bytes, sha256 });
|
|
221
343
|
if (minted.status !== 200) {
|
|
222
|
-
return {
|
|
344
|
+
return {
|
|
345
|
+
outcome: await stepped(mutationOutcome(minted.status, minted.body, entry.locator, entry.handle)),
|
|
346
|
+
};
|
|
223
347
|
}
|
|
224
348
|
const grant = parseGrant(minted.body);
|
|
225
349
|
if (grant === null || grant.method !== 'POST')
|
|
@@ -261,7 +385,7 @@ export function createAgentsHouseAdapter(options) {
|
|
|
261
385
|
}
|
|
262
386
|
return true;
|
|
263
387
|
}
|
|
264
|
-
async function submit(batch, head, changes, delta, bytes, entry, handed, keep) {
|
|
388
|
+
async function submit(batch, head, changes, delta, bytes, entry, handed, keep, into) {
|
|
265
389
|
const inline = { ...head, changes, runtime: notifications.runtime };
|
|
266
390
|
const prepared = uploadable(inline, bytes, declared())
|
|
267
391
|
? await uploaded(head, changes, delta, bytes, entry, handed, keep)
|
|
@@ -275,9 +399,14 @@ export function createAgentsHouseAdapter(options) {
|
|
|
275
399
|
const settled = stagedFiles === null ? answer : await transport.door(DOOR_BATCH, prepared.body);
|
|
276
400
|
if (stagedFiles === true && (await staged(settled, batch)) !== null)
|
|
277
401
|
return UNCERTAIN;
|
|
278
|
-
return mutationOutcome(settled.status, settled.body, entry.locator, entry.handle
|
|
402
|
+
return stepped(mutationOutcome(settled.status, settled.body, entry.locator, entry.handle, into === undefined
|
|
403
|
+
? undefined
|
|
404
|
+
: {
|
|
405
|
+
source: { authority: entry.authority, handle: entry.handle },
|
|
406
|
+
destination: { authority: into.authority, handle: into.handle },
|
|
407
|
+
}));
|
|
279
408
|
}
|
|
280
|
-
|
|
409
|
+
function granted(answer, locator) {
|
|
281
410
|
const failure = classifyDoor(answer.status, answer.body, locator);
|
|
282
411
|
if (failure !== null) {
|
|
283
412
|
return failure.kind === 'scoped-refusal'
|
|
@@ -290,19 +419,40 @@ export function createAgentsHouseAdapter(options) {
|
|
|
290
419
|
: failure;
|
|
291
420
|
}
|
|
292
421
|
const grant = parseGrant(answer.body);
|
|
293
|
-
|
|
422
|
+
const name = answer.body.name;
|
|
423
|
+
if (grant === null || grant.method !== 'GET' || typeof name !== 'string') {
|
|
294
424
|
return UNCERTAIN_READ;
|
|
295
|
-
|
|
425
|
+
}
|
|
426
|
+
return { kind: 'link', url: grant.url, name };
|
|
427
|
+
}
|
|
428
|
+
async function downloaded(answer, locator) {
|
|
429
|
+
const link = granted(answer, locator);
|
|
430
|
+
if (link.kind !== 'link')
|
|
431
|
+
return link;
|
|
432
|
+
const read = await transport.download(link.url);
|
|
296
433
|
if (read.status !== 200 || read.blob === null)
|
|
297
434
|
return UNCERTAIN_READ;
|
|
298
435
|
return { kind: 'binary', blob: read.blob };
|
|
299
436
|
}
|
|
437
|
+
function pathGrant(request) {
|
|
438
|
+
const entry = authorityFor(request.locator);
|
|
439
|
+
if (entry === undefined)
|
|
440
|
+
return null;
|
|
441
|
+
return transport.door(DOOR_FILES_GET, {
|
|
442
|
+
room_ref: entry.authority,
|
|
443
|
+
path: request.path,
|
|
444
|
+
revision: request.revision,
|
|
445
|
+
});
|
|
446
|
+
}
|
|
300
447
|
return {
|
|
301
448
|
async authorize() {
|
|
302
449
|
return options.authorize();
|
|
303
450
|
},
|
|
304
|
-
async discover(_authorization) {
|
|
305
|
-
const answer = await transport.door(DOOR_DISCOVER, {
|
|
451
|
+
async discover(_authorization, scope) {
|
|
452
|
+
const answer = await transport.door(DOOR_DISCOVER, {
|
|
453
|
+
prefixes: [...DISCOVER_PREFIXES],
|
|
454
|
+
...(scope === undefined ? {} : { rooms: scope.authorities }),
|
|
455
|
+
});
|
|
306
456
|
const failed = discoveryFailure(answer.status, answer.body);
|
|
307
457
|
if (failed !== null)
|
|
308
458
|
return failed;
|
|
@@ -311,26 +461,42 @@ export function createAgentsHouseAdapter(options) {
|
|
|
311
461
|
return { kind: 'transport-uncertainty' };
|
|
312
462
|
bounds = document.bounds;
|
|
313
463
|
const entries = trackedFrom(document);
|
|
314
|
-
|
|
464
|
+
const covered = scope === undefined
|
|
465
|
+
? null
|
|
466
|
+
: [
|
|
467
|
+
...new Set([
|
|
468
|
+
...[...tracked.values()]
|
|
469
|
+
.filter((entry) => scope.authorities.includes(entry.authority))
|
|
470
|
+
.map((entry) => entry.locator),
|
|
471
|
+
...entries.map((entry) => entry.locator),
|
|
472
|
+
]),
|
|
473
|
+
];
|
|
474
|
+
if (covered === null)
|
|
475
|
+
tracked.clear();
|
|
476
|
+
else
|
|
477
|
+
for (const locator of covered)
|
|
478
|
+
tracked.delete(locator);
|
|
315
479
|
for (const entry of entries)
|
|
316
480
|
tracked.set(entry.locator, entry);
|
|
317
481
|
for (const locator of handed.keys())
|
|
318
482
|
if (!tracked.has(locator))
|
|
319
483
|
handed.delete(locator);
|
|
320
|
-
notifications.track(
|
|
484
|
+
notifications.track([...tracked.values()]);
|
|
321
485
|
return {
|
|
322
|
-
...mapDiscovery(document, document.authorities.map((authority) => discoveredRoom(authority)), capabilities(document)),
|
|
486
|
+
...mapDiscovery(document, document.authorities.map((authority) => discoveredRoom(authority)), capabilities(document.actions, [...tracked.values()])),
|
|
323
487
|
member: document.member,
|
|
488
|
+
...(covered === null ? {} : { covered }),
|
|
324
489
|
};
|
|
325
490
|
},
|
|
326
491
|
async bootstrap(_authorization, request) {
|
|
327
492
|
const entry = authorityFor(request.locator);
|
|
328
493
|
if (entry === undefined)
|
|
329
494
|
return { kind: 'room-not-readable', locator: request.locator };
|
|
495
|
+
const asked = { entry, held: request.held };
|
|
330
496
|
if (request.pageToken !== undefined) {
|
|
331
|
-
return continuedBootstrap(
|
|
497
|
+
return continuedBootstrap(asked, JSON.parse(request.pageToken));
|
|
332
498
|
}
|
|
333
|
-
return bootstrapTogether(
|
|
499
|
+
return bootstrapTogether(asked);
|
|
334
500
|
},
|
|
335
501
|
async refresh(_authorization, request) {
|
|
336
502
|
const entry = authorityFor(request.locator);
|
|
@@ -350,18 +516,27 @@ export function createAgentsHouseAdapter(options) {
|
|
|
350
516
|
const entry = authorityFor(request.locator);
|
|
351
517
|
if (entry === undefined)
|
|
352
518
|
return { kind: 'room-not-readable', locator: request.locator };
|
|
353
|
-
const
|
|
519
|
+
const batch = request.changeBatch;
|
|
520
|
+
const destination = batch.between?.destination.room;
|
|
521
|
+
const into = destination === undefined ? undefined : authorityFor(destination);
|
|
522
|
+
if (destination !== undefined && into === undefined) {
|
|
523
|
+
return { kind: 'room-not-readable', locator: destination };
|
|
524
|
+
}
|
|
525
|
+
const mapped = into === undefined
|
|
526
|
+
? houseChanges(batch, entry.handle)
|
|
527
|
+
: houseMove(batch, entry.handle, into.handle);
|
|
354
528
|
if (mapped.kind === 'unmapped')
|
|
355
529
|
return rejectedMapping(mapped);
|
|
356
|
-
const head = operationHead(
|
|
530
|
+
const head = operationHead(batch);
|
|
357
531
|
const delta = deltaBody(mapped.changes);
|
|
358
532
|
const bytes = deltaBytes(delta);
|
|
359
533
|
const handed = request.upload;
|
|
360
|
-
const
|
|
361
|
-
const
|
|
534
|
+
const answer = (upload) => submit(batch, head, mapped.changes, delta, bytes, entry, upload, keep, into);
|
|
535
|
+
const settle = (outcome) => into === undefined ? adopted(entry, outcome) : adoptedMove(entry, into, outcome);
|
|
536
|
+
const outcome = await answer(handed);
|
|
362
537
|
if (handed === undefined || !lapsedGrant(outcome))
|
|
363
|
-
return
|
|
364
|
-
return
|
|
538
|
+
return settle(outcome);
|
|
539
|
+
return settle(await answer(undefined));
|
|
365
540
|
},
|
|
366
541
|
async history(_authorization, request) {
|
|
367
542
|
const entry = authorityFor(request.locator);
|
|
@@ -380,15 +555,10 @@ export function createAgentsHouseAdapter(options) {
|
|
|
380
555
|
},
|
|
381
556
|
async binary(_authorization, request) {
|
|
382
557
|
if (request.kind === 'path') {
|
|
383
|
-
const
|
|
384
|
-
if (
|
|
558
|
+
const asked = pathGrant(request);
|
|
559
|
+
if (asked === null)
|
|
385
560
|
return { kind: 'room-not-readable', locator: request.locator };
|
|
386
|
-
|
|
387
|
-
room_ref: entry.authority,
|
|
388
|
-
path: request.path,
|
|
389
|
-
revision: request.revision,
|
|
390
|
-
});
|
|
391
|
-
return downloaded(answer, request.locator);
|
|
561
|
+
return downloaded(await asked, request.locator);
|
|
392
562
|
}
|
|
393
563
|
const referrer = houseReferrer(request.referrer, tracked);
|
|
394
564
|
if (referrer === null) {
|
|
@@ -400,6 +570,12 @@ export function createAgentsHouseAdapter(options) {
|
|
|
400
570
|
});
|
|
401
571
|
return downloaded(answer, referrer.locator);
|
|
402
572
|
},
|
|
573
|
+
async link(_authorization, request) {
|
|
574
|
+
const asked = pathGrant(request);
|
|
575
|
+
if (asked === null)
|
|
576
|
+
return { kind: 'room-not-readable', locator: request.locator };
|
|
577
|
+
return granted(await asked, request.locator);
|
|
578
|
+
},
|
|
403
579
|
referrerScope(referrer) {
|
|
404
580
|
const held = houseReferrer(referrer, tracked);
|
|
405
581
|
if (held === null)
|
|
@@ -418,7 +594,16 @@ export function createAgentsHouseAdapter(options) {
|
|
|
418
594
|
async action(_authorization, request) {
|
|
419
595
|
return withAt(await houseAction(transport, request));
|
|
420
596
|
},
|
|
421
|
-
roomStream: {
|
|
597
|
+
roomStream: {
|
|
598
|
+
hold: held,
|
|
599
|
+
reopen: notifications.reopen,
|
|
600
|
+
observe: notifications.observe,
|
|
601
|
+
published(locator, roomVersion) {
|
|
602
|
+
const at = logPosition(roomVersion);
|
|
603
|
+
if (at !== null)
|
|
604
|
+
notifications.published(locator, at);
|
|
605
|
+
},
|
|
606
|
+
},
|
|
422
607
|
conversations: notifications.conversations,
|
|
423
608
|
serviceMode: notifications.serviceMode,
|
|
424
609
|
};
|
package/dist/discovery.d.ts
CHANGED
|
@@ -13,15 +13,16 @@ export type HouseBounds = {
|
|
|
13
13
|
max_inline_body_bytes: number;
|
|
14
14
|
max_upload_bytes: number;
|
|
15
15
|
max_prefixes: number;
|
|
16
|
+
max_held_files: number;
|
|
16
17
|
change_retention_seconds: number;
|
|
17
18
|
operation_retention_seconds: number;
|
|
19
|
+
max_move_documents?: number;
|
|
18
20
|
};
|
|
19
21
|
export type HouseOperations = {
|
|
20
|
-
read: readonly string[];
|
|
21
|
-
sync: readonly string[];
|
|
22
22
|
write: readonly string[];
|
|
23
23
|
delete: readonly string[];
|
|
24
24
|
};
|
|
25
|
+
export type HouseReadLevel = 'mcp' | 'online' | 'local';
|
|
25
26
|
export type HouseModel = {
|
|
26
27
|
root: string;
|
|
27
28
|
paths: readonly string[];
|
|
@@ -34,6 +35,7 @@ export type HouseAuthority = LogPosition & {
|
|
|
34
35
|
houseModel: HouseModel | null;
|
|
35
36
|
managedReplication: 'admitted' | 'unavailable';
|
|
36
37
|
protectedPaths: readonly string[];
|
|
38
|
+
read: HouseReadLevel;
|
|
37
39
|
operations: HouseOperations;
|
|
38
40
|
};
|
|
39
41
|
export type HouseDiscoveryDocument = {
|
|
@@ -65,7 +67,7 @@ export declare function siteTracked(document: HouseSiteDiscovery): TrackedAuthor
|
|
|
65
67
|
export declare function trackedFrom(document: HouseDiscoveryDocument): TrackedAuthority[];
|
|
66
68
|
export declare function providerLimits(bounds: HouseBounds): readonly ProviderLimit[];
|
|
67
69
|
export declare function declaredRetention(bounds: HouseBounds): Retention;
|
|
68
|
-
export declare function capabilities(
|
|
70
|
+
export declare function capabilities(actions: HouseDiscoveryDocument['actions'], rooms: readonly Pick<TrackedAuthority, 'authority' | 'houseModel'>[]): readonly Capability[];
|
|
69
71
|
export declare function siteCapabilities(): readonly Capability[];
|
|
70
72
|
export declare function discoveredRoom(authority: HouseAuthority): DiscoveredRoom;
|
|
71
73
|
export declare function mapDiscovery(document: Pick<HouseDiscoveryDocument, 'bounds'>, rooms: readonly DiscoveredRoom[], declared: readonly Capability[]): Discovery;
|
package/dist/discovery.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../src/discovery.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,cAAc,EACd,SAAS,EACT,MAAM,EACN,aAAa,EACb,SAAS,EACV,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAA6B,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3E,eAAO,MAAM,gBAAgB,qBAAqB,CAAC;AAEnD,eAAO,MAAM,iBAAiB,EAAE,SAAS,MAAM,EAAU,CAAC;AAE1D,MAAM,MAAM,WAAW,GAAG;IACxB,uBAAuB,EAAE,MAAM,CAAC;IAChC,oBAAoB,EAAE,MAAM,CAAC;IAC7B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,wBAAwB,EAAE,MAAM,CAAC;IACjC,2BAA2B,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../src/discovery.ts"],"names":[],"mappings":"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"}
|