@agentshouse/mdruntime-adapter 0.1.0-cdc1a38081f09 → 0.1.0-cde42e60258b9

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.
Files changed (66) hide show
  1. package/README.md +2 -2
  2. package/dist/actions.d.ts +6 -59
  3. package/dist/actions.d.ts.map +1 -1
  4. package/dist/actions.js +22 -292
  5. package/dist/adapter.d.ts.map +1 -1
  6. package/dist/adapter.js +158 -358
  7. package/dist/app-reference.d.ts +2 -0
  8. package/dist/app-reference.d.ts.map +1 -0
  9. package/dist/app-reference.js +2 -0
  10. package/dist/chunks.d.ts +38 -0
  11. package/dist/chunks.d.ts.map +1 -0
  12. package/dist/chunks.js +247 -0
  13. package/dist/crockford.d.ts +4 -0
  14. package/dist/crockford.d.ts.map +1 -0
  15. package/dist/crockford.js +3 -0
  16. package/dist/discovery.d.ts +22 -15
  17. package/dist/discovery.d.ts.map +1 -1
  18. package/dist/discovery.js +75 -56
  19. package/dist/history.d.ts +12 -0
  20. package/dist/history.d.ts.map +1 -0
  21. package/dist/history.js +92 -0
  22. package/dist/house-model.d.ts +682 -0
  23. package/dist/house-model.d.ts.map +1 -0
  24. package/dist/house-model.js +396 -0
  25. package/dist/index.d.ts +9 -2
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +9 -2
  28. package/dist/model.d.ts +5 -8
  29. package/dist/model.d.ts.map +1 -1
  30. package/dist/model.js +32 -46
  31. package/dist/read.d.ts +21 -59
  32. package/dist/read.d.ts.map +1 -1
  33. package/dist/read.js +50 -330
  34. package/dist/refusal.d.ts +1 -3
  35. package/dist/refusal.d.ts.map +1 -1
  36. package/dist/refusal.js +4 -5
  37. package/dist/room-types.d.ts +2 -3
  38. package/dist/room-types.d.ts.map +1 -1
  39. package/dist/room-types.js +49 -12
  40. package/dist/search.d.ts +5 -0
  41. package/dist/search.d.ts.map +1 -0
  42. package/dist/search.js +47 -0
  43. package/dist/site.d.ts.map +1 -1
  44. package/dist/site.js +75 -167
  45. package/dist/space-schema.d.ts +7 -0
  46. package/dist/space-schema.d.ts.map +1 -0
  47. package/dist/space-schema.js +18 -0
  48. package/dist/stream.d.ts +16 -15
  49. package/dist/stream.d.ts.map +1 -1
  50. package/dist/stream.js +233 -423
  51. package/dist/transport.d.ts +47 -36
  52. package/dist/transport.d.ts.map +1 -1
  53. package/dist/transport.js +54 -11
  54. package/dist/upload.d.ts.map +1 -1
  55. package/dist/upload.js +2 -2
  56. package/dist/write.d.ts +6 -1
  57. package/dist/write.d.ts.map +1 -1
  58. package/dist/write.js +21 -30
  59. package/package.json +7 -3
  60. package/AGENTS.md +0 -317
  61. package/dist/gather.d.ts +0 -4
  62. package/dist/gather.d.ts.map +0 -1
  63. package/dist/gather.js +0 -35
  64. package/dist/part.d.ts +0 -15
  65. package/dist/part.d.ts.map +0 -1
  66. package/dist/part.js +0 -82
package/dist/adapter.js CHANGED
@@ -1,210 +1,123 @@
1
1
  import { stepped } from '@agentshouse/mdruntime';
2
- import { DISCOVER_PREFIXES, capabilities, discoveredRoom, mapDiscovery, parseDiscovery, trackedFrom, } from './discovery.js';
3
- import { manifestedModels, partModels, streamedModels, viewModels } from './model.js';
4
- import { gathered, gatheredEach, selections } from './gather.js';
2
+ import { capabilities, discoveredRoom, mapDiscovery, parseAuthorityChange, parseDiscovery, trackedFrom, } from './discovery.js';
5
3
  import { createSiteAdapter } from './site.js';
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';
4
+ import { parseFileAddress, parseGrant } from './read.js';
5
+ import { chunkedHead, chunkLister, chunkPart } from './chunks.js';
6
+ import { spaceSchemas } from './space-schema.js';
9
7
  import { finishedUpload, houseUpload } from './upload.js';
