@agentshouse/mdruntime-adapter 0.1.0-ce1f911b3b195 → 0.1.0-ce3ced044cab5

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 (61) hide show
  1. package/README.md +2 -2
  2. package/dist/actions.d.ts +6 -23
  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 +241 -209
  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 +38 -34
  16. package/dist/gather.d.ts +1 -2
  17. package/dist/gather.d.ts.map +1 -1
  18. package/dist/gather.js +5 -15
  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 +486 -0
  22. package/dist/index.d.ts +8 -2
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +7 -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 +14 -0
  29. package/dist/part.d.ts.map +1 -0
  30. package/dist/part.js +90 -0
  31. package/dist/read.d.ts +54 -25
  32. package/dist/read.d.ts.map +1 -1
  33. package/dist/read.js +264 -150
  34. package/dist/room-stream.d.ts +19 -0
  35. package/dist/room-stream.d.ts.map +1 -0
  36. package/dist/room-stream.js +57 -0
  37. package/dist/room-types.d.ts +7 -0
  38. package/dist/room-types.d.ts.map +1 -0
  39. package/dist/room-types.js +125 -0
  40. package/dist/site.d.ts.map +1 -1
  41. package/dist/site.js +67 -115
  42. package/dist/stream.d.ts +5 -5
  43. package/dist/stream.d.ts.map +1 -1
  44. package/dist/stream.js +66 -39
  45. package/dist/transport.d.ts +36 -14
  46. package/dist/transport.d.ts.map +1 -1
  47. package/dist/transport.js +2 -1
  48. package/dist/upload.d.ts +12 -0
  49. package/dist/upload.d.ts.map +1 -0
  50. package/dist/upload.js +63 -0
  51. package/dist/write.d.ts +16 -1
  52. package/dist/write.d.ts.map +1 -1
  53. package/dist/write.js +101 -55
  54. package/package.json +8 -4
  55. package/AGENTS.md +0 -303
  56. package/dist/handed.d.ts +0 -15
  57. package/dist/handed.d.ts.map +0 -1
  58. package/dist/handed.js +0 -38
  59. package/dist/referrer.d.ts +0 -7
  60. package/dist/referrer.d.ts.map +0 -1
  61. package/dist/referrer.js +0 -35
