@agentshouse/mdruntime-adapter 0.1.0-cd23c45caa798 → 0.1.0-cd519a0f878dc

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 (54) hide show
  1. package/README.md +2 -2
  2. package/dist/actions.d.ts +3 -16
  3. package/dist/actions.d.ts.map +1 -1
  4. package/dist/actions.js +23 -53
  5. package/dist/adapter.d.ts.map +1 -1
  6. package/dist/adapter.js +124 -132
  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 +3 -8
  14. package/dist/discovery.d.ts.map +1 -1
  15. package/dist/discovery.js +11 -24
  16. package/dist/house-model.d.ts +2 -0
  17. package/dist/house-model.d.ts.map +1 -0
  18. package/dist/house-model.js +441 -0
  19. package/dist/index.d.ts +6 -1
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +6 -1
  22. package/dist/model.d.ts +5 -1
  23. package/dist/model.d.ts.map +1 -1
  24. package/dist/model.js +11 -13
  25. package/dist/part.d.ts +4 -2
  26. package/dist/part.d.ts.map +1 -1
  27. package/dist/part.js +15 -13
  28. package/dist/read.d.ts +28 -7
  29. package/dist/read.d.ts.map +1 -1
  30. package/dist/read.js +172 -95
  31. package/dist/room-types.d.ts +7 -0
  32. package/dist/room-types.d.ts.map +1 -0
  33. package/dist/room-types.js +124 -0
  34. package/dist/site.d.ts.map +1 -1
  35. package/dist/site.js +45 -55
  36. package/dist/stream.d.ts +1 -0
  37. package/dist/stream.d.ts.map +1 -1
  38. package/dist/stream.js +17 -2
  39. package/dist/transport.d.ts +10 -10
  40. package/dist/transport.d.ts.map +1 -1
  41. package/dist/transport.js +1 -1
  42. package/dist/upload.d.ts +12 -0
  43. package/dist/upload.d.ts.map +1 -0
  44. package/dist/upload.js +63 -0
  45. package/dist/write.d.ts.map +1 -1
  46. package/dist/write.js +13 -17
  47. package/package.json +8 -4
  48. package/AGENTS.md +0 -304
  49. package/dist/handed.d.ts +0 -15
  50. package/dist/handed.d.ts.map +0 -1
  51. package/dist/handed.js +0 -45
  52. package/dist/referrer.d.ts +0 -7
  53. package/dist/referrer.d.ts.map +0 -1
  54. package/dist/referrer.js +0 -33
