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