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