@agentshouse/mdruntime-adapter 0.1.0-ca0d16f904a93 → 0.1.0-ca231c2cbedb4

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 (58) hide show
  1. package/README.md +2 -2
  2. package/dist/actions.d.ts +4 -22
  3. package/dist/actions.d.ts.map +1 -1
  4. package/dist/actions.js +23 -134
  5. package/dist/adapter.d.ts.map +1 -1
  6. package/dist/adapter.js +292 -171
  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 +6 -10
  14. package/dist/discovery.d.ts.map +1 -1
  15. package/dist/discovery.js +37 -32
  16. package/dist/gather.d.ts +1 -1
  17. package/dist/gather.d.ts.map +1 -1
  18. package/dist/gather.js +4 -14
  19. package/dist/house-model.d.ts +2 -0
  20. package/dist/house-model.d.ts.map +1 -0
  21. package/dist/house-model.js +484 -0
  22. package/dist/index.d.ts +7 -2
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +6 -1
  25. package/dist/model.d.ts +5 -1
  26. package/dist/model.d.ts.map +1 -1
  27. package/dist/model.js +11 -13
  28. package/dist/part.d.ts +15 -0
  29. package/dist/part.d.ts.map +1 -0
  30. package/dist/part.js +82 -0
  31. package/dist/read.d.ts +58 -24
  32. package/dist/read.d.ts.map +1 -1
  33. package/dist/read.js +270 -144
  34. package/dist/room-types.d.ts +7 -0
  35. package/dist/room-types.d.ts.map +1 -0
  36. package/dist/room-types.js +124 -0
  37. package/dist/site.d.ts.map +1 -1
  38. package/dist/site.js +101 -107
  39. package/dist/stream.d.ts +5 -5
  40. package/dist/stream.d.ts.map +1 -1
  41. package/dist/stream.js +66 -39
  42. package/dist/transport.d.ts +33 -16
  43. package/dist/transport.d.ts.map +1 -1
  44. package/dist/transport.js +2 -1
  45. package/dist/upload.d.ts +12 -0
  46. package/dist/upload.d.ts.map +1 -0
  47. package/dist/upload.js +63 -0
  48. package/dist/write.d.ts +12 -1
  49. package/dist/write.d.ts.map +1 -1
  50. package/dist/write.js +93 -55
  51. package/package.json +8 -4
  52. package/AGENTS.md +0 -300
  53. package/dist/handed.d.ts +0 -15
  54. package/dist/handed.d.ts.map +0 -1
  55. package/dist/handed.js +0 -38
  56. package/dist/referrer.d.ts +0 -7
  57. package/dist/referrer.d.ts.map +0 -1
  58. package/dist/referrer.js +0 -35
package/dist/site.js CHANGED
@@ -1,34 +1,38 @@
1
+ import { stepped } from '@agentshouse/mdruntime';
1
2
  import { actionOutcome, SITE_SUBMIT } from './actions.js';
2
3
  import { mapDiscovery, parseSiteDiscovery, siteCapabilities, siteTracked, } from './discovery.js';
3
- import { partModels, streamedModels, viewModels } from './model.js';
4
- import { advancedScope, bootstrapOf, parseEnumeration, parseSiteBootstrap, refreshFrom, siteBootstrapNext, siteBootstrapOffset, viewOf, wholeView, } from './read.js';
5
- import { sha256Hex } from './digest.js';
4
+ import { manifestedModels, partModels, streamedModels, viewModels } from './model.js';
5
+ import { bootstrapping, emptyScope, housePart, manifested, parseEnumeration, parseSiteParted, partInventory, refreshFrom, viewOf, wholeView, } from './read.js';
6
+ import { PART_ATTEMPTS, partArrival, partFrame, unverifiedPart } from './part.js';
6
7
  import { classifyDoor, classifyRead, discoveryFailure } from './refusal.js';
8
+ import { HOUSE_NAMEABLE_ROOM_TYPES, HOUSE_ROOM_TYPES } from './room-types.js';
7
9
  import { houseNotifications } from './stream.js';
8
- import { SITE_DOOR_BOOTSTRAP, SITE_DOOR_DISCOVER, SITE_DOOR_ENUMERATE, SITE_DOOR_SUBMIT, SITE_FILE_RECORD, SITE_FILES, } from './transport.js';
9
- import { houseReferrer } from './referrer.js';
10
+ import { SITE_DOOR_BOOTSTRAP, SITE_DOOR_DISCOVER, SITE_DOOR_ENUMERATE, SITE_DOOR_SUBMIT, SITE_FILES, } from './transport.js';
10
11
  import { followsVersion, logPosition, presented, reachedVersion, roomVersion, } from './version.js';
11
12
  const UNCERTAIN = { kind: 'transport-uncertainty' };