10
- import { houseAction } from './actions.js';
11
- import { classifyDoor, classifyRead, discoveryFailure, doorRefusal } from './refusal.js';
12
- import { houseNotifications } from './stream.js';
13
- import { DOOR_BATCH, DOOR_BATCH_UPLOAD, DOOR_BOOTSTRAP, DOOR_DISCOVER, DOOR_ENUMERATE, DOOR_FILES_GET, DOOR_REVISIONS, } from './transport.js';
8
+ import { houseAction, HOUSE_LEADING_READS, HOUSE_UNKEPT_READS } from './actions.js';
9
+ import { houseSearch } from './search.js';
10
+ import { classifyDoor, doorRefusal } from './refusal.js';
11
+ import { houseNotifications, } from './stream.js';
12
+ import { houseDoorRoute, } from './transport.js';
14
13
  import { batchPaths, deltaBody, deltaBytes, deltaDigest, historyOutcome, houseChanges, houseMove, mutationOutcome, operationHead, rejectedMapping, revisionBody, uploadable, } from './write.js';
15
- import { HOUSE_ROOM_TYPES } from './room-types.js';
16
- import { followsVersion, logPosition, presented, reachedVersion, roomVersion, } from './version.js';
14
+ import { HOUSE_TYPED_PATHS } from './house-model.js';
15
+ import { HOUSE_NAMEABLE_ROOM_TYPES, HOUSE_ROOM_TYPES } from './room-types.js';
16
+ import { followsVersion, reachedVersion, roomVersion } from './version.js';
17
17
  const UNCERTAIN = { kind: 'transport-uncertainty' };
18
18
  const UNCERTAIN_READ = { kind: 'transport-uncertainty' };
19
+ const OCTET_STREAM = 'application/octet-stream';
19
20
  const EXPIRED_ADDRESS = 403;
20
21
  function lapsedGrant(outcome) {
21
22
  return outcome.kind === 'rejected' && outcome.code === 'upload_unavailable';
22
23
  }
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
- }
49
24
  export function createAgentsHouseAdapter(options) {
50
25
  if ('site' in options)
51
26
  return createSiteAdapter(options.site, options.snapshot === true);
52
27
  const transport = options.transport;
53
- const notifications = houseNotifications(transport, 'credential');
28
+ const spaces = spaceSchemas();
29
+ const notifications = houseNotifications(transport, 'credential', {
30
+ opened: async (frame) => (frame === null ? null : opened(frame)),
31
+ changed,
32
+ expired: (authorities) => lister.askAll(authorities.flatMap((authority) => [...tracked.values()].filter((entry) => entry.authority === authority))),
33
+ }, spaces);
54
34
  const tracked = new Map();
55
- const handed = new Map();
56
- let heldRevisions;
35
+ const lister = chunkLister(transport, () => declared().max_prefixes);
36
+ const chunked = new Map();
37
+ const renewals = new WeakMap();
57
38
  let bounds;
58
39
  function declared() {
59
40
  if (bounds === undefined)
60
41
  throw new Error('no Discovery declared the bounds');
61
42
  return bounds;
62
43
  }
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));
115
- else
116
- handed.delete(entry.locator);
117
- notifications.reach(entry.authority, scope);
118
- const seen = [
119
- ...continued.seen,
120
- ...entry.schemas.filter((schema) => scope.files.some((file) => file.path === schema)),
121
- ];
122
- const models = await partModels(scope, entry, new Set(seen), !more);
44
+ function opened(frame) {
45
+ const document = parseDiscovery(frame);
46
+ if (document === null)
47
+ return null;
48
+ bounds = document.bounds;
49
+ tracked.clear();
50
+ lister.forget();
51
+ for (const entry of trackedFrom(document))
52
+ tracked.set(entry.locator, entry);
53
+ notifications.track([...tracked.values()]);
123
54
  return {
124
- ...(await stepped(bootstrapping(scope))),
125
- ...(models === undefined ? {} : { models }),
126
- ...(more ? { pageToken: JSON.stringify({ ...continued, next, seen }) } : {}),
127
- ...(sent.frame === null ? {} : { received: sent.frame }),
55
+ ...mapDiscovery(document, document.authorities.map((authority) => discoveredRoom(authority)), capabilities(document.actions)),
56
+ member: document.member,
128
57
  };
129
58
  }
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 });
59
+ function changed(frame) {
60
+ const change = parseAuthorityChange(frame);
61
+ if (change === null)
62
+ return null;
63
+ for (const locator of change.left)
64
+ tracked.delete(locator);
65
+ for (const entry of trackedFrom(change))
66
+ tracked.set(entry.locator, entry);
67
+ notifications.track([...tracked.values()]);
68
+ return {
69
+ kind: 'authority-changed',
70
+ rooms: change.authorities.map((authority) => discoveredRoom(authority)),
71
+ left: change.left,
72
+ member: change.member,
73
+ };
147
74
  }
