@agentshouse/mdruntime-adapter 0.1.0-cf22c8563f9f4 → 0.1.0-cf599f631976d

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 (72) hide show
  1. package/README.md +2 -2
  2. package/dist/actions.d.ts +6 -5
  3. package/dist/actions.d.ts.map +1 -1
  4. package/dist/actions.js +25 -322
  5. package/dist/adapter.d.ts +1 -0
  6. package/dist/adapter.d.ts.map +1 -1
  7. package/dist/adapter.js +278 -206
  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/chunks.d.ts +38 -0
  12. package/dist/chunks.d.ts.map +1 -0
  13. package/dist/chunks.js +247 -0
  14. package/dist/crockford.d.ts +4 -0
  15. package/dist/crockford.d.ts.map +1 -0
  16. package/dist/crockford.js +3 -0
  17. package/dist/discovery.d.ts +25 -15
  18. package/dist/discovery.d.ts.map +1 -1
  19. package/dist/discovery.js +104 -52
  20. package/dist/history.d.ts +12 -0
  21. package/dist/history.d.ts.map +1 -0
  22. package/dist/history.js +92 -0
  23. package/dist/house-model.d.ts +682 -0
  24. package/dist/house-model.d.ts.map +1 -0
  25. package/dist/house-model.js +396 -0
  26. package/dist/index.d.ts +11 -1
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +10 -0
  29. package/dist/model.d.ts +5 -4
  30. package/dist/model.d.ts.map +1 -1
  31. package/dist/model.js +40 -33
  32. package/dist/read.d.ts +63 -42
  33. package/dist/read.d.ts.map +1 -1
  34. package/dist/read.js +179 -262
  35. package/dist/refusal.d.ts +3 -8
  36. package/dist/refusal.d.ts.map +1 -1
  37. package/dist/refusal.js +10 -14
  38. package/dist/room-types.d.ts +5 -0
  39. package/dist/room-types.d.ts.map +1 -0
  40. package/dist/room-types.js +120 -0
  41. package/dist/search.d.ts +5 -0
  42. package/dist/search.d.ts.map +1 -0
  43. package/dist/search.js +47 -0
  44. package/dist/site.d.ts +1 -1
  45. package/dist/site.d.ts.map +1 -1
  46. package/dist/site.js +120 -105
  47. package/dist/space-schema.d.ts +7 -0
  48. package/dist/space-schema.d.ts.map +1 -0
  49. package/dist/space-schema.js +18 -0
  50. package/dist/stream.d.ts +15 -12
  51. package/dist/stream.d.ts.map +1 -1
  52. package/dist/stream.js +275 -147
  53. package/dist/transport.d.ts +90 -24
  54. package/dist/transport.d.ts.map +1 -1
  55. package/dist/transport.js +55 -9
  56. package/dist/upload.d.ts +12 -0
  57. package/dist/upload.d.ts.map +1 -0
  58. package/dist/upload.js +63 -0
  59. package/dist/version.d.ts +1 -0
  60. package/dist/version.d.ts.map +1 -1
  61. package/dist/version.js +1 -1
  62. package/dist/write.d.ts +20 -10
  63. package/dist/write.d.ts.map +1 -1
  64. package/dist/write.js +165 -59
  65. package/package.json +9 -6
  66. package/AGENTS.md +0 -244
  67. package/dist/gather.d.ts +0 -3
  68. package/dist/gather.d.ts.map +0 -1
  69. package/dist/gather.js +0 -40
  70. package/dist/links.d.ts +0 -20
  71. package/dist/links.d.ts.map +0 -1
  72. package/dist/links.js +0 -104
