@agentshouse/mdruntime-adapter 0.1.0-ce5ef9afd7e9a → 0.1.0-ce752a56d9714

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