@agentshouse/mdruntime-adapter 0.1.0-cae3b43168023 → 0.1.0-cb0bbc4ccacd0

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