package/dist/adapter.js CHANGED
@@ -1,96 +1,123 @@
1
- import { DISCOVER_PREFIXES, capabilities, discoveredRoom, mapDiscovery, parseDiscovery, trackedFrom, } from './discovery.js';
2
- import { scopeModels, streamedModels, viewModels } from './model.js';
3
- import { gathered, 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 { acceptedLinks, enumeratedLinks, heldLinks, scopeLinks, streamedLinks, } from './links.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_REVISIONS, } from './transport.js';
11
- import { batchPaths, deltaBody, deltaBytes, deltaDigest, historyOutcome, houseChanges, mutationOutcome, operationHead, rejectedMapping, revisionBody, uploadable, } from './write.js';
12
- 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 { HOUSE_TYPED_PATHS } from './house-model.js';
15
+ import { HOUSE_NAMEABLE_ROOM_TYPES, HOUSE_ROOM_TYPES } from './room-types.js';
16
+ import { followsVersion, reachedVersion, roomVersion } from './version.js';
13
17
  const UNCERTAIN = { kind: 'transport-uncertainty' };
14
18
  const UNCERTAIN_READ = { kind: 'transport-uncertainty' };
19
+ const OCTET_STREAM = 'application/octet-stream';
20
+ const EXPIRED_ADDRESS = 403;
15
21
  function lapsedGrant(outcome) {
16
22
  return outcome.kind === 'rejected' && outcome.code === 'upload_unavailable';
17
23
  }
18
24
  export function createAgentsHouseAdapter(options) {
19
25
  if ('site' in options)
20
- return createSiteAdapter(options.site);
26
+ return createSiteAdapter(options.site, options.snapshot === true);
21
27
  const transport = options.transport;
22
- const notifications = houseNotifications(transport, 'credential');
28
+ const spaces = spaceSchemas();
29
+ const notifications = houseNotifications(transport, 'credential', {
30
+ opened: async (frame) => (frame === null ? null : opened(frame)),
31
+ changed,
32
+ expired: (authorities) => lister.askAll(authorities.flatMap((authority) => [...tracked.values()].filter((entry) => entry.authority === authority))),
33
+ }, spaces);
23
34
  const tracked = new Map();
24
- const handed = new Map();
35
+ const lister = chunkLister(transport, () => declared().max_prefixes);
36
+ const chunked = new Map();
37
+ const renewals = new WeakMap();
25
38
  let bounds;
26
39
  function declared() {
27
40
  if (bounds === undefined)
28
41
  throw new Error('no Discovery declared the bounds');
29
42
  return bounds;
30
43
  }
31
- async function bootstrappedRoom(envelope, entry) {
32
- const scope = envelope.scopes.find((candidate) => matchesScope(candidate, entry));
33
- if (scope === undefined)
34
- return { kind: 'room-not-readable', locator: entry.locator };
35
- handed.set(entry.locator, scopeLinks(scope));
36
- notifications.reach(entry.authority, scope);
37
- const models = await scopeModels(scope, entry);
38
- return { ...bootstrapOf(scope), ...(models === undefined ? {} : { models }) };
39
- }
40
- async function enumeratedRoom(enumeration, ask) {
41
- const view = viewOf(enumeration, ask.entry);
42
- if (view === undefined)
43
- return { kind: 'room-not-readable', locator: ask.entry.locator };
44
- notifications.reach(ask.entry.authority, view);
45
- const answer = refreshFrom(view, ask.fromRoomVersion);
46
- if (answer.kind !== 'net-advancement')
47
- return answer;
48
- handed.set(ask.entry.locator, enumeratedLinks(view, handed.get(ask.entry.locator)));
49
- const models = await viewModels(view, ask.entry);
50
- return models === undefined ? answer : { ...answer, models };
44
+ function opened(frame) {
45
+ const document = parseDiscovery(frame);
46
+ if (document === null)
47
+ return null;
48
+ bounds = document.bounds;
49
+ tracked.clear();
50
+ lister.forget();
51
+ for (const entry of trackedFrom(document))
52
+ tracked.set(entry.locator, entry);
53
+ notifications.track([...tracked.values()]);
54
+ return {
55
+ ...mapDiscovery(document, document.authorities.map((authority) => discoveredRoom(authority)), capabilities(document.actions)),
56
+ member: document.member,
57
+ };
51
58
  }
52
- async function streamed(entry, request, from) {
53
- const links = handed.get(entry.locator);
54
- if (links?.roomVersion !== request.fromRoomVersion)
59
+ function changed(frame) {
60
+ const change = parseAuthorityChange(frame);
61
+ if (change === null)
55
62
  return null;
56
- const advanced = notifications.advance(entry, from, (path, revision) => links.revisions.get(path) === revision);
57
- if (advanced === null)
63
+ for (const locator of change.left)
64
+ tracked.delete(locator);
65
+ for (const entry of trackedFrom(change))
66
+ tracked.set(entry.locator, entry);
67
+ notifications.track([...tracked.values()]);
68
+ return {
69
+ kind: 'authority-changed',
70
+ rooms: change.authorities.map((authority) => discoveredRoom(authority)),
71
+ left: change.left,
72
+ member: change.member,
73
+ };
74
+ }
75
+ async function listedRoom(entry) {
76
+ const listed = await lister.list(entry);
77
+ if ('kind' in listed)
78
+ return listed;
79
+ const head = await chunkedHead(listed, entry, spaces);
80
+ if (head.kind === 'bootstrap')
81
+ chunked.set(entry.locator, listed);
82
+ return head;
83
+ }
84
+ async function chunkRead(entry, id) {
85
+ const room = chunked.get(entry.locator);
86
+ const address = room?.chunks.get(id);
87
+ if (room === undefined || address === undefined)
58
88
  return null;
59
- const relinked = streamedLinks(links, advanced);
60
- if (relinked === null)
89
+ const read = await transport.download(address.url);
90
+ if (read.status !== EXPIRED_ADDRESS)
91
+ return { room, read };
92
+ const renewed = await renewal(entry, room);
93
+ if ('kind' in renewed || !renewed.chunks.has(id))
61
94
  return null;
62
- handed.set(entry.locator, relinked);
63
- const models = await streamedModels(advanced.schemas, entry, 'credential');
64
- return models === undefined ? advanced.page : { ...advanced.page, models };
95
+ chunked.set(entry.locator, renewed);
96
+ return { room: renewed, read: await transport.download(renewed.chunks.get(id).url) };
65
97
  }
66
- function held(rooms) {
67
- const kept = rooms.flatMap((room) => {
68
- const entry = tracked.get(room.locator);
69
- const at = logPosition(room.roomVersion);
70
- if (entry === undefined || at === null)
71
- return [];
72
- handed.set(room.locator, heldLinks(room));
73
- notifications.reach(entry.authority, at);
74
- return [presented(entry.authority, at)];
75
- });
76
- notifications.send(kept);
98
+ function renewal(entry, stale) {
99
+ const held = renewals.get(stale);
100
+ if (held !== undefined)
101
+ return held;
102
+ const renewed = lister.renew(entry, [...chunked.keys()].flatMap((locator) => tracked.get(locator) ?? []));
103
+ renewals.set(stale, renewed);
104
+ return renewed;
105
+ }
106
+ function hold(rooms) {
107
+ for (const room of rooms) {
108
+ const listed = chunked.get(room.locator);
109
+ if (listed !== undefined &&
110
+ room.roomVersion !== null &&
111
+ reachedVersion(room.roomVersion, roomVersion(listed), listed.logEpoch))
112
+ chunked.delete(room.locator);
113
+ }
114
+ notifications.send(rooms);
77
115
  }
78
- function adopted(entry, outcome) {
79
- if (outcome.kind !== 'accepted')
80
- return outcome;
81
- const { originals, ...accepted } = outcome;
82
- const links = handed.get(entry.locator);
83
- if (links === undefined)
84
- return accepted;
85
- const follows = followsVersion(links.roomVersion, accepted.roomVersion);
86
- const relinked = acceptedLinks(links, accepted, follows ? accepted.roomVersion : links.roomVersion, originals);
87
- handed.set(entry.locator, relinked.links);
88
- const at = logPosition(accepted.roomVersion);
89
- if (follows && at !== null)
90
- notifications.reach(entry.authority, at);
91
- return relinked.attachments === undefined
92
- ? accepted
93
- : { ...accepted, attachments: relinked.attachments };
116
+ async function chunkPage(entry, id, arriving) {
117
+ const fetched = await chunkRead(entry, id);
118
+ if (fetched === null || fetched.read.status !== 200)
119
+ return UNCERTAIN_READ;
120
+ return chunkPart(fetched.read.body, fetched.room, id, arriving);
94
121
  }
95
122
  function authorityFor(locator) {
96
123
  return tracked.get(locator);
@@ -109,194 +136,192 @@ export function createAgentsHouseAdapter(options) {
109
136
  return result;
110
137
  return { ...result, at: { locator, roomVersion: roomVersion({ position, logEpoch }) } };
111
138
  }
112
- async function bundled(body) {
113
- const grant = parseGrant(body);
114
- if (grant === null)
115
- return body;
116
- if (grant.method !== 'GET')
117
- return null;
118
- const answer = await transport.transfer({
119
- act: 'local_copy_bundle',
120
- method: 'GET',
121
- url: grant.url,
122
- answer: 'json',
123
- });
124
- return answer.status === 200 ? answer.body : null;
125
- }
126
- function weight(entry) {
127
- return contentPrefixes(entry).length;
128
- }
129
- async function bootstrapped(entries) {
130
- const answers = [];
131
- for (const part of selections(entries, weight, declared().max_prefixes)) {
132
- const answer = await transport.door(DOOR_BOOTSTRAP, {
133
- prefixes: part.flatMap((entry) => contentPrefixes(entry)),
134
- });
135
- const classified = part.map((entry) => classifyDoor(answer.status, answer.body, entry.locator));
136
- const envelope = classified[0] === null ? parseEnvelope(await bundled(answer.body)) : null;
137
- for (const [index, entry] of part.entries()) {
138
- const failure = classified[index];
139
- if (failure === null || failure === undefined) {
140
- answers.push(envelope === null ? UNCERTAIN_READ : await bootstrappedRoom(envelope, entry));
141
- continue;
142
- }
143
- answers.push(failure);
144
- }
145
- }
146
- return answers;
147
- }
148
- function layered(asks) {
149
- const depths = new Map();
150
- const layers = [];
151
- for (const ask of asks) {
152
- const depth = depths.get(ask.entry.authority) ?? 0;
153
- depths.set(ask.entry.authority, depth + 1);
154
- (layers[depth] ??= []).push(ask);
155
- }
156
- return layers;
157
- }
158
- async function enumerated(asks) {
159
- const answers = new Map();
160
- const parts = layered(asks).flatMap((layer) => selections(layer, (ask) => weight(ask.entry), declared().max_prefixes));
161
- for (const part of parts) {
162
- let open = [...part];
163
- while (open.length > 0) {
164
- const answer = await transport.door(DOOR_ENUMERATE, {
165
- prefixes: open.flatMap((ask) => contentPrefixes(ask.entry)),
166
- positions: open.map((ask) => presented(ask.entry.authority, ask.from)),
167
- });
168
- const classified = open.map((ask) => classifyRead(answer.status, answer.body, ask.entry.locator));
169
- if (classified[0]?.kind === 'beyond-horizon') {
170
- const named = doorRefusal(answer.body)?.fields.authority;
171
- const expired = open.filter((ask) => ask.entry.authority === named);
172
- const answered = expired.length === 0 ? UNCERTAIN_READ : { kind: 'beyond-horizon' };
173
- const gone = expired.length === 0 ? open : expired;
174
- for (const ask of gone)
175
- answers.set(ask, answered);
176
- open = open.filter((ask) => !gone.includes(ask));
177
- continue;
178
- }
179
- const enumeration = classified[0] === null ? parseEnumeration(answer.body, 'credential') : null;
180
- for (const [index, ask] of open.entries()) {
181
- const failure = classified[index];
182
- if (failure !== null && failure !== undefined) {
183
- answers.set(ask, failure);
184
- continue;
185
- }
186
- answers.set(ask, enumeration === null ? UNCERTAIN_READ : await enumeratedRoom(enumeration, ask));
187
- }
188
- open = [];
189
- }
190
- }
191
- return asks.map((ask) => answers.get(ask) ?? UNCERTAIN_READ);
192
- }
193
- const bootstrapTogether = gathered(bootstrapped);
194
- const enumerateTogether = gathered(enumerated);
195
139
  async function uploaded(head, changes, delta, bytes, entry, handed, keep) {
196
140
  const paths = batchPaths(changes);
197
141
  const sha256 = await deltaDigest(delta);
198
142
  if (handed !== undefined && handed.sha256 === sha256) {
199
143
  return { body: { ...head, paths, upload: handed, runtime: notifications.runtime } };
200
144
  }
201
- const minted = await transport.door(DOOR_BATCH_UPLOAD, { ...head, paths, bytes, sha256 });
145
+ const minted = await transport.door(houseDoorRoute('files/batch-upload'), {
146
+ ...head,
147
+ paths,
148
+ bytes,
149
+ sha256,
150
+ });
202
151
  if (minted.status !== 200) {
203
- return { outcome: mutationOutcome(minted.status, minted.body, entry.locator, entry.handle) };
152
+ return {
153
+ outcome: await stepped(mutationOutcome(minted.status, minted.body, entry.locator, entry.handle)),
154
+ };
204
155
  }
205
156
  const grant = parseGrant(minted.body);
206
- if (grant === null || grant.method !== 'POST')
157
+ if (grant === null)
207
158
  return { outcome: UNCERTAIN };
208
159
  const delivered = await transport.transfer({
209
160
  act: 'authored_batch_upload',
210
- method: 'POST',
211
161
  url: grant.url,
212
- answer: 'json',
213
162
  body: `${grant.operation}\n${delta}`,
214
163
  });
215
164
  if (delivered.status !== 200)
216
165
  return { outcome: UNCERTAIN };
217
166
  const holding = parseGrant(delivered.body);
218
- if (holding === null || holding.method !== 'POST')
167
+ if (holding === null)
219
168
  return { outcome: UNCERTAIN };
220
169
  const upload = { url: holding.url, operation: holding.operation, bytes, sha256 };
221
170
  await keep(upload);
222
171
  return { body: { ...head, paths, upload, runtime: notifications.runtime } };
223
172
  }
224
- async function submit(head, changes, delta, bytes, entry, handed, keep) {
173
+ async function staged(answer, { changeBatch: batch, file }) {
174
+ const refusal = doorRefusal(answer.body);
175
+ if (refusal?.code !== 'files_unstaged')
176
+ return null;
177
+ const uploads = refusal.fields.upload;
178
+ if (!Array.isArray(uploads))
179
+ return false;
180
+ for (const named of uploads) {
181
+ const upload = houseUpload(named);
182
+ const change = batch.changes.find((held) => held.kind === 'binary' && held.sha256 === named.sha256);
183
+ if (upload === null || change === undefined)
184
+ return false;
185
+ const finished = await finishedUpload(transport, upload, await file(change.localId));
186
+ if (finished?.body?.staged !== true)
187
+ return false;
188
+ }
189
+ return true;
190
+ }
191
+ async function submit(request, head, changes, delta, bytes, entry, handed, keep, into) {
225
192
  const inline = { ...head, changes, runtime: notifications.runtime };
226
193
  const prepared = uploadable(inline, bytes, declared())
227
194
  ? await uploaded(head, changes, delta, bytes, entry, handed, keep)
228
195
  : { body: inline };
229
196
  if ('outcome' in prepared)
230
197
  return prepared.outcome;
231
- const answer = await transport.door(DOOR_BATCH, prepared.body);
232
- return mutationOutcome(answer.status, answer.body, entry.locator, entry.handle);
198
+ const answer = await transport.door(houseDoorRoute('files/batch'), prepared.body);
199
+ const stagedFiles = await staged(answer, request);
200
+ if (stagedFiles === false)
201
+ return UNCERTAIN;
202
+ const settled = stagedFiles === null
203
+ ? answer
204
+ : await transport.door(houseDoorRoute('files/batch'), prepared.body);
205
+ if (stagedFiles === true && (await staged(settled, request)) !== null)
206
+ return UNCERTAIN;
207
+ return stepped(mutationOutcome(settled.status, settled.body, entry.locator, entry.handle, into === undefined
208
+ ? undefined
209
+ : {
210
+ source: { authority: entry.authority, handle: entry.handle },
211
+ destination: { authority: into.authority, handle: into.handle },
212
+ }));
213
+ }
214
+ function addressed(answer, locator) {
215
+ const failure = classifyDoor(answer.status, answer.body, locator);
216
+ if (failure !== null) {
217
+ return failure.kind === 'scoped-refusal'
218
+ ? {
219
+ kind: 'rejected',
220
+ code: failure.code,
221
+ fields: failure.fields,
222
+ retryable: failure.retryable,
223
+ }
224
+ : failure;
225
+ }
226
+ const address = parseFileAddress(answer.body);
227
+ return address === null ? UNCERTAIN_READ : { kind: 'address', ...address };
228
+ }
229
+ function linked(answer, locator) {
230
+ const address = addressed(answer, locator);
231
+ return address.kind === 'address'
232
+ ? { kind: 'link', url: address.url, name: address.name, mediaType: address.mediaType }
233
+ : address;
234
+ }
235
+ function refusedAttach(answer, locator) {
236
+ const failure = classifyDoor(answer.status, answer.body, locator);
237
+ if (failure === null)
238
+ return null;
239
+ return failure.kind === 'scoped-refusal'
240
+ ? {
241
+ kind: 'rejected',
242
+ code: failure.code,
243
+ fields: failure.fields,
244
+ retryable: failure.retryable,
245
+ }
246
+ : failure;
247
+ }
248
+ async function downloaded(ask, locator) {
249
+ let address = addressed(await ask(), locator);
250
+ if (address.kind !== 'address')
251
+ return address;
252
+ let read = await transport.download(address.url);
253
+ if (read.status === EXPIRED_ADDRESS) {
254
+ address = addressed(await ask(), locator);
255
+ if (address.kind !== 'address')
256
+ return address;
257
+ read = await transport.download(address.url);
258
+ }
259
+ if (read.status !== 200 || read.body === null)
260
+ return UNCERTAIN_READ;
261
+ return {
262
+ kind: 'binary',
263
+ body: read.body,
264
+ mediaType: address.mediaType,
265
+ sha256: address.sha256,
266
+ };
267
+ }
268
+ function pathAddress(request) {
269
+ const entry = authorityFor(request.locator);
270
+ if (entry === undefined)
271
+ return null;
272
+ return () => transport.door(houseDoorRoute('files/read'), {
273
+ room_ref: entry.authority,
274
+ path: request.path,
275
+ revision: request.revision,
276
+ });
233
277
  }
234
278
  return {
279
+ roomTypes: HOUSE_ROOM_TYPES,
280
+ nameableRoomTypes: HOUSE_NAMEABLE_ROOM_TYPES,
281
+ typedPaths: HOUSE_TYPED_PATHS,
282
+ unkeptReads: HOUSE_UNKEPT_READS,
283
+ leadingReads: HOUSE_LEADING_READS,
235
284
  async authorize() {
236
285
  return options.authorize();
237
286
  },
238
- async discover(_authorization) {
239
- const answer = await transport.door(DOOR_DISCOVER, { prefixes: [...DISCOVER_PREFIXES] });
240
- const failed = discoveryFailure(answer.status, answer.body);
241
- if (failed !== null)
242
- return failed;
243
- const document = parseDiscovery(answer.body);
244
- if (document === null)
245
- return { kind: 'transport-uncertainty' };
246
- bounds = document.bounds;
247
- const entries = trackedFrom(document);
248
- tracked.clear();
249
- for (const entry of entries)
250
- tracked.set(entry.locator, entry);
251
- for (const locator of handed.keys())
252
- if (!tracked.has(locator))
253
- handed.delete(locator);
254
- notifications.track(entries);
255
- return mapDiscovery(document, document.authorities.map((authority) => discoveredRoom(authority)), capabilities(document));
256
- },
257
287
  async bootstrap(_authorization, request) {
258
288
  const entry = authorityFor(request.locator);
259
289
  if (entry === undefined)
260
290
  return { kind: 'room-not-readable', locator: request.locator };
261
- if (request.pageToken !== undefined)
262
- return { kind: 'transport-uncertainty' };
263
- return bootstrapTogether(entry);
264
- },
265
- async refresh(_authorization, request) {
266
- const entry = authorityFor(request.locator);
267
- if (entry === undefined)
268
- return { kind: 'room-not-readable', locator: request.locator };
269
- if (request.pageToken !== undefined)
270
- return { kind: 'transport-uncertainty' };
271
- const from = logPosition(request.fromRoomVersion);
272
- if (from === null)
273
- return { kind: 'beyond-horizon' };
274
- const answered = await streamed(entry, request, from);
275
- if (answered !== null)
276
- return answered;
277
- return enumerateTogether({ entry, from, fromRoomVersion: request.fromRoomVersion });
291
+ return request.pageToken === undefined
292
+ ? listedRoom(entry)
293
+ : chunkPage(entry, request.pageToken, request.arriving);
278
294
  },
279
295
  async mutate(_authorization, request, keep) {
280
296
  const entry = authorityFor(request.locator);
281
297
  if (entry === undefined)
282
298
  return { kind: 'room-not-readable', locator: request.locator };
283
- const mapped = houseChanges(request.changeBatch, entry.handle);
299
+ const batch = request.changeBatch;
300
+ const destination = batch.between?.destination.room;
301
+ const into = destination === undefined ? undefined : authorityFor(destination);
302
+ if (destination !== undefined && into === undefined) {
303
+ return { kind: 'room-not-readable', locator: destination };
304
+ }
305
+ const mapped = into === undefined
306
+ ? houseChanges(batch, entry.handle)
307
+ : houseMove(batch, entry.handle, into.handle);
284
308
  if (mapped.kind === 'unmapped')
285
309
  return rejectedMapping(mapped);
286
- const head = operationHead(request.changeBatch);
310
+ const head = operationHead(batch, entry.authority);
287
311
  const delta = deltaBody(mapped.changes);
288
312
  const bytes = deltaBytes(delta);
289
313
  const handed = request.upload;
290
- const outcome = await submit(head, mapped.changes, delta, bytes, entry, handed, keep);
314
+ const answer = (upload) => submit(request, head, mapped.changes, delta, bytes, entry, upload, keep, into);
315
+ const outcome = await answer(handed);
291
316
  if (handed === undefined || !lapsedGrant(outcome))
292
- return adopted(entry, outcome);
293
- return adopted(entry, await submit(head, mapped.changes, delta, bytes, entry, undefined, keep));
317
+ return outcome;
318
+ return answer(undefined);
294
319
  },
295
320
  async history(_authorization, request) {
296
321
  const entry = authorityFor(request.locator);
297
322
  if (entry === undefined)
298
323
  return { kind: 'room-not-readable', locator: request.locator };
299
- const answer = await transport.door(DOOR_REVISIONS, revisionBody({
324
+ const answer = await transport.door(houseDoorRoute('files/revisions/list'), revisionBody({
300
325
  path: request.path,
301
326
  handle: entry.handle,
302
327
  ...(request.revision === undefined ? {} : { revision: request.revision }),
@@ -307,6 +332,47 @@ export function createAgentsHouseAdapter(options) {
307
332
  ...(request.revision === undefined ? {} : { revision: request.revision }),
308
333
  });
309
334
  },
335
+ async binary(_authorization, request) {
336
+ const ask = pathAddress(request);
337
+ if (ask === null)
338
+ return { kind: 'room-not-readable', locator: request.locator };
339
+ return downloaded(ask, request.locator);
340
+ },
341
+ async link(_authorization, request) {
342
+ const ask = pathAddress(request);
343
+ if (ask === null)
344
+ return { kind: 'room-not-readable', locator: request.locator };
345
+ return linked(await ask(), request.locator);
346
+ },
347
+ async attach(_authorization, request) {
348
+ const entry = authorityFor(request.locator);
349
+ if (entry === undefined)
350
+ return { kind: 'room-not-readable', locator: request.locator };
351
+ const declared = await transport.door(houseDoorRoute('files/attach'), {
352
+ room_ref: entry.authority,
353
+ name: request.name,
354
+ bytes: request.byteLength,
355
+ sha256: request.sha256,
356
+ media_type: request.mediaType.length > 0 ? request.mediaType : OCTET_STREAM,
357
+ });
358
+ const refused = refusedAttach(declared, request.locator);
359
+ if (refused !== null)
360
+ return refused;
361
+ const upload = houseUpload(declared.body?.upload);
362
+ if (upload === null)
363
+ return UNCERTAIN_READ;
364
+ const finished = await finishedUpload(transport, upload, request.blob);
365
+ if (finished === null)
366
+ return UNCERTAIN_READ;
367
+ const unfinished = refusedAttach(finished, request.locator);
368
+ if (unfinished !== null)
369
+ return unfinished;
370
+ const { path, revision } = (finished.body ?? {});
371
+ const root = `/rooms/${entry.handle}/`;
372
+ return typeof path === 'string' && path.startsWith(root) && typeof revision === 'string'
373
+ ? { kind: 'attached', path: path.slice(root.length), revision }
374
+ : UNCERTAIN_READ;
375
+ },
310
376
  reached(locator, version, target) {
311
377
  return reachedVersion(version, target, authorityFor(locator)?.logEpoch);
312
378
  },
@@ -316,7 +382,13 @@ export function createAgentsHouseAdapter(options) {
316
382
  async action(_authorization, request) {
317
383
  return withAt(await houseAction(transport, request));
318
384
  },
319
- roomStream: { hold: held, observe: notifications.observe },
385
+ search(_authorization, request) {
386
+ return houseSearch(transport, [...tracked.values()], request);
387
+ },
388
+ roomStream: {
389
+ hold,
390
+ observe: notifications.observe,
391
+ },
320
392
  conversations: notifications.conversations,
321
393
  serviceMode: notifications.serviceMode,
322
394
  };
@@ -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,38 @@
1
+ import { type AdapterFailure, type BootstrapPart, type BootstrapResult } from '@agentshouse/mdruntime';
2
+ import type { TrackedAuthority } from './discovery.js';
3
+ import { type HouseModels, type HouseScope } from './read.js';
4
+ import type { SpaceSchemas } from './space-schema.js';
5
+ import { type HouseDoor, type HouseMode } from './transport.js';
6
+ import { type LogPosition } from './version.js';
7
+ export type ChunkAddress = {
8
+ readonly url: string;
9
+ readonly bytes: number;
10
+ };
11
+ export type ChunkedRoom = LogPosition & {
12
+ readonly anchor: string;
13
+ readonly mode: HouseMode;
14
+ readonly chunks: ReadonlyMap<string, ChunkAddress>;
15
+ readonly parts: readonly BootstrapPart[];
16
+ readonly names: ReadonlyMap<string, string>;
17
+ readonly models: HouseModels;
18
+ };
19
+ export type Listed = ChunkedRoom | {
20
+ readonly kind: 'not-ready';
21
+ } | AdapterFailure;
22
+ type Answered = {
23
+ readonly status: number;
24
+ readonly body: unknown;
25
+ };
26
+ export declare function partId(logEpoch: string, hash: string): string;
27
+ export declare function siteChunkList(answer: Answered, locator: string): Listed;
28
+ export declare function chunkedHead(listed: ChunkedRoom, entry: TrackedAuthority, spaces: SpaceSchemas): Promise<BootstrapResult>;
29
+ export declare function chunkPart(body: ReadableStream<Uint8Array> | null, room: ChunkedRoom, id: string, arriving: ((bytes: number) => void) | undefined): Promise<BootstrapResult>;
30
+ export declare function chunkLister(transport: HouseDoor, most: () => number): {
31
+ askAll: (entries: readonly TrackedAuthority[]) => void;
32
+ list: (entry: TrackedAuthority) => Promise<Listed>;
33
+ renew(entry: TrackedAuthority, installing: readonly TrackedAuthority[]): Promise<Listed>;
34
+ forget(): void;
35
+ };
36
+ export declare function chunkScope(body: ReadableStream<Uint8Array>, room: ChunkedRoom, arriving: ((bytes: number) => void) | undefined): Promise<HouseScope | null>;
37
+ export {};
38
+ //# sourceMappingURL=chunks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunks.d.ts","sourceRoot":"","sources":["../src/chunks.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,eAAe,EACrB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEvD,OAAO,EAML,KAAK,WAAW,EAChB,KAAK,UAAU,EAChB,MAAM,WAAW,CAAC;AAEnB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAkB,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChF,OAAO,EAA6B,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3E,MAAM,MAAM,YAAY,GAAG;IAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAE5E,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG;IACtC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACnD,QAAQ,CAAC,KAAK,EAAE,SAAS,aAAa,EAAE,CAAC;IACzC,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,MAAM,GAAG,WAAW,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAA;CAAE,GAAG,cAAc,CAAC;AAEnF,KAAK,QAAQ,GAAG;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC;AAoCpE,wBAAgB,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAE7D;AA4BD,wBAAgB,aAAa,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAIvE;AAED,wBAAsB,WAAW,CAC/B,MAAM,EAAE,WAAW,EACnB,KAAK,EAAE,gBAAgB,EACvB,MAAM,EAAE,YAAY,GACnB,OAAO,CAAC,eAAe,CAAC,CAuB1B;AAED,wBAAsB,SAAS,CAC7B,IAAI,EAAE,cAAc,CAAC,UAAU,CAAC,GAAG,IAAI,EACvC,IAAI,EAAE,WAAW,EACjB,EAAE,EAAE,MAAM,EACV,QAAQ,EAAE,CAAC,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,GAC9C,OAAO,CAAC,eAAe,CAAC,CAM1B;AAgBD,wBAAgB,WAAW,CAAC,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,MAAM;sBAwCzC,SAAS,gBAAgB,EAAE;kBAU/B,gBAAgB,KAAG,OAAO,CAAC,MAAM,CAAC;iBASxC,gBAAgB,cAAc,SAAS,gBAAgB,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;;EAc3F;AAuCD,wBAAsB,UAAU,CAC9B,IAAI,EAAE,cAAc,CAAC,UAAU,CAAC,EAChC,IAAI,EAAE,WAAW,EACjB,QAAQ,EAAE,CAAC,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,GAC9C,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAgC5B"}