@agentshouse/mdruntime-adapter 0.1.0-cfd03209dcd82 → 0.1.0-cfd2dc1938a2c

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 (78) hide show
  1. package/README.md +2 -2
  2. package/dist/actions.d.ts +6 -46
  3. package/dist/actions.d.ts.map +1 -1
  4. package/dist/actions.js +23 -342
  5. package/dist/adapter.d.ts +1 -0
  6. package/dist/adapter.d.ts.map +1 -1
  7. package/dist/adapter.js +250 -278
  8. package/dist/app-reference.d.ts +2 -0
  9. package/dist/app-reference.d.ts.map +1 -0
  10. package/dist/app-reference.js +2 -0
  11. package/dist/authored-path.d.ts +11 -0
  12. package/dist/authored-path.d.ts.map +1 -0
  13. package/dist/authored-path.js +35 -0
  14. package/dist/chunks.d.ts +38 -0
  15. package/dist/chunks.d.ts.map +1 -0
  16. package/dist/chunks.js +247 -0
  17. package/dist/crockford.d.ts +4 -0
  18. package/dist/crockford.d.ts.map +1 -0
  19. package/dist/crockford.js +3 -0
  20. package/dist/discovery.d.ts +23 -14
  21. package/dist/discovery.d.ts.map +1 -1
  22. package/dist/discovery.js +92 -54
  23. package/dist/history.d.ts +12 -0
  24. package/dist/history.d.ts.map +1 -0
  25. package/dist/history.js +92 -0
  26. package/dist/house-model.d.ts +682 -0
  27. package/dist/house-model.d.ts.map +1 -0
  28. package/dist/house-model.js +397 -0
  29. package/dist/index.d.ts +12 -2
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +11 -1
  32. package/dist/model.d.ts +5 -5
  33. package/dist/model.d.ts.map +1 -1
  34. package/dist/model.js +33 -43
  35. package/dist/read.d.ts +59 -44
  36. package/dist/read.d.ts.map +1 -1
  37. package/dist/read.js +158 -236
  38. package/dist/refusal.d.ts +1 -3
  39. package/dist/refusal.d.ts.map +1 -1
  40. package/dist/refusal.js +6 -6
  41. package/dist/room-types.d.ts +5 -0
  42. package/dist/room-types.d.ts.map +1 -0
  43. package/dist/room-types.js +120 -0
  44. package/dist/search.d.ts +5 -0
  45. package/dist/search.d.ts.map +1 -0
  46. package/dist/search.js +47 -0
  47. package/dist/site.d.ts +1 -1
  48. package/dist/site.d.ts.map +1 -1
  49. package/dist/site.js +110 -131
  50. package/dist/space-schema.d.ts +7 -0
  51. package/dist/space-schema.d.ts.map +1 -0
  52. package/dist/space-schema.js +18 -0
  53. package/dist/stream.d.ts +15 -13
  54. package/dist/stream.d.ts.map +1 -1
  55. package/dist/stream.js +279 -159
  56. package/dist/transport.d.ts +81 -25
  57. package/dist/transport.d.ts.map +1 -1
  58. package/dist/transport.js +54 -11
  59. package/dist/upload.d.ts +12 -0
  60. package/dist/upload.d.ts.map +1 -0
  61. package/dist/upload.js +63 -0
  62. package/dist/version.d.ts +1 -0
  63. package/dist/version.d.ts.map +1 -1
  64. package/dist/version.js +1 -1
  65. package/dist/write.d.ts +18 -2
  66. package/dist/write.d.ts.map +1 -1
  67. package/dist/write.js +108 -76
  68. package/package.json +8 -4
  69. package/AGENTS.md +0 -299
  70. package/dist/gather.d.ts +0 -4
  71. package/dist/gather.d.ts.map +0 -1
  72. package/dist/gather.js +0 -45
  73. package/dist/handed.d.ts +0 -15
  74. package/dist/handed.d.ts.map +0 -1
  75. package/dist/handed.js +0 -38
  76. package/dist/referrer.d.ts +0 -7
  77. package/dist/referrer.d.ts.map +0 -1
  78. package/dist/referrer.js +0 -35
package/dist/adapter.js CHANGED
@@ -1,102 +1,124 @@
1
- import { DISCOVER_PREFIXES, capabilities, discoveredRoom, mapDiscovery, parseDiscovery, trackedFrom, } from './discovery.js';
2
- import { partModels, streamedModels, viewModels } from './model.js';
3
- import { gathered, gatheredEach, selections } from './gather.js';
1
+ import { stepped } from '@agentshouse/mdruntime';
2
+ import { capabilities, discoveredRoom, mapDiscovery, parseAuthorityChange, parseDiscovery, trackedFrom, } from './discovery.js';
4
3
  import { createSiteAdapter } from './site.js';
