@agentshouse/mdruntime-adapter 0.1.0-cbbc9b36e7fd4 → 0.1.0-cbc5a1c3e1e08

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 +10 -21
  3. package/dist/actions.d.ts.map +1 -1
  4. package/dist/actions.js +68 -133
  5. package/dist/adapter.d.ts.map +1 -1
  6. package/dist/adapter.js +248 -170
  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 +98 -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 +32 -16
  43. package/dist/transport.d.ts.map +1 -1
  44. package/dist/transport.js +1 -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 -299
  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) {
@@ -198,30 +194,25 @@ export function createSiteAdapter(transport, snapshot = false) {
198
194
  return { kind: 'no-history' };
199
195
  },
200
196
  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 }
197
+ const route = fileRoute(request);
198
+ if (typeof route !== 'string')
199
+ return route;
200
+ const read = await transport.download(route);
201
+ if (read.status === 404)
202
+ return unfound();
203
+ return read.status === 200 && read.body !== null
204
+ ? { kind: 'binary', body: read.body, mediaType: read.mediaType }
217
205
  : UNCERTAIN;
218
206
  },
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 }) };
207
+ async link(_authorization, request) {
208
+ const route = fileRoute(request);
209
+ if (typeof route !== 'string')
210
+ return route;
211
+ return {
212
+ kind: 'link',
213
+ url: route,
214
+ name: request.path.slice(request.path.lastIndexOf('/') + 1),
215
+ };
225
216
  },
226
217
  reached(locator, version, target) {
227
218
  return reachedVersion(version, target, tracked.get(locator)?.logEpoch);
@@ -253,7 +244,7 @@ export function createSiteAdapter(transport, snapshot = false) {
253
244
  return [];
254
245
  notifications.reach(entry.authority, at);
255
246
  return [presented(entry.authority, at)];
256
- }), null);
247
+ }));
257
248
  },
258
249
  reopen: notifications.reopen,
259
250
  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,26 @@ 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_FINISH = "/door/files/finish";
18
19
  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;
20
+ export type HouseByteAct = 'authored_batch_upload';
22
21
  export type HouseBytesAnswer = {
23
22
  status: number;
24
- blob: Blob | null;
23
+ body: ReadableStream<Uint8Array> | null;
24
+ mediaType: string;
25
25
  };
26
26
  export type HouseByteTransfer = {
27
27
  act: HouseByteAct;
28
28
  url: string;
29
- body: HouseByteBody;
30
- operation?: string;
29
+ body: string;
31
30
  };
32
31
  export type HouseStreamEntry = {
33
32
  path: string;
34
- kind: 'edited' | 'removed' | 'piece';
33
+ kind: 'edited' | 'removed' | 'piece' | 'folder';
34
+ removed?: true;
35
35
  revision?: string;
36
36
  sha256?: string;
37
37
  text?: string;
38
- file?: string;
39
38
  bytes?: number;
40
39
  media_type?: string;
41
40
  author?: {
@@ -58,6 +57,7 @@ export type HouseListedConversation = {
58
57
  created_at: string;
59
58
  finished_at: string | null;
60
59
  outcome: string | null;
60
+ waiting: boolean;
61
61
  };
62
62
  export type HouseStreamFrame = {
63
63
  type: 'part';
@@ -80,6 +80,9 @@ export type HouseStreamFrame = {
80
80
  } | {
81
81
  type: 'opening';
82
82
  expired: readonly string[];
83
+ } | {
84
+ type: 'authority_changed';
85
+ rooms: readonly string[];
83
86
  } | ({
84
87
  type: 'turn';
85
88
  conversation_id: string;
@@ -92,6 +95,10 @@ export type HouseStreamFrame = {
92
95
  type: 'interactions';
93
96
  conversation_id: string;
94
97
  interactions: readonly unknown[];
98
+ } | {
99
+ type: 'deliveries';
100
+ conversation_id: string;
101
+ unknown: readonly string[];
95
102
  } | {
96
103
  type: 'process';
97
104
  conversation_id: string;
@@ -104,6 +111,14 @@ export type HouseStreamFrame = {
104
111
  type: 'options';
105
112
  conversation_id: string;
106
113
  options: LiveFact<'options'>['options'];
114
+ } | {
115
+ type: 'starting';
116
+ conversation_id: string;
117
+ step: string | null;
118
+ } | {
119
+ type: 'link';
120
+ conversation_id: string;
121
+ connected: boolean;
107
122
  } | {
108
123
  type: 'conversations';
109
124
  conversations: readonly HouseListedConversation[];
@@ -134,31 +149,32 @@ export type HouseHeldRoom = {
134
149
  log_epoch: string;
135
150
  carry?: HouseHeldCarry;
136
151
  };
137
- export type HouseDemand = {
138
- rooms: readonly string[];
139
- conversations: boolean;
140
- };
141
152
  export type HouseOpening = {
142
- hold(rooms: readonly HouseHeldRoom[], runtime: string, demand: HouseDemand | null): void;
153
+ hold(rooms: readonly HouseHeldRoom[], runtime: string): void;
143
154
  received(authority: string, carry: string, index: number): void;
144
155
  close(): void;
145
156
  };
157
+ export type HouseClosing = {
158
+ revoked: boolean;
159
+ lost: boolean;
160
+ };
146
161
  export type HouseStream = {
147
162
  observe(receive: (frame: HouseStreamFrame) => void): () => void;
148
163
  opened(notify: (opening: HouseOpening) => void): () => void;
149
- closed(notify: (revoked: boolean) => void): () => void;
164
+ closed(notify: (closing: HouseClosing) => void): () => void;
150
165
  };
151
166
  export type HouseDoor = {
152
- door(route: string, body: unknown): Promise<HouseDoorAnswer>;
167
+ door(route: string, body: unknown, arriving?: (bytes: number) => void): Promise<HouseDoorAnswer>;
153
168
  stream: HouseStream;
154
169
  };
155
170
  export type HouseTransport = HouseDoor & {
156
171
  transfer(request: HouseByteTransfer): Promise<HouseDoorAnswer>;
172
+ put(url: string, body: Blob, headers: Readonly<Record<string, string>>): Promise<number>;
157
173
  download(url: string): Promise<HouseBytesAnswer>;
158
174
  };
159
175
  export type HouseSiteTransport = HouseDoor & {
160
176
  online(): boolean;
161
- read(route: string): Promise<HouseDoorAnswer>;
177
+ read(route: string, arriving?: (bytes: number) => void): Promise<HouseDoorAnswer>;
162
178
  download(route: string): Promise<HouseBytesAnswer>;
163
179
  };
164
180
  export {};