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

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 +4 -59
  3. package/dist/actions.d.ts.map +1 -1
  4. package/dist/actions.js +7 -289
  5. package/dist/adapter.d.ts.map +1 -1
  6. package/dist/adapter.js +138 -328
  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/crockford.d.ts +4 -0
  11. package/dist/crockford.d.ts.map +1 -0
  12. package/dist/crockford.js +3 -0
  13. package/dist/discovery.d.ts +21 -13
  14. package/dist/discovery.d.ts.map +1 -1
  15. package/dist/discovery.js +73 -53
  16. package/dist/history.d.ts +12 -0
  17. package/dist/history.d.ts.map +1 -0
  18. package/dist/history.js +92 -0
  19. package/dist/house-model.d.ts +719 -0
  20. package/dist/house-model.d.ts.map +1 -0
  21. package/dist/house-model.js +393 -0
  22. package/dist/index.d.ts +10 -3
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +9 -2
  25. package/dist/model.d.ts +5 -4
  26. package/dist/model.d.ts.map +1 -1
  27. package/dist/model.js +31 -35
  28. package/dist/part.d.ts +11 -12
  29. package/dist/part.d.ts.map +1 -1
  30. package/dist/part.js +63 -55
  31. package/dist/read.d.ts +20 -58
  32. package/dist/read.d.ts.map +1 -1
  33. package/dist/read.js +60 -221
  34. package/dist/refusal.d.ts +0 -3
  35. package/dist/refusal.d.ts.map +1 -1
  36. package/dist/refusal.js +0 -5
  37. package/dist/room-stream.d.ts +20 -0
  38. package/dist/room-stream.d.ts.map +1 -0
  39. package/dist/room-stream.js +57 -0
  40. package/dist/room-types.d.ts +4 -3
  41. package/dist/room-types.d.ts.map +1 -1
  42. package/dist/room-types.js +49 -9
  43. package/dist/search.d.ts +5 -0
  44. package/dist/search.d.ts.map +1 -0
  45. package/dist/search.js +47 -0
  46. package/dist/site.d.ts.map +1 -1
  47. package/dist/site.js +79 -159
  48. package/dist/space-schema.d.ts +7 -0
  49. package/dist/space-schema.d.ts.map +1 -0
  50. package/dist/space-schema.js +18 -0
  51. package/dist/stream.d.ts +14 -14
  52. package/dist/stream.d.ts.map +1 -1
  53. package/dist/stream.js +169 -424
  54. package/dist/transport.d.ts +51 -36
  55. package/dist/transport.d.ts.map +1 -1
  56. package/dist/transport.js +54 -11
  57. package/dist/upload.d.ts.map +1 -1
  58. package/dist/upload.js +2 -2
  59. package/dist/write.d.ts +6 -1
  60. package/dist/write.d.ts.map +1 -1
  61. package/dist/write.js +18 -25
  62. package/package.json +7 -3
  63. package/AGENTS.md +0 -317
  64. package/dist/gather.d.ts +0 -4
  65. package/dist/gather.d.ts.map +0 -1
  66. package/dist/gather.js +0 -35
package/dist/adapter.js CHANGED
@@ -1,21 +1,23 @@
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';
4
+ import { authorityRoot, bootstrapping, parseFileAddress, parseGrant, } from './read.js';
5
+ import { roomStreams } from './room-stream.js';
6
+ import { spaceSchemas } from './space-schema.js';
8
7
  import { sha256Hex } from './digest.js';
9
8
  import { finishedUpload, houseUpload } from './upload.js';
10
9
  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';
10
+ import { houseSearch } from './search.js';
11
+ import { classifyDoor, doorRefusal } from './refusal.js';
12
+ import { houseNotifications, } from './stream.js';
13
+ import { houseDoorRoute, } from './transport.js';
14
14
  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';