5
- import { bootstrapOf, contentPrefixes, matchesScope, parseEnumeration, parseEnvelope, parseGrant, refreshFrom, viewOf, } from './read.js';
6
- import { acceptedHanded, enumeratedHanded, heldHanded, scopeHanded, streamedHanded, } from './handed.js';
7
- import { houseAction } from './actions.js';
8
- import { classifyDoor, classifyRead, discoveryFailure, doorRefusal } from './refusal.js';
9
- import { houseNotifications } from './stream.js';
10
- import { DOOR_BATCH, DOOR_BATCH_UPLOAD, DOOR_BOOTSTRAP, DOOR_DISCOVER, DOOR_ENUMERATE, DOOR_FILES_GET, DOOR_REVISIONS, } from './transport.js';
11
- import { batchPaths, deltaBody, deltaBytes, deltaDigest, historyOutcome, houseChanges, mutationOutcome, operationHead, rejectedMapping, revisionBody, uploadable, } from './write.js';
12
- import { houseReferrer } from './referrer.js';
13
- import { followsVersion, logPosition, presented, reachedVersion, roomVersion, } from './version.js';
4
+ import { parseFileAddress, parseGrant } from './read.js';
5
+ import { chunkedHead, chunkLister, chunkPart } from './chunks.js';
6
+ import { spaceSchemas } from './space-schema.js';
7
+ import { finishedUpload, houseUpload } from './upload.js';
8
+ import { houseAction, HOUSE_LEADING_READS, HOUSE_UNKEPT_READS } from './actions.js';
9
+ import { houseSearch } from './search.js';
10
+ import { classifyDoor, doorRefusal } from './refusal.js';
11
+ import { houseNotifications, } from './stream.js';
12
+ import { houseDoorRoute, } from './transport.js';
13
+ import { batchPaths, deltaBody, deltaBytes, deltaDigest, historyOutcome, houseChanges, houseMove, mutationOutcome, operationHead, rejectedMapping, revisionBody, uploadable, } from './write.js';
14
+ import { admittedPath } from './authored-path.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, reachedVersion, roomVersion } from './version.js';
14
18
  const UNCERTAIN = { kind: 'transport-uncertainty' };
15
19
  const UNCERTAIN_READ = { kind: 'transport-uncertainty' };
20
+ const OCTET_STREAM = 'application/octet-stream';
21
+ const EXPIRED_ADDRESS = 403;
16
22
  function lapsedGrant(outcome) {
17
23
  return outcome.kind === 'rejected' && outcome.code === 'upload_unavailable';
18
24
  }