148
- async function enumeratedRoom(enumeration, ask) {
149
- const first = viewOf(enumeration, ask.entry);
150
- if (first === undefined)
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;
155
- notifications.reach(ask.entry.authority, view);
156
- const answer = await refreshFrom(view, ask.fromRoomVersion);
157
- if (answer.kind !== 'net-advancement')
158
- return answer;
159
- handed.set(ask.entry.locator, roomVersion(view));
160
- const models = await viewModels(view, ask.entry);
161
- return models === undefined ? answer : { ...answer, models };
75
+ async function listedRoom(entry) {
76
+ const listed = await lister.list(entry);
77
+ if ('kind' in listed)
78
+ return listed;
79
+ const head = await chunkedHead(listed, entry, spaces);
80
+ if (head.kind === 'bootstrap')
81
+ chunked.set(entry.locator, listed);
82
+ return head;
162
83
  }
163
- async function streamed(entry, request, from) {
164
- if (handed.get(entry.locator) !== request.fromRoomVersion)
84
+ async function chunkRead(entry, id) {
85
+ const room = chunked.get(entry.locator);
86
+ const address = room?.chunks.get(id);
87
+ if (room === undefined || address === undefined)
165
88
  return null;
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));
171
- if (advanced === null)
89
+ const read = await transport.download(address.url);
90
+ if (read.status !== EXPIRED_ADDRESS)
91
+ return { room, read };
92
+ const renewed = await renewal(entry, room);
93
+ if ('kind' in renewed || !renewed.chunks.has(id))
172
94
  return null;
173
- handed.set(entry.locator, advanced.page.roomVersion);
174
- const models = await streamedModels(advanced.schemas, entry, 'credential');
175
- return models === undefined ? advanced.page : { ...advanced.page, models };
95
+ chunked.set(entry.locator, renewed);
96
+ return { room: renewed, read: await transport.download(renewed.chunks.get(id).url) };
176
97
  }
177
- function held(rooms) {
178
- const kept = rooms.flatMap((room) => {
179
- const entry = tracked.get(room.locator);
180
- const at = logPosition(room.roomVersion);
181
- if (entry === undefined || at === null)
182
- return [];
183
- handed.set(room.locator, room.roomVersion);
184
- notifications.reach(entry.authority, at);
185
- return [presented(entry.authority, at)];
186
- });
187
- notifications.send(kept);
98
+ function renewal(entry, stale) {
99
+ const held = renewals.get(stale);
100
+ if (held !== undefined)
101
+ return held;
102
+ const renewed = lister.renew(entry, [...chunked.keys()].flatMap((locator) => tracked.get(locator) ?? []));
103
+ renewals.set(stale, renewed);
104
+ return renewed;
188
105
  }
189
- async function adoptedMove(entry, into, outcome) {
190
- if (outcome.kind === 'accepted' && outcome.destination !== undefined) {
191
- await adopted(into, { kind: 'accepted', ...outcome.destination });
106
+ function hold(rooms) {
107
+ for (const room of rooms) {
108
+ const listed = chunked.get(room.locator);
109
+ if (listed !== undefined &&
110
+ room.roomVersion !== null &&
111
+ reachedVersion(room.roomVersion, roomVersion(listed), listed.logEpoch))
112
+ chunked.delete(room.locator);
192
113
  }
193
- return adopted(entry, outcome);
114
+ notifications.send(rooms);
194
115
  }
195
- async function adopted(entry, outcome) {
196
- if (outcome.kind !== 'accepted')
197
- return outcome;
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);
205
- if (follows && at !== null)
206
- notifications.reach(entry.authority, at);
207
- return outcome;
116
+ async function chunkPage(entry, id, arriving) {
117
+ const fetched = await chunkRead(entry, id);
118
+ if (fetched === null || fetched.read.status !== 200)
119
+ return UNCERTAIN_READ;
120
+ return chunkPart(fetched.read.body, fetched.room, id, arriving);
208
121
  }
209
122
  function authorityFor(locator) {
210
123
  return tracked.get(locator);
@@ -223,116 +136,18 @@ export function createAgentsHouseAdapter(options) {
223
136
  return result;
224
137
  return { ...result, at: { locator, roomVersion: roomVersion({ position, logEpoch }) } };
225
138
  }
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;
237
- }
238
- async function bootstrapped(asks, answer) {
239
- let open = asks.map((asked, ask) => ({ entry: asked.entry, asked, ask }));
240
- while (open.length > 0) {
241
- const unreached = [];
242
- for (const part of selections(open, declared().max_prefixes)) {
243
- const held = await holding(part.map(({ asked }) => asked));
244
- const answered = await transport.door(DOOR_BOOTSTRAP, {
245
- prefixes: part.map(({ entry }) => authorityRoot(entry.handle)),
246
- ...(held.length === 0 ? {} : { held }),
247
- });
248
- const classified = part.map(({ entry }) => classifyDoor(answered.status, answered.body, entry.locator));
249
- const parted = classified[0] === null ? parseParted(answered.body) : null;
250
- for (const [index, asked] of part.entries()) {
251
- const failure = classified[index];
252
- if (failure !== null && failure !== undefined) {
253
- answer(asked.ask, failure);
254
- continue;
255
- }
256
- if (parted === null) {
257
- answer(asked.ask, UNCERTAIN_READ);
258
- continue;
259
- }
260
- const room = parted.rooms.find((candidate) => candidate.anchor === asked.entry.authority);
261
- if (room === undefined && parted.next !== undefined) {
262
- unreached.push(asked);
263
- continue;
264
- }
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
- }));
276
- }
277
- }
278
- open = unreached;
279
- }
280
- }
281
- function layered(asks) {
282
- const depths = new Map();
283
- const layers = [];
284
- for (const ask of asks) {
285
- const depth = depths.get(ask.ask.entry.authority) ?? 0;
286
- depths.set(ask.ask.entry.authority, depth + 1);
287
- (layers[depth] ??= []).push(ask);
288
- }
289
- return layers;
290
- }
291
- async function enumerated(asks) {
292
- const answers = [];
293
- const parts = layered(asks.map((ask, index) => ({ ask, index }))).flatMap((layer) => selections(layer, declared().max_prefixes));
294
- for (const part of parts) {
295
- let open = [...part];
296
- while (open.length > 0) {
297
- const answer = await transport.door(DOOR_ENUMERATE, {
298
- prefixes: open.map(({ ask }) => authorityRoot(ask.entry.handle)),
299
- positions: open.map(({ ask }) => presented(ask.entry.authority, ask.from)),
300
- });
301
- const classified = open.map(({ ask }) => classifyRead(answer.status, answer.body, ask.entry.locator));
302
- if (classified[0]?.kind === 'beyond-horizon') {
303
- const named = doorRefusal(answer.body)?.fields.authority;
304
- const expired = open.filter(({ ask }) => ask.entry.authority === named);
305
- const answered = expired.length === 0 ? UNCERTAIN_READ : { kind: 'beyond-horizon' };
306
- const gone = expired.length === 0 ? open : expired;
307
- for (const ask of gone)
308
- answers[ask.index] = answered;
309
- open = open.filter((ask) => !gone.includes(ask));
310
- continue;
311
- }
312
- const enumeration = classified[0] === null ? parseEnumeration(answer.body, 'credential') : null;
313
- for (const [answerIndex, { ask, index }] of open.entries()) {
314
- const failure = classified[answerIndex];
315
- if (failure !== null && failure !== undefined) {
316
- answers[index] = failure;
317
- continue;
318
- }
319
- answers[index] =
320
- enumeration === null ? UNCERTAIN_READ : await enumeratedRoom(enumeration, ask);
321
- }
322
- open = [];
323
- }
324
- }
325
- return answers;
326
- }
327
- const bootstrapTogether = gatheredEach(bootstrapped);
328
- const enumerateTogether = gathered(enumerated);
329
139
  async function uploaded(head, changes, delta, bytes, entry, handed, keep) {
330
140
  const paths = batchPaths(changes);
331
141
  const sha256 = await deltaDigest(delta);
332
142
  if (handed !== undefined && handed.sha256 === sha256) {
333
143
  return { body: { ...head, paths, upload: handed, runtime: notifications.runtime } };
334
144
  }
335
- const minted = await transport.door(DOOR_BATCH_UPLOAD, { ...head, paths, bytes, sha256 });
145
+ const minted = await transport.door(houseDoorRoute('files/batch-upload'), {
146
+ ...head,
147
+ paths,
148
+ bytes,
149
+ sha256,
150
+ });
336
151
  if (minted.status !== 200) {
337
152
  return {
338
153
  outcome: await stepped(mutationOutcome(minted.status, minted.body, entry.locator, entry.handle)),
@@ -380,11 +195,13 @@ export function createAgentsHouseAdapter(options) {
380
195
  : { body: inline };
381
196
  if ('outcome' in prepared)
382
197
  return prepared.outcome;
383
- const answer = await transport.door(DOOR_BATCH, prepared.body);
198
+ const answer = await transport.door(houseDoorRoute('files/batch'), prepared.body);
384
199
  const stagedFiles = await staged(answer, request);
385
200
  if (stagedFiles === false)
386
201
  return UNCERTAIN;
387
- const settled = stagedFiles === null ? answer : await transport.door(DOOR_BATCH, prepared.body);
202
+ const settled = stagedFiles === null
203
+ ? answer
204
+ : await transport.door(houseDoorRoute('files/batch'), prepared.body);
388
205
  if (stagedFiles === true && (await staged(settled, request)) !== null)
389
206
  return UNCERTAIN;
390
207
  return stepped(mutationOutcome(settled.status, settled.body, entry.locator, entry.handle, into === undefined
@@ -412,9 +229,22 @@ export function createAgentsHouseAdapter(options) {
412
229
  function linked(answer, locator) {
413
230
  const address = addressed(answer, locator);
414
231
  return address.kind === 'address'
415
- ? { kind: 'link', url: address.url, name: address.name }
232
+ ? { kind: 'link', url: address.url, name: address.name, mediaType: address.mediaType }
416
233
  : address;
417
234
  }
235
+ function refusedAttach(answer, locator) {
236
+ const failure = classifyDoor(answer.status, answer.body, locator);
237
+ if (failure === null)
238
+ return null;
239
+ return failure.kind === 'scoped-refusal'
240
+ ? {
241
+ kind: 'rejected',
242
+ code: failure.code,
243
+ fields: failure.fields,
244
+ retryable: failure.retryable,
245
+ }
246
+ : failure;
247
+ }
418
248
  async function downloaded(ask, locator) {
419
249
  let address = addressed(await ask(), locator);
420
250
  if (address.kind !== 'address')
@@ -439,7 +269,7 @@ export function createAgentsHouseAdapter(options) {
439
269
  const entry = authorityFor(request.locator);
440
270
  if (entry === undefined)
441
271
  return null;
442
- return () => transport.door(DOOR_FILES_GET, {
272
+ return () => transport.door(houseDoorRoute('files/read'), {
443
273
  room_ref: entry.authority,
444
274
  path: request.path,
445
275
  revision: request.revision,
@@ -447,72 +277,20 @@ export function createAgentsHouseAdapter(options) {
447
277
  }
448
278
  return {
449
279
  roomTypes: HOUSE_ROOM_TYPES,
280
+ nameableRoomTypes: HOUSE_NAMEABLE_ROOM_TYPES,
281
+ typedPaths: HOUSE_TYPED_PATHS,
282
+ unkeptReads: HOUSE_UNKEPT_READS,
283
+ leadingReads: HOUSE_LEADING_READS,
450
284
  async authorize() {
451
285
  return options.authorize();
452
286
  },
453
- async discover(_authorization, scope) {
454
- const answer = await transport.door(DOOR_DISCOVER, {
455
- prefixes: [...DISCOVER_PREFIXES],
456
- ...(scope === undefined ? {} : { rooms: scope.authorities }),
457
- });
458
- const failed = discoveryFailure(answer.status, answer.body);
459
- if (failed !== null)
460
- return failed;
461
- const document = parseDiscovery(answer.body);
462
- if (document === null)
463
- return { kind: 'transport-uncertainty' };
464
- bounds = document.bounds;
465
- const entries = trackedFrom(document);
466
- const covered = scope === undefined
467
- ? null
468
- : [
469
- ...new Set([
470
- ...[...tracked.values()]
471
- .filter((entry) => scope.authorities.includes(entry.authority))
472
- .map((entry) => entry.locator),
473
- ...entries.map((entry) => entry.locator),
474
- ]),
475
- ];
476
- if (covered === null)
477
- tracked.clear();
478
- else
479
- for (const locator of covered)
480
- tracked.delete(locator);
481
- for (const entry of entries)
482
- tracked.set(entry.locator, entry);
483
- for (const locator of handed.keys())
484
- if (!tracked.has(locator))
485
- handed.delete(locator);
486
- notifications.track([...tracked.values()]);
487
- return {
488
- ...mapDiscovery(document, document.authorities.map((authority) => discoveredRoom(authority)), capabilities(document.actions, [...tracked.values()])),
489
- member: document.member,
490
- ...(covered === null ? {} : { covered }),
491
- };
492
- },
493
287
  async bootstrap(_authorization, request) {
494
288
  const entry = authorityFor(request.locator);
495
289
  if (entry === undefined)
496
290
  return { kind: 'room-not-readable', locator: request.locator };
497
- const asked = { entry, held: request.held };
498
- if (request.pageToken !== undefined) {
499
- return partPage(asked, JSON.parse(request.pageToken), request.received, request.arriving);
500
- }
501
- return bootstrapTogether(asked);
502
- },
503
- async refresh(_authorization, request) {
504
- const entry = authorityFor(request.locator);
505
- if (entry === undefined)
506
- return { kind: 'room-not-readable', locator: request.locator };
507
- if (request.pageToken !== undefined)
508
- return { kind: 'transport-uncertainty' };
509
- const from = logPosition(request.fromRoomVersion);
510
- if (from === null)
511
- return { kind: 'beyond-horizon' };
512
- const answered = await streamed(entry, request, from);
513
- if (answered !== null)
514
- return answered;
515
- return enumerateTogether({ entry, from, fromRoomVersion: request.fromRoomVersion });
291
+ return request.pageToken === undefined
292
+ ? listedRoom(entry)
293
+ : chunkPage(entry, request.pageToken, request.arriving);
516
294
  },
517
295
  async mutate(_authorization, request, keep) {
518
296
  const entry = authorityFor(request.locator);
@@ -529,22 +307,21 @@ export function createAgentsHouseAdapter(options) {
529
307
  : houseMove(batch, entry.handle, into.handle);
530
308
  if (mapped.kind === 'unmapped')
531
309
  return rejectedMapping(mapped);
532
- const head = operationHead(batch);
310
+ const head = operationHead(batch, entry.authority);
533
311
  const delta = deltaBody(mapped.changes);
534
312
  const bytes = deltaBytes(delta);
535
313
  const handed = request.upload;
536
314
  const answer = (upload) => submit(request, head, mapped.changes, delta, bytes, entry, upload, keep, into);
537
- const settle = (outcome) => into === undefined ? adopted(entry, outcome) : adoptedMove(entry, into, outcome);
538
315
  const outcome = await answer(handed);
539
316
  if (handed === undefined || !lapsedGrant(outcome))
540
- return settle(outcome);
541
- return settle(await answer(undefined));
317
+ return outcome;
318
+ return answer(undefined);
542
319
  },
543
320
  async history(_authorization, request) {
544
321
  const entry = authorityFor(request.locator);
545
322
  if (entry === undefined)
546
323
  return { kind: 'room-not-readable', locator: request.locator };
547
- const answer = await transport.door(DOOR_REVISIONS, revisionBody({
324
+ const answer = await transport.door(houseDoorRoute('files/revisions/list'), revisionBody({
548
325
  path: request.path,
549
326
  handle: entry.handle,
550
327
  ...(request.revision === undefined ? {} : { revision: request.revision }),
@@ -567,6 +344,35 @@ export function createAgentsHouseAdapter(options) {
567
344
  return { kind: 'room-not-readable', locator: request.locator };
568
345
  return linked(await ask(), request.locator);
569
346
  },
347
+ async attach(_authorization, request) {
348
+ const entry = authorityFor(request.locator);
349
+ if (entry === undefined)
350
+ return { kind: 'room-not-readable', locator: request.locator };
351
+ const declared = await transport.door(houseDoorRoute('files/attach'), {
352
+ room_ref: entry.authority,
353
+ name: request.name,
354
+ bytes: request.byteLength,
355
+ sha256: request.sha256,
356
+ media_type: request.mediaType.length > 0 ? request.mediaType : OCTET_STREAM,
357
+ });
358
+ const refused = refusedAttach(declared, request.locator);
359
+ if (refused !== null)
360
+ return refused;
361
+ const upload = houseUpload(declared.body?.upload);
362
+ if (upload === null)
363
+ return UNCERTAIN_READ;
364
+ const finished = await finishedUpload(transport, upload, request.blob);
365
+ if (finished === null)
366
+ return UNCERTAIN_READ;
367
+ const unfinished = refusedAttach(finished, request.locator);
368
+ if (unfinished !== null)
369
+ return unfinished;
370
+ const { path, revision } = (finished.body ?? {});
371
+ const root = `/rooms/${entry.handle}/`;
372
+ return typeof path === 'string' && path.startsWith(root) && typeof revision === 'string'
373
+ ? { kind: 'attached', path: path.slice(root.length), revision }
374
+ : UNCERTAIN_READ;
375
+ },
570
376
  reached(locator, version, target) {
571
377
  return reachedVersion(version, target, authorityFor(locator)?.logEpoch);
572
378
  },
@@ -576,18 +382,12 @@ export function createAgentsHouseAdapter(options) {
576
382
  async action(_authorization, request) {
577
383
  return withAt(await houseAction(transport, request));
578
384
  },
385
+ search(_authorization, request) {
386
+ return houseSearch(transport, [...tracked.values()], request);
387
+ },
579
388
  roomStream: {
580
- hold: held,
581
- reopen: notifications.reopen,
582
- observe(notify, receipts, revisions) {
583
- heldRevisions = revisions;
584
- return notifications.observe(notify, receipts);
585
- },
586
- published(locator, roomVersion) {
587
- const at = logPosition(roomVersion);
588
- if (at !== null)
589
- notifications.published(locator, at);
590
- },
389
+ hold,
390
+ observe: notifications.observe,
591
391
  },
592
392
  conversations: notifications.conversations,
593
393
  serviceMode: notifications.serviceMode,
@@ -0,0 +1,2 @@
1
+ export declare const APP_REFERENCE_PATTERN: RegExp;
2
+ //# sourceMappingURL=app-reference.d.ts.map
@@ -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"}
@@ -0,0 +1,2 @@
1
+ import { CROCKFORD_CHARACTER } from './crockford.js';
2
+ export const APP_REFERENCE_PATTERN = new RegExp(`^a_(${CROCKFORD_CHARACTER}+)\\.(${CROCKFORD_CHARACTER})$`);