@@ -0,0 +1,125 @@
1
+ import { APP_REFERENCE_PATTERN } from './app-reference.js';
2
+ import { HOUSE_MODEL_TYPES } from './house-model.js';
3
+ export const INBOX_CHANNEL_KINDS = [
4
+ 'telegram-bot',
5
+ 'telegram-business',
6
+ 'whatsapp',
7
+ 'messenger',
8
+ 'instagram',
9
+ 'viber',
10
+ 'tiktok',
11
+ ];
12
+ export const INBOX_CHAT_TYPE = 'inbox-chat';
13
+ export const INBOX_MESSAGE_TYPE = 'inbox-message';
14
+ export const HOUSE_NAMEABLE_ROOM_TYPES = [INBOX_CHAT_TYPE, INBOX_MESSAGE_TYPE];
15
+ const WAYFINDING_MAP_TYPE = 'wayfinding-map';
16
+ const WAYFINDING_TICKET_TYPE = 'wayfinding-ticket';
17
+ const WAYFINDING_MAP_SECTIONS = [
18
+ 'Destination',
19
+ 'Notes',
20
+ 'Decisions so far',
21
+ 'Not yet specified',
22
+ 'Out of scope',
23
+ ];
24
+ const IDENTITY = "{ type: string, pattern: '^\\S+$' }";
25
+ const AGENT = [' agent: { type: string, format: uuid }', ' name: { type: string }'];
26
+ const ASSIGNEE = [
27
+ ' type: object',
28
+ ' properties:',
29
+ ' user: { type: string, format: uuid }',
30
+ ...AGENT,
31
+ ];
32
+ export const HOUSE_ROOM_TYPES = [
33
+ `${INBOX_CHAT_TYPE}:`,
34
+ ' properties:',
35
+ ' inbox: { type: string, format: uuid }',
36
+ ` channel: { enum: [${INBOX_CHANNEL_KINDS.join(', ')}] }`,
37
+ ` chat: ${IDENTITY}`,
38
+ ' customer:',
39
+ ' type: object',
40
+ ' properties:',
41
+ ' name: { type: string }',
42
+ ` username: ${IDENTITY}`,
43
+ ` phone: ${IDENTITY}`,
44
+ ' assignee:',
45
+ ...ASSIGNEE,
46
+ ' return_to:',
47
+ ' type: object',
48
+ ' properties:',
49
+ ...AGENT,
50
+ ' conversation: { type: string, format: uuid }',
51
+ ' required: [inbox, channel, chat, customer]',
52
+ `${INBOX_MESSAGE_TYPE}:`,
53
+ ' properties:',
54
+ ' direction: { enum: [in, out] }',
55
+ ' sender:',
56
+ ' type: object',
57
+ ' properties:',
58
+ ' name: { type: string }',
59
+ ` username: ${IDENTITY}`,
60
+ ' sent_at: { type: string, format: date-time }',
61
+ ` channel_message: ${IDENTITY}`,
62
+ ' state: { enum: [sending, sent, failed] }',
63
+ ' reason: { type: string }',
64
+ ' reactions:',
65
+ ' type: array',
66
+ ' items:',
67
+ ' type: object',
68
+ ' properties:',
69
+ ' reaction: { type: string, minLength: 1 }',
70
+ ' sender:',
71
+ ' type: object',
72
+ ' properties:',
73
+ ` id: ${IDENTITY}`,
74
+ ' name: { type: string }',
75
+ ` username: ${IDENTITY}`,
76
+ ' count: { type: integer, minimum: 1 }',
77
+ ' required: [reaction]',
78
+ ` post: ${IDENTITY}`,
79
+ ' assignee:',
80
+ ...ASSIGNEE,
81
+ ' sent_outside: { const: true }',
82
+ ' template:',
83
+ ' type: object',
84
+ ' properties:',
85
+ ' name: { type: string, minLength: 1 }',
86
+ ' language: { type: string, minLength: 1 }',
87
+ ' required: [name, language]',
88
+ ' values:',
89
+ ' type: object',
90
+ ' additionalProperties: { type: string }',
91
+ ' withheld: { const: true }',
92
+ ' required: [direction]',
93
+ ' if: { properties: { direction: { const: in } } }',
94
+ ' then: { required: [sent_at] }',
95
+ `${WAYFINDING_MAP_TYPE}:`,
96
+ ' properties:',
97
+ ' title: { type: string }',
98
+ ' required: [title]',
99
+ ' sections:',
100
+ ...WAYFINDING_MAP_SECTIONS.map((name) => ` ${name}: { kind: text, required: true }`),
101
+ `${WAYFINDING_TICKET_TYPE}:`,
102
+ ' properties:',
103
+ ' title: { type: string }',
104
+ ' kind: { enum: [research, prototype, grilling, task] }',
105
+ ' status: { enum: [open, in-progress, done, out-of-scope] }',
106
+ ` map: { type: string, reference: ${WAYFINDING_MAP_TYPE} }`,
107
+ ` blocked_by: { type: array, items: { type: string, reference: ${WAYFINDING_TICKET_TYPE} } }`,
108
+ ' assignee:',
109
+ ...ASSIGNEE,
110
+ ' conversation: { type: string, format: uuid }',
111
+ ` app: { type: string, pattern: '${APP_REFERENCE_PATTERN.source}' }`,
112
+ ' required: [title, kind, status, map, blocked_by]',
113
+ ' sections:',
114
+ ' Question: { kind: text }',
115
+ ' Answer: { kind: text }',
116
+ ' allOf:',
117
+ ' - if: { properties: { status: { enum: [in-progress, done, out-of-scope] } }, required: [status] }',
118
+ ' then: { required: [assignee] }',
119
+ ' else: { properties: { assignee: false } }',
120
+ ' - if: { properties: { assignee: { required: [agent] } }, required: [assignee] }',
121
+ ' else: { properties: { conversation: false } }',
122
+ ' - if: { properties: { status: { enum: [done, out-of-scope] } }, required: [status] }',
123
+ ' then: { sections: { Answer: { kind: text, required: true } } }',
124
+ HOUSE_MODEL_TYPES,
125
+ ].join('\n');
@@ -1 +1 @@
1
- {"version":3,"file":"site.d.ts","sourceRoot":"","sources":["../src/site.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EAOR,MAAM,wBAAwB,CAAC;AA2BhC,OAAO,EAOL,KAAK,kBAAkB,EACxB,MAAM,gBAAgB,CAAC;AAmCxB,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,kBAAkB,EAAE,QAAQ,UAAQ,GAAG,OAAO,CA+Q1F"}
1
+ {"version":3,"file":"site.d.ts","sourceRoot":"","sources":["../src/site.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EAUR,MAAM,wBAAwB,CAAC;AAwBhC,OAAO,EAML,KAAK,kBAAkB,EACxB,MAAM,gBAAgB,CAAC;AAgCxB,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,kBAAkB,EAAE,QAAQ,UAAQ,GAAG,OAAO,CAqN1F"}
package/dist/site.js CHANGED
@@ -1,34 +1,39 @@
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 { streamedModels, viewModels } from './model.js';
5
+ import { bootstrapping, parseEnumeration, refreshFrom, viewOf, wholeView, } from './read.js';
6
+ import { roomStreams } from './room-stream.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 read = new Map();
31
+ const rooms = roomStreams('site');
32
+ function fileRoute(request) {
33
+ const entry = tracked.get(request.locator);
34
+ if (entry === undefined)
35
+ return { kind: 'room-not-readable', locator: request.locator };
36
+ return `${SITE_FILES}${entry.authority}/${encodeURIComponent(request.revision)}/${encodeURIComponent(request.path)}`;
32
37
  }
33
38
  async function enumerated(entry, from, page) {
34
39
  const answer = await transport.door(SITE_DOOR_ENUMERATE, {
@@ -53,73 +58,35 @@ export function createSiteAdapter(transport, snapshot = false) {
53
58
  const first = await enumerated(entry, from);
54
59
  return 'kind' in first ? first : wholeView(first, (page) => enumerated(entry, from, page));
55
60
  }
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('&')}`}`);
62
- const classified = classifyDoor(answer.status, answer.body, entry.locator);
63
- 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 }] };
61
+ async function openedRoom(entry, arriving, signal) {
62
+ const cut = read.get(entry.locator);
63
+ const answered = await transport.streamed(`${SITE_DOOR_BOOTSTRAP}${entry.authority}${cut === undefined ? '' : `?expired=${cut}`}`, signal ?? new AbortController().signal);
64
+ if (answered.room === null)
65
+ return classifyDoor(answered.status, answered.body, entry.locator) ?? UNCERTAIN;
66
+ const head = await rooms.head(entry, answered.room, arriving, signal);
67
+ const at = head.kind === 'bootstrap' ? logPosition(head.roomVersion) : null;
68
+ if (at !== null)
69
+ read.set(entry.locator, at.position);
70
+ return head;
96
71
  }