19
25
  export function createAgentsHouseAdapter(options) {
20
26
  if ('site' in options)
21
- return createSiteAdapter(options.site);
27
+ return createSiteAdapter(options.site, options.snapshot === true);
22
28
  const transport = options.transport;
23
- const notifications = houseNotifications(transport, 'credential');
29
+ const spaces = spaceSchemas();
30
+ const notifications = houseNotifications(transport, 'credential', {
31
+ opened: async (frame) => (frame === null ? null : opened(frame)),
32
+ changed,
33
+ expired: (authorities) => lister.askAll(authorities.flatMap((authority) => [...tracked.values()].filter((entry) => entry.authority === authority))),
34
+ }, spaces);
24
35
  const tracked = new Map();
25
- const handed = new Map();
36
+ const lister = chunkLister(transport, () => declared().max_prefixes);
37
+ const chunked = new Map();
38
+ const renewals = new WeakMap();
26
39
  let bounds;
27
40
  function declared() {
28
41
  if (bounds === undefined)
29
42
  throw new Error('no Discovery declared the bounds');
30
43
  return bounds;
31
44
  }
32
- async function bootstrappedRoom(envelope, scope, entry, continued) {
33
- if (scope === undefined)
34
- return { kind: 'room-not-readable', locator: entry.locator };
35
- const next = envelope.next?.room === entry.authority ? envelope.next.path : null;
36
- if (continued === null && next === null)
37
- handed.set(entry.locator, scopeHanded(scope));
38
- else
39
- handed.delete(entry.locator);
40
- notifications.reach(entry.authority, scope);
41
- const seen = [
42
- ...(continued?.seen ?? []),
43
- ...entry.schemas.filter((schema) => scope.files.some((file) => file.path === schema)),
44
- ];
45
- const models = await partModels(scope, entry, new Set(seen), next === null);
46
- const continuation = next === null ? null : { after: next, seen };
45
+ function opened(frame) {
46
+ const document = parseDiscovery(frame);
47
+ if (document === null)
48
+ return null;
49
+ bounds = document.bounds;
50
+ tracked.clear();
51
+ lister.forget();
52
+ for (const entry of trackedFrom(document))
53
+ tracked.set(entry.locator, entry);
54
+ notifications.track([...tracked.values()]);
55
+ return {
56
+ ...mapDiscovery(document, document.authorities.map((authority) => discoveredRoom(authority)), capabilities(document.actions)),
57
+ member: document.member,
58
+ };
59
+ }
60
+ function changed(frame) {
61
+ const change = parseAuthorityChange(frame);
62
+ if (change === null)
63
+ return null;
64
+ for (const locator of change.left)
65
+ tracked.delete(locator);
66
+ for (const entry of trackedFrom(change))
67
+ tracked.set(entry.locator, entry);
68
+ notifications.track([...tracked.values()]);
47
69
  return {
48
- ...bootstrapOf(scope),
49
- ...(models === undefined ? {} : { models }),
50
- ...(continuation === null ? {} : { pageToken: JSON.stringify(continuation) }),
70
+ kind: 'authority-changed',
71
+ rooms: change.authorities.map((authority) => discoveredRoom(authority)),
72
+ left: change.left,
73
+ member: change.member,
51
74
  };
52
75
  }
53
- async function enumeratedRoom(enumeration, ask) {
54
- const view = viewOf(enumeration, ask.entry);
55
- if (view === undefined)
56
- return { kind: 'room-not-readable', locator: ask.entry.locator };
57
- notifications.reach(ask.entry.authority, view);
58
- const answer = refreshFrom(view, ask.fromRoomVersion);
59
- if (answer.kind !== 'net-advancement')
60
- return answer;
61
- handed.set(ask.entry.locator, enumeratedHanded(view, handed.get(ask.entry.locator)));
62
- const models = await viewModels(view, ask.entry);
63
- return models === undefined ? answer : { ...answer, models };
76
+ async function listedRoom(entry) {
77
+ const listed = await lister.list(entry);
78
+ if ('kind' in listed)
79
+ return listed;
80
+ const head = await chunkedHead(listed, entry, spaces);
81
+ if (head.kind === 'bootstrap')
82
+ chunked.set(entry.locator, listed);
83
+ return head;
64
84
  }
65
- async function streamed(entry, request, from) {
66
- const held = handed.get(entry.locator);
67
- if (held?.roomVersion !== request.fromRoomVersion)
85
+ async function chunkRead(entry, id) {
86
+ const room = chunked.get(entry.locator);
87
+ const address = room?.chunks.get(id);
88
+ if (room === undefined || address === undefined)
68
89
  return null;
69
- const advanced = notifications.advance(entry, from, (path, revision) => held.revisions.get(path) === revision);
70
- if (advanced === null)
90
+ const read = await transport.download(address.url);
91
+ if (read.status !== EXPIRED_ADDRESS)
92
+ return { room, read };
93
+ const renewed = await renewal(entry, room);
94
+ if ('kind' in renewed || !renewed.chunks.has(id))
71
95
  return null;
72
- handed.set(entry.locator, streamedHanded(held, advanced));
73
- const models = await streamedModels(advanced.schemas, entry, 'credential');
74
- return models === undefined ? advanced.page : { ...advanced.page, models };
96
+ chunked.set(entry.locator, renewed);
97
+ return { room: renewed, read: await transport.download(renewed.chunks.get(id).url) };
75
98
  }
76
- function held(rooms) {
77
- const kept = rooms.flatMap((room) => {
78
- const entry = tracked.get(room.locator);
79
- const at = logPosition(room.roomVersion);
80
- if (entry === undefined || at === null)
81
- return [];
82
- handed.set(room.locator, heldHanded(room));
83
- notifications.reach(entry.authority, at);
84
- return [presented(entry.authority, at)];
85
- });
86
- notifications.send(kept);
99
+ function renewal(entry, stale) {
100
+ const held = renewals.get(stale);
101
+ if (held !== undefined)
102
+ return held;
103
+ const renewed = lister.renew(entry, [...chunked.keys()].flatMap((locator) => tracked.get(locator) ?? []));
104
+ renewals.set(stale, renewed);
105
+ return renewed;
106
+ }
107
+ function hold(rooms) {
108
+ for (const room of rooms) {
109
+ const listed = chunked.get(room.locator);
110
+ if (listed !== undefined &&
111
+ room.roomVersion !== null &&
112
+ reachedVersion(room.roomVersion, roomVersion(listed), listed.logEpoch))
113
+ chunked.delete(room.locator);
114
+ }
115
+ notifications.send(rooms);
87
116
  }
88
- function adopted(entry, outcome) {
89
- if (outcome.kind !== 'accepted')
90
- return outcome;
91
- const held = handed.get(entry.locator);
92
- if (held === undefined)
93
- return outcome;
94
- const follows = followsVersion(held.roomVersion, outcome.roomVersion);
95
- handed.set(entry.locator, acceptedHanded(held, outcome, follows ? outcome.roomVersion : held.roomVersion));
96
- const at = logPosition(outcome.roomVersion);
97
- if (follows && at !== null)
98
- notifications.reach(entry.authority, at);
99
- return outcome;
117
+ async function chunkPage(entry, id, arriving) {
118
+ const fetched = await chunkRead(entry, id);
119
+ if (fetched === null || fetched.read.status !== 200)
120
+ return UNCERTAIN_READ;
121
+ return chunkPart(fetched.read.body, fetched.room, id, arriving);
100
122
  }
101
123
  function authorityFor(locator) {
102
124
  return tracked.get(locator);
@@ -115,114 +137,25 @@ export function createAgentsHouseAdapter(options) {
115
137
  return result;
116
138
  return { ...result, at: { locator, roomVersion: roomVersion({ position, logEpoch }) } };
117
139
  }
118
- function weight(entry) {
119
- return contentPrefixes(entry).length;
120
- }
121
- async function bootstrapped(entries, answer) {
122
- let open = entries.map((entry, ask) => ({ entry, ask }));
123
- while (open.length > 0) {
124
- const unreached = [];
125
- for (const part of selections(open, ({ entry }) => weight(entry), declared().max_prefixes)) {
126
- const answered = await transport.door(DOOR_BOOTSTRAP, {
127
- prefixes: part.flatMap(({ entry }) => contentPrefixes(entry)),
128
- });
129
- const classified = part.map(({ entry }) => classifyDoor(answered.status, answered.body, entry.locator));
130
- const envelope = classified[0] === null ? parseEnvelope(answered.body) : null;
131
- for (const [index, asked] of part.entries()) {
132
- const failure = classified[index];
133
- if (failure !== null && failure !== undefined) {
134
- answer(asked.ask, failure);
135
- continue;
136
- }
137
- if (envelope === null) {
138
- answer(asked.ask, UNCERTAIN_READ);
139
- continue;
140
- }
141
- const scope = envelope.scopes.find((candidate) => matchesScope(candidate, asked.entry));
142
- if (scope === undefined && envelope.next !== undefined) {
143
- unreached.push(asked);
144
- continue;
145
- }
146
- answer(asked.ask, await bootstrappedRoom(envelope, scope, asked.entry, null));
147
- }
148
- }
149
- open = unreached;
150
- }
151
- }
152
- async function continuedBootstrap(entry, continued) {
153
- const after = { room: entry.authority, path: continued.after };
154
- const answered = await transport.door(DOOR_BOOTSTRAP, {
155
- prefixes: contentPrefixes(entry),
156
- after,
157
- });
158
- const failure = classifyDoor(answered.status, answered.body, entry.locator);
159
- if (failure !== null)
160
- return failure;
161
- const envelope = parseEnvelope(answered.body);
162
- if (envelope === null)
163
- return UNCERTAIN_READ;
164
- const scope = envelope.scopes.find((candidate) => matchesScope(candidate, entry));
165
- return bootstrappedRoom(envelope, scope, entry, continued);
166
- }
167
- function layered(asks) {
168
- const depths = new Map();
169
- const layers = [];
170
- for (const ask of asks) {
171
- const depth = depths.get(ask.entry.authority) ?? 0;
172
- depths.set(ask.entry.authority, depth + 1);
173
- (layers[depth] ??= []).push(ask);
174
- }
175
- return layers;
176
- }
177
- async function enumerated(asks) {
178
- const answers = new Map();
179
- const parts = layered(asks).flatMap((layer) => selections(layer, (ask) => weight(ask.entry), declared().max_prefixes));
180
- for (const part of parts) {
181
- let open = [...part];
182
- while (open.length > 0) {
183
- const answer = await transport.door(DOOR_ENUMERATE, {
184
- prefixes: open.flatMap((ask) => contentPrefixes(ask.entry)),
185
- positions: open.map((ask) => presented(ask.entry.authority, ask.from)),
186
- });
187
- const classified = open.map((ask) => classifyRead(answer.status, answer.body, ask.entry.locator));
188
- if (classified[0]?.kind === 'beyond-horizon') {
189
- const named = doorRefusal(answer.body)?.fields.authority;
190
- const expired = open.filter((ask) => ask.entry.authority === named);
191
- const answered = expired.length === 0 ? UNCERTAIN_READ : { kind: 'beyond-horizon' };
192
- const gone = expired.length === 0 ? open : expired;
193
- for (const ask of gone)
194
- answers.set(ask, answered);
195
- open = open.filter((ask) => !gone.includes(ask));
196
- continue;
197
- }
198
- const enumeration = classified[0] === null ? parseEnumeration(answer.body, 'credential') : null;
199
- for (const [index, ask] of open.entries()) {
200
- const failure = classified[index];
201
- if (failure !== null && failure !== undefined) {
202
- answers.set(ask, failure);
203
- continue;
204
- }
205
- answers.set(ask, enumeration === null ? UNCERTAIN_READ : await enumeratedRoom(enumeration, ask));
206
- }
207
- open = [];
208
- }
209
- }
210
- return asks.map((ask) => answers.get(ask) ?? UNCERTAIN_READ);
211
- }
212
- const bootstrapTogether = gatheredEach(bootstrapped);
213
- const enumerateTogether = gathered(enumerated);
214
140
  async function uploaded(head, changes, delta, bytes, entry, handed, keep) {
215
141
  const paths = batchPaths(changes);
216
142
  const sha256 = await deltaDigest(delta);
217
143
  if (handed !== undefined && handed.sha256 === sha256) {
218
144
  return { body: { ...head, paths, upload: handed, runtime: notifications.runtime } };
219
145
  }
220
- const minted = await transport.door(DOOR_BATCH_UPLOAD, { ...head, paths, bytes, sha256 });
146
+ const minted = await transport.door(houseDoorRoute('files/batch-upload'), {
147
+ ...head,
148
+ paths,
149
+ bytes,
150
+ sha256,
151
+ });
221
152
  if (minted.status !== 200) {
222
- return { outcome: mutationOutcome(minted.status, minted.body, entry.locator, entry.handle) };
153
+ return {
154
+ outcome: await stepped(mutationOutcome(minted.status, minted.body, entry.locator, entry.handle)),
155
+ };
223
156
  }
224
157
  const grant = parseGrant(minted.body);
225
- if (grant === null || grant.method !== 'POST')
158
+ if (grant === null)
226
159
  return { outcome: UNCERTAIN };
227
160
  const delivered = await transport.transfer({
228
161
  act: 'authored_batch_upload',
@@ -232,52 +165,54 @@ export function createAgentsHouseAdapter(options) {
232
165
  if (delivered.status !== 200)
233
166
  return { outcome: UNCERTAIN };
234
167
  const holding = parseGrant(delivered.body);
235
- if (holding === null || holding.method !== 'POST')
168
+ if (holding === null)
236
169
  return { outcome: UNCERTAIN };
237
170
  const upload = { url: holding.url, operation: holding.operation, bytes, sha256 };
238
171
  await keep(upload);
239
172
  return { body: { ...head, paths, upload, runtime: notifications.runtime } };
240
173
  }
241
- async function staged(answer, batch) {
174
+ async function staged(answer, { changeBatch: batch, file }) {
242
175
  const refusal = doorRefusal(answer.body);
243
176
  if (refusal?.code !== 'files_unstaged')
244
177
  return null;
245
- const grants = refusal.fields.upload;
246
- if (!Array.isArray(grants))
178
+ const uploads = refusal.fields.upload;
179
+ if (!Array.isArray(uploads))
247
180
  return false;
248
- for (const grant of grants) {
249
- const { url, operation, sha256 } = grant;
250
- const change = batch.changes.find((held) => held.kind === 'binary' && held.sha256 === sha256);
251
- if (typeof url !== 'string' || typeof operation !== 'string' || change === undefined)
181
+ for (const named of uploads) {
182
+ const upload = houseUpload(named);
183
+ const change = batch.changes.find((held) => held.kind === 'binary' && held.sha256 === named.sha256);
184
+ if (upload === null || change === undefined)
252
185
  return false;
253
- const delivered = await transport.transfer({
254
- act: 'binary_stage',
255
- url,
256
- body: change.blob,
257
- operation,
258
- });
259
- if (delivered.status !== 200)
186
+ const finished = await finishedUpload(transport, upload, await file(change.localId));
187
+ if (finished?.body?.staged !== true)
260
188
  return false;
261
189
  }
262
190
  return true;
263
191
  }
264
- async function submit(batch, head, changes, delta, bytes, entry, handed, keep) {
192
+ async function submit(request, head, changes, delta, bytes, entry, handed, keep, into) {
265
193
  const inline = { ...head, changes, runtime: notifications.runtime };
266
194
  const prepared = uploadable(inline, bytes, declared())
267
195
  ? await uploaded(head, changes, delta, bytes, entry, handed, keep)
268
196
  : { body: inline };
269
197
  if ('outcome' in prepared)
270
198
  return prepared.outcome;
271
- const answer = await transport.door(DOOR_BATCH, prepared.body);
272
- const stagedFiles = await staged(answer, batch);
199
+ const answer = await transport.door(houseDoorRoute('files/batch'), prepared.body);
200
+ const stagedFiles = await staged(answer, request);
273
201
  if (stagedFiles === false)
274
202
  return UNCERTAIN;
275
- const settled = stagedFiles === null ? answer : await transport.door(DOOR_BATCH, prepared.body);
276
- if (stagedFiles === true && (await staged(settled, batch)) !== null)
203
+ const settled = stagedFiles === null
204
+ ? answer
205
+ : await transport.door(houseDoorRoute('files/batch'), prepared.body);
206
+ if (stagedFiles === true && (await staged(settled, request)) !== null)
277
207
  return UNCERTAIN;
278
- return mutationOutcome(settled.status, settled.body, entry.locator, entry.handle);
208
+ return stepped(mutationOutcome(settled.status, settled.body, entry.locator, entry.handle, into === undefined
209
+ ? undefined
210
+ : {
211
+ source: { authority: entry.authority, handle: entry.handle },
212
+ destination: { authority: into.authority, handle: into.handle },
213
+ }));
279
214
  }
280
- async function downloaded(answer, locator) {
215
+ function addressed(answer, locator) {
281
216
  const failure = classifyDoor(answer.status, answer.body, locator);
282
217
  if (failure !== null) {
283
218
  return failure.kind === 'scoped-refusal'
@@ -289,85 +224,106 @@ export function createAgentsHouseAdapter(options) {
289
224
  }
290
225
  : failure;
291
226
  }
292
- const grant = parseGrant(answer.body);
293
- if (grant === null || grant.method !== 'GET')
294
- return UNCERTAIN_READ;
295
- const read = await transport.download(grant.url);
296
- if (read.status !== 200 || read.blob === null)
227
+ const address = parseFileAddress(answer.body);
228
+ return address === null ? UNCERTAIN_READ : { kind: 'address', ...address };
229
+ }
230
+ function linked(answer, locator) {
231
+ const address = addressed(answer, locator);
232
+ return address.kind === 'address'
233
+ ? { kind: 'link', url: address.url, name: address.name, mediaType: address.mediaType }
234
+ : address;
235
+ }
236
+ function refusedAttach(answer, locator) {
237
+ const failure = classifyDoor(answer.status, answer.body, locator);
238
+ if (failure === null)
239
+ return null;
240
+ return failure.kind === 'scoped-refusal'
241
+ ? {
242
+ kind: 'rejected',
243
+ code: failure.code,
244
+ fields: failure.fields,
245
+ retryable: failure.retryable,
246
+ }
247
+ : failure;
248
+ }
249
+ async function downloaded(ask, locator) {
250
+ let address = addressed(await ask(), locator);
251
+ if (address.kind !== 'address')
252
+ return address;
253
+ let read = await transport.download(address.url);
254
+ if (read.status === EXPIRED_ADDRESS) {
255
+ address = addressed(await ask(), locator);
256
+ if (address.kind !== 'address')
257
+ return address;
258
+ read = await transport.download(address.url);
259
+ }
260
+ if (read.status !== 200 || read.body === null)
297
261
  return UNCERTAIN_READ;
298
- return { kind: 'binary', blob: read.blob };
262
+ return {
263
+ kind: 'binary',
264
+ body: read.body,
265
+ mediaType: address.mediaType,
266
+ sha256: address.sha256,
267
+ };
268
+ }
269
+ function pathAddress(request) {
270
+ const entry = authorityFor(request.locator);
271
+ if (entry === undefined)
272
+ return null;
273
+ return () => transport.door(houseDoorRoute('files/read'), {
274
+ room_ref: entry.authority,
275
+ path: request.path,
276
+ revision: request.revision,
277
+ });
299
278
  }
300
279
  return {
280
+ roomTypes: HOUSE_ROOM_TYPES,
281
+ nameableRoomTypes: HOUSE_NAMEABLE_ROOM_TYPES,
282
+ typedPaths: HOUSE_TYPED_PATHS,
283
+ pathRule: admittedPath,
284
+ unkeptReads: HOUSE_UNKEPT_READS,
285
+ leadingReads: HOUSE_LEADING_READS,
301
286
  async authorize() {
302
287
  return options.authorize();
303
288
  },
304
- async discover(_authorization) {
305
- const answer = await transport.door(DOOR_DISCOVER, { prefixes: [...DISCOVER_PREFIXES] });
306
- const failed = discoveryFailure(answer.status, answer.body);
307
- if (failed !== null)
308
- return failed;
309
- const document = parseDiscovery(answer.body);
310
- if (document === null)
311
- return { kind: 'transport-uncertainty' };
312
- bounds = document.bounds;
313
- const entries = trackedFrom(document);
314
- tracked.clear();
315
- for (const entry of entries)
316
- tracked.set(entry.locator, entry);
317
- for (const locator of handed.keys())
318
- if (!tracked.has(locator))
319
- handed.delete(locator);
320
- notifications.track(entries);
321
- return {
322
- ...mapDiscovery(document, document.authorities.map((authority) => discoveredRoom(authority)), capabilities(document)),
323
- member: document.member,
324
- };
325
- },
326
289
  async bootstrap(_authorization, request) {
327
290
  const entry = authorityFor(request.locator);
328
291
  if (entry === undefined)
329
292
  return { kind: 'room-not-readable', locator: request.locator };
330
- if (request.pageToken !== undefined) {
331
- return continuedBootstrap(entry, JSON.parse(request.pageToken));
332
- }
333
- return bootstrapTogether(entry);
334
- },
335
- async refresh(_authorization, request) {
336
- const entry = authorityFor(request.locator);
337
- if (entry === undefined)
338
- return { kind: 'room-not-readable', locator: request.locator };
339
- if (request.pageToken !== undefined)
340
- return { kind: 'transport-uncertainty' };
341
- const from = logPosition(request.fromRoomVersion);
342
- if (from === null)
343
- return { kind: 'beyond-horizon' };
344
- const answered = await streamed(entry, request, from);
345
- if (answered !== null)
346
- return answered;
347
- return enumerateTogether({ entry, from, fromRoomVersion: request.fromRoomVersion });
293
+ return request.pageToken === undefined
294
+ ? listedRoom(entry)
295
+ : chunkPage(entry, request.pageToken, request.arriving);
348
296
  },
349
297
  async mutate(_authorization, request, keep) {
350
298
  const entry = authorityFor(request.locator);
351
299
  if (entry === undefined)
352
300
  return { kind: 'room-not-readable', locator: request.locator };
353
- const mapped = houseChanges(request.changeBatch, entry.handle);
301
+ const batch = request.changeBatch;
302
+ const destination = batch.between?.destination.room;
303
+ const into = destination === undefined ? undefined : authorityFor(destination);
304
+ if (destination !== undefined && into === undefined) {
305
+ return { kind: 'room-not-readable', locator: destination };
306
+ }
307
+ const mapped = into === undefined
308
+ ? houseChanges(batch, entry.handle)
309
+ : houseMove(batch, entry.handle, into.handle);
354
310
  if (mapped.kind === 'unmapped')
355
311
  return rejectedMapping(mapped);
356
- const head = operationHead(request.changeBatch);
312
+ const head = operationHead(batch, entry.authority);
357
313
  const delta = deltaBody(mapped.changes);
358
314
  const bytes = deltaBytes(delta);
359
315
  const handed = request.upload;
360
- const batch = request.changeBatch;
361
- const outcome = await submit(batch, head, mapped.changes, delta, bytes, entry, handed, keep);
316
+ const answer = (upload) => submit(request, head, mapped.changes, delta, bytes, entry, upload, keep, into);
317
+ const outcome = await answer(handed);
362
318
  if (handed === undefined || !lapsedGrant(outcome))
363
- return adopted(entry, outcome);
364
- return adopted(entry, await submit(batch, head, mapped.changes, delta, bytes, entry, undefined, keep));
319
+ return outcome;
320
+ return answer(undefined);
365
321
  },
366
322
  async history(_authorization, request) {
367
323
  const entry = authorityFor(request.locator);
368
324
  if (entry === undefined)
369
325
  return { kind: 'room-not-readable', locator: request.locator };
370
- const answer = await transport.door(DOOR_REVISIONS, revisionBody({
326
+ const answer = await transport.door(houseDoorRoute('files/revisions/list'), revisionBody({
371
327
  path: request.path,
372
328
  handle: entry.handle,
373
329
  ...(request.revision === undefined ? {} : { revision: request.revision }),
@@ -379,35 +335,45 @@ export function createAgentsHouseAdapter(options) {
379
335
  });
380
336
  },
381
337
  async binary(_authorization, request) {
382
- if (request.kind === 'path') {
383
- const entry = authorityFor(request.locator);
384
- if (entry === undefined)
385
- return { kind: 'room-not-readable', locator: request.locator };
386
- const answer = await transport.door(DOOR_FILES_GET, {
387
- room_ref: entry.authority,
388
- path: request.path,
389
- revision: request.revision,
390
- });
391
- return downloaded(answer, request.locator);
392
- }
393
- const referrer = houseReferrer(request.referrer, tracked);
394
- if (referrer === null) {
395
- return { kind: 'rejected', code: 'referrer_unknown', fields: {}, retryable: false };
396
- }
397
- const answer = await transport.door(DOOR_FILES_GET, {
398
- file: request.fileRef,
399
- referrer: referrer.referrer,
400
- });
401
- return downloaded(answer, referrer.locator);
338
+ const ask = pathAddress(request);
339
+ if (ask === null)
340
+ return { kind: 'room-not-readable', locator: request.locator };
341
+ return downloaded(ask, request.locator);
402
342
  },
403
- referrerScope(referrer) {
404
- const held = houseReferrer(referrer, tracked);
405
- if (held === null)
406
- return null;
407
- if (held.locator === '')
408
- return { kind: 'conversations' };
409
- const { path } = held.referrer;
410
- return { kind: 'room', locator: held.locator, ...(path === undefined ? {} : { path }) };
343
+ async link(_authorization, request) {
344
+ const ask = pathAddress(request);
345
+ if (ask === null)
346
+ return { kind: 'room-not-readable', locator: request.locator };
347
+ return linked(await ask(), request.locator);
348
+ },
349
+ async attach(_authorization, request) {
350
+ const entry = authorityFor(request.locator);
351
+ if (entry === undefined)
352
+ return { kind: 'room-not-readable', locator: request.locator };
353
+ const declared = await transport.door(houseDoorRoute('files/attach'), {
354
+ room_ref: entry.authority,
355
+ name: request.name,
356
+ bytes: request.byteLength,
357
+ sha256: request.sha256,
358
+ media_type: request.mediaType.length > 0 ? request.mediaType : OCTET_STREAM,
359
+ });
360
+ const refused = refusedAttach(declared, request.locator);
361
+ if (refused !== null)
362
+ return refused;
363
+ const upload = houseUpload(declared.body?.upload);
364
+ if (upload === null)
365
+ return UNCERTAIN_READ;
366
+ const finished = await finishedUpload(transport, upload, request.blob);
367
+ if (finished === null)
368
+ return UNCERTAIN_READ;
369
+ const unfinished = refusedAttach(finished, request.locator);
370
+ if (unfinished !== null)
371
+ return unfinished;
372
+ const { path, revision } = (finished.body ?? {});
373
+ const root = `/rooms/${entry.handle}/`;
374
+ return typeof path === 'string' && path.startsWith(root) && typeof revision === 'string'
375
+ ? { kind: 'attached', path: path.slice(root.length), revision }
376
+ : UNCERTAIN_READ;
411
377
  },
412
378
  reached(locator, version, target) {
413
379
  return reachedVersion(version, target, authorityFor(locator)?.logEpoch);
@@ -418,7 +384,13 @@ export function createAgentsHouseAdapter(options) {
418
384
  async action(_authorization, request) {
419
385
  return withAt(await houseAction(transport, request));
420
386
  },
421
- roomStream: { hold: held, reopen: notifications.reopen, observe: notifications.observe },
387
+ search(_authorization, request) {
388
+ return houseSearch(transport, [...tracked.values()], request);
389
+ },
390
+ roomStream: {
391
+ hold,
392
+ observe: notifications.observe,
393
+ },
422
394
  conversations: notifications.conversations,
423
395
  serviceMode: notifications.serviceMode,
424
396
  };
@@ -0,0 +1,2 @@
1
+ export declare const APP_REFERENCE_PATTERN: RegExp;
2
+ //# sourceMappingURL=app-reference.d.ts.map