package/README.md CHANGED
@@ -24,8 +24,8 @@ host.attach(port, 'my-app');
24
24
  ```
25
25
 
26
26
  App code never imports this package; it reaches the Runtime only through the
27
- client its host hands it. The rules an App's agent follows are in the
28
- package's `AGENTS.md`.
27
+ client its host hands it. The rules an App's agent follows are the `apps/` pages
28
+ of House's Guide, which House's `open_guide` opens.
29
29
 
30
30
  ## License
31
31
 
package/dist/actions.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import { type AcceptedMessage, type ActionDeclaration, type ActionRequest, type ActionResult, type ConversationAgents, type ConversationHistories, type ConversationHistoryRequest, type FileDeclaration } from '@agentshouse/mdruntime';
2
2
  import type { HouseDoorAnswer, HouseTransport } from './transport.js';
3
+ export declare const HISTORY_PAGE = 100;
4
+ export declare const TRIGGER_ROOMS = 100;
3
5
  type Operated = {
4
6
  readonly operation_id?: string;
5
7
  };
@@ -19,6 +21,7 @@ export type AppInputs = {
19
21
  };
20
22
  readonly 'conversation.create': Operated & {
21
23
  readonly agent_id: string;
24
+ readonly replaces?: string;
22
25
  };
23
26
  readonly 'conversation.delete': ConversationControl;
24
27
  readonly 'conversation.history': ConversationHistoryRequest;
@@ -45,22 +48,6 @@ export type AppAnswerAdditions = {
45
48
  readonly 'conversation.send': {
46
49
  readonly turn: string;
47
50
  };
48
- readonly 'record.list': {
49
- readonly attachments: readonly {
50
- readonly referrer: {
51
- readonly room_ref: string;
52
- readonly record: string;
53
- };
54
- }[];
55
- readonly records: readonly {
56
- readonly attachments: readonly {
57
- readonly referrer: {
58
- readonly room_ref: string;
59
- readonly record: string;
60
- };
61
- }[];
62
- }[];
63
- };
64
51
  };
65
52
  export declare const HOUSE_APP_ACTS: Readonly<Record<string, string>>;
66
53
  export declare const SITE_SUBMIT = "submit";
@@ -1 +1 @@
1
- {"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../src/actions.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,EAE1B,KAAK,0BAA0B,EAC/B,KAAK,eAAe,EACrB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAUtE,KAAK,QAAQ,GAAG;IAAE,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnD,KAAK,mBAAmB,GAAG,QAAQ,GAAG;IAAE,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC;AAExE,MAAM,MAAM,SAAS,GAAG;IACtB,QAAQ,CAAC,qBAAqB,EAAE,eAAe,GAAG;QAChD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;KACrB,CAAC;IACF,QAAQ,CAAC,qBAAqB,EAAE,mBAAmB,GAAG;QACpD,QAAQ,CAAC,MAAM,EAAE;YAAE,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;YAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;SAAE,CAAC;KAClF,CAAC;IACF,QAAQ,CAAC,qBAAqB,EAAE,QAAQ,GAAG;QAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IACzE,QAAQ,CAAC,qBAAqB,EAAE,mBAAmB,CAAC;IACpD,QAAQ,CAAC,sBAAsB,EAAE,0BAA0B,CAAC;IAC5D,QAAQ,CAAC,wBAAwB,EAAE,mBAAmB,CAAC;IACvD,QAAQ,CAAC,mBAAmB,EAAE,mBAAmB,CAAC;IAClD,QAAQ,CAAC,mBAAmB,EAAE,mBAAmB,GAAG;QAClD,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;QAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;KACvB,CAAC;IACF,QAAQ,CAAC,cAAc,EAAE;QAAE,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAA;KAAE,CAAC;CACpE,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,CAAC,qBAAqB,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1D,QAAQ,CAAC,sBAAsB,EAAE,qBAAqB,CAAC;CACxD,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,CAAC,qBAAqB,EAAE;QAAE,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC;IAClE,QAAQ,CAAC,mBAAmB,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACxD,QAAQ,CAAC,aAAa,EAAE;QACtB,QAAQ,CAAC,WAAW,EAAE,SAAS;YAC7B,QAAQ,CAAC,QAAQ,EAAE;gBAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;gBAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;aAAE,CAAC;SAC3E,EAAE,CAAC;QACJ,QAAQ,CAAC,OAAO,EAAE,SAAS;YACzB,QAAQ,CAAC,WAAW,EAAE,SAAS;gBAC7B,QAAQ,CAAC,QAAQ,EAAE;oBAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;oBAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;iBAAE,CAAC;aAC3E,EAAE,CAAC;SACL,EAAE,CAAC;KACL,CAAC;CACH,CAAC;AA6EF,eAAO,MAAM,cAAc,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAE3D,CAAC;AAEF,eAAO,MAAM,WAAW,WAAW,CAAC;AAEpC,eAAO,MAAM,eAAe,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAE5D,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC,CAKhF,CAAC;AAYJ,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,kBAAkB,CAE3E;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,iBAAiB,EAAE,CAKvF;AAED,wBAAgB,WAAW,CACzB,SAAS,EAAE,cAAc,EACzB,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,YAAY,CAAC,CAQvB;AA6ED,wBAAgB,aAAa,CAAC,MAAM,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,GAAG,YAAY,CAQjF;AAyID,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,eAAe,GAAG,IAAI,CAUtE"}
1
+ {"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../src/actions.ts"],"names":[],"mappings":"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"}
package/dist/actions.js CHANGED
@@ -1,8 +1,8 @@
1
- import { changeBatchIdentity, } from '@agentshouse/mdruntime';
1
+ import { changeBatchIdentity, HISTORY_CONVERSATIONS, } from '@agentshouse/mdruntime';
2
2
  import { doorRefusal } from './refusal.js';
3
- const HISTORY_PAGE = 100;
4
- const HISTORY_CONVERSATIONS = 64;
5
- const TRIGGER_ROOMS = 100;
3
+ import { finishedUpload, houseUpload } from './upload.js';
4
+ export const HISTORY_PAGE = 100;
5
+ export const TRIGGER_ROOMS = 100;
6
6
  const OCTET_STREAM = 'application/octet-stream';
7
7
  function mapped(door, input = (given) => given) {
8
8
  return {
@@ -37,10 +37,13 @@ const APP_ACTS = {
37
37
  'feature.list': mapped('apps/features/read'),
38
38
  'holder.list': mapped('apps/holders'),
39
39
  'holder.ring': mapped('apps/ring'),
40
+ 'inboxChat.assign': mapped('chats/assign'),
41
+ 'inboxChat.return': mapped('chats/return'),
42
+ 'inboxChat.takeControl': mapped('chats/take-control'),
40
43
  'member.list': mapped('rooms/members'),
41
44
  'message.send': mapped('apps/messages/send'),
42
45
  'processing.read': mapped('rooms/settings/processing/read'),
43
- 'record.list': carried('records/read', recordsAction),
46
+ 'record.list': mapped('records/read'),
44
47
  'relay.read': mapped('apps/relay'),
45
48
  'routine.list': mapped('routines/access'),
46
49
  'routineAccess.list': mapped('routines/granted'),
@@ -106,7 +109,12 @@ function unimplemented(name) {
106
109
  return { kind: 'rejected', code: 'action-unimplemented', fields: { name }, retryable: false };
107
110
  }
108
111
  function createBody(input) {
109
- return { agent_id: text(input, 'agent_id'), operation_id: operationId(input) };
112
+ const replaces = text(input, 'replaces');
113
+ return {
114
+ agent_id: text(input, 'agent_id'),
115
+ operation_id: operationId(input),
116
+ ...(replaces === undefined ? {} : { replaces }),
117
+ };
110
118
  }
111
119
  function controlBody(input) {
112
120
  return { conversation_id: text(input, 'conversation'), operation_id: operationId(input) };
@@ -154,32 +162,6 @@ export function actionOutcome(answer, name) {
154
162
  return { kind: 'transport-uncertainty' };
155
163
  return { kind: 'action', result: answer.body };
156
164
  }
157
- function referredRecord(view, roomRef) {
158
- const held = record(view);
159
- if (roomRef === undefined || typeof held.record !== 'string' || !Array.isArray(held.attachments))
160
- return view;
161
- const referrer = {
162
- room_ref: roomRef,
163
- record: held.record,
164
- };
165
- return {
166
- ...held,
167
- attachments: held.attachments.map((attachment) => ({ ...record(attachment), referrer })),
168
- };
169
- }
170
- async function recordsAction(transport, route, input, name) {
171
- const outcome = actionOutcome(await transport.door(route, input), name);
172
- if (outcome.kind !== 'action')
173
- return outcome;
174
- const roomRef = text(input, 'room_ref');
175
- const read = record(outcome.result);
176
- return {
177
- ...outcome,
178
- result: Array.isArray(read.records)
179
- ? { ...read, records: read.records.map((view) => referredRecord(view, roomRef)) }
180
- : referredRecord(outcome.result, roomRef),
181
- };
182
- }
183
165
  async function carry(transport, route, name, input) {
184
166
  return actionOutcome(await transport.door(route, input), name);
185
167
  }
@@ -217,14 +199,6 @@ function attachBody(input) {
217
199
  media_type: mediaType !== undefined && mediaType.length > 0 ? mediaType : OCTET_STREAM,
218
200
  };
219
201
  }
220
- function uploadGrant(answer) {
221
- const upload = record(record(answer).upload);
222
- if (upload.method !== 'POST')
223
- return null;
224
- if (typeof upload.operation !== 'string' || typeof upload.url !== 'string')
225
- return null;
226
- return { operation: upload.operation, url: upload.url };
227
- }
228
202
  async function attachAction(transport, route, input, name) {
229
203
  const body = attachBody(input);
230
204
  const blob = record(input).blob;
@@ -233,20 +207,16 @@ async function attachAction(transport, route, input, name) {
233
207
  const outcome = actionOutcome(await transport.door(route, body), name);
234
208
  if (outcome.kind !== 'action')
235
209
  return outcome;
236
- const grant = uploadGrant(outcome.result);
237
- if (grant === null)
210
+ const upload = houseUpload(record(outcome.result).upload);
211
+ if (upload === null)
238
212
  return { kind: 'transport-uncertainty' };
239
- const sent = await transport.transfer({
240
- act: 'file_upload',
241
- url: grant.url,
242
- body: new Uint8Array(await blob.arrayBuffer()),
243
- operation: grant.operation,
244
- });
245
- if (sent.status !== 200) {
246
- const refusal = doorRefusal(sent.body);
247
- return refusal === null ? { kind: 'transport-uncertainty' } : { kind: 'rejected', ...refusal };
248
- }
249
- const path = text(sent.body, 'path');
213
+ const finished = await finishedUpload(transport, upload, blob);
214
+ if (finished === null)
215
+ return { kind: 'transport-uncertainty' };
216
+ const settled = actionOutcome(finished, name);
217
+ if (settled.kind !== 'action')
218
+ return settled;
219
+ const path = text(settled.result, 'path');
250
220
  if (path === undefined)
251
221
  return { kind: 'transport-uncertainty' };
252
222
  const result = { path };
@@ -1 +1 @@
1
- {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAIV,OAAO,EACP,aAAa,EAmBd,MAAM,wBAAwB,CAAC;AA6ChC,OAAO,EASL,KAAK,kBAAkB,EACvB,KAAK,cAAc,EACpB,MAAM,gBAAgB,CAAC;AA0BxB,MAAM,MAAM,yBAAyB,GACjC;IACE,SAAS,EAAE,cAAc,CAAC;IAC1B,SAAS,IAAI,OAAO,CAAC,aAAa,CAAC,CAAC;CACrC,GACD;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAuFrD,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CA2mBpF"}
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAIV,OAAO,EACP,aAAa,EAkBd,MAAM,wBAAwB,CAAC;AA0ChC,OAAO,EASL,KAAK,kBAAkB,EACvB,KAAK,cAAc,EACpB,MAAM,gBAAgB,CAAC;AA0BxB,MAAM,MAAM,yBAAyB,GACjC;IACE,SAAS,EAAE,cAAc,CAAC;IAC1B,SAAS,IAAI,OAAO,CAAC,aAAa,CAAC,CAAC;CACrC,GACD;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AA8ErD,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAmoBpF"}
package/dist/adapter.js CHANGED
@@ -1,60 +1,48 @@
1
- import { sortedKeys, stepped } from '@agentshouse/mdruntime';
1
+ import { stepped } from '@agentshouse/mdruntime';
2
2
  import { DISCOVER_PREFIXES, capabilities, discoveredRoom, mapDiscovery, parseDiscovery, trackedFrom, } from './discovery.js';
3
- import { partModels, streamedModels, viewModels } from './model.js';
3
+ import { manifestedModels, partModels, streamedModels, viewModels } from './model.js';
4
4
  import { gathered, gatheredEach, selections } from './gather.js';
5
5
  import { createSiteAdapter } from './site.js';
6
- import { authorityRoot, bootstrapping, emptyScope, housePart, parseEnumeration, parseGrant, parseParted, refreshFrom, viewOf, wholeView, } from './read.js';
7
- import { PART_ATTEMPTS, unverifiedPart } from './part.js';
6
+ import { authorityRoot, bootstrapping, emptyScope, housePart, manifested, parseEnumeration, parseFileAddress, parseGrant, parseParted, partInventory, refreshFrom, viewOf, wholeView, } from './read.js';
7
+ import { PART_ATTEMPTS, partArrival, partFrame, unverifiedPart } from './part.js';
8
8
  import { sha256Hex } from './digest.js';
9
- import { acceptedHanded, enumeratedHanded, heldHanded, scopeHanded, streamedHanded, } from './handed.js';
9
+ import { finishedUpload, houseUpload } from './upload.js';
10
10
  import { houseAction } from './actions.js';
11
11
  import { classifyDoor, classifyRead, discoveryFailure, doorRefusal } from './refusal.js';
12
12
  import { houseNotifications } from './stream.js';
13
13
  import { DOOR_BATCH, DOOR_BATCH_UPLOAD, DOOR_BOOTSTRAP, DOOR_DISCOVER, DOOR_ENUMERATE, DOOR_FILES_GET, DOOR_REVISIONS, } from './transport.js';
14
14
  import { batchPaths, deltaBody, deltaBytes, deltaDigest, historyOutcome, houseChanges, houseMove, mutationOutcome, operationHead, rejectedMapping, revisionBody, uploadable, } from './write.js';
15
- import { houseReferrer } from './referrer.js';
15
+ import { HOUSE_NAMEABLE_ROOM_TYPES, HOUSE_ROOM_TYPES } from './room-types.js';
16
16
  import { followsVersion, logPosition, presented, reachedVersion, roomVersion, } from './version.js';
17
17
  const UNCERTAIN = { kind: 'transport-uncertainty' };
18
18
  const UNCERTAIN_READ = { kind: 'transport-uncertainty' };
19
+ const EXPIRED_ADDRESS = 403;
19
20
  function lapsedGrant(outcome) {
20
21
  return outcome.kind === 'rejected' && outcome.code === 'upload_unavailable';
21
22
  }
22
- const heldPaths = new WeakMap();
23
- async function sortedHeld(held) {
24
- const known = heldPaths.get(held.documents);
25
- if (known !== undefined)
26
- return known;
27
- const sorted = await stepped(sortedKeys(held.documents));
28
- heldPaths.set(held.documents, sorted);
29
- return sorted;
30
- }
31
- function heldFiles(authority, held, paths, bound) {
32
- const files = [];
33
- let through = null;
34
- for (let at = 0; at < paths.length && through === null; at += 1) {
35
- const revision = held.revisions[paths[at]];
36
- if (revision === undefined)
37
- continue;
38
- if (files.length === bound)
39
- through = files.at(-1).path;
40
- else
41
- files.push({ path: paths[at], revision });
42
- }
23
+ async function heldFiles(authority, held, bound) {
24
+ const listed = await held.files(null, bound + 1);
25
+ const files = listed.slice(0, bound);
26
+ const through = listed.length > bound ? (files.at(-1)?.path ?? null) : null;
43
27
  return files.length === 0 ? null : { room: authority, through, files };
44
28
  }
45
29
  async function reused(scope, held) {
46
30
  const sent = new Set(scope.contents.map((entry) => entry.path));
47
31
  const contents = [...scope.contents];
48
- for (const file of scope.files) {
49
- if (file.binary !== undefined || sent.has(file.path))
50
- continue;
51
- const content = held?.documents[file.path];
52
- if (content === undefined ||
53
- held?.revisions[file.path] !== file.revision ||
54
- (await sha256Hex(content)) !== file.sha256) {
32
+ const asked = scope.files.filter((file) => file.binary === undefined && !sent.has(file.path));
33
+ if (asked.length === 0)
34
+ return scope;
35
+ if (held === undefined)
36
+ return null;
37
+ const kept = await held.contents(asked.map(({ path }) => path));
38
+ for (const file of asked) {
39
+ const found = kept.get(file.path);
40
+ if (found === undefined ||
41
+ found.revision !== file.revision ||
42
+ (await sha256Hex(found.content)) !== file.sha256) {
55
43
  return null;
56
44
  }
57
- contents.push({ path: file.path, content });
45
+ contents.push({ path: file.path, content: found.content });
58
46
  }
59
47
  return { ...scope, contents };
60
48
  }
@@ -65,51 +53,56 @@ export function createAgentsHouseAdapter(options) {
65
53
  const notifications = houseNotifications(transport, 'credential');
66
54
  const tracked = new Map();
67
55
  const handed = new Map();
56
+ let heldRevisions;
68
57
  let bounds;
69
58
  function declared() {
70
59
  if (bounds === undefined)
71
60
  throw new Error('no Discovery declared the bounds');
72
61
  return bounds;
73
62
  }
74
- function hand(locator, steps) {
75
- const held = handed.get(locator);
76
- const next = stepped(steps);
77
- if (!(next instanceof Promise)) {
78
- handed.set(locator, next);
79
- return;
63
+ async function fetchedPart(entry, continued, listed, received, arriving) {
64
+ if (received !== undefined) {
65
+ const kept = new Uint8Array(received);
66
+ const held = await housePart(continued.room, kept, listed, 'credential');
67
+ if (held !== null)
68
+ return 'kind' in held ? held : { scope: held, frame: kept };
80
69
  }
81
- return next.then((room) => {
82
- if (handed.get(locator) === held)
83
- handed.set(locator, room);
84
- else
85
- handed.delete(locator);
86
- });
87
- }
88
- async function fetchedPart(entry, continued, listed) {
89
70
  for (let attempt = 0; attempt < PART_ATTEMPTS; attempt += 1) {
90
71
  const answered = await transport.door(DOOR_BOOTSTRAP, {
91
72
  prefixes: [authorityRoot(entry.handle)],
92
73
  part: listed.part,
93
74
  ...(continued.held === undefined ? {} : { held: [continued.held] }),
94
- });
75
+ }, partArrival(listed, arriving));
95
76
  const failure = classifyRead(answered.status, answered.body, entry.locator);
96
77
  if (failure !== null)
97
78
  return failure;
98
- const scope = await housePart(continued.room, answered.body, listed, 'credential');
79
+ const frame = partFrame(answered.body);
80
+ if (frame === null)
81
+ continue;
82
+ const scope = await housePart(continued.room, frame, listed, 'credential');
99
83
  if (scope !== null)
100
- return scope;
84
+ return 'kind' in scope ? scope : { scope, frame };
101
85
  }
102
86
  return unverifiedPart(entry.locator);
103
87
  }
104
- async function partPage(asked, continued) {
88
+ async function manifestPage(asked, continued) {
89
+ const inventory = partInventory(continued.room);
90
+ if (inventory.parts === 0) {
91
+ const page = await partPage(asked, continued, undefined, undefined);
92
+ return page.kind === 'bootstrap' ? { ...page, inventory } : page;
93
+ }
94
+ const { models, seen } = await manifestedModels(continued.room, asked.entry);
95
+ return manifested(continued.room, inventory, JSON.stringify({ ...continued, seen }), models);
96
+ }
97
+ async function partPage(asked, continued, received, arriving) {
105
98
  const { entry } = asked;
106
99
  const listed = continued.room.parts[continued.next];
107
100
  const sent = listed === undefined
108
- ? emptyScope(continued.room)
109
- : await fetchedPart(entry, continued, listed);
101
+ ? { scope: emptyScope(continued.room), frame: null }
102
+ : await fetchedPart(entry, continued, listed, received, arriving);
110
103
  if ('kind' in sent)
111
104
  return sent;
112
- const scope = await reused(sent, asked.held);
105
+ const scope = await reused(sent.scope, asked.held);
113
106
  if (scope === null) {
114
107
  return asked.held === undefined
115
108
  ? UNCERTAIN_READ
@@ -118,19 +111,20 @@ export function createAgentsHouseAdapter(options) {
118
111
  const next = continued.next + 1;
119
112
  const more = next < continued.room.parts.length;
120
113
  if (continued.next === 0 && !more)
121
- await hand(entry.locator, scopeHanded(scope));
114
+ handed.set(entry.locator, roomVersion(scope));
122
115
  else
123
116
  handed.delete(entry.locator);
124
117
  notifications.reach(entry.authority, scope);
125
118
  const seen = [
126
119
  ...continued.seen,
127
- ...entry.schemas.filter((schema) => scope.files.some((file) => file.path === schema)),
120
+ ...(scope.files.some((file) => file.path === entry.schema) ? [entry.schema] : []),
128
121
  ];
129
122
  const models = await partModels(scope, entry, new Set(seen), !more);
130
123
  return {
131
124
  ...(await stepped(bootstrapping(scope))),
132
125
  ...(models === undefined ? {} : { models }),
133
126
  ...(more ? { pageToken: JSON.stringify({ ...continued, next, seen }) } : {}),
127
+ ...(sent.frame === null ? {} : { received: sent.frame }),
134
128
  };
135
129
  }
136
130
  async function enumeratedPage(ask, page) {
@@ -162,18 +156,21 @@ export function createAgentsHouseAdapter(options) {
162
156
  const answer = await refreshFrom(view, ask.fromRoomVersion);
163
157
  if (answer.kind !== 'net-advancement')
164
158
  return answer;
165
- await hand(ask.entry.locator, enumeratedHanded(view, handed.get(ask.entry.locator)));
159
+ handed.set(ask.entry.locator, roomVersion(view));
166
160
  const models = await viewModels(view, ask.entry);
167
161
  return models === undefined ? answer : { ...answer, models };
168
162
  }
169
163
  async function streamed(entry, request, from) {
170
- const held = handed.get(entry.locator);
171
- if (held?.roomVersion !== request.fromRoomVersion)
164
+ if (handed.get(entry.locator) !== request.fromRoomVersion)
172
165
  return null;
173
- const advanced = await stepped(notifications.advance(entry, from, (path, revision) => held.revisions[path] === revision));
166
+ const asked = notifications.edited(entry, from);
167
+ const kept = asked.length === 0 || heldRevisions === undefined
168
+ ? new Map()
169
+ : await heldRevisions(entry.locator, asked);
170
+ const advanced = await stepped(notifications.advance(entry, from, (path, revision) => kept.get(path) === revision));
174
171
  if (advanced === null)
175
172
  return null;
176
- await hand(entry.locator, streamedHanded(held, advanced));
173
+ handed.set(entry.locator, advanced.page.roomVersion);
177
174
  const models = await streamedModels(advanced.schemas, entry, 'credential');
178
175
  return models === undefined ? advanced.page : { ...advanced.page, models };
179
176
  }
@@ -183,7 +180,7 @@ export function createAgentsHouseAdapter(options) {
183
180
  const at = logPosition(room.roomVersion);
184
181
  if (entry === undefined || at === null)
185
182
  return [];
186
- handed.set(room.locator, heldHanded(room));
183
+ handed.set(room.locator, room.roomVersion);
187
184
  notifications.reach(entry.authority, at);
188
185
  return [presented(entry.authority, at)];
189
186
  });
@@ -201,8 +198,9 @@ export function createAgentsHouseAdapter(options) {
201
198
  const held = handed.get(entry.locator);
202
199
  if (held === undefined)
203
200
  return outcome;
204
- const follows = followsVersion(held.roomVersion, outcome.roomVersion);
205
- await hand(entry.locator, acceptedHanded(held, outcome, follows ? outcome.roomVersion : held.roomVersion));
201
+ const follows = followsVersion(held, outcome.roomVersion);
202
+ if (follows)
203
+ handed.set(entry.locator, outcome.roomVersion);
206
204
  const at = logPosition(outcome.roomVersion);
207
205
  if (follows && at !== null)
208
206
  notifications.reach(entry.authority, at);
@@ -231,8 +229,7 @@ export function createAgentsHouseAdapter(options) {
231
229
  for (const ask of asks) {
232
230
  if (ask.held === undefined || bound === 0)
233
231
  continue;
234
- const paths = await sortedHeld(ask.held);
235
- const files = heldFiles(ask.entry.authority, ask.held, paths, bound);
232
+ const files = await heldFiles(ask.entry.authority, ask.held, bound);
236
233
  if (files !== null)
237
234
  held.push(files);
238
235
  }
@@ -270,7 +267,7 @@ export function createAgentsHouseAdapter(options) {
270
267
  continue;
271
268
  }
272
269
  const kept = held.find((files) => files.room === asked.entry.authority);
273
- answer(asked.ask, await partPage(asked.asked, {
270
+ answer(asked.ask, await manifestPage(asked.asked, {
274
271
  room,
275
272
  next: 0,
276
273
  seen: [],
@@ -342,7 +339,7 @@ export function createAgentsHouseAdapter(options) {
342
339
  };
343
340
  }
344
341
  const grant = parseGrant(minted.body);
345
- if (grant === null || grant.method !== 'POST')
342
+ if (grant === null)
346
343
  return { outcome: UNCERTAIN };
347
344
  const delivered = await transport.transfer({
348
345
  act: 'authored_batch_upload',
@@ -352,36 +349,31 @@ export function createAgentsHouseAdapter(options) {
352
349
  if (delivered.status !== 200)
353
350
  return { outcome: UNCERTAIN };
354
351
  const holding = parseGrant(delivered.body);
355
- if (holding === null || holding.method !== 'POST')
352
+ if (holding === null)
356
353
  return { outcome: UNCERTAIN };
357
354
  const upload = { url: holding.url, operation: holding.operation, bytes, sha256 };
358
355
  await keep(upload);
359
356
  return { body: { ...head, paths, upload, runtime: notifications.runtime } };
360
357
  }
361
- async function staged(answer, batch) {
358
+ async function staged(answer, { changeBatch: batch, file }) {
362
359
  const refusal = doorRefusal(answer.body);
363
360
  if (refusal?.code !== 'files_unstaged')
364
361
  return null;
365
- const grants = refusal.fields.upload;
366
- if (!Array.isArray(grants))
362
+ const uploads = refusal.fields.upload;
363
+ if (!Array.isArray(uploads))
367
364
  return false;
368
- for (const grant of grants) {
369
- const { url, operation, sha256 } = grant;
370
- const change = batch.changes.find((held) => held.kind === 'binary' && held.sha256 === sha256);
371
- if (typeof url !== 'string' || typeof operation !== 'string' || change === undefined)
365
+ for (const named of uploads) {
366
+ const upload = houseUpload(named);
367
+ const change = batch.changes.find((held) => held.kind === 'binary' && held.sha256 === named.sha256);
368
+ if (upload === null || change === undefined)
372
369
  return false;
373
- const delivered = await transport.transfer({
374
- act: 'binary_stage',
375
- url,
376
- body: change.blob,
377
- operation,
378
- });
379
- if (delivered.status !== 200)
370
+ const finished = await finishedUpload(transport, upload, await file(change.localId));
371
+ if (finished?.body?.staged !== true)
380
372
  return false;
381
373
  }
382
374
  return true;
383
375
  }
384
- async function submit(batch, head, changes, delta, bytes, entry, handed, keep, into) {
376
+ async function submit(request, head, changes, delta, bytes, entry, handed, keep, into) {
385
377
  const inline = { ...head, changes, runtime: notifications.runtime };
386
378
  const prepared = uploadable(inline, bytes, declared())
387
379
  ? await uploaded(head, changes, delta, bytes, entry, handed, keep)
@@ -389,11 +381,11 @@ export function createAgentsHouseAdapter(options) {
389
381
  if ('outcome' in prepared)
390
382
  return prepared.outcome;
391
383
  const answer = await transport.door(DOOR_BATCH, prepared.body);
392
- const stagedFiles = await staged(answer, batch);
384
+ const stagedFiles = await staged(answer, request);
393
385
  if (stagedFiles === false)
394
386
  return UNCERTAIN;
395
387
  const settled = stagedFiles === null ? answer : await transport.door(DOOR_BATCH, prepared.body);
396
- if (stagedFiles === true && (await staged(settled, batch)) !== null)
388
+ if (stagedFiles === true && (await staged(settled, request)) !== null)
397
389
  return UNCERTAIN;
398
390
  return stepped(mutationOutcome(settled.status, settled.body, entry.locator, entry.handle, into === undefined
399
391
  ? undefined
@@ -402,7 +394,7 @@ export function createAgentsHouseAdapter(options) {
402
394
  destination: { authority: into.authority, handle: into.handle },
403
395
  }));
404
396
  }
405
- function granted(answer, locator) {
397
+ function addressed(answer, locator) {
406
398
  const failure = classifyDoor(answer.status, answer.body, locator);
407
399
  if (failure !== null) {
408
400
  return failure.kind === 'scoped-refusal'
@@ -414,33 +406,48 @@ export function createAgentsHouseAdapter(options) {
414
406
  }
415
407
  : failure;
416
408
  }
417
- const grant = parseGrant(answer.body);
418
- const name = answer.body.name;
419
- if (grant === null || grant.method !== 'GET' || typeof name !== 'string') {
420
- return UNCERTAIN_READ;
421
- }
422
- return { kind: 'link', url: grant.url, name };
409
+ const address = parseFileAddress(answer.body);
410
+ return address === null ? UNCERTAIN_READ : { kind: 'address', ...address };
411
+ }
412
+ function linked(answer, locator) {
413
+ const address = addressed(answer, locator);
414
+ return address.kind === 'address'
415
+ ? { kind: 'link', url: address.url, name: address.name }
416
+ : address;
423
417
  }
424
- async function downloaded(answer, locator) {
425
- const link = granted(answer, locator);
426
- if (link.kind !== 'link')
427
- return link;
428
- const read = await transport.download(link.url);
429
- if (read.status !== 200 || read.blob === null)
418
+ async function downloaded(ask, locator) {
419
+ let address = addressed(await ask(), locator);
420
+ if (address.kind !== 'address')
421
+ return address;
422
+ let read = await transport.download(address.url);
423
+ if (read.status === EXPIRED_ADDRESS) {
424
+ address = addressed(await ask(), locator);
425
+ if (address.kind !== 'address')
426
+ return address;
427
+ read = await transport.download(address.url);
428
+ }
429
+ if (read.status !== 200 || read.body === null)
430
430
  return UNCERTAIN_READ;
431
- return { kind: 'binary', blob: read.blob };
431
+ return {
432
+ kind: 'binary',
433
+ body: read.body,
434
+ mediaType: address.mediaType,
435
+ sha256: address.sha256,
436
+ };
432
437
  }
433
- function pathGrant(request) {
438
+ function pathAddress(request) {
434
439
  const entry = authorityFor(request.locator);
435
440
  if (entry === undefined)
436
441
  return null;
437
- return transport.door(DOOR_FILES_GET, {
442
+ return () => transport.door(DOOR_FILES_GET, {
438
443
  room_ref: entry.authority,
439
444
  path: request.path,
440
445
  revision: request.revision,
441
446
  });
442
447
  }
443
448
  return {
449
+ roomTypes: HOUSE_ROOM_TYPES,
450
+ nameableRoomTypes: HOUSE_NAMEABLE_ROOM_TYPES,
444
451
  async authorize() {
445
452
  return options.authorize();
446
453
  },
@@ -490,7 +497,7 @@ export function createAgentsHouseAdapter(options) {
490
497
  return { kind: 'room-not-readable', locator: request.locator };
491
498
  const asked = { entry, held: request.held };
492
499
  if (request.pageToken !== undefined) {
493
- return partPage(asked, JSON.parse(request.pageToken));
500
+ return partPage(asked, JSON.parse(request.pageToken), request.received, request.arriving);
494
501
  }
495
502
  return bootstrapTogether(asked);
496
503
  },
@@ -527,7 +534,7 @@ export function createAgentsHouseAdapter(options) {
527
534
  const delta = deltaBody(mapped.changes);
528
535
  const bytes = deltaBytes(delta);
529
536
  const handed = request.upload;
530
- const answer = (upload) => submit(batch, head, mapped.changes, delta, bytes, entry, upload, keep, into);
537
+ const answer = (upload) => submit(request, head, mapped.changes, delta, bytes, entry, upload, keep, into);
531
538
  const settle = (outcome) => into === undefined ? adopted(entry, outcome) : adoptedMove(entry, into, outcome);
532
539
  const outcome = await answer(handed);
533
540
  if (handed === undefined || !lapsedGrant(outcome))
@@ -550,34 +557,16 @@ export function createAgentsHouseAdapter(options) {
550
557
  });
551
558
  },
552
559
  async binary(_authorization, request) {
553
- if (request.kind === 'path') {
554
- const asked = pathGrant(request);
555
- if (asked === null)
556
- return { kind: 'room-not-readable', locator: request.locator };
557
- return downloaded(await asked, request.locator);
558
- }
559
- const referrer = houseReferrer(request.referrer, tracked);
560
- if (referrer === null) {
561
- return { kind: 'rejected', code: 'referrer_unknown', fields: {}, retryable: false };
562
- }
563
- const answer = await transport.door(DOOR_FILES_GET, {
564
- file: request.fileRef,
565
- referrer: referrer.referrer,
566
- });
567
- return downloaded(answer, referrer.locator);
560
+ const ask = pathAddress(request);
561
+ if (ask === null)
562
+ return { kind: 'room-not-readable', locator: request.locator };
563
+ return downloaded(ask, request.locator);
568
564
  },
569
565
  async link(_authorization, request) {
570
- const asked = pathGrant(request);
571
- if (asked === null)
566
+ const ask = pathAddress(request);
567
+ if (ask === null)
572
568
  return { kind: 'room-not-readable', locator: request.locator };
573
- return granted(await asked, request.locator);
574
- },
575
- referrerScope(referrer) {
576
- const held = houseReferrer(referrer, tracked);
577
- if (held === null)
578
- return null;
579
- const { path } = held.referrer;
580
- return { locator: held.locator, ...(path === undefined ? {} : { path }) };
569
+ return linked(await ask(), request.locator);
581
570
  },
582
571
  reached(locator, version, target) {
583
572
  return reachedVersion(version, target, authorityFor(locator)?.logEpoch);
@@ -591,7 +580,10 @@ export function createAgentsHouseAdapter(options) {
591
580
  roomStream: {
592
581
  hold: held,
593
582
  reopen: notifications.reopen,
594
- observe: notifications.observe,
583
+ observe(notify, receipts, revisions) {
584
+ heldRevisions = revisions;
585
+ return notifications.observe(notify, receipts);
586
+ },
595
587
  published(locator, roomVersion) {
596
588
  const at = logPosition(roomVersion);
597
589
  if (at !== null)