15
+ import { HOUSE_TYPED_PATHS } from './house-model.js';
16
+ import { HOUSE_NAMEABLE_ROOM_TYPES, HOUSE_ROOM_TYPES } from './room-types.js';
17
+ import { followsVersion, logPosition, presented, reachedVersion, roomVersion } from './version.js';
17
18
  const UNCERTAIN = { kind: 'transport-uncertainty' };
18
19
  const UNCERTAIN_READ = { kind: 'transport-uncertainty' };
20
+ const OCTET_STREAM = 'application/octet-stream';
19
21
  const EXPIRED_ADDRESS = 403;
20
22
  function lapsedGrant(outcome) {
21
23
  return outcome.kind === 'rejected' && outcome.code === 'upload_unavailable';
@@ -50,161 +52,86 @@ export function createAgentsHouseAdapter(options) {
50
52
  if ('site' in options)
51
53
  return createSiteAdapter(options.site, options.snapshot === true);
52
54
  const transport = options.transport;
53
- const notifications = houseNotifications(transport, 'credential');
55
+ const spaces = spaceSchemas();
56
+ const notifications = houseNotifications(transport, 'credential', {
57
+ opened: async (frame) => (frame === null ? null : opened(frame)),
58
+ changed,
59
+ }, spaces);
54
60
  const tracked = new Map();
55
- const handed = new Map();
56
- let heldRevisions;
61
+ const rooms = roomStreams('credential', spaces);
62
+ const unreused = new Set();
57
63
  let bounds;
58
64
  function declared() {
59
65
  if (bounds === undefined)
60
66
  throw new Error('no Discovery declared the bounds');
61
67
  return bounds;
62
68
  }
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);
69
+ function opened(frame) {
70
+ const document = parseDiscovery(frame);
71
+ if (document === null)
72
+ return null;
73
+ bounds = document.bounds;
74
+ tracked.clear();
75
+ for (const entry of trackedFrom(document))
76
+ tracked.set(entry.locator, entry);
77
+ notifications.track([...tracked.values()]);
78
+ return {
79
+ ...mapDiscovery(document, document.authorities.map((authority) => discoveredRoom(authority)), capabilities(document.actions)),
80
+ member: document.member,
81
+ };
87
82
  }
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);
83
+ function changed(frame) {
84
+ const change = parseAuthorityChange(frame);
85
+ if (change === null)
86
+ return null;
87
+ for (const locator of change.left)
88
+ tracked.delete(locator);
89
+ for (const entry of trackedFrom(change))
90
+ tracked.set(entry.locator, entry);
91
+ notifications.track([...tracked.values()]);
92
+ return {
93
+ kind: 'authority-changed',
94
+ rooms: change.authorities.map((authority) => discoveredRoom(authority)),
95
+ left: change.left,
96
+ member: change.member,
97
+ };
96
98
  }
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);
99
+ async function streamedPage(entry, token, held) {
100
+ const part = await rooms.part(entry, token);
101
+ if ('kind' in part)
102
+ return part;
103
+ const scope = await reused(part.scope, held);
106
104
  if (scope === null) {
107
- return asked.held === undefined
108
- ? UNCERTAIN_READ
109
- : bootstrapTogether({ entry, held: undefined });
105
+ unreused.add(entry.locator);
106
+ return UNCERTAIN_READ;
110
107
  }
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);
123
108
  return {
124
109
  ...(await stepped(bootstrapping(scope))),
125
- ...(models === undefined ? {} : { models }),
126
- ...(more ? { pageToken: JSON.stringify({ ...continued, next, seen }) } : {}),
127
- ...(sent.frame === null ? {} : { received: sent.frame }),
110
+ ...(part.models === undefined ? {} : { models: part.models }),
111
+ ...(part.pageToken === undefined ? {} : { pageToken: part.pageToken }),
112
+ bytes: part.bytes,
128
113
  };
129
114
  }