13
+ function unfound() {
14
+ return { kind: 'rejected', code: 'file_not_found', fields: {}, retryable: false };
15
+ }
12
16
  function siteRoom(tracked) {
13
17
  return {
14
18
  name: tracked.handle,
15
19
  locator: tracked.locator,
16
20
  handle: tracked.handle,
17
21
  roomVersion: roomVersion(tracked),
18
- operations: { read: [''], sync: [''], write: [], delete: [] },
19
- replica: { selection: tracked.locator, admission: 'admitted' },
22
+ operations: { write: [], delete: [] },
23
+ replica: { selection: tracked.locator, admission: 'admitted', offline: 'open' },
20
24
  providerOwnedPaths: [],
21
25
  };
22
26
  }
23
- function settled(refused) {
24
- return refused.kind === 'beyond-horizon' ? UNCERTAIN : refused;
25
- }
26
27
  export function createSiteAdapter(transport, snapshot = false) {
27
28
  const notifications = houseNotifications(transport, 'site');
28
29
  const tracked = new Map();
29
- function published(referrer) {
30
- const named = houseReferrer(referrer, tracked);
31
- return named === null || !tracked.has(named.locator) ? null : named;
30
+ const expired = new Map();
31
+ function fileRoute(request) {
32
+ const entry = tracked.get(request.locator);
33
+ if (entry === undefined)
34
+ return { kind: 'room-not-readable', locator: request.locator };
35
+ return `${SITE_FILES}${entry.authority}/${encodeURIComponent(request.revision)}/${encodeURIComponent(request.path)}`;
32
36
  }
33
37
  async function enumerated(entry, from, page) {
34
38
  const answer = await transport.door(SITE_DOOR_ENUMERATE, {
@@ -53,73 +57,43 @@ export function createSiteAdapter(transport, snapshot = false) {
53
57
  const first = await enumerated(entry, from);
54
58
  return 'kind' in first ? first : wholeView(first, (page) => enumerated(entry, from, page));
55
59
  }
56
- async function bootstrapPart(entry, after, offset) {
57
- const query = [
58
- ...(after === null ? [] : [`after=${encodeURIComponent(after)}`]),
59
- ...(offset === null ? [] : [`offset=${offset}`]),
60
- ];
61
- const answer = await transport.read(`${SITE_DOOR_BOOTSTRAP}${entry.authority}${query.length === 0 ? '' : `?${query.join('&')}`}`);
60
+ async function partedRoom(entry) {
61
+ const cut = expired.get(entry.locator);
62
+ const answer = await transport.read(`${SITE_DOOR_BOOTSTRAP}${entry.authority}${cut === undefined ? '' : `?expired=${cut}`}`);
62
63
  const classified = classifyDoor(answer.status, answer.body, entry.locator);
63
64
  if (classified !== null)
64
- return { failure: classified };
65
- const scope = parseSiteBootstrap(answer.body);
66
- return scope === null ? { failure: UNCERTAIN } : { body: answer.body, scope };
67
- }
68
- async function assembled(entry, scope, path, offset) {
69
- const opened = scope.contents.at(-1);
70
- if (opened === undefined || opened.path !== path || opened.content.length !== offset) {
71
- return UNCERTAIN;
72
- }
73
- let text = opened.content;
74
- for (;;) {
75
- const part = await bootstrapPart(entry, path, text.length);
76
- if ('failure' in part)
77
- return part.failure;
78
- const [piece] = part.scope.contents;
79
- if (piece === undefined ||
80
- part.scope.contents.length !== 1 ||
81
- piece.path !== path ||
82
- piece.content.length === 0) {
83
- return UNCERTAIN;
84
- }
85
- text += piece.content;
86
- const more = siteBootstrapOffset(part.body);
87
- if (more === null)
88
- break;
89
- if (more !== text.length)
90
- return UNCERTAIN;
91
- }
92
- const file = scope.files.find((held) => held.path === path);
93
- if (file === undefined || (await sha256Hex(text)) !== file.version)
94
- return UNCERTAIN;
95
- return { ...scope, contents: [...scope.contents.slice(0, -1), { path, content: text }] };
65
+ return classified;
66
+ const room = parseSiteParted(answer.body);
67
+ return room === null || room.anchor !== entry.authority ? UNCERTAIN : room;
96
68
  }
97
- async function caughtUp(entry, after) {
98
- const part = await bootstrapPart(entry, after, null);
99
- if ('failure' in part)
100
- return part;
101
- const next = siteBootstrapNext(part.body);
102
- const offset = siteBootstrapOffset(part.body);
103
- if (offset !== null) {
104
- if (next === null)
105
- return { failure: UNCERTAIN };
106
- const scope = await assembled(entry, part.scope, next, offset);
107
- return 'kind' in scope ? { failure: scope } : { scope, next };
69
+ async function fetchedPart(entry, room, listed, received, arriving) {
70
+ if (received !== undefined) {
71
+ const kept = new Uint8Array(received);
72
+ const held = await housePart(room, kept, listed, 'site');
73
+ if (held !== null)
74
+ return 'kind' in held ? held : { scope: held, frame: kept };
108
75
  }
109
- const { scope } = part;
110
- if (after !== null || next !== null || roomVersion(scope) === roomVersion(entry)) {
111
- return { scope, next };
76
+ for (let attempt = 0; attempt < PART_ATTEMPTS; attempt += 1) {
77
+ const answer = await transport.read(`${SITE_DOOR_BOOTSTRAP}${entry.authority}?part=${encodeURIComponent(listed.part)}`, partArrival(listed, arriving));
78
+ const failure = classifyRead(answer.status, answer.body, entry.locator);
79
+ if (failure?.kind === 'beyond-horizon')
80
+ expired.set(entry.locator, room.position);
81
+ if (failure !== null)
82
+ return failure;
83
+ const frame = partFrame(answer.body);
84
+ if (frame === null)
85
+ continue;
86
+ const scope = await housePart(room, frame, listed, 'site');
87
+ if (scope !== null)
88
+ return 'kind' in scope ? scope : { scope, frame };
112
89
  }
113
- const view = await enumeration(entry, scope);
114
- if ('kind' in view)
115
- return { failure: settled(view) };
116
- return { scope: advancedScope(scope, view), next: null };
90
+ return unverifiedPart(entry.locator);
117
91
  }
118
92
  async function refreshed(entry, fromRoomVersion) {
119
93
  const from = logPosition(fromRoomVersion);
120
94
  if (from === null)
121
95
  return { kind: 'beyond-horizon' };
122
- const streamed = notifications.advance(entry, from);
96
+ const streamed = await stepped(notifications.advance(entry, from));
123
97
  if (streamed !== null) {
124
98
  const models = await streamedModels(streamed.schemas, entry, 'site');
125
99
  return models === undefined ? streamed.page : { ...streamed.page, models };
@@ -128,7 +102,7 @@ export function createSiteAdapter(transport, snapshot = false) {
128
102
  if ('kind' in view)
129
103
  return view;
130
104
  notifications.reach(entry.authority, view);
131
- const answer = refreshFrom(view, fromRoomVersion);
105
+ const answer = await refreshFrom(view, fromRoomVersion);
132
106
  if (answer.kind !== 'net-advancement')
133
107
  return answer;
134
108
  const models = await viewModels(view, entry);
@@ -137,6 +111,8 @@ export function createSiteAdapter(transport, snapshot = false) {
137
111
  return {
138
112
  localSaves: true,
139
113
  exhaustiveDiscovery: true,
114
+ roomTypes: HOUSE_ROOM_TYPES,
115
+ nameableRoomTypes: HOUSE_NAMEABLE_ROOM_TYPES,
140
116
  async authorize() {
141
117
  return {};
142
118
  },
@@ -159,23 +135,43 @@ export function createSiteAdapter(transport, snapshot = false) {
159
135
  const entry = tracked.get(request.locator);
160
136
  if (entry === undefined)
161
137
  return { kind: 'room-not-readable', locator: request.locator };
162
- const continued = request.pageToken === undefined
163
- ? null
164
- : JSON.parse(request.pageToken);
165
- const held = await caughtUp(entry, continued?.after ?? null);
166
- if ('failure' in held)
167
- return held.failure;
168
- notifications.reach(entry.authority, held.scope);
138
+ let continued;
139
+ let inventory;
140
+ if (request.pageToken === undefined) {
141
+ const room = await partedRoom(entry);
142
+ if ('kind' in room)
143
+ return room;
144
+ const { models, seen } = await manifestedModels(room, entry);
145
+ continued = { room, next: 0, seen };
146
+ inventory = partInventory(room);
147
+ if (inventory.parts > 0) {
148
+ return manifested(room, inventory, JSON.stringify(continued), models);
149
+ }
150
+ }
151
+ else
152
+ continued = JSON.parse(request.pageToken);
153
+ const listed = continued.room.parts[continued.next];
154
+ const sent = listed === undefined
155
+ ? { scope: emptyScope(continued.room), frame: null }
156
+ : await fetchedPart(entry, continued.room, listed, request.received, request.arriving);
157
+ if ('kind' in sent)
158
+ return sent;
159
+ const { scope } = sent;
160
+ notifications.reach(entry.authority, scope);
161
+ const next = continued.next + 1;
162
+ const more = next < continued.room.parts.length;
169
163
  const seen = [
170
- ...(continued?.seen ?? []),
171
- ...entry.schemas.filter((schema) => held.scope.files.some((file) => file.path === schema)),
164
+ ...continued.seen,
165
+ ...(scope.files.some((file) => file.path === entry.schema) ? [entry.schema] : []),
172
166
  ];
173
- const models = await partModels(held.scope, entry, new Set(seen), held.next === null);
174
- const continuation = held.next === null ? null : { after: held.next, seen };
167
+ const models = await partModels(scope, entry, new Set(seen), !more);
168
+ const { folders: _folders, ...page } = await stepped(bootstrapping(scope));
175
169
  return {
176
- ...bootstrapOf(held.scope),
170
+ ...page,
177
171
  ...(models === undefined ? {} : { models }),
178
- ...(continuation === null ? {} : { pageToken: JSON.stringify(continuation) }),
172
+ ...(more ? { pageToken: JSON.stringify({ ...continued, next, seen }) } : {}),
173
+ ...(inventory === undefined ? {} : { inventory }),
174
+ ...(sent.frame === null ? {} : { received: sent.frame }),
179
175
  };
180
176
  },
181
177
  async refresh(_authorization, request) {
@@ -194,34 +190,32 @@ export function createSiteAdapter(transport, snapshot = false) {
194
190
  async mutate() {
195
191
  return { kind: 'rejected', affected: {}, code: 'write-grant', fields: {}, retryable: false };
196
192
  },
193
+ async attach() {
194
+ return { kind: 'rejected', code: 'write-grant', fields: {}, retryable: false };
195
+ },
197
196
  async history() {
198
197
  return { kind: 'no-history' };
199
198
  },
200
199
  async binary(_authorization, request) {
201
- const named = published(request.kind === 'path' ? { room: request.locator, path: request.path } : request.referrer);
202
- if (named === null) {
203
- return { kind: 'rejected', code: 'referrer_unknown', fields: {}, retryable: false };
204
- }
205
- if (request.fileRef === undefined) {
206
- return { kind: 'rejected', code: 'file_not_found', fields: {}, retryable: false };
207
- }
208
- const { room_ref, path, record } = named.referrer;
209
- const read = await transport.download(`${SITE_FILES}${room_ref}/${encodeURIComponent(request.fileRef)}/${path === undefined
210
- ? `${SITE_FILE_RECORD}/${encodeURIComponent(record)}`
211
- : encodeURIComponent(path)}`);
212
- if (read.status === 404) {
213
- return { kind: 'rejected', code: 'file_not_found', fields: {}, retryable: false };
214
- }
215
- return read.status === 200 && read.blob !== null
216
- ? { kind: 'binary', blob: read.blob }
200
+ const route = fileRoute(request);
201
+ if (typeof route !== 'string')
202
+ return route;
203
+ const read = await transport.download(route);
204
+ if (read.status === 404)
205
+ return unfound();
206
+ return read.status === 200 && read.body !== null
207
+ ? { kind: 'binary', body: read.body, mediaType: read.mediaType }
217
208
  : UNCERTAIN;
218
209
  },
219
- referrerScope(referrer) {
220
- const named = published(referrer);
221
- if (named === null)
222
- return null;
223
- const { path } = named.referrer;
224
- return { kind: 'room', locator: named.locator, ...(path === undefined ? {} : { path }) };
210
+ async link(_authorization, request) {
211
+ const route = fileRoute(request);
212
+ if (typeof route !== 'string')
213
+ return route;
214
+ return {
215
+ kind: 'link',
216
+ url: route,
217
+ name: request.path.slice(request.path.lastIndexOf('/') + 1),
218
+ };
225
219
  },
226
220
  reached(locator, version, target) {
227
221
  return reachedVersion(version, target, tracked.get(locator)?.logEpoch);
@@ -253,7 +247,7 @@ export function createSiteAdapter(transport, snapshot = false) {
253
247
  return [];
254
248
  notifications.reach(entry.authority, at);
255
249
  return [presented(entry.authority, at)];
256
- }), null);
250
+ }));
257
251
  },
258
252
  reopen: notifications.reopen,
259
253
  observe: notifications.observe,
package/dist/stream.d.ts CHANGED
@@ -1,22 +1,22 @@
1
- import type { ConversationStream, RefreshPage, RoomStreamEvent, ServiceModeSignal, StreamReceipts } from '@agentshouse/mdruntime';
1
+ import { type ConversationStream, type RefreshPage, type RoomStreamEvent, type ServiceModeSignal, type StreamReceipts } from '@agentshouse/mdruntime';
2
2
  import type { TrackedAuthority } from './discovery.js';
3
- import type { HouseDemand, HouseDoor, HouseHeldRoom, HouseMode, HouseStreamEntry } from './transport.js';
3
+ import type { HouseDoor, HouseHeldRoom, HouseMode, HouseStreamEntry } from './transport.js';
4
4
  import { type LogPosition } from './version.js';
5
5
  export type HeldRevision = (path: string, revision: string) => boolean;
6
6
  export type StreamedAdvancement = {
7
7
  page: RefreshPage;
8
- entries: readonly HouseStreamEntry[];
9
8
  schemas: ReadonlyMap<string, HouseStreamEntry>;
10
9
  };
11
10
  export type HouseNotifications = {
12
11
  runtime: string;
13
12
  observe(notify: (event: RoomStreamEvent) => void, receipts: StreamReceipts): () => void;
14
- send(rooms: readonly HouseHeldRoom[], demand: HouseDemand | null): void;
13
+ send(rooms: readonly HouseHeldRoom[]): void;
15
14
  published(locator: string, at: LogPosition): void;
16
15
  reopen(): void;
17
16
  track(authorities: readonly TrackedAuthority[]): void;
18
17
  reach(authority: string, at: LogPosition): void;
19
- advance(tracked: TrackedAuthority, from: LogPosition, holds?: HeldRevision): StreamedAdvancement | null;
18
+ edited(tracked: TrackedAuthority, from: LogPosition): readonly string[];
19
+ advance(tracked: TrackedAuthority, from: LogPosition, holds?: HeldRevision): Generator<number, StreamedAdvancement | null, void>;
20
20
  conversations: ConversationStream;
21
21
  serviceMode: ServiceModeSignal;
22
22
  };
@@ -1 +1 @@
1
- {"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../src/stream.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAGV,kBAAkB,EAElB,WAAW,EACX,eAAe,EAEf,iBAAiB,EACjB,cAAc,EAEf,MAAM,wBAAwB,CAAC;AAGhC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEvD,OAAO,KAAK,EACV,WAAW,EACX,SAAS,EACT,aAAa,EAEb,SAAS,EAET,gBAAgB,EAEjB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAe,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AA0C7D,MAAM,MAAM,YAAY,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC;AAEvE,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,SAAS,gBAAgB,EAAE,CAAC;IACrC,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;CAChD,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,EAAE,QAAQ,EAAE,cAAc,GAAG,MAAM,IAAI,CAAC;IACxF,IAAI,CAAC,KAAK,EAAE,SAAS,aAAa,EAAE,EAAE,MAAM,EAAE,WAAW,GAAG,IAAI,GAAG,IAAI,CAAC;IACxE,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,WAAW,GAAG,IAAI,CAAC;IAClD,MAAM,IAAI,IAAI,CAAC;IACf,KAAK,CAAC,WAAW,EAAE,SAAS,gBAAgB,EAAE,GAAG,IAAI,CAAC;IACtD,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,WAAW,GAAG,IAAI,CAAC;IAChD,OAAO,CACL,OAAO,EAAE,gBAAgB,EACzB,IAAI,EAAE,WAAW,EACjB,KAAK,CAAC,EAAE,YAAY,GACnB,mBAAmB,GAAG,IAAI,CAAC;IAC9B,aAAa,EAAE,kBAAkB,CAAC;IAClC,WAAW,EAAE,iBAAiB,CAAC;CAChC,CAAC;AAkFF,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,GAAG,kBAAkB,CAmhB5F"}
1
+ {"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../src/stream.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,kBAAkB,EAEvB,KAAK,WAAW,EAChB,KAAK,eAAe,EAEpB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EAEpB,MAAM,wBAAwB,CAAC;AAGhC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAUvD,OAAO,KAAK,EACV,SAAS,EACT,aAAa,EAEb,SAAS,EAET,gBAAgB,EAEjB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAe,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AA0C7D,MAAM,MAAM,YAAY,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC;AAEvE,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;CAChD,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,EAAE,QAAQ,EAAE,cAAc,GAAG,MAAM,IAAI,CAAC;IACxF,IAAI,CAAC,KAAK,EAAE,SAAS,aAAa,EAAE,GAAG,IAAI,CAAC;IAC5C,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,WAAW,GAAG,IAAI,CAAC;IAClD,MAAM,IAAI,IAAI,CAAC;IACf,KAAK,CAAC,WAAW,EAAE,SAAS,gBAAgB,EAAE,GAAG,IAAI,CAAC;IACtD,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,WAAW,GAAG,IAAI,CAAC;IAChD,MAAM,CAAC,OAAO,EAAE,gBAAgB,EAAE,IAAI,EAAE,WAAW,GAAG,SAAS,MAAM,EAAE,CAAC;IACxE,OAAO,CACL,OAAO,EAAE,gBAAgB,EACzB,IAAI,EAAE,WAAW,EACjB,KAAK,CAAC,EAAE,YAAY,GACnB,SAAS,CAAC,MAAM,EAAE,mBAAmB,GAAG,IAAI,EAAE,IAAI,CAAC,CAAC;IACvD,aAAa,EAAE,kBAAkB,CAAC;IAClC,WAAW,EAAE,iBAAiB,CAAC;CAChC,CAAC;AAmFF,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,GAAG,kBAAkB,CA8jB5F"}
package/dist/stream.js CHANGED
@@ -1,6 +1,7 @@
1
+ import { ROW_WEIGHT, } from '@agentshouse/mdruntime';
1
2
  import { acceptedMessage } from './actions.js';
2
3
  import { sha256Hex } from './digest.js';
3
- import { binaryFileOf, headChange } from './read.js';
4
+ import { answered, answering, binaryFileOf, folded, forgotten, headChange, revised, } from './read.js';
4
5
  import { roomVersion } from './version.js';
5
6
  const HELD_FRAMES_MAX = 256;
6
7
  function turnOperation(frame) {
@@ -15,6 +16,7 @@ function listedConversation(listed) {
15
16
  created: listed.created_at,
16
17
  finished: listed.finished_at,
17
18
  outcome: listed.outcome,
19
+ waiting: listed.waiting,
18
20
  };
19
21
  }
20
22
  function position(value) {
@@ -111,7 +113,10 @@ export function houseNotifications(transport, mode) {
111
113
  event.kind === 'questions' ||
112
114
  event.kind === 'process' ||
113
115
  event.kind === 'commands' ||
114
- event.kind === 'options') {
116
+ event.kind === 'options' ||
117
+ event.kind === 'starting' ||
118
+ event.kind === 'link' ||
119
+ event.kind === 'deliveries') {
115
120
  live.add(event.conversation);
116
121
  }
117
122
  for (const observer of conversationObservers)
@@ -157,6 +162,18 @@ export function houseNotifications(transport, mode) {
157
162
  emitConversation({ kind: 'options', conversation, options: structuredClone(frame.options) });
158
163
  return;
159
164
  }
165
+ if (frame.type === 'starting') {
166
+ emitConversation({ kind: 'starting', conversation, step: frame.step });
167
+ return;
168
+ }
169
+ if (frame.type === 'link') {
170
+ emitConversation({ kind: 'link', conversation, connected: frame.connected });
171
+ return;
172
+ }
173
+ if (frame.type === 'deliveries') {
174
+ emitConversation({ kind: 'deliveries', conversation, unknown: [...frame.unknown] });
175
+ return;
176
+ }
160
177
  if (frame.type === 'interactions') {
161
178
  emitConversation({
162
179
  kind: 'questions',
@@ -352,6 +369,10 @@ export function houseNotifications(transport, mode) {
352
369
  });
353
370
  return;
354
371
  }
372
+ if (frame.type === 'authority_changed') {
373
+ emit({ kind: 'authority-changed', scope: { authorities: frame.rooms } });
374
+ return;
375
+ }
355
376
  if (frame.type === 'conversations') {
356
377
  emitConversation({ kind: 'list', entries: frame.conversations.map(listedConversation) });
357
378
  return;
@@ -368,6 +389,9 @@ export function houseNotifications(transport, mode) {
368
389
  frame.type === 'process' ||
369
390
  frame.type === 'commands' ||
370
391
  frame.type === 'options' ||
392
+ frame.type === 'starting' ||
393
+ frame.type === 'link' ||
394
+ frame.type === 'deliveries' ||
371
395
  frame.type === 'interactions' ||
372
396
  frame.type === 'message') {
373
397
  converse(frame);
@@ -421,10 +445,10 @@ export function houseNotifications(transport, mode) {
421
445
  });
422
446
  });
423
447
  stream.opened(reopened);
424
- stream.closed((revoked) => {
448
+ stream.closed(({ revoked, lost }) => {
425
449
  opening = null;
426
450
  acknowledging = null;
427
- emit({ kind: 'closed', revoked });
451
+ emit({ kind: 'closed', revoked, lost });
428
452
  });
429
453
  return {
430
454
  runtime,
@@ -463,7 +487,7 @@ export function houseNotifications(transport, mode) {
463
487
  observers.delete(notify);
464
488
  };
465
489
  },
466
- send(rooms, demand) {
490
+ send(rooms) {
467
491
  for (const room of rooms) {
468
492
  const line = lines.get(room.authority);
469
493
  const held = position(room.position);
@@ -478,7 +502,7 @@ export function houseNotifications(transport, mode) {
478
502
  void loaded
479
503
  .then(() => Promise.all(rooms.map(restored)))
480
504
  .then((presented) => {
481
- current.hold(presented, runtime, demand);
505
+ current.hold(presented, runtime);
482
506
  for (const [index, room] of presented.entries()) {
483
507
  if (room.position === rooms[index].position)
484
508
  continue;
@@ -532,7 +556,18 @@ export function houseNotifications(transport, mode) {
532
556
  held,
533
557
  });
534
558
  },
535
- advance(tracked, from, holds) {
559
+ edited(tracked, from) {
560
+ const line = lines.get(tracked.authority);
561
+ const start = position(from.position);
562
+ if (line === undefined || start === null || line.logEpoch !== from.logEpoch)
563
+ return [];
564
+ return [
565
+ ...new Set(line.frames
566
+ .filter((frame) => frame.position > start)
567
+ .flatMap((frame) => frame.entries.flatMap((entry) => entry.kind === 'edited' && entry.revision !== undefined ? [entry.path] : []))),
568
+ ];
569
+ },
570
+ *advance(tracked, from, holds) {
536
571
  const line = lines.get(tracked.authority);
537
572
  const start = position(from.position);
538
573
  if (line === undefined || start === null || line.logEpoch !== from.logEpoch)
@@ -541,36 +576,26 @@ export function houseNotifications(transport, mode) {
541
576
  const first = pending[0];
542
577
  if (first === undefined || first.after > start)
543
578
  return null;
544
- const documents = {};
545
- const revisions = {};
546
- const changes = {};
547
- const binaryFiles = {};
548
- const removals = [];
549
- const applied = [];
579
+ const answer = answering();
550
580
  const schemas = new Map();
551
- const forget = (path) => {
552
- delete documents[path];
553
- delete revisions[path];
554
- delete changes[path];
555
- delete binaryFiles[path];
556
- const removed = removals.indexOf(path);
557
- if (removed !== -1)
558
- removals.splice(removed, 1);
559
- };
560
581
  for (const frame of pending) {
561
582
  for (const entry of frame.entries) {
583
+ yield ROW_WEIGHT + (entry.text?.length ?? 0);
584
+ if (entry.kind === 'folder') {
585
+ folded(answer, entry.path, entry.removed === true);
586
+ continue;
587
+ }
562
588
  if (entry.kind === 'edited' &&
563
589
  entry.revision !== undefined &&
564
590
  holds?.(entry.path, entry.revision) === true) {
565
- forget(entry.path);
591
+ forgotten(answer, entry.path);
566
592
  continue;
567
593
  }
568
- applied.push(entry);
569
- if (tracked.schemas.includes(entry.path))
594
+ if (entry.path === tracked.schema)
570
595
  schemas.set(entry.path, entry);
571
596
  if (entry.kind === 'removed') {
572
- forget(entry.path);
573
- removals.push(entry.path);
597
+ forgotten(answer, entry.path);
598
+ answer.removals.add(entry.path);
574
599
  continue;
575
600
  }
576
601
  const binary = binaryFileOf(entry);
@@ -581,31 +606,33 @@ export function houseNotifications(transport, mode) {
581
606
  const head = headChange(entry);
582
607
  if (head === null)
583
608
  return null;
584
- forget(entry.path);
609
+ forgotten(answer, entry.path);
585
610
  if (binary === undefined)
586
- documents[entry.path] = entry.text;
611
+ answer.documents.set(entry.path, entry.text);
587
612
  else
588
- binaryFiles[entry.path] = binary;
613
+ answer.binaryFiles.set(entry.path, binary);
589
614
  const revision = entry.revision ?? binary?.sha256;
590
615
  if (revision !== undefined)
591
- revisions[entry.path] = revision;
616
+ revised(answer, entry.path, revision);
592
617
  if (head !== undefined)
593
- changes[entry.path] = head;
618
+ answer.changes.set(entry.path, head);
594
619
  }
595
620
  }
596
621
  const last = pending.at(-1);
597
- lines.set(tracked.authority, { ...line, frames: pending, held: last.position });
622
+ const current = lines.get(tracked.authority);
623
+ if (current?.logEpoch === line.logEpoch) {
624
+ lines.set(tracked.authority, {
625
+ ...current,
626
+ frames: current.frames.filter((frame) => frame.position > start),
627
+ held: current.held !== null && current.held > last.position ? current.held : last.position,
628
+ });
629
+ }
598
630
  return {
599
631
  page: {
600
632
  kind: 'net-advancement',
601
633
  roomVersion: roomVersion({ position: last.position.toString(), logEpoch: line.logEpoch }),
602
- documents,
603
- revisions,
604
- changes,
605
- binaryFiles,
606
- removals: removals.sort(),
634
+ ...(yield* answered(answer)),
607
635
  },
608
- entries: applied,
609
636
  schemas,
610
637
  };
611
638
  },
@@ -15,27 +15,27 @@ export type HouseDoorAnswer = {
15
15
  body: unknown;
16
16
  };
17
17
  export declare const DOOR_FILES_GET = "/door/files/get";
18
+ export declare const DOOR_FILES_ATTACH = "/door/files/attach";
19
+ export declare const DOOR_FILES_FINISH = "/door/files/finish";
18
20
  export declare const SITE_FILES = "/_house/files/";
19
- export declare const SITE_FILE_RECORD = "record";
20
- export type HouseByteAct = 'authored_batch_upload' | 'conversation_file_upload' | 'binary_stage';
21
- export type HouseByteBody = string | ArrayBuffer | Uint8Array | Blob;
21
+ export type HouseByteAct = 'authored_batch_upload';
22
22
  export type HouseBytesAnswer = {
23
23
  status: number;
24
- blob: Blob | null;
24
+ body: ReadableStream<Uint8Array> | null;
25
+ mediaType: string;
25
26
  };
26
27
  export type HouseByteTransfer = {
27
28
  act: HouseByteAct;
28
29
  url: string;
29
- body: HouseByteBody;
30
- operation?: string;
30
+ body: string;
31
31
  };
32
32
  export type HouseStreamEntry = {
33
33
  path: string;
34
- kind: 'edited' | 'removed' | 'piece';
34
+ kind: 'edited' | 'removed' | 'piece' | 'folder';
35
+ removed?: true;
35
36
  revision?: string;
36
37
  sha256?: string;
37
38
  text?: string;
38
- file?: string;
39
39
  bytes?: number;
40
40
  media_type?: string;
41
41
  author?: {
@@ -58,6 +58,7 @@ export type HouseListedConversation = {
58
58
  created_at: string;
59
59
  finished_at: string | null;
60
60
  outcome: string | null;
61
+ waiting: boolean;
61
62
  };
62
63
  export type HouseStreamFrame = {
63
64
  type: 'part';
@@ -80,6 +81,9 @@ export type HouseStreamFrame = {
80
81
  } | {
81
82
  type: 'opening';
82
83
  expired: readonly string[];
84
+ } | {
85
+ type: 'authority_changed';
86
+ rooms: readonly string[];
83
87
  } | ({
84
88
  type: 'turn';
85
89
  conversation_id: string;
@@ -92,6 +96,10 @@ export type HouseStreamFrame = {
92
96
  type: 'interactions';
93
97
  conversation_id: string;
94
98
  interactions: readonly unknown[];
99
+ } | {
100
+ type: 'deliveries';
101
+ conversation_id: string;
102
+ unknown: readonly string[];
95
103
  } | {
96
104
  type: 'process';
97
105
  conversation_id: string;
@@ -104,6 +112,14 @@ export type HouseStreamFrame = {
104
112
  type: 'options';
105
113
  conversation_id: string;
106
114
  options: LiveFact<'options'>['options'];
115
+ } | {
116
+ type: 'starting';
117
+ conversation_id: string;
118
+ step: string | null;
119
+ } | {
120
+ type: 'link';
121
+ conversation_id: string;
122
+ connected: boolean;
107
123
  } | {
108
124
  type: 'conversations';
109
125
  conversations: readonly HouseListedConversation[];
@@ -134,31 +150,32 @@ export type HouseHeldRoom = {
134
150
  log_epoch: string;
135
151
  carry?: HouseHeldCarry;
136
152
  };
137
- export type HouseDemand = {
138
- rooms: readonly string[];
139
- conversations: boolean;
140
- };
141
153
  export type HouseOpening = {
142
- hold(rooms: readonly HouseHeldRoom[], runtime: string, demand: HouseDemand | null): void;
154
+ hold(rooms: readonly HouseHeldRoom[], runtime: string): void;
143
155
  received(authority: string, carry: string, index: number): void;
144
156
  close(): void;
145
157
  };
158
+ export type HouseClosing = {
159
+ revoked: boolean;
160
+ lost: boolean;
161
+ };
146
162
  export type HouseStream = {
147
163
  observe(receive: (frame: HouseStreamFrame) => void): () => void;
148
164
  opened(notify: (opening: HouseOpening) => void): () => void;
149
- closed(notify: (revoked: boolean) => void): () => void;
165
+ closed(notify: (closing: HouseClosing) => void): () => void;
150
166
  };
151
167
  export type HouseDoor = {
152
- door(route: string, body: unknown): Promise<HouseDoorAnswer>;
168
+ door(route: string, body: unknown, arriving?: (bytes: number) => void): Promise<HouseDoorAnswer>;
153
169
  stream: HouseStream;
154
170
  };
155
171
  export type HouseTransport = HouseDoor & {
156
172
  transfer(request: HouseByteTransfer): Promise<HouseDoorAnswer>;
173
+ put(url: string, body: Blob, headers: Readonly<Record<string, string>>): Promise<number>;
157
174
  download(url: string): Promise<HouseBytesAnswer>;
158
175
  };
159
176
  export type HouseSiteTransport = HouseDoor & {
160
177
  online(): boolean;
161
- read(route: string): Promise<HouseDoorAnswer>;
178
+ read(route: string, arriving?: (bytes: number) => void): Promise<HouseDoorAnswer>;
162
179
  download(route: string): Promise<HouseBytesAnswer>;
163
180
  };
164
181
  export {};