97
- async function caughtUp(entry, after) {
98
- const part = await bootstrapPart(entry, after, null);
99
- if ('failure' in part)
72
+ async function streamedPage(entry, token) {
73
+ const part = await rooms.part(entry, token);
74
+ if ('kind' in part)
100
75
  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 };
108
- }
109
- const { scope } = part;
110
- if (after !== null || next !== null || roomVersion(scope) === roomVersion(entry)) {
111
- return { scope, next };
112
- }
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 };
76
+ notifications.reach(entry.authority, part.scope);
77
+ const { folders: _folders, ...page } = await stepped(bootstrapping(part.scope));
78
+ return {
79
+ ...page,
80
+ ...(part.models === undefined ? {} : { models: part.models }),
81
+ ...(part.pageToken === undefined ? {} : { pageToken: part.pageToken }),
82
+ bytes: part.bytes,
83
+ };
117
84
  }
118
85
  async function refreshed(entry, fromRoomVersion) {
119
86
  const from = logPosition(fromRoomVersion);
120
87
  if (from === null)
121
88
  return { kind: 'beyond-horizon' };
122
- const streamed = notifications.advance(entry, from);
89
+ const streamed = await stepped(notifications.advance(entry, from));
123
90
  if (streamed !== null) {
124
91
  const models = await streamedModels(streamed.schemas, entry, 'site');
125
92
  return models === undefined ? streamed.page : { ...streamed.page, models };
@@ -128,7 +95,7 @@ export function createSiteAdapter(transport, snapshot = false) {
128
95
  if ('kind' in view)
129
96
  return view;
130
97
  notifications.reach(entry.authority, view);
131
- const answer = refreshFrom(view, fromRoomVersion);
98
+ const answer = await refreshFrom(view, fromRoomVersion);
132
99
  if (answer.kind !== 'net-advancement')
133
100
  return answer;
134
101
  const models = await viewModels(view, entry);
@@ -137,6 +104,8 @@ export function createSiteAdapter(transport, snapshot = false) {
137
104
  return {
138
105
  localSaves: true,
139
106
  exhaustiveDiscovery: true,
107
+ roomTypes: HOUSE_ROOM_TYPES,
108
+ nameableRoomTypes: HOUSE_NAMEABLE_ROOM_TYPES,
140
109
  async authorize() {
141
110
  return {};
142
111
  },
@@ -159,24 +128,9 @@ export function createSiteAdapter(transport, snapshot = false) {
159
128
  const entry = tracked.get(request.locator);
160
129
  if (entry === undefined)
161
130
  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);
169
- const seen = [
170
- ...(continued?.seen ?? []),
171
- ...entry.schemas.filter((schema) => held.scope.files.some((file) => file.path === schema)),
172
- ];
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 };
175
- return {
176
- ...bootstrapOf(held.scope),
177
- ...(models === undefined ? {} : { models }),
178
- ...(continuation === null ? {} : { pageToken: JSON.stringify(continuation) }),
179
- };
131
+ return request.pageToken === undefined
132
+ ? openedRoom(entry, request.arriving, request.signal)
133
+ : streamedPage(entry, request.pageToken);
180
134
  },
181
135
  async refresh(_authorization, request) {
182
136
  const entry = tracked.get(request.locator);
@@ -194,34 +148,32 @@ export function createSiteAdapter(transport, snapshot = false) {
194
148
  async mutate() {
195
149
  return { kind: 'rejected', affected: {}, code: 'write-grant', fields: {}, retryable: false };
196
150
  },
151
+ async attach() {
152
+ return { kind: 'rejected', code: 'write-grant', fields: {}, retryable: false };
153
+ },
197
154
  async history() {
198
155
  return { kind: 'no-history' };
199
156
  },
200
157
  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 }
158
+ const route = fileRoute(request);
159
+ if (typeof route !== 'string')
160
+ return route;
161
+ const read = await transport.download(route);
162
+ if (read.status === 404)
163
+ return unfound();
164
+ return read.status === 200 && read.body !== null
165
+ ? { kind: 'binary', body: read.body, mediaType: read.mediaType }
217
166
  : UNCERTAIN;
218
167
  },
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 }) };
168
+ async link(_authorization, request) {
169
+ const route = fileRoute(request);
170
+ if (typeof route !== 'string')
171
+ return route;
172
+ return {
173
+ kind: 'link',
174
+ url: route,
175
+ name: request.path.slice(request.path.lastIndexOf('/') + 1),
176
+ };
225
177
  },
226
178
  reached(locator, version, target) {
227
179
  return reachedVersion(version, target, tracked.get(locator)?.logEpoch);
@@ -253,7 +205,7 @@ export function createSiteAdapter(transport, snapshot = false) {
253
205
  return [];
254
206
  notifications.reach(entry.authority, at);
255
207
  return [presented(entry.authority, at)];
256
- }), null);
208
+ }));
257
209
  },
258
210
  reopen: notifications.reopen,
259
211
  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
  },