@agentshouse/mdruntime-adapter 0.1.0-cca5d676bc88e → 0.1.0-ccd2ccb1f7214
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +278 -210
- package/dist/actions.d.ts +63 -2
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +157 -138
- package/dist/adapter.d.ts +1 -0
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js +322 -100
- package/dist/discovery.d.ts +4 -4
- package/dist/discovery.d.ts.map +1 -1
- package/dist/discovery.js +26 -12
- package/dist/gather.d.ts +1 -0
- package/dist/gather.d.ts.map +1 -1
- package/dist/gather.js +11 -6
- package/dist/handed.d.ts +15 -0
- package/dist/handed.d.ts.map +1 -0
- package/dist/handed.js +38 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/model.d.ts +1 -1
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +21 -7
- package/dist/read.d.ts +35 -7
- package/dist/read.d.ts.map +1 -1
- package/dist/read.js +160 -79
- package/dist/referrer.d.ts +7 -0
- package/dist/referrer.d.ts.map +1 -0
- package/dist/referrer.js +35 -0
- package/dist/refusal.d.ts +5 -9
- package/dist/refusal.d.ts.map +1 -1
- package/dist/refusal.js +31 -17
- package/dist/site.d.ts +1 -1
- package/dist/site.d.ts.map +1 -1
- package/dist/site.js +189 -54
- package/dist/stream.d.ts +4 -2
- package/dist/stream.d.ts.map +1 -1
- package/dist/stream.js +330 -16
- package/dist/transport.d.ts +64 -11
- package/dist/transport.d.ts.map +1 -1
- package/dist/transport.js +3 -1
- package/dist/version.d.ts +1 -0
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +1 -1
- package/dist/write.d.ts +3 -9
- package/dist/write.d.ts.map +1 -1
- package/dist/write.js +97 -20
- package/package.json +3 -4
- 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,23 +1,52 @@
|
|
|
1
1
|
import { DISCOVER_PREFIXES, capabilities, discoveredRoom, mapDiscovery, parseDiscovery, trackedFrom, } from './discovery.js';
|
|
2
|
-
import {
|
|
3
|
-
import { gathered, selections } from './gather.js';
|
|
2
|
+
import { partModels, streamedModels, viewModels } from './model.js';
|
|
3
|
+
import { gathered, gatheredEach, selections } from './gather.js';
|
|
4
4
|
import { createSiteAdapter } from './site.js';
|
|
5
|
-
import { bootstrapOf, contentPrefixes, matchesScope, parseEnumeration, parseEnvelope, parseGrant, refreshFrom, viewOf, } from './read.js';
|
|
6
|
-
import {
|
|
5
|
+
import { bootstrapOf, contentPrefixes, matchesScope, parseEnumeration, parseEnvelope, parseGrant, refreshFrom, viewOf, wholeView, } from './read.js';
|
|
6
|
+
import { sha256Hex } from './digest.js';
|
|
7
|
+
import { acceptedHanded, enumeratedHanded, heldHanded, scopeHanded, streamedHanded, } from './handed.js';
|
|
7
8
|
import { houseAction } from './actions.js';
|
|
8
|
-
import { classifyDoor, discoveryFailure, doorRefusal } from './refusal.js';
|
|
9
|
+
import { classifyDoor, classifyRead, discoveryFailure, doorRefusal } from './refusal.js';
|
|
9
10
|
import { houseNotifications } from './stream.js';
|
|
10
|
-
import { DOOR_BATCH, DOOR_BATCH_UPLOAD, DOOR_BOOTSTRAP, DOOR_DISCOVER, DOOR_ENUMERATE,
|
|
11
|
+
import { DOOR_BATCH, DOOR_BATCH_UPLOAD, DOOR_BOOTSTRAP, DOOR_DISCOVER, DOOR_ENUMERATE, DOOR_FILES_GET, DOOR_REVISIONS, } from './transport.js';
|
|
11
12
|
import { batchPaths, deltaBody, deltaBytes, deltaDigest, historyOutcome, houseChanges, mutationOutcome, operationHead, rejectedMapping, revisionBody, uploadable, } from './write.js';
|
|
13
|
+
import { houseReferrer } from './referrer.js';
|
|
12
14
|
import { followsVersion, logPosition, presented, reachedVersion, roomVersion, } from './version.js';
|
|
13
15
|
const UNCERTAIN = { kind: 'transport-uncertainty' };
|
|
14
16
|
const UNCERTAIN_READ = { kind: 'transport-uncertainty' };
|
|
15
17
|
function lapsedGrant(outcome) {
|
|
16
18
|
return outcome.kind === 'rejected' && outcome.code === 'upload_unavailable';
|
|
17
19
|
}
|
|
20
|
+
function heldFiles(authority, held, after, budget) {
|
|
21
|
+
if (held === undefined || budget === 0)
|
|
22
|
+
return null;
|
|
23
|
+
const paths = Object.keys(held.revisions)
|
|
24
|
+
.filter((path) => held.documents[path] !== undefined && (after === null || path > after))
|
|
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 };
|
|
30
|
+
}
|
|
31
|
+
async function reused(scope, held) {
|
|
32
|
+
const sent = new Set(scope.contents.map((entry) => entry.path));
|
|
33
|
+
const contents = [...scope.contents];
|
|
34
|
+
for (const file of scope.files) {
|
|
35
|
+
if (file.binary !== undefined || sent.has(file.path))
|
|
36
|
+
continue;
|
|
37
|
+
const content = held?.documents[file.path];
|
|
38
|
+
if (content === undefined ||
|
|
39
|
+
held?.revisions[file.path] !== file.revision ||
|
|
40
|
+
(await sha256Hex(content)) !== file.sha256) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
contents.push({ path: file.path, content });
|
|
44
|
+
}
|
|
45
|
+
return { ...scope, contents };
|
|
46
|
+
}
|
|
18
47
|
export function createAgentsHouseAdapter(options) {
|
|
19
48
|
if ('site' in options)
|
|
20
|
-
return createSiteAdapter(options.site);
|
|
49
|
+
return createSiteAdapter(options.site, options.snapshot === true);
|
|
21
50
|
const transport = options.transport;
|
|
22
51
|
const notifications = houseNotifications(transport, 'credential');
|
|
23
52
|
const tracked = new Map();
|
|
@@ -28,38 +57,80 @@ export function createAgentsHouseAdapter(options) {
|
|
|
28
57
|
throw new Error('no Discovery declared the bounds');
|
|
29
58
|
return bounds;
|
|
30
59
|
}
|
|
31
|
-
async function bootstrappedRoom(envelope,
|
|
32
|
-
const
|
|
33
|
-
if (
|
|
60
|
+
async function bootstrappedRoom(envelope, sent, asked, continued) {
|
|
61
|
+
const { entry } = asked;
|
|
62
|
+
if (sent === undefined)
|
|
34
63
|
return { kind: 'room-not-readable', locator: entry.locator };
|
|
35
|
-
|
|
64
|
+
const scope = await reused(sent, asked.held);
|
|
65
|
+
if (scope === null) {
|
|
66
|
+
return asked.held === undefined
|
|
67
|
+
? UNCERTAIN_READ
|
|
68
|
+
: continued === null
|
|
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);
|
|
36
78
|
notifications.reach(entry.authority, scope);
|
|
37
|
-
const
|
|
38
|
-
|
|
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
|
+
return {
|
|
88
|
+
...bootstrapOf(scope),
|
|
89
|
+
...(models === undefined ? {} : { models }),
|
|
90
|
+
...(continuation === null ? {} : { pageToken: JSON.stringify(continuation) }),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
async function enumeratedPage(ask, page) {
|
|
94
|
+
const answer = await transport.door(DOOR_ENUMERATE, {
|
|
95
|
+
prefixes: contentPrefixes(ask.entry),
|
|
96
|
+
positions: [
|
|
97
|
+
{
|
|
98
|
+
...presented(ask.entry.authority, { position: page.after, logEpoch: ask.from.logEpoch }),
|
|
99
|
+
page: { through: page.through, read: page.read },
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
});
|
|
103
|
+
const failure = classifyRead(answer.status, answer.body, ask.entry.locator);
|
|
104
|
+
if (failure !== null)
|
|
105
|
+
return failure;
|
|
106
|
+
const enumeration = parseEnumeration(answer.body, 'credential');
|
|
107
|
+
if (enumeration === null)
|
|
108
|
+
return UNCERTAIN_READ;
|
|
109
|
+
return (viewOf(enumeration, ask.entry) ?? { kind: 'room-not-readable', locator: ask.entry.locator });
|
|
39
110
|
}
|
|
40
111
|
async function enumeratedRoom(enumeration, ask) {
|
|
41
|
-
const
|
|
42
|
-
if (
|
|
112
|
+
const first = viewOf(enumeration, ask.entry);
|
|
113
|
+
if (first === undefined)
|
|
43
114
|
return { kind: 'room-not-readable', locator: ask.entry.locator };
|
|
115
|
+
const view = await wholeView(first, (page) => enumeratedPage(ask, page));
|
|
116
|
+
if ('kind' in view)
|
|
117
|
+
return view;
|
|
44
118
|
notifications.reach(ask.entry.authority, view);
|
|
45
119
|
const answer = refreshFrom(view, ask.fromRoomVersion);
|
|
46
120
|
if (answer.kind !== 'net-advancement')
|
|
47
121
|
return answer;
|
|
48
|
-
handed.set(ask.entry.locator,
|
|
122
|
+
handed.set(ask.entry.locator, enumeratedHanded(view, handed.get(ask.entry.locator)));
|
|
49
123
|
const models = await viewModels(view, ask.entry);
|
|
50
124
|
return models === undefined ? answer : { ...answer, models };
|
|
51
125
|
}
|
|
52
126
|
async function streamed(entry, request, from) {
|
|
53
|
-
const
|
|
54
|
-
if (
|
|
127
|
+
const held = handed.get(entry.locator);
|
|
128
|
+
if (held?.roomVersion !== request.fromRoomVersion)
|
|
55
129
|
return null;
|
|
56
|
-
const advanced = notifications.advance(entry, from, (path, revision) =>
|
|
130
|
+
const advanced = notifications.advance(entry, from, (path, revision) => held.revisions.get(path) === revision);
|
|
57
131
|
if (advanced === null)
|
|
58
132
|
return null;
|
|
59
|
-
|
|
60
|
-
if (relinked === null)
|
|
61
|
-
return null;
|
|
62
|
-
handed.set(entry.locator, relinked);
|
|
133
|
+
handed.set(entry.locator, streamedHanded(held, advanced));
|
|
63
134
|
const models = await streamedModels(advanced.schemas, entry, 'credential');
|
|
64
135
|
return models === undefined ? advanced.page : { ...advanced.page, models };
|
|
65
136
|
}
|
|
@@ -69,7 +140,7 @@ export function createAgentsHouseAdapter(options) {
|
|
|
69
140
|
const at = logPosition(room.roomVersion);
|
|
70
141
|
if (entry === undefined || at === null)
|
|
71
142
|
return [];
|
|
72
|
-
handed.set(room.locator,
|
|
143
|
+
handed.set(room.locator, heldHanded(room));
|
|
73
144
|
notifications.reach(entry.authority, at);
|
|
74
145
|
return [presented(entry.authority, at)];
|
|
75
146
|
});
|
|
@@ -78,19 +149,15 @@ export function createAgentsHouseAdapter(options) {
|
|
|
78
149
|
function adopted(entry, outcome) {
|
|
79
150
|
if (outcome.kind !== 'accepted')
|
|
80
151
|
return outcome;
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
const
|
|
87
|
-
handed.set(entry.locator, relinked.links);
|
|
88
|
-
const at = logPosition(accepted.roomVersion);
|
|
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);
|
|
89
158
|
if (follows && at !== null)
|
|
90
159
|
notifications.reach(entry.authority, at);
|
|
91
|
-
return
|
|
92
|
-
? accepted
|
|
93
|
-
: { ...accepted, attachments: relinked.attachments };
|
|
160
|
+
return outcome;
|
|
94
161
|
}
|
|
95
162
|
function authorityFor(locator) {
|
|
96
163
|
return tracked.get(locator);
|
|
@@ -104,95 +171,120 @@ export function createAgentsHouseAdapter(options) {
|
|
|
104
171
|
const { authority, position, log_epoch: logEpoch } = body;
|
|
105
172
|
if (typeof position !== 'string' || typeof logEpoch !== 'string')
|
|
106
173
|
return result;
|
|
107
|
-
const locator = authority ===
|
|
108
|
-
? PRIVATE_LOCATOR
|
|
109
|
-
: [...tracked.values()].find((entry) => entry.authority === authority)?.locator;
|
|
174
|
+
const locator = [...tracked.values()].find((entry) => entry.authority === authority)?.locator;
|
|
110
175
|
if (locator === undefined)
|
|
111
176
|
return result;
|
|
112
177
|
return { ...result, at: { locator, roomVersion: roomVersion({ position, logEpoch }) } };
|
|
113
178
|
}
|
|
114
|
-
async function bundled(body) {
|
|
115
|
-
const grant = parseGrant(body);
|
|
116
|
-
if (grant === null)
|
|
117
|
-
return body;
|
|
118
|
-
if (grant.method !== 'GET')
|
|
119
|
-
return null;
|
|
120
|
-
const answer = await transport.transfer({
|
|
121
|
-
act: 'local_copy_bundle',
|
|
122
|
-
method: 'GET',
|
|
123
|
-
url: grant.url,
|
|
124
|
-
answer: 'json',
|
|
125
|
-
});
|
|
126
|
-
return answer.status === 200 ? answer.body : null;
|
|
127
|
-
}
|
|
128
179
|
function weight(entry) {
|
|
129
180
|
return contentPrefixes(entry).length;
|
|
130
181
|
}
|
|
131
|
-
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
const
|
|
139
|
-
for (const
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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));
|
|
144
214
|
}
|
|
145
|
-
answers.push(failure.kind === 'beyond-horizon' ? UNCERTAIN_READ : failure);
|
|
146
215
|
}
|
|
216
|
+
open = unreached;
|
|
147
217
|
}
|
|
148
|
-
|
|
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);
|
|
149
240
|
}
|
|
150
241
|
function layered(asks) {
|
|
151
242
|
const depths = new Map();
|
|
152
243
|
const layers = [];
|
|
153
244
|
for (const ask of asks) {
|
|
154
|
-
const depth = depths.get(ask.entry.authority) ?? 0;
|
|
155
|
-
depths.set(ask.entry.authority, depth + 1);
|
|
245
|
+
const depth = depths.get(ask.ask.entry.authority) ?? 0;
|
|
246
|
+
depths.set(ask.ask.entry.authority, depth + 1);
|
|
156
247
|
(layers[depth] ??= []).push(ask);
|
|
157
248
|
}
|
|
158
249
|
return layers;
|
|
159
250
|
}
|
|
160
251
|
async function enumerated(asks) {
|
|
161
|
-
const answers =
|
|
162
|
-
const parts = layered(asks).flatMap((layer) => selections(layer, (ask) => weight(ask.entry), declared().max_prefixes));
|
|
252
|
+
const answers = [];
|
|
253
|
+
const parts = layered(asks.map((ask, index) => ({ ask, index }))).flatMap((layer) => selections(layer, ({ ask }) => weight(ask.entry), declared().max_prefixes));
|
|
163
254
|
for (const part of parts) {
|
|
164
255
|
let open = [...part];
|
|
165
256
|
while (open.length > 0) {
|
|
166
257
|
const answer = await transport.door(DOOR_ENUMERATE, {
|
|
167
|
-
prefixes: open.flatMap((ask) => contentPrefixes(ask.entry)),
|
|
168
|
-
positions: open.map((ask) => presented(ask.entry.authority, ask.from)),
|
|
258
|
+
prefixes: open.flatMap(({ ask }) => contentPrefixes(ask.entry)),
|
|
259
|
+
positions: open.map(({ ask }) => presented(ask.entry.authority, ask.from)),
|
|
169
260
|
});
|
|
170
|
-
const classified = open.map((ask) =>
|
|
261
|
+
const classified = open.map(({ ask }) => classifyRead(answer.status, answer.body, ask.entry.locator));
|
|
171
262
|
if (classified[0]?.kind === 'beyond-horizon') {
|
|
172
263
|
const named = doorRefusal(answer.body)?.fields.authority;
|
|
173
|
-
const expired = open.filter((ask) => ask.entry.authority === named);
|
|
264
|
+
const expired = open.filter(({ ask }) => ask.entry.authority === named);
|
|
174
265
|
const answered = expired.length === 0 ? UNCERTAIN_READ : { kind: 'beyond-horizon' };
|
|
175
266
|
const gone = expired.length === 0 ? open : expired;
|
|
176
267
|
for (const ask of gone)
|
|
177
|
-
answers.
|
|
268
|
+
answers[ask.index] = answered;
|
|
178
269
|
open = open.filter((ask) => !gone.includes(ask));
|
|
179
270
|
continue;
|
|
180
271
|
}
|
|
181
272
|
const enumeration = classified[0] === null ? parseEnumeration(answer.body, 'credential') : null;
|
|
182
|
-
for (const [
|
|
183
|
-
const failure = classified[
|
|
273
|
+
for (const [answerIndex, { ask, index }] of open.entries()) {
|
|
274
|
+
const failure = classified[answerIndex];
|
|
184
275
|
if (failure !== null && failure !== undefined) {
|
|
185
|
-
answers
|
|
276
|
+
answers[index] = failure;
|
|
186
277
|
continue;
|
|
187
278
|
}
|
|
188
|
-
answers
|
|
279
|
+
answers[index] =
|
|
280
|
+
enumeration === null ? UNCERTAIN_READ : await enumeratedRoom(enumeration, ask);
|
|
189
281
|
}
|
|
190
282
|
open = [];
|
|
191
283
|
}
|
|
192
284
|
}
|
|
193
|
-
return
|
|
285
|
+
return answers;
|
|
194
286
|
}
|
|
195
|
-
const bootstrapTogether =
|
|
287
|
+
const bootstrapTogether = gatheredEach(bootstrapped);
|
|
196
288
|
const enumerateTogether = gathered(enumerated);
|
|
197
289
|
async function uploaded(head, changes, delta, bytes, entry, handed, keep) {
|
|
198
290
|
const paths = batchPaths(changes);
|
|
@@ -209,9 +301,7 @@ export function createAgentsHouseAdapter(options) {
|
|
|
209
301
|
return { outcome: UNCERTAIN };
|
|
210
302
|
const delivered = await transport.transfer({
|
|
211
303
|
act: 'authored_batch_upload',
|
|
212
|
-
method: 'POST',
|
|
213
304
|
url: grant.url,
|
|
214
|
-
answer: 'json',
|
|
215
305
|
body: `${grant.operation}\n${delta}`,
|
|
216
306
|
});
|
|
217
307
|
if (delivered.status !== 200)
|
|
@@ -223,7 +313,30 @@ export function createAgentsHouseAdapter(options) {
|
|
|
223
313
|
await keep(upload);
|
|
224
314
|
return { body: { ...head, paths, upload, runtime: notifications.runtime } };
|
|
225
315
|
}
|
|
226
|
-
async function
|
|
316
|
+
async function staged(answer, batch) {
|
|
317
|
+
const refusal = doorRefusal(answer.body);
|
|
318
|
+
if (refusal?.code !== 'files_unstaged')
|
|
319
|
+
return null;
|
|
320
|
+
const grants = refusal.fields.upload;
|
|
321
|
+
if (!Array.isArray(grants))
|
|
322
|
+
return false;
|
|
323
|
+
for (const grant of grants) {
|
|
324
|
+
const { url, operation, sha256 } = grant;
|
|
325
|
+
const change = batch.changes.find((held) => held.kind === 'binary' && held.sha256 === sha256);
|
|
326
|
+
if (typeof url !== 'string' || typeof operation !== 'string' || change === undefined)
|
|
327
|
+
return false;
|
|
328
|
+
const delivered = await transport.transfer({
|
|
329
|
+
act: 'binary_stage',
|
|
330
|
+
url,
|
|
331
|
+
body: change.blob,
|
|
332
|
+
operation,
|
|
333
|
+
});
|
|
334
|
+
if (delivered.status !== 200)
|
|
335
|
+
return false;
|
|
336
|
+
}
|
|
337
|
+
return true;
|
|
338
|
+
}
|
|
339
|
+
async function submit(batch, head, changes, delta, bytes, entry, handed, keep) {
|
|
227
340
|
const inline = { ...head, changes, runtime: notifications.runtime };
|
|
228
341
|
const prepared = uploadable(inline, bytes, declared())
|
|
229
342
|
? await uploaded(head, changes, delta, bytes, entry, handed, keep)
|
|
@@ -231,38 +344,105 @@ export function createAgentsHouseAdapter(options) {
|
|
|
231
344
|
if ('outcome' in prepared)
|
|
232
345
|
return prepared.outcome;
|
|
233
346
|
const answer = await transport.door(DOOR_BATCH, prepared.body);
|
|
234
|
-
|
|
347
|
+
const stagedFiles = await staged(answer, batch);
|
|
348
|
+
if (stagedFiles === false)
|
|
349
|
+
return UNCERTAIN;
|
|
350
|
+
const settled = stagedFiles === null ? answer : await transport.door(DOOR_BATCH, prepared.body);
|
|
351
|
+
if (stagedFiles === true && (await staged(settled, batch)) !== null)
|
|
352
|
+
return UNCERTAIN;
|
|
353
|
+
return mutationOutcome(settled.status, settled.body, entry.locator, entry.handle);
|
|
354
|
+
}
|
|
355
|
+
function granted(answer, locator) {
|
|
356
|
+
const failure = classifyDoor(answer.status, answer.body, locator);
|
|
357
|
+
if (failure !== null) {
|
|
358
|
+
return failure.kind === 'scoped-refusal'
|
|
359
|
+
? {
|
|
360
|
+
kind: 'rejected',
|
|
361
|
+
code: failure.code,
|
|
362
|
+
fields: failure.fields,
|
|
363
|
+
retryable: failure.retryable,
|
|
364
|
+
}
|
|
365
|
+
: failure;
|
|
366
|
+
}
|
|
367
|
+
const grant = parseGrant(answer.body);
|
|
368
|
+
const name = answer.body.name;
|
|
369
|
+
if (grant === null || grant.method !== 'GET' || typeof name !== 'string') {
|
|
370
|
+
return UNCERTAIN_READ;
|
|
371
|
+
}
|
|
372
|
+
return { kind: 'link', url: grant.url, name };
|
|
373
|
+
}
|
|
374
|
+
async function downloaded(answer, locator) {
|
|
375
|
+
const link = granted(answer, locator);
|
|
376
|
+
if (link.kind !== 'link')
|
|
377
|
+
return link;
|
|
378
|
+
const read = await transport.download(link.url);
|
|
379
|
+
if (read.status !== 200 || read.blob === null)
|
|
380
|
+
return UNCERTAIN_READ;
|
|
381
|
+
return { kind: 'binary', blob: read.blob };
|
|
382
|
+
}
|
|
383
|
+
function pathGrant(request) {
|
|
384
|
+
const entry = authorityFor(request.locator);
|
|
385
|
+
if (entry === undefined)
|
|
386
|
+
return null;
|
|
387
|
+
return transport.door(DOOR_FILES_GET, {
|
|
388
|
+
room_ref: entry.authority,
|
|
389
|
+
path: request.path,
|
|
390
|
+
revision: request.revision,
|
|
391
|
+
});
|
|
235
392
|
}
|
|
236
393
|
return {
|
|
237
394
|
async authorize() {
|
|
238
395
|
return options.authorize();
|
|
239
396
|
},
|
|
240
|
-
async discover(_authorization) {
|
|
241
|
-
const answer = await transport.door(DOOR_DISCOVER, {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
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;
|
|
245
405
|
const document = parseDiscovery(answer.body);
|
|
246
406
|
if (document === null)
|
|
247
407
|
return { kind: 'transport-uncertainty' };
|
|
248
408
|
bounds = document.bounds;
|
|
249
409
|
const entries = trackedFrom(document);
|
|
250
|
-
|
|
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);
|
|
251
425
|
for (const entry of entries)
|
|
252
426
|
tracked.set(entry.locator, entry);
|
|
253
427
|
for (const locator of handed.keys())
|
|
254
428
|
if (!tracked.has(locator))
|
|
255
429
|
handed.delete(locator);
|
|
256
|
-
notifications.track(
|
|
257
|
-
return
|
|
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
|
+
};
|
|
258
436
|
},
|
|
259
437
|
async bootstrap(_authorization, request) {
|
|
260
438
|
const entry = authorityFor(request.locator);
|
|
261
439
|
if (entry === undefined)
|
|
262
440
|
return { kind: 'room-not-readable', locator: request.locator };
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
441
|
+
const asked = { entry, held: request.held };
|
|
442
|
+
if (request.pageToken !== undefined) {
|
|
443
|
+
return continuedBootstrap(asked, JSON.parse(request.pageToken));
|
|
444
|
+
}
|
|
445
|
+
return bootstrapTogether(asked);
|
|
266
446
|
},
|
|
267
447
|
async refresh(_authorization, request) {
|
|
268
448
|
const entry = authorityFor(request.locator);
|
|
@@ -289,10 +469,11 @@ export function createAgentsHouseAdapter(options) {
|
|
|
289
469
|
const delta = deltaBody(mapped.changes);
|
|
290
470
|
const bytes = deltaBytes(delta);
|
|
291
471
|
const handed = request.upload;
|
|
292
|
-
const
|
|
472
|
+
const batch = request.changeBatch;
|
|
473
|
+
const outcome = await submit(batch, head, mapped.changes, delta, bytes, entry, handed, keep);
|
|
293
474
|
if (handed === undefined || !lapsedGrant(outcome))
|
|
294
475
|
return adopted(entry, outcome);
|
|
295
|
-
return adopted(entry, await submit(head, mapped.changes, delta, bytes, entry, undefined, keep));
|
|
476
|
+
return adopted(entry, await submit(batch, head, mapped.changes, delta, bytes, entry, undefined, keep));
|
|
296
477
|
},
|
|
297
478
|
async history(_authorization, request) {
|
|
298
479
|
const entry = authorityFor(request.locator);
|
|
@@ -309,6 +490,38 @@ export function createAgentsHouseAdapter(options) {
|
|
|
309
490
|
...(request.revision === undefined ? {} : { revision: request.revision }),
|
|
310
491
|
});
|
|
311
492
|
},
|
|
493
|
+
async binary(_authorization, request) {
|
|
494
|
+
if (request.kind === 'path') {
|
|
495
|
+
const asked = pathGrant(request);
|
|
496
|
+
if (asked === null)
|
|
497
|
+
return { kind: 'room-not-readable', locator: request.locator };
|
|
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);
|
|
509
|
+
},
|
|
510
|
+
async link(_authorization, request) {
|
|
511
|
+
const asked = pathGrant(request);
|
|
512
|
+
if (asked === null)
|
|
513
|
+
return { kind: 'room-not-readable', locator: request.locator };
|
|
514
|
+
return granted(await asked, request.locator);
|
|
515
|
+
},
|
|
516
|
+
referrerScope(referrer) {
|
|
517
|
+
const held = houseReferrer(referrer, tracked);
|
|
518
|
+
if (held === null)
|
|
519
|
+
return null;
|
|
520
|
+
if (held.locator === '')
|
|
521
|
+
return { kind: 'conversations' };
|
|
522
|
+
const { path } = held.referrer;
|
|
523
|
+
return { kind: 'room', locator: held.locator, ...(path === undefined ? {} : { path }) };
|
|
524
|
+
},
|
|
312
525
|
reached(locator, version, target) {
|
|
313
526
|
return reachedVersion(version, target, authorityFor(locator)?.logEpoch);
|
|
314
527
|
},
|
|
@@ -318,7 +531,16 @@ export function createAgentsHouseAdapter(options) {
|
|
|
318
531
|
async action(_authorization, request) {
|
|
319
532
|
return withAt(await houseAction(transport, request));
|
|
320
533
|
},
|
|
321
|
-
roomStream: {
|
|
534
|
+
roomStream: {
|
|
535
|
+
hold: held,
|
|
536
|
+
reopen: notifications.reopen,
|
|
537
|
+
observe: notifications.observe,
|
|
538
|
+
published(locator, roomVersion) {
|
|
539
|
+
const at = logPosition(roomVersion);
|
|
540
|
+
if (at !== null)
|
|
541
|
+
notifications.published(locator, at);
|
|
542
|
+
},
|
|
543
|
+
},
|
|
322
544
|
conversations: notifications.conversations,
|
|
323
545
|
serviceMode: notifications.serviceMode,
|
|
324
546
|
};
|
package/dist/discovery.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import type { Capability, DiscoveredRoom, Discovery, ProviderLimit, Retention } from '@agentshouse/mdruntime';
|
|
1
|
+
import type { Capability, DiscoveredRoom, Discovery, Member, ProviderLimit, Retention } from '@agentshouse/mdruntime';
|
|
2
2
|
import { type LogPosition } from './version.js';
|
|
3
3
|
export declare const PROVIDER_ACTIONS = "provider-actions";
|
|
4
4
|
export declare const DISCOVER_PREFIXES: readonly string[];
|
|
5
|
-
export declare const PRIVATE_HANDLE = "private";
|
|
6
5
|
export type HouseBounds = {
|
|
7
6
|
max_authored_file_bytes: number;
|
|
8
7
|
max_batch_operations: number;
|
|
@@ -14,6 +13,7 @@ export type HouseBounds = {
|
|
|
14
13
|
max_inline_body_bytes: number;
|
|
15
14
|
max_upload_bytes: number;
|
|
16
15
|
max_prefixes: number;
|
|
16
|
+
max_held_files: number;
|
|
17
17
|
change_retention_seconds: number;
|
|
18
18
|
operation_retention_seconds: number;
|
|
19
19
|
};
|
|
@@ -38,6 +38,7 @@ export type HouseAuthority = LogPosition & {
|
|
|
38
38
|
operations: HouseOperations;
|
|
39
39
|
};
|
|
40
40
|
export type HouseDiscoveryDocument = {
|
|
41
|
+
member: Member;
|
|
41
42
|
bounds: HouseBounds;
|
|
42
43
|
authorities: readonly HouseAuthority[];
|
|
43
44
|
actions: readonly string[];
|
|
@@ -62,11 +63,10 @@ export type HouseSiteDiscovery = {
|
|
|
62
63
|
export declare function parseDiscovery(body: unknown): HouseDiscoveryDocument | null;
|
|
63
64
|
export declare function parseSiteDiscovery(body: unknown): HouseSiteDiscovery | null;
|
|
64
65
|
export declare function siteTracked(document: HouseSiteDiscovery): TrackedAuthority[];
|
|
65
|
-
export declare function locatorOf(authority: HouseAuthority): string;
|
|
66
66
|
export declare function trackedFrom(document: HouseDiscoveryDocument): TrackedAuthority[];
|
|
67
67
|
export declare function providerLimits(bounds: HouseBounds): readonly ProviderLimit[];
|
|
68
68
|
export declare function declaredRetention(bounds: HouseBounds): Retention;
|
|
69
|
-
export declare function capabilities(actions: readonly
|
|
69
|
+
export declare function capabilities(actions: HouseDiscoveryDocument['actions'], rooms: readonly Pick<TrackedAuthority, 'authority' | 'houseModel'>[]): readonly Capability[];
|
|
70
70
|
export declare function siteCapabilities(): readonly Capability[];
|
|
71
71
|
export declare function discoveredRoom(authority: HouseAuthority): DiscoveredRoom;
|
|
72
72
|
export declare function mapDiscovery(document: Pick<HouseDiscoveryDocument, 'bounds'>, rooms: readonly DiscoveredRoom[], declared: readonly Capability[]): Discovery;
|
package/dist/discovery.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../src/discovery.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,cAAc,EACd,SAAS,EACT,aAAa,EACb,SAAS,EACV,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../src/discovery.ts"],"names":[],"mappings":"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"}
|