130
- async function enumeratedPage(ask, page) {
131
- const answer = await transport.door(DOOR_ENUMERATE, {
132
- prefixes: [authorityRoot(ask.entry.handle)],
133
- positions: [
134
- {
135
- ...presented(ask.entry.authority, { position: page.after, logEpoch: ask.from.logEpoch }),
136
- page: { through: page.through, read: page.read },
137
- },
138
- ],
139
- });
140
- const failure = classifyRead(answer.status, answer.body, ask.entry.locator);
141
- if (failure !== null)
142
- return failure;
143
- const enumeration = parseEnumeration(answer.body, 'credential');
144
- if (enumeration === null)
145
- return UNCERTAIN_READ;
146
- return (viewOf(enumeration, ask.entry) ?? { kind: 'room-not-readable', locator: ask.entry.locator });
147
- }
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 };
162
- }
163
- async function streamed(entry, request, from) {
164
- if (handed.get(entry.locator) !== request.fromRoomVersion)
165
- 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)
172
- 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 };
115
+ async function openedRoom(entry, request) {
116
+ const reusing = unreused.delete(entry.locator) ? undefined : request.held;
117
+ const files = reusing === undefined
118
+ ? null
119
+ : await heldFiles(entry.authority, reusing, declared().max_held_files);
120
+ const answered = await transport.streamed(houseDoorRoute('bootstrap'), {
121
+ room_ref: entry.authority,
122
+ prefixes: [authorityRoot(entry.handle)],
123
+ ...(files === null ? {} : { held: [files] }),
124
+ }, request.signal ?? new AbortController().signal);
125
+ if (answered.room === null)
126
+ return classifyDoor(answered.status, answered.body, entry.locator) ?? UNCERTAIN_READ;
127
+ return rooms.head(entry, answered.room, request.arriving, request.signal);
176
128
  }
