@agentshouse/mdruntime-adapter 0.1.0-cb6436097e8a2 → 0.1.0-cb8fc491b1a92
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 +2 -2
- package/dist/actions.d.ts +122 -23
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +27 -141
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js +220 -337
- package/dist/app-reference.d.ts +2 -0
- package/dist/app-reference.d.ts.map +1 -0
- package/dist/app-reference.js +2 -0
- package/dist/crockford.d.ts +4 -0
- package/dist/crockford.d.ts.map +1 -0
- package/dist/crockford.js +3 -0
- package/dist/discovery.d.ts +6 -10
- package/dist/discovery.d.ts.map +1 -1
- package/dist/discovery.js +36 -40
- package/dist/house-model.d.ts +2983 -0
- package/dist/house-model.d.ts.map +1 -0
- package/dist/house-model.js +1643 -0
- package/dist/index.d.ts +9 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -1
- package/dist/model.d.ts +5 -2
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +10 -29
- package/dist/part.d.ts +14 -0
- package/dist/part.d.ts.map +1 -0
- package/dist/part.js +90 -0
- package/dist/read.d.ts +46 -63
- package/dist/read.d.ts.map +1 -1
- package/dist/read.js +216 -283
- package/dist/refusal.d.ts +0 -3
- package/dist/refusal.d.ts.map +1 -1
- package/dist/refusal.js +0 -5
- package/dist/room-stream.d.ts +19 -0
- package/dist/room-stream.d.ts.map +1 -0
- package/dist/room-stream.js +57 -0
- package/dist/room-types.d.ts +7 -0
- package/dist/room-types.d.ts.map +1 -0
- package/dist/room-types.js +126 -0
- package/dist/site.d.ts.map +1 -1
- package/dist/site.js +80 -179
- package/dist/stream.d.ts +4 -14
- package/dist/stream.d.ts.map +1 -1
- package/dist/stream.js +125 -413
- package/dist/transport.d.ts +53 -43
- package/dist/transport.d.ts.map +1 -1
- package/dist/transport.js +49 -11
- package/dist/upload.d.ts +12 -0
- package/dist/upload.d.ts.map +1 -0
- package/dist/upload.js +63 -0
- package/dist/write.d.ts +16 -1
- package/dist/write.d.ts.map +1 -1
- package/dist/write.js +101 -56
- package/package.json +8 -4
- package/AGENTS.md +0 -299
- package/dist/gather.d.ts +0 -4
- package/dist/gather.d.ts.map +0 -1
- package/dist/gather.js +0 -45
- package/dist/handed.d.ts +0 -15
- package/dist/handed.d.ts.map +0 -1
- package/dist/handed.js +0 -38
- package/dist/referrer.d.ts +0 -7
- package/dist/referrer.d.ts.map +0 -1
- package/dist/referrer.js +0 -35
package/dist/adapter.js
CHANGED
|
@@ -1,46 +1,47 @@
|
|
|
1
|
+
import { stepped } from '@agentshouse/mdruntime';
|
|
1
2
|
import { DISCOVER_PREFIXES, capabilities, discoveredRoom, mapDiscovery, parseDiscovery, trackedFrom, } from './discovery.js';
|
|
2
|
-
import { partModels, streamedModels, viewModels } from './model.js';
|
|
3
|
-
import { gathered, gatheredEach, selections } from './gather.js';
|
|
4
3
|
import { createSiteAdapter } from './site.js';
|
|
5
|
-
import {
|
|
4
|
+
import { authorityRoot, bootstrapping, parseFileAddress, parseGrant, } from './read.js';
|
|
5
|
+
import { roomStreams } from './room-stream.js';
|
|
6
6
|
import { sha256Hex } from './digest.js';
|
|
7
|
-
import {
|
|
7
|
+
import { finishedUpload, houseUpload } from './upload.js';
|
|
8
8
|
import { houseAction } from './actions.js';
|
|
9
|
-
import { classifyDoor,
|
|
9
|
+
import { classifyDoor, discoveryFailure, doorRefusal } from './refusal.js';
|
|
10
10
|
import { houseNotifications } from './stream.js';
|
|
11
|
-
import {
|
|
12
|
-
import { batchPaths, deltaBody, deltaBytes, deltaDigest, historyOutcome, houseChanges, mutationOutcome, operationHead, rejectedMapping, revisionBody, uploadable, } from './write.js';
|
|
13
|
-
import {
|
|
14
|
-
import { followsVersion, logPosition, presented, reachedVersion, roomVersion
|
|
11
|
+
import { houseDoorRoute, } from './transport.js';
|
|
12
|
+
import { batchPaths, deltaBody, deltaBytes, deltaDigest, historyOutcome, houseChanges, houseMove, mutationOutcome, operationHead, rejectedMapping, revisionBody, uploadable, } from './write.js';
|
|
13
|
+
import { HOUSE_NAMEABLE_ROOM_TYPES, HOUSE_ROOM_TYPES } from './room-types.js';
|
|
14
|
+
import { followsVersion, logPosition, presented, reachedVersion, roomVersion } from './version.js';
|
|
15
15
|
const UNCERTAIN = { kind: 'transport-uncertainty' };
|
|
16
16
|
const UNCERTAIN_READ = { kind: 'transport-uncertainty' };
|
|
17
|
+
const OCTET_STREAM = 'application/octet-stream';
|
|
18
|
+
const EXPIRED_ADDRESS = 403;
|
|
17
19
|
function lapsedGrant(outcome) {
|
|
18
20
|
return outcome.kind === 'rejected' && outcome.code === 'upload_unavailable';
|
|
19
21
|
}
|
|
20
|
-
function heldFiles(authority, held,
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
.sort();
|
|
26
|
-
if (paths.length === 0)
|
|
27
|
-
return null;
|
|
28
|
-
const files = paths.slice(0, budget).map((path) => ({ path, revision: held.revisions[path] }));
|
|
29
|
-
return { room: authority, through: paths.length > budget ? files.at(-1).path : null, files };
|
|
22
|
+
async function heldFiles(authority, held, bound) {
|
|
23
|
+
const listed = await held.files(null, bound + 1);
|
|
24
|
+
const files = listed.slice(0, bound);
|
|
25
|
+
const through = listed.length > bound ? (files.at(-1)?.path ?? null) : null;
|
|
26
|
+
return files.length === 0 ? null : { room: authority, through, files };
|
|
30
27
|
}
|
|
31
28
|
async function reused(scope, held) {
|
|
32
29
|
const sent = new Set(scope.contents.map((entry) => entry.path));
|
|
33
30
|
const contents = [...scope.contents];
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
31
|
+
const asked = scope.files.filter((file) => file.binary === undefined && !sent.has(file.path));
|
|
32
|
+
if (asked.length === 0)
|
|
33
|
+
return scope;
|
|
34
|
+
if (held === undefined)
|
|
35
|
+
return null;
|
|
36
|
+
const kept = await held.contents(asked.map(({ path }) => path));
|
|
37
|
+
for (const file of asked) {
|
|
38
|
+
const found = kept.get(file.path);
|
|
39
|
+
if (found === undefined ||
|
|
40
|
+
found.revision !== file.revision ||
|
|
41
|
+
(await sha256Hex(found.content)) !== file.sha256) {
|
|
41
42
|
return null;
|
|
42
43
|
}
|
|
43
|
-
contents.push({ path: file.path, content });
|
|
44
|
+
contents.push({ path: file.path, content: found.content });
|
|
44
45
|
}
|
|
45
46
|
return { ...scope, contents };
|
|
46
47
|
}
|
|
@@ -50,122 +51,49 @@ export function createAgentsHouseAdapter(options) {
|
|
|
50
51
|
const transport = options.transport;
|
|
51
52
|
const notifications = houseNotifications(transport, 'credential');
|
|
52
53
|
const tracked = new Map();
|
|
53
|
-
const
|
|
54
|
+
const rooms = roomStreams('credential');
|
|
55
|
+
const unreused = new Set();
|
|
54
56
|
let bounds;
|
|
55
57
|
function declared() {
|
|
56
58
|
if (bounds === undefined)
|
|
57
59
|
throw new Error('no Discovery declared the bounds');
|
|
58
60
|
return bounds;
|
|
59
61
|
}
|
|
60
|
-
async function
|
|
61
|
-
const
|
|
62
|
-
if (
|
|
63
|
-
return
|
|
64
|
-
const scope = await reused(
|
|
62
|
+
async function streamedPage(entry, token, held) {
|
|
63
|
+
const part = await rooms.part(entry, token);
|
|
64
|
+
if ('kind' in part)
|
|
65
|
+
return part;
|
|
66
|
+
const scope = await reused(part.scope, held);
|
|
65
67
|
if (scope === null) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
: continued === null
|
|
69
|
-
? bootstrapTogether({ entry, held: undefined })
|
|
70
|
-
: continuedBootstrap({ entry, held: undefined }, continued);
|
|
68
|
+
unreused.add(entry.locator);
|
|
69
|
+
return UNCERTAIN_READ;
|
|
71
70
|
}
|
|
72
|
-
const cursor = envelope.next?.room === entry.authority ? envelope.next : undefined;
|
|
73
|
-
const next = cursor?.path ?? null;
|
|
74
|
-
if (continued === null && next === null)
|
|
75
|
-
handed.set(entry.locator, scopeHanded(scope));
|
|
76
|
-
else
|
|
77
|
-
handed.delete(entry.locator);
|
|
78
|
-
notifications.reach(entry.authority, scope);
|
|
79
|
-
const seen = [
|
|
80
|
-
...(continued?.seen ?? []),
|
|
81
|
-
...entry.schemas.filter((schema) => scope.files.some((file) => file.path === schema)),
|
|
82
|
-
];
|
|
83
|
-
const models = await partModels(scope, entry, new Set(seen), next === null);
|
|
84
|
-
const continuation = next === null
|
|
85
|
-
? null
|
|
86
|
-
: { after: next, seen, ...(cursor?.cut === undefined ? {} : { cut: cursor.cut }) };
|
|
87
71
|
return {
|
|
88
|
-
...
|
|
89
|
-
...(models === undefined ? {} : { models }),
|
|
90
|
-
...(
|
|
72
|
+
...(await stepped(bootstrapping(scope))),
|
|
73
|
+
...(part.models === undefined ? {} : { models: part.models }),
|
|
74
|
+
...(part.pageToken === undefined ? {} : { pageToken: part.pageToken }),
|
|
75
|
+
bytes: part.bytes,
|
|
91
76
|
};
|
|
92
77
|
}
|
|
93
|
-
async function
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
return failure;
|
|
106
|
-
const enumeration = parseEnumeration(answer.body, 'credential');
|
|
107
|
-
if (enumeration === null)
|
|
108
|
-
return UNCERTAIN_READ;
|
|
109
|
-
return (viewOf(enumeration, ask.entry) ?? { kind: 'room-not-readable', locator: ask.entry.locator });
|
|
110
|
-
}
|
|
111
|
-
async function enumeratedRoom(enumeration, ask) {
|
|
112
|
-
const first = viewOf(enumeration, ask.entry);
|
|
113
|
-
if (first === undefined)
|
|
114
|
-
return { kind: 'room-not-readable', locator: ask.entry.locator };
|
|
115
|
-
const view = await wholeView(first, (page) => enumeratedPage(ask, page));
|
|
116
|
-
if ('kind' in view)
|
|
117
|
-
return view;
|
|
118
|
-
notifications.reach(ask.entry.authority, view);
|
|
119
|
-
const answer = refreshFrom(view, ask.fromRoomVersion);
|
|
120
|
-
if (answer.kind !== 'net-advancement')
|
|
121
|
-
return answer;
|
|
122
|
-
handed.set(ask.entry.locator, enumeratedHanded(view, handed.get(ask.entry.locator)));
|
|
123
|
-
const models = await viewModels(view, ask.entry);
|
|
124
|
-
return models === undefined ? answer : { ...answer, models };
|
|
125
|
-
}
|
|
126
|
-
async function streamed(entry, request, from) {
|
|
127
|
-
const held = handed.get(entry.locator);
|
|
128
|
-
if (held?.roomVersion !== request.fromRoomVersion)
|
|
129
|
-
return null;
|
|
130
|
-
const advanced = notifications.advance(entry, from, (path, revision) => held.revisions.get(path) === revision);
|
|
131
|
-
if (advanced === null)
|
|
132
|
-
return null;
|
|
133
|
-
handed.set(entry.locator, streamedHanded(held, advanced));
|
|
134
|
-
const models = await streamedModels(advanced.schemas, entry, 'credential');
|
|
135
|
-
return models === undefined ? advanced.page : { ...advanced.page, models };
|
|
78
|
+
async function openedRoom(entry, request) {
|
|
79
|
+
const reusing = unreused.delete(entry.locator) ? undefined : request.held;
|
|
80
|
+
const files = reusing === undefined
|
|
81
|
+
? null
|
|
82
|
+
: await heldFiles(entry.authority, reusing, declared().max_held_files);
|
|
83
|
+
const answered = await transport.streamed(houseDoorRoute('bootstrap'), {
|
|
84
|
+
prefixes: [authorityRoot(entry.handle)],
|
|
85
|
+
...(files === null ? {} : { held: [files] }),
|
|
86
|
+
}, request.signal ?? new AbortController().signal);
|
|
87
|
+
if (answered.room === null)
|
|
88
|
+
return classifyDoor(answered.status, answered.body, entry.locator) ?? UNCERTAIN_READ;
|
|
89
|
+
return rooms.head(entry, answered.room, request.arriving, request.signal);
|
|
136
90
|
}
|
|
137
|
-
function held(rooms
|
|
138
|
-
|
|
91
|
+
function held(rooms) {
|
|
92
|
+
notifications.send(rooms.flatMap((room) => {
|
|
139
93
|
const entry = tracked.get(room.locator);
|
|
140
94
|
const at = logPosition(room.roomVersion);
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
handed.set(room.locator, heldHanded(room));
|
|
144
|
-
notifications.reach(entry.authority, at);
|
|
145
|
-
return [presented(entry.authority, at)];
|
|
146
|
-
});
|
|
147
|
-
notifications.send(kept, demand === null
|
|
148
|
-
? null
|
|
149
|
-
: {
|
|
150
|
-
rooms: demand.rooms.flatMap((locator) => {
|
|
151
|
-
const entry = tracked.get(locator);
|
|
152
|
-
return entry === undefined ? [] : [entry.authority];
|
|
153
|
-
}),
|
|
154
|
-
conversations: demand.conversations,
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
function adopted(entry, outcome) {
|
|
158
|
-
if (outcome.kind !== 'accepted')
|
|
159
|
-
return outcome;
|
|
160
|
-
const held = handed.get(entry.locator);
|
|
161
|
-
if (held === undefined)
|
|
162
|
-
return outcome;
|
|
163
|
-
const follows = followsVersion(held.roomVersion, outcome.roomVersion);
|
|
164
|
-
handed.set(entry.locator, acceptedHanded(held, outcome, follows ? outcome.roomVersion : held.roomVersion));
|
|
165
|
-
const at = logPosition(outcome.roomVersion);
|
|
166
|
-
if (follows && at !== null)
|
|
167
|
-
notifications.reach(entry.authority, at);
|
|
168
|
-
return outcome;
|
|
95
|
+
return entry === undefined || at === null ? [] : [presented(entry.authority, at)];
|
|
96
|
+
}));
|
|
169
97
|
}
|
|
170
98
|
function authorityFor(locator) {
|
|
171
99
|
return tracked.get(locator);
|
|
@@ -184,128 +112,25 @@ export function createAgentsHouseAdapter(options) {
|
|
|
184
112
|
return result;
|
|
185
113
|
return { ...result, at: { locator, roomVersion: roomVersion({ position, logEpoch }) } };
|
|
186
114
|
}
|
|
187
|
-
function weight(entry) {
|
|
188
|
-
return contentPrefixes(entry).length;
|
|
189
|
-
}
|
|
190
|
-
function holding(asks, after) {
|
|
191
|
-
const budget = Math.floor(declared().max_held_files / Math.max(1, asks.filter((ask) => ask.held !== undefined).length));
|
|
192
|
-
return asks.flatMap((ask) => heldFiles(ask.entry.authority, ask.held, after, budget) ?? []);
|
|
193
|
-
}
|
|
194
|
-
async function bootstrapped(asks, answer) {
|
|
195
|
-
let open = asks.map((asked, ask) => ({ entry: asked.entry, asked, ask }));
|
|
196
|
-
while (open.length > 0) {
|
|
197
|
-
const unreached = [];
|
|
198
|
-
for (const part of selections(open, ({ entry }) => weight(entry), declared().max_prefixes)) {
|
|
199
|
-
const held = holding(part.map(({ asked }) => asked), null);
|
|
200
|
-
const answered = await transport.door(DOOR_BOOTSTRAP, {
|
|
201
|
-
prefixes: part.flatMap(({ entry }) => contentPrefixes(entry)),
|
|
202
|
-
...(held.length === 0 ? {} : { held }),
|
|
203
|
-
});
|
|
204
|
-
const classified = part.map(({ entry }) => classifyDoor(answered.status, answered.body, entry.locator));
|
|
205
|
-
const envelope = classified[0] === null ? parseEnvelope(answered.body) : null;
|
|
206
|
-
for (const [index, asked] of part.entries()) {
|
|
207
|
-
const failure = classified[index];
|
|
208
|
-
if (failure !== null && failure !== undefined) {
|
|
209
|
-
answer(asked.ask, failure);
|
|
210
|
-
continue;
|
|
211
|
-
}
|
|
212
|
-
if (envelope === null) {
|
|
213
|
-
answer(asked.ask, UNCERTAIN_READ);
|
|
214
|
-
continue;
|
|
215
|
-
}
|
|
216
|
-
const scope = envelope.scopes.find((candidate) => matchesScope(candidate, asked.entry));
|
|
217
|
-
if (scope === undefined && envelope.next !== undefined) {
|
|
218
|
-
unreached.push(asked);
|
|
219
|
-
continue;
|
|
220
|
-
}
|
|
221
|
-
answer(asked.ask, await bootstrappedRoom(envelope, scope, asked.asked, null));
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
open = unreached;
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
async function continuedBootstrap(asked, continued) {
|
|
228
|
-
const { entry } = asked;
|
|
229
|
-
const after = {
|
|
230
|
-
room: entry.authority,
|
|
231
|
-
path: continued.after,
|
|
232
|
-
...(continued.cut === undefined ? {} : { cut: continued.cut }),
|
|
233
|
-
};
|
|
234
|
-
const held = holding([asked], continued.after);
|
|
235
|
-
const answered = await transport.door(DOOR_BOOTSTRAP, {
|
|
236
|
-
prefixes: contentPrefixes(entry),
|
|
237
|
-
after,
|
|
238
|
-
...(held.length === 0 ? {} : { held }),
|
|
239
|
-
});
|
|
240
|
-
const failure = classifyRead(answered.status, answered.body, entry.locator);
|
|
241
|
-
if (failure !== null)
|
|
242
|
-
return failure;
|
|
243
|
-
const envelope = parseEnvelope(answered.body);
|
|
244
|
-
if (envelope === null)
|
|
245
|
-
return UNCERTAIN_READ;
|
|
246
|
-
const scope = envelope.scopes.find((candidate) => matchesScope(candidate, entry));
|
|
247
|
-
return bootstrappedRoom(envelope, scope, asked, continued);
|
|
248
|
-
}
|
|
249
|
-
function layered(asks) {
|
|
250
|
-
const depths = new Map();
|
|
251
|
-
const layers = [];
|
|
252
|
-
for (const ask of asks) {
|
|
253
|
-
const depth = depths.get(ask.ask.entry.authority) ?? 0;
|
|
254
|
-
depths.set(ask.ask.entry.authority, depth + 1);
|
|
255
|
-
(layers[depth] ??= []).push(ask);
|
|
256
|
-
}
|
|
257
|
-
return layers;
|
|
258
|
-
}
|
|
259
|
-
async function enumerated(asks) {
|
|
260
|
-
const answers = [];
|
|
261
|
-
const parts = layered(asks.map((ask, index) => ({ ask, index }))).flatMap((layer) => selections(layer, ({ ask }) => weight(ask.entry), declared().max_prefixes));
|
|
262
|
-
for (const part of parts) {
|
|
263
|
-
let open = [...part];
|
|
264
|
-
while (open.length > 0) {
|
|
265
|
-
const answer = await transport.door(DOOR_ENUMERATE, {
|
|
266
|
-
prefixes: open.flatMap(({ ask }) => contentPrefixes(ask.entry)),
|
|
267
|
-
positions: open.map(({ ask }) => presented(ask.entry.authority, ask.from)),
|
|
268
|
-
});
|
|
269
|
-
const classified = open.map(({ ask }) => classifyRead(answer.status, answer.body, ask.entry.locator));
|
|
270
|
-
if (classified[0]?.kind === 'beyond-horizon') {
|
|
271
|
-
const named = doorRefusal(answer.body)?.fields.authority;
|
|
272
|
-
const expired = open.filter(({ ask }) => ask.entry.authority === named);
|
|
273
|
-
const answered = expired.length === 0 ? UNCERTAIN_READ : { kind: 'beyond-horizon' };
|
|
274
|
-
const gone = expired.length === 0 ? open : expired;
|
|
275
|
-
for (const ask of gone)
|
|
276
|
-
answers[ask.index] = answered;
|
|
277
|
-
open = open.filter((ask) => !gone.includes(ask));
|
|
278
|
-
continue;
|
|
279
|
-
}
|
|
280
|
-
const enumeration = classified[0] === null ? parseEnumeration(answer.body, 'credential') : null;
|
|
281
|
-
for (const [answerIndex, { ask, index }] of open.entries()) {
|
|
282
|
-
const failure = classified[answerIndex];
|
|
283
|
-
if (failure !== null && failure !== undefined) {
|
|
284
|
-
answers[index] = failure;
|
|
285
|
-
continue;
|
|
286
|
-
}
|
|
287
|
-
answers[index] =
|
|
288
|
-
enumeration === null ? UNCERTAIN_READ : await enumeratedRoom(enumeration, ask);
|
|
289
|
-
}
|
|
290
|
-
open = [];
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
return answers;
|
|
294
|
-
}
|
|
295
|
-
const bootstrapTogether = gatheredEach(bootstrapped);
|
|
296
|
-
const enumerateTogether = gathered(enumerated);
|
|
297
115
|
async function uploaded(head, changes, delta, bytes, entry, handed, keep) {
|
|
298
116
|
const paths = batchPaths(changes);
|
|
299
117
|
const sha256 = await deltaDigest(delta);
|
|
300
118
|
if (handed !== undefined && handed.sha256 === sha256) {
|
|
301
119
|
return { body: { ...head, paths, upload: handed, runtime: notifications.runtime } };
|
|
302
120
|
}
|
|
303
|
-
const minted = await transport.door(
|
|
121
|
+
const minted = await transport.door(houseDoorRoute('batch-upload'), {
|
|
122
|
+
...head,
|
|
123
|
+
paths,
|
|
124
|
+
bytes,
|
|
125
|
+
sha256,
|
|
126
|
+
});
|
|
304
127
|
if (minted.status !== 200) {
|
|
305
|
-
return {
|
|
128
|
+
return {
|
|
129
|
+
outcome: await stepped(mutationOutcome(minted.status, minted.body, entry.locator, entry.handle)),
|
|
130
|
+
};
|
|
306
131
|
}
|
|
307
132
|
const grant = parseGrant(minted.body);
|
|
308
|
-
if (grant === null
|
|
133
|
+
if (grant === null)
|
|
309
134
|
return { outcome: UNCERTAIN };
|
|
310
135
|
const delivered = await transport.transfer({
|
|
311
136
|
act: 'authored_batch_upload',
|
|
@@ -315,52 +140,52 @@ export function createAgentsHouseAdapter(options) {
|
|
|
315
140
|
if (delivered.status !== 200)
|
|
316
141
|
return { outcome: UNCERTAIN };
|
|
317
142
|
const holding = parseGrant(delivered.body);
|
|
318
|
-
if (holding === null
|
|
143
|
+
if (holding === null)
|
|
319
144
|
return { outcome: UNCERTAIN };
|
|
320
145
|
const upload = { url: holding.url, operation: holding.operation, bytes, sha256 };
|
|
321
146
|
await keep(upload);
|
|
322
147
|
return { body: { ...head, paths, upload, runtime: notifications.runtime } };
|
|
323
148
|
}
|
|
324
|
-
async function staged(answer, batch) {
|
|
149
|
+
async function staged(answer, { changeBatch: batch, file }) {
|
|
325
150
|
const refusal = doorRefusal(answer.body);
|
|
326
151
|
if (refusal?.code !== 'files_unstaged')
|
|
327
152
|
return null;
|
|
328
|
-
const
|
|
329
|
-
if (!Array.isArray(
|
|
153
|
+
const uploads = refusal.fields.upload;
|
|
154
|
+
if (!Array.isArray(uploads))
|
|
330
155
|
return false;
|
|
331
|
-
for (const
|
|
332
|
-
const
|
|
333
|
-
const change = batch.changes.find((held) => held.kind === 'binary' && held.sha256 === sha256);
|
|
334
|
-
if (
|
|
156
|
+
for (const named of uploads) {
|
|
157
|
+
const upload = houseUpload(named);
|
|
158
|
+
const change = batch.changes.find((held) => held.kind === 'binary' && held.sha256 === named.sha256);
|
|
159
|
+
if (upload === null || change === undefined)
|
|
335
160
|
return false;
|
|
336
|
-
const
|
|
337
|
-
|
|
338
|
-
url,
|
|
339
|
-
body: change.blob,
|
|
340
|
-
operation,
|
|
341
|
-
});
|
|
342
|
-
if (delivered.status !== 200)
|
|
161
|
+
const finished = await finishedUpload(transport, upload, await file(change.localId));
|
|
162
|
+
if (finished?.body?.staged !== true)
|
|
343
163
|
return false;
|
|
344
164
|
}
|
|
345
165
|
return true;
|
|
346
166
|
}
|
|
347
|
-
async function submit(
|
|
167
|
+
async function submit(request, head, changes, delta, bytes, entry, handed, keep, into) {
|
|
348
168
|
const inline = { ...head, changes, runtime: notifications.runtime };
|
|
349
169
|
const prepared = uploadable(inline, bytes, declared())
|
|
350
170
|
? await uploaded(head, changes, delta, bytes, entry, handed, keep)
|
|
351
171
|
: { body: inline };
|
|
352
172
|
if ('outcome' in prepared)
|
|
353
173
|
return prepared.outcome;
|
|
354
|
-
const answer = await transport.door(
|
|
355
|
-
const stagedFiles = await staged(answer,
|
|
174
|
+
const answer = await transport.door(houseDoorRoute('batch'), prepared.body);
|
|
175
|
+
const stagedFiles = await staged(answer, request);
|
|
356
176
|
if (stagedFiles === false)
|
|
357
177
|
return UNCERTAIN;
|
|
358
|
-
const settled = stagedFiles === null ? answer : await transport.door(
|
|
359
|
-
if (stagedFiles === true && (await staged(settled,
|
|
178
|
+
const settled = stagedFiles === null ? answer : await transport.door(houseDoorRoute('batch'), prepared.body);
|
|
179
|
+
if (stagedFiles === true && (await staged(settled, request)) !== null)
|
|
360
180
|
return UNCERTAIN;
|
|
361
|
-
return mutationOutcome(settled.status, settled.body, entry.locator, entry.handle
|
|
181
|
+
return stepped(mutationOutcome(settled.status, settled.body, entry.locator, entry.handle, into === undefined
|
|
182
|
+
? undefined
|
|
183
|
+
: {
|
|
184
|
+
source: { authority: entry.authority, handle: entry.handle },
|
|
185
|
+
destination: { authority: into.authority, handle: into.handle },
|
|
186
|
+
}));
|
|
362
187
|
}
|
|
363
|
-
|
|
188
|
+
function addressed(answer, locator) {
|
|
364
189
|
const failure = classifyDoor(answer.status, answer.body, locator);
|
|
365
190
|
if (failure !== null) {
|
|
366
191
|
return failure.kind === 'scoped-refusal'
|
|
@@ -372,20 +197,69 @@ export function createAgentsHouseAdapter(options) {
|
|
|
372
197
|
}
|
|
373
198
|
: failure;
|
|
374
199
|
}
|
|
375
|
-
const
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
200
|
+
const address = parseFileAddress(answer.body);
|
|
201
|
+
return address === null ? UNCERTAIN_READ : { kind: 'address', ...address };
|
|
202
|
+
}
|
|
203
|
+
function linked(answer, locator) {
|
|
204
|
+
const address = addressed(answer, locator);
|
|
205
|
+
return address.kind === 'address'
|
|
206
|
+
? { kind: 'link', url: address.url, name: address.name, mediaType: address.mediaType }
|
|
207
|
+
: address;
|
|
208
|
+
}
|
|
209
|
+
function refusedAttach(answer, locator) {
|
|
210
|
+
const failure = classifyDoor(answer.status, answer.body, locator);
|
|
211
|
+
if (failure === null)
|
|
212
|
+
return null;
|
|
213
|
+
return failure.kind === 'scoped-refusal'
|
|
214
|
+
? {
|
|
215
|
+
kind: 'rejected',
|
|
216
|
+
code: failure.code,
|
|
217
|
+
fields: failure.fields,
|
|
218
|
+
retryable: failure.retryable,
|
|
219
|
+
}
|
|
220
|
+
: failure;
|
|
221
|
+
}
|
|
222
|
+
async function downloaded(ask, locator) {
|
|
223
|
+
let address = addressed(await ask(), locator);
|
|
224
|
+
if (address.kind !== 'address')
|
|
225
|
+
return address;
|
|
226
|
+
let read = await transport.download(address.url);
|
|
227
|
+
if (read.status === EXPIRED_ADDRESS) {
|
|
228
|
+
address = addressed(await ask(), locator);
|
|
229
|
+
if (address.kind !== 'address')
|
|
230
|
+
return address;
|
|
231
|
+
read = await transport.download(address.url);
|
|
232
|
+
}
|
|
233
|
+
if (read.status !== 200 || read.body === null)
|
|
380
234
|
return UNCERTAIN_READ;
|
|
381
|
-
return {
|
|
235
|
+
return {
|
|
236
|
+
kind: 'binary',
|
|
237
|
+
body: read.body,
|
|
238
|
+
mediaType: address.mediaType,
|
|
239
|
+
sha256: address.sha256,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
function pathAddress(request) {
|
|
243
|
+
const entry = authorityFor(request.locator);
|
|
244
|
+
if (entry === undefined)
|
|
245
|
+
return null;
|
|
246
|
+
return () => transport.door(houseDoorRoute('files/get'), {
|
|
247
|
+
room_ref: entry.authority,
|
|
248
|
+
path: request.path,
|
|
249
|
+
revision: request.revision,
|
|
250
|
+
});
|
|
382
251
|
}
|
|
383
252
|
return {
|
|
253
|
+
roomTypes: HOUSE_ROOM_TYPES,
|
|
254
|
+
nameableRoomTypes: HOUSE_NAMEABLE_ROOM_TYPES,
|
|
384
255
|
async authorize() {
|
|
385
256
|
return options.authorize();
|
|
386
257
|
},
|
|
387
|
-
async discover(_authorization) {
|
|
388
|
-
const answer = await transport.door(
|
|
258
|
+
async discover(_authorization, scope) {
|
|
259
|
+
const answer = await transport.door(houseDoorRoute('discover'), {
|
|
260
|
+
prefixes: [...DISCOVER_PREFIXES],
|
|
261
|
+
...(scope === undefined ? {} : { rooms: scope.authorities }),
|
|
262
|
+
});
|
|
389
263
|
const failed = discoveryFailure(answer.status, answer.body);
|
|
390
264
|
if (failed !== null)
|
|
391
265
|
return failed;
|
|
@@ -394,64 +268,68 @@ export function createAgentsHouseAdapter(options) {
|
|
|
394
268
|
return { kind: 'transport-uncertainty' };
|
|
395
269
|
bounds = document.bounds;
|
|
396
270
|
const entries = trackedFrom(document);
|
|
397
|
-
|
|
271
|
+
const covered = scope === undefined
|
|
272
|
+
? null
|
|
273
|
+
: [
|
|
274
|
+
...new Set([
|
|
275
|
+
...[...tracked.values()]
|
|
276
|
+
.filter((entry) => scope.authorities.includes(entry.authority))
|
|
277
|
+
.map((entry) => entry.locator),
|
|
278
|
+
...entries.map((entry) => entry.locator),
|
|
279
|
+
]),
|
|
280
|
+
];
|
|
281
|
+
if (covered === null)
|
|
282
|
+
tracked.clear();
|
|
283
|
+
else
|
|
284
|
+
for (const locator of covered)
|
|
285
|
+
tracked.delete(locator);
|
|
398
286
|
for (const entry of entries)
|
|
399
287
|
tracked.set(entry.locator, entry);
|
|
400
|
-
|
|
401
|
-
if (!tracked.has(locator))
|
|
402
|
-
handed.delete(locator);
|
|
403
|
-
notifications.track(entries);
|
|
288
|
+
notifications.track([...tracked.values()]);
|
|
404
289
|
return {
|
|
405
|
-
...mapDiscovery(document, document.authorities.map((authority) => discoveredRoom(authority)), capabilities(document)),
|
|
290
|
+
...mapDiscovery(document, document.authorities.map((authority) => discoveredRoom(authority)), capabilities(document.actions)),
|
|
406
291
|
member: document.member,
|
|
292
|
+
...(covered === null ? {} : { covered }),
|
|
407
293
|
};
|
|
408
294
|
},
|
|
409
295
|
async bootstrap(_authorization, request) {
|
|
410
296
|
const entry = authorityFor(request.locator);
|
|
411
297
|
if (entry === undefined)
|
|
412
298
|
return { kind: 'room-not-readable', locator: request.locator };
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
}
|
|
417
|
-
return bootstrapTogether(asked);
|
|
418
|
-
},
|
|
419
|
-
async refresh(_authorization, request) {
|
|
420
|
-
const entry = authorityFor(request.locator);
|
|
421
|
-
if (entry === undefined)
|
|
422
|
-
return { kind: 'room-not-readable', locator: request.locator };
|
|
423
|
-
if (request.pageToken !== undefined)
|
|
424
|
-
return { kind: 'transport-uncertainty' };
|
|
425
|
-
const from = logPosition(request.fromRoomVersion);
|
|
426
|
-
if (from === null)
|
|
427
|
-
return { kind: 'beyond-horizon' };
|
|
428
|
-
const answered = await streamed(entry, request, from);
|
|
429
|
-
if (answered !== null)
|
|
430
|
-
return answered;
|
|
431
|
-
return enumerateTogether({ entry, from, fromRoomVersion: request.fromRoomVersion });
|
|
299
|
+
return request.pageToken === undefined
|
|
300
|
+
? openedRoom(entry, request)
|
|
301
|
+
: streamedPage(entry, request.pageToken, request.held);
|
|
432
302
|
},
|
|
433
303
|
async mutate(_authorization, request, keep) {
|
|
434
304
|
const entry = authorityFor(request.locator);
|
|
435
305
|
if (entry === undefined)
|
|
436
306
|
return { kind: 'room-not-readable', locator: request.locator };
|
|
437
|
-
const
|
|
307
|
+
const batch = request.changeBatch;
|
|
308
|
+
const destination = batch.between?.destination.room;
|
|
309
|
+
const into = destination === undefined ? undefined : authorityFor(destination);
|
|
310
|
+
if (destination !== undefined && into === undefined) {
|
|
311
|
+
return { kind: 'room-not-readable', locator: destination };
|
|
312
|
+
}
|
|
313
|
+
const mapped = into === undefined
|
|
314
|
+
? houseChanges(batch, entry.handle)
|
|
315
|
+
: houseMove(batch, entry.handle, into.handle);
|
|
438
316
|
if (mapped.kind === 'unmapped')
|
|
439
317
|
return rejectedMapping(mapped);
|
|
440
|
-
const head = operationHead(
|
|
318
|
+
const head = operationHead(batch);
|
|
441
319
|
const delta = deltaBody(mapped.changes);
|
|
442
320
|
const bytes = deltaBytes(delta);
|
|
443
321
|
const handed = request.upload;
|
|
444
|
-
const
|
|
445
|
-
const outcome = await
|
|
322
|
+
const answer = (upload) => submit(request, head, mapped.changes, delta, bytes, entry, upload, keep, into);
|
|
323
|
+
const outcome = await answer(handed);
|
|
446
324
|
if (handed === undefined || !lapsedGrant(outcome))
|
|
447
|
-
return
|
|
448
|
-
return
|
|
325
|
+
return outcome;
|
|
326
|
+
return answer(undefined);
|
|
449
327
|
},
|
|
450
328
|
async history(_authorization, request) {
|
|
451
329
|
const entry = authorityFor(request.locator);
|
|
452
330
|
if (entry === undefined)
|
|
453
331
|
return { kind: 'room-not-readable', locator: request.locator };
|
|
454
|
-
const answer = await transport.door(
|
|
332
|
+
const answer = await transport.door(houseDoorRoute('revisions'), revisionBody({
|
|
455
333
|
path: request.path,
|
|
456
334
|
handle: entry.handle,
|
|
457
335
|
...(request.revision === undefined ? {} : { revision: request.revision }),
|
|
@@ -463,35 +341,45 @@ export function createAgentsHouseAdapter(options) {
|
|
|
463
341
|
});
|
|
464
342
|
},
|
|
465
343
|
async binary(_authorization, request) {
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
}
|
|
477
|
-
const referrer = houseReferrer(request.referrer, tracked);
|
|
478
|
-
if (referrer === null) {
|
|
479
|
-
return { kind: 'rejected', code: 'referrer_unknown', fields: {}, retryable: false };
|
|
480
|
-
}
|
|
481
|
-
const answer = await transport.door(DOOR_FILES_GET, {
|
|
482
|
-
file: request.fileRef,
|
|
483
|
-
referrer: referrer.referrer,
|
|
484
|
-
});
|
|
485
|
-
return downloaded(answer, referrer.locator);
|
|
344
|
+
const ask = pathAddress(request);
|
|
345
|
+
if (ask === null)
|
|
346
|
+
return { kind: 'room-not-readable', locator: request.locator };
|
|
347
|
+
return downloaded(ask, request.locator);
|
|
348
|
+
},
|
|
349
|
+
async link(_authorization, request) {
|
|
350
|
+
const ask = pathAddress(request);
|
|
351
|
+
if (ask === null)
|
|
352
|
+
return { kind: 'room-not-readable', locator: request.locator };
|
|
353
|
+
return linked(await ask(), request.locator);
|
|
486
354
|
},
|
|
487
|
-
|
|
488
|
-
const
|
|
489
|
-
if (
|
|
490
|
-
return
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
355
|
+
async attach(_authorization, request) {
|
|
356
|
+
const entry = authorityFor(request.locator);
|
|
357
|
+
if (entry === undefined)
|
|
358
|
+
return { kind: 'room-not-readable', locator: request.locator };
|
|
359
|
+
const declared = await transport.door(houseDoorRoute('files/attach'), {
|
|
360
|
+
room_ref: entry.authority,
|
|
361
|
+
name: request.name,
|
|
362
|
+
bytes: request.byteLength,
|
|
363
|
+
sha256: request.sha256,
|
|
364
|
+
media_type: request.mediaType.length > 0 ? request.mediaType : OCTET_STREAM,
|
|
365
|
+
});
|
|
366
|
+
const refused = refusedAttach(declared, request.locator);
|
|
367
|
+
if (refused !== null)
|
|
368
|
+
return refused;
|
|
369
|
+
const upload = houseUpload(declared.body?.upload);
|
|
370
|
+
if (upload === null)
|
|
371
|
+
return UNCERTAIN_READ;
|
|
372
|
+
const finished = await finishedUpload(transport, upload, request.blob);
|
|
373
|
+
if (finished === null)
|
|
374
|
+
return UNCERTAIN_READ;
|
|
375
|
+
const unfinished = refusedAttach(finished, request.locator);
|
|
376
|
+
if (unfinished !== null)
|
|
377
|
+
return unfinished;
|
|
378
|
+
const { path, revision } = (finished.body ?? {});
|
|
379
|
+
const root = `/rooms/${entry.handle}/`;
|
|
380
|
+
return typeof path === 'string' && path.startsWith(root) && typeof revision === 'string'
|
|
381
|
+
? { kind: 'attached', path: path.slice(root.length), revision }
|
|
382
|
+
: UNCERTAIN_READ;
|
|
495
383
|
},
|
|
496
384
|
reached(locator, version, target) {
|
|
497
385
|
return reachedVersion(version, target, authorityFor(locator)?.logEpoch);
|
|
@@ -506,11 +394,6 @@ export function createAgentsHouseAdapter(options) {
|
|
|
506
394
|
hold: held,
|
|
507
395
|
reopen: notifications.reopen,
|
|
508
396
|
observe: notifications.observe,
|
|
509
|
-
published(locator, roomVersion) {
|
|
510
|
-
const at = logPosition(roomVersion);
|
|
511
|
-
if (at !== null)
|
|
512
|
-
notifications.published(locator, at);
|
|
513
|
-
},
|
|
514
397
|
},
|
|
515
398
|
conversations: notifications.conversations,
|
|
516
399
|
serviceMode: notifications.serviceMode,
|