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