177
129
  function held(rooms) {
178
- const kept = rooms.flatMap((room) => {
130
+ notifications.send(rooms.flatMap((room) => {
179
131
  const entry = tracked.get(room.locator);
180
132
  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);
188
- }
189
- async function adoptedMove(entry, into, outcome) {
190
- if (outcome.kind === 'accepted' && outcome.destination !== undefined) {
191
- await adopted(into, { kind: 'accepted', ...outcome.destination });
192
- }
193
- return adopted(entry, outcome);
194
- }
195
- async function adopted(entry, outcome) {
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;
133
+ return entry === undefined || at === null ? [] : [presented(entry.authority, at)];
134
+ }));
208
135
  }
209
136
  function authorityFor(locator) {
210
137
  return tracked.get(locator);
@@ -223,116 +150,18 @@ export function createAgentsHouseAdapter(options) {
223
150
  return result;
224
151
  return { ...result, at: { locator, roomVersion: roomVersion({ position, logEpoch }) } };
225
152
  }
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
153
  async function uploaded(head, changes, delta, bytes, entry, handed, keep) {
330
154
  const paths = batchPaths(changes);
331
155
  const sha256 = await deltaDigest(delta);
332
156
  if (handed !== undefined && handed.sha256 === sha256) {
333
157
  return { body: { ...head, paths, upload: handed, runtime: notifications.runtime } };
334
158
  }
335
- const minted = await transport.door(DOOR_BATCH_UPLOAD, { ...head, paths, bytes, sha256 });
159
+ const minted = await transport.door(houseDoorRoute('batch-upload'), {
160
+ ...head,
161
+ paths,
162
+ bytes,
163
+ sha256,
164
+ });
336
165
  if (minted.status !== 200) {
337
166
  return {
338
167
  outcome: await stepped(mutationOutcome(minted.status, minted.body, entry.locator, entry.handle)),
@@ -380,11 +209,11 @@ export function createAgentsHouseAdapter(options) {
380
209
  : { body: inline };
381
210
  if ('outcome' in prepared)
382
211
  return prepared.outcome;
383
- const answer = await transport.door(DOOR_BATCH, prepared.body);
212
+ const answer = await transport.door(houseDoorRoute('batch'), prepared.body);
384
213
  const stagedFiles = await staged(answer, request);
385
214
  if (stagedFiles === false)
386
215
  return UNCERTAIN;
387
- const settled = stagedFiles === null ? answer : await transport.door(DOOR_BATCH, prepared.body);
216
+ const settled = stagedFiles === null ? answer : await transport.door(houseDoorRoute('batch'), prepared.body);
388
217
  if (stagedFiles === true && (await staged(settled, request)) !== null)
389
218
  return UNCERTAIN;
390
219
  return stepped(mutationOutcome(settled.status, settled.body, entry.locator, entry.handle, into === undefined
@@ -412,9 +241,22 @@ export function createAgentsHouseAdapter(options) {
412
241
  function linked(answer, locator) {
413
242
  const address = addressed(answer, locator);
414
243
  return address.kind === 'address'
415
- ? { kind: 'link', url: address.url, name: address.name }
244
+ ? { kind: 'link', url: address.url, name: address.name, mediaType: address.mediaType }
416
245
  : address;
417
246
  }
247
+ function refusedAttach(answer, locator) {
248
+ const failure = classifyDoor(answer.status, answer.body, locator);
249
+ if (failure === null)
250
+ return null;
251
+ return failure.kind === 'scoped-refusal'
252
+ ? {
253
+ kind: 'rejected',
254
+ code: failure.code,
255
+ fields: failure.fields,
256
+ retryable: failure.retryable,
257
+ }
258
+ : failure;
259
+ }
418
260
  async function downloaded(ask, locator) {
419
261
  let address = addressed(await ask(), locator);
420
262
  if (address.kind !== 'address')
@@ -439,7 +281,7 @@ export function createAgentsHouseAdapter(options) {
439
281
  const entry = authorityFor(request.locator);
440
282
  if (entry === undefined)
441
283
  return null;
442
- return () => transport.door(DOOR_FILES_GET, {
284
+ return () => transport.door(houseDoorRoute('files/get'), {
443
285
  room_ref: entry.authority,
444
286
  path: request.path,
445
287
  revision: request.revision,
@@ -447,72 +289,18 @@ export function createAgentsHouseAdapter(options) {
447
289
  }
448
290
  return {
449
291
  roomTypes: HOUSE_ROOM_TYPES,
292
+ nameableRoomTypes: HOUSE_NAMEABLE_ROOM_TYPES,
293
+ typedPaths: HOUSE_TYPED_PATHS,
450
294
  async authorize() {
451
295
  return options.authorize();
452
296
  },
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
297
  async bootstrap(_authorization, request) {
494
298
  const entry = authorityFor(request.locator);
495
299
  if (entry === undefined)
496
300
  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 });
301
+ return request.pageToken === undefined
302
+ ? openedRoom(entry, request)
303
+ : streamedPage(entry, request.pageToken, request.held);
516
304
  },
517
305
  async mutate(_authorization, request, keep) {
518
306
  const entry = authorityFor(request.locator);
@@ -529,22 +317,21 @@ export function createAgentsHouseAdapter(options) {
529
317
  : houseMove(batch, entry.handle, into.handle);
530
318
  if (mapped.kind === 'unmapped')
531
319
  return rejectedMapping(mapped);
532
- const head = operationHead(batch);
320
+ const head = operationHead(batch, entry.authority);
533
321
  const delta = deltaBody(mapped.changes);
534
322
  const bytes = deltaBytes(delta);
535
323
  const handed = request.upload;
536
324
  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
325
  const outcome = await answer(handed);
539
326
  if (handed === undefined || !lapsedGrant(outcome))
540
- return settle(outcome);
541
- return settle(await answer(undefined));
327
+ return outcome;
328
+ return answer(undefined);
542
329
  },
543
330
  async history(_authorization, request) {
544
331
  const entry = authorityFor(request.locator);
545
332
  if (entry === undefined)
546
333
  return { kind: 'room-not-readable', locator: request.locator };
547
- const answer = await transport.door(DOOR_REVISIONS, revisionBody({
334
+ const answer = await transport.door(houseDoorRoute('revisions'), revisionBody({
548
335
  path: request.path,
549
336
  handle: entry.handle,
550
337
  ...(request.revision === undefined ? {} : { revision: request.revision }),
@@ -567,6 +354,35 @@ export function createAgentsHouseAdapter(options) {
567
354
  return { kind: 'room-not-readable', locator: request.locator };
568
355
  return linked(await ask(), request.locator);
569
356
  },
357
+ async attach(_authorization, request) {
358
+ const entry = authorityFor(request.locator);
359
+ if (entry === undefined)
360
+ return { kind: 'room-not-readable', locator: request.locator };
361
+ const declared = await transport.door(houseDoorRoute('files/attach'), {
362
+ room_ref: entry.authority,
363
+ name: request.name,
364
+ bytes: request.byteLength,
365
+ sha256: request.sha256,
366
+ media_type: request.mediaType.length > 0 ? request.mediaType : OCTET_STREAM,
367
+ });
368
+ const refused = refusedAttach(declared, request.locator);
369
+ if (refused !== null)
370
+ return refused;
371
+ const upload = houseUpload(declared.body?.upload);
372
+ if (upload === null)
373
+ return UNCERTAIN_READ;
374
+ const finished = await finishedUpload(transport, upload, request.blob);
375
+ if (finished === null)
376
+ return UNCERTAIN_READ;
377
+ const unfinished = refusedAttach(finished, request.locator);
378
+ if (unfinished !== null)
379
+ return unfinished;
380
+ const { path, revision } = (finished.body ?? {});
381
+ const root = `/rooms/${entry.handle}/`;
382
+ return typeof path === 'string' && path.startsWith(root) && typeof revision === 'string'
383
+ ? { kind: 'attached', path: path.slice(root.length), revision }
384
+ : UNCERTAIN_READ;
385
+ },
570
386
  reached(locator, version, target) {
571
387
  return reachedVersion(version, target, authorityFor(locator)?.logEpoch);
572
388
  },
@@ -576,18 +392,12 @@ export function createAgentsHouseAdapter(options) {
576
392
  async action(_authorization, request) {
577
393
  return withAt(await houseAction(transport, request));
578
394
  },
395
+ search(_authorization, request) {
396
+ return houseSearch(transport, [...tracked.values()], request);
397
+ },
579
398
  roomStream: {
580
399
  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
- },
400
+ observe: notifications.observe,
591
401
  },
592
402
  conversations: notifications.conversations,
593
403
  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})$`);
@@ -0,0 +1,4 @@
1
+ export declare const CROCKFORD_ALPHABET = "0123456789abcdefghjkmnpqrstvwxyz";
2
+ export declare const CROCKFORD_CHARACTER = "[0123456789abcdefghjkmnpqrstvwxyz]";
3
+ export declare const CROCKFORD_LEADING_CHARACTER: string;
4
+ //# sourceMappingURL=crockford.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crockford.d.ts","sourceRoot":"","sources":["../src/crockford.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,kBAAkB,qCAAqC,CAAC;AAErE,eAAO,MAAM,mBAAmB,uCAA4B,CAAC;AAE7D,eAAO,MAAM,2BAA2B,QAAqC,CAAC"}
@@ -0,0 +1,3 @@
1
+ export const CROCKFORD_ALPHABET = '0123456789abcdefghjkmnpqrstvwxyz';
2
+ export const CROCKFORD_CHARACTER = `[${CROCKFORD_ALPHABET}]`;
3
+ export const CROCKFORD_LEADING_CHARACTER = `[${CROCKFORD_ALPHABET.slice(1)}]`;