@agentshouse/mdruntime-adapter 0.1.0-cedb8e57837a7 → 0.1.0-cf1ebfb67feeb

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 (70) hide show
  1. package/README.md +2 -2
  2. package/dist/actions.d.ts +4 -69
  3. package/dist/actions.d.ts.map +1 -1
  4. package/dist/actions.js +7 -357
  5. package/dist/adapter.d.ts.map +1 -1
  6. package/dist/adapter.js +204 -411
  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 +21 -13
  14. package/dist/discovery.d.ts.map +1 -1
  15. package/dist/discovery.js +73 -53
  16. package/dist/history.d.ts +12 -0
  17. package/dist/history.d.ts.map +1 -0
  18. package/dist/history.js +92 -0
  19. package/dist/house-model.d.ts +719 -0
  20. package/dist/house-model.d.ts.map +1 -0
  21. package/dist/house-model.js +393 -0
  22. package/dist/index.d.ts +11 -2
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +10 -1
  25. package/dist/model.d.ts +5 -2
  26. package/dist/model.d.ts.map +1 -1
  27. package/dist/model.js +10 -29
  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 +35 -59
  32. package/dist/read.d.ts.map +1 -1
  33. package/dist/read.js +178 -266
  34. package/dist/refusal.d.ts +0 -3
  35. package/dist/refusal.d.ts.map +1 -1
  36. package/dist/refusal.js +0 -5
  37. package/dist/room-stream.d.ts +19 -0
  38. package/dist/room-stream.d.ts.map +1 -0
  39. package/dist/room-stream.js +57 -0
  40. package/dist/room-types.d.ts +7 -0
  41. package/dist/room-types.d.ts.map +1 -0
  42. package/dist/room-types.js +123 -0
  43. package/dist/search.d.ts +5 -0
  44. package/dist/search.d.ts.map +1 -0
  45. package/dist/search.js +47 -0
  46. package/dist/site.d.ts.map +1 -1
  47. package/dist/site.js +83 -193
  48. package/dist/stream.d.ts +13 -13
  49. package/dist/stream.d.ts.map +1 -1
  50. package/dist/stream.js +166 -411
  51. package/dist/transport.d.ts +54 -40
  52. package/dist/transport.d.ts.map +1 -1
  53. package/dist/transport.js +54 -11
  54. package/dist/upload.d.ts +12 -0
  55. package/dist/upload.d.ts.map +1 -0
  56. package/dist/upload.js +63 -0
  57. package/dist/write.d.ts +6 -1
  58. package/dist/write.d.ts.map +1 -1
  59. package/dist/write.js +28 -39
  60. package/package.json +8 -4
  61. package/AGENTS.md +0 -304
  62. package/dist/gather.d.ts +0 -4
  63. package/dist/gather.d.ts.map +0 -1
  64. package/dist/gather.js +0 -35
  65. package/dist/handed.d.ts +0 -15
  66. package/dist/handed.d.ts.map +0 -1
  67. package/dist/handed.js +0 -45
  68. package/dist/referrer.d.ts +0 -7
  69. package/dist/referrer.d.ts.map +0 -1
  70. package/dist/referrer.js +0 -35
@@ -0,0 +1,57 @@
1
+ import { manifestedModels, partModels } from './model.js';
2
+ import { roomFrames } from './part.js';
3
+ import { housePart, manifested, roomHead } from './read.js';
4
+ const UNCERTAIN = { kind: 'transport-uncertainty' };
5
+ export function roomStreams(mode) {
6
+ const open = new Map();
7
+ let opened = 0;
8
+ async function head(entry, room, arriving, signal) {
9
+ const frames = roomFrames(room, arriving, signal);
10
+ const first = await frames();
11
+ if (first.kind !== 'frame')
12
+ return UNCERTAIN;
13
+ const held = await roomHead(first.payload, mode);
14
+ if (held === null || held.anchor !== entry.authority)
15
+ return UNCERTAIN;
16
+ const { models, seen } = await manifestedModels(held, entry);
17
+ opened += 1;
18
+ const id = String(opened);
19
+ open.set(id, { entry, head: held, frames, next: 1, seen });
20
+ signal?.addEventListener('abort', () => open.delete(id), { once: true });
21
+ return { ...manifested(held, `${id}:1`, models), bytes: first.bytes };
22
+ }
23
+ async function part(entry, token) {
24
+ const [id = '', at = ''] = token.split(':');
25
+ const room = open.get(id);
26
+ if (room === undefined || room.entry.locator !== entry.locator || String(room.next) !== at)
27
+ return UNCERTAIN;
28
+ const frame = await room.frames();
29
+ if (frame.kind !== 'frame') {
30
+ open.delete(id);
31
+ return UNCERTAIN;
32
+ }
33
+ const read = await housePart(room.head, frame.payload, mode);
34
+ if ('kind' in read) {
35
+ open.delete(id);
36
+ return read;
37
+ }
38
+ const { scope, ended } = read;
39
+ const first = room.next === 1;
40
+ room.next += 1;
41
+ room.seen = [
42
+ ...room.seen,
43
+ ...(scope.files.some((file) => file.path === entry.schema) ? [entry.schema] : []),
44
+ ];
45
+ if (ended)
46
+ open.delete(id);
47
+ return {
48
+ scope,
49
+ first,
50
+ ended,
51
+ models: await partModels(scope, entry, new Set(room.seen), ended),
52
+ pageToken: ended ? undefined : `${id}:${room.next}`,
53
+ bytes: frame.bytes,
54
+ };
55
+ }
56
+ return { head, part };
57
+ }
@@ -0,0 +1,7 @@
1
+ export declare const INBOX_CHANNEL_KINDS: readonly ['telegram-bot', 'telegram-business', 'whatsapp', 'messenger', 'instagram', 'viber', 'tiktok'];
2
+ export type InboxChannelKind = (typeof INBOX_CHANNEL_KINDS)[number];
3
+ export declare const CHAT_TYPE = "chat";
4
+ export declare const CHAT_MESSAGE_TYPE = "chat-message";
5
+ export declare const HOUSE_NAMEABLE_ROOM_TYPES: string[];
6
+ export declare const HOUSE_ROOM_TYPES: string;
7
+ //# sourceMappingURL=room-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"room-types.d.ts","sourceRoot":"","sources":["../src/room-types.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,mBAAmB,YAC9B,cAAc,EACd,mBAAmB,EACnB,UAAU,EACV,WAAW,EACX,WAAW,EACX,OAAO,EACP,QAAQ,CACA,CAAC;AAEX,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEpE,eAAO,MAAM,SAAS,SAAS,CAAC;AAEhC,eAAO,MAAM,iBAAiB,iBAAiB,CAAC;AAEhD,eAAO,MAAM,yBAAyB,UAAiC,CAAC;AAyBxE,eAAO,MAAM,gBAAgB,QA2FjB,CAAC"}
@@ -0,0 +1,123 @@
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 CHAT_TYPE = 'chat';
13
+ export const CHAT_MESSAGE_TYPE = 'chat-message';
14
+ export const HOUSE_NAMEABLE_ROOM_TYPES = [CHAT_TYPE, CHAT_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
+ `${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
+ ' required: [chat, customer]',
51
+ `${CHAT_MESSAGE_TYPE}:`,
52
+ ' properties:',
53
+ ' direction: { enum: [in, out] }',
54
+ ' sender:',
55
+ ' type: object',
56
+ ' properties:',
57
+ ' name: { type: string }',
58
+ ` username: ${IDENTITY}`,
59
+ ' sent_at: { type: string, format: date-time }',
60
+ ` channel_message: ${IDENTITY}`,
61
+ ' labelled: { const: true }',
62
+ ' reactions:',
63
+ ' type: array',
64
+ ' items:',
65
+ ' type: object',
66
+ ' properties:',
67
+ ' reaction: { type: string, minLength: 1 }',
68
+ ' sender:',
69
+ ' type: object',
70
+ ' properties:',
71
+ ` id: ${IDENTITY}`,
72
+ ' name: { type: string }',
73
+ ` username: ${IDENTITY}`,
74
+ ' count: { type: integer, minimum: 1 }',
75
+ ' required: [reaction]',
76
+ ` post: ${IDENTITY}`,
77
+ ' assignee:',
78
+ ...ASSIGNEE,
79
+ ' sent_outside: { const: true }',
80
+ ' template:',
81
+ ' type: object',
82
+ ' properties:',
83
+ ' name: { type: string, minLength: 1 }',
84
+ ' language: { type: string, minLength: 1 }',
85
+ ' required: [name, language]',
86
+ ' values:',
87
+ ' type: object',
88
+ ' additionalProperties: { type: string }',
89
+ ' withheld: { const: true }',
90
+ ' required: [direction]',
91
+ ' if: { properties: { direction: { const: in } } }',
92
+ ' then: { required: [sent_at] }',
93
+ `${WAYFINDING_MAP_TYPE}:`,
94
+ ' properties:',
95
+ ' title: { type: string }',
96
+ ' required: [title]',
97
+ ' sections:',
98
+ ...WAYFINDING_MAP_SECTIONS.map((name) => ` ${name}: { kind: text, required: true }`),
99
+ `${WAYFINDING_TICKET_TYPE}:`,
100
+ ' properties:',
101
+ ' title: { type: string }',
102
+ ' kind: { enum: [research, prototype, grilling, task] }',
103
+ ' status: { enum: [open, in-progress, done, out-of-scope] }',
104
+ ` map: { type: string, reference: ${WAYFINDING_MAP_TYPE} }`,
105
+ ` blocked_by: { type: array, items: { type: string, reference: ${WAYFINDING_TICKET_TYPE} } }`,
106
+ ' assignee:',
107
+ ...ASSIGNEE,
108
+ ' conversation: { type: string, format: uuid }',
109
+ ` app: { type: string, pattern: '${APP_REFERENCE_PATTERN.source}' }`,
110
+ ' required: [title, kind, status, map, blocked_by]',
111
+ ' sections:',
112
+ ' Question: { kind: text }',
113
+ ' Answer: { kind: text }',
114
+ ' allOf:',
115
+ ' - if: { properties: { status: { enum: [in-progress, done, out-of-scope] } }, required: [status] }',
116
+ ' then: { required: [assignee] }',
117
+ ' else: { properties: { assignee: false } }',
118
+ ' - if: { properties: { assignee: { required: [agent] } }, required: [assignee] }',
119
+ ' else: { properties: { conversation: false } }',
120
+ ' - if: { properties: { status: { enum: [done, out-of-scope] } }, required: [status] }',
121
+ ' then: { sections: { Answer: { kind: text, required: true } } }',
122
+ HOUSE_MODEL_TYPES,
123
+ ].join('\n');
@@ -0,0 +1,5 @@
1
+ import type { SearchAnswer, SearchAsk } from '@agentshouse/mdruntime';
2
+ import type { TrackedAuthority } from './discovery.js';
3
+ import { type HouseTransport } from './transport.js';
4
+ export declare function houseSearch(transport: Pick<HouseTransport, 'door'>, tracked: readonly TrackedAuthority[], request: SearchAsk): Promise<SearchAnswer>;
5
+ //# sourceMappingURL=search.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../src/search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAa,MAAM,wBAAwB,CAAC;AACjF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEvD,OAAO,EAAkB,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAsBrE,wBAAsB,WAAW,CAC/B,SAAS,EAAE,IAAI,CAAC,cAAc,EAAE,MAAM,CAAC,EACvC,OAAO,EAAE,SAAS,gBAAgB,EAAE,EACpC,OAAO,EAAE,SAAS,GACjB,OAAO,CAAC,YAAY,CAAC,CAoBvB"}
package/dist/search.js ADDED
@@ -0,0 +1,47 @@
1
+ import { doorRefusal } from './refusal.js';
2
+ import { houseDoorRoute } from './transport.js';
3
+ const ROOMS = '/rooms/';
4
+ const UNCERTAIN = { kind: 'transport-uncertainty' };
5
+ function hitOf(file, locators) {
6
+ if (typeof file !== 'object' || file === null)
7
+ return null;
8
+ const { path, line, snippet } = file;
9
+ if (typeof path !== 'string' || !path.startsWith(ROOMS))
10
+ return null;
11
+ const slash = path.indexOf('/', ROOMS.length);
12
+ if (slash < 0)
13
+ return null;
14
+ const room = locators.get(path.slice(ROOMS.length, slash));
15
+ if (room === undefined)
16
+ return null;
17
+ return {
18
+ room,
19
+ path: path.slice(slash + 1),
20
+ ...(typeof line === 'number' ? { line } : {}),
21
+ ...(typeof snippet === 'string' ? { snippet } : {}),
22
+ };
23
+ }
24
+ export async function houseSearch(transport, tracked, request) {
25
+ const named = request.rooms === null
26
+ ? null
27
+ : tracked.filter((entry) => request.rooms.includes(entry.locator));
28
+ if (named !== null && named.length === 0)
29
+ return { kind: 'found', documents: [] };
30
+ const answer = await transport.door(houseDoorRoute('search'), {
31
+ query: request.text,
32
+ ...(named === null ? {} : { paths: named.map((entry) => `${ROOMS}${entry.handle}`) }),
33
+ });
34
+ if (answer.status === 401)
35
+ return { kind: 'revoked-authorization' };
36
+ const refusal = doorRefusal(answer.body);
37
+ if (refusal !== null)
38
+ return { kind: 'rejected', ...refusal };
39
+ const files = answer.body?.files;
40
+ if (answer.status !== 200 || !Array.isArray(files))
41
+ return UNCERTAIN;
42
+ const locators = new Map(tracked.map((entry) => [entry.handle, entry.locator]));
43
+ return {
44
+ kind: 'found',
45
+ documents: files.flatMap((file) => hitOf(file, locators) ?? []),
46
+ };
47
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"site.d.ts","sourceRoot":"","sources":["../src/site.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EAUR,MAAM,wBAAwB,CAAC;AA4BhC,OAAO,EAOL,KAAK,kBAAkB,EACxB,MAAM,gBAAgB,CAAC;AAyCxB,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,kBAAkB,EAAE,QAAQ,UAAQ,GAAG,OAAO,CAwR1F"}
1
+ {"version":3,"file":"site.d.ts","sourceRoot":"","sources":["../src/site.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EAQR,MAAM,wBAAwB,CAAC;AAgBhC,OAAO,EAKL,KAAK,kBAAkB,EACxB,MAAM,gBAAgB,CAAC;AAuBxB,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,kBAAkB,EAAE,QAAQ,UAAQ,GAAG,OAAO,CAuK1F"}
package/dist/site.js CHANGED
@@ -1,14 +1,14 @@
1
1
  import { stepped } from '@agentshouse/mdruntime';
2
- import { actionOutcome, SITE_SUBMIT } from './actions.js';
2
+ import { actionOutcome } from './actions.js';
3
3
  import { mapDiscovery, parseSiteDiscovery, siteCapabilities, siteTracked, } from './discovery.js';
4
- import { partModels, streamedModels, viewModels } from './model.js';
5
- import { advancedScope, bootstrapping, parseEnumeration, parseSiteBootstrap, refreshFrom, siteBootstrapNext, siteBootstrapOffset, viewOf, wholeView, } from './read.js';
6
- import { sha256Hex } from './digest.js';
7
- import { classifyDoor, classifyRead, discoveryFailure } from './refusal.js';
4
+ import { bootstrapping } from './read.js';
5
+ import { roomStreams } from './room-stream.js';
6
+ import { classifyDoor, discoveryFailure } from './refusal.js';
7
+ import { HOUSE_TYPED_PATHS } from './house-model.js';
8
+ import { HOUSE_NAMEABLE_ROOM_TYPES, HOUSE_ROOM_TYPES } from './room-types.js';
8
9
  import { houseNotifications } from './stream.js';
9
- import { SITE_DOOR_BOOTSTRAP, SITE_DOOR_DISCOVER, SITE_DOOR_ENUMERATE, SITE_DOOR_SUBMIT, SITE_FILE_RECORD, SITE_FILES, } from './transport.js';
10
- import { houseReferrer } from './referrer.js';
11
- import { followsVersion, logPosition, presented, reachedVersion, roomVersion, } from './version.js';
10
+ import { houseSiteRoute, SITE_DOOR_SUBMIT, SITE_FILES, SITE_SUBMISSION, } from './transport.js';
11
+ import { followsVersion, logPosition, presented, reachedVersion, roomVersion } from './version.js';
12
12
  const UNCERTAIN = { kind: 'transport-uncertainty' };
13
13
  function unfound() {
14
14
  return { kind: 'rejected', code: 'file_not_found', fields: {}, retryable: false };
@@ -24,192 +24,98 @@ function siteRoom(tracked) {
24
24
  providerOwnedPaths: [],
25
25
  };
26
26
  }
27
- function settled(refused) {
28
- return refused.kind === 'beyond-horizon' ? UNCERTAIN : refused;
29
- }
30
27
  export function createSiteAdapter(transport, snapshot = false) {
31
- const notifications = houseNotifications(transport, 'site');
28
+ const notifications = houseNotifications(transport, 'site', {
29
+ opened: async () => {
30
+ const found = await discovered();
31
+ return found.kind === 'discovery' ? found : null;
32
+ },
33
+ changed: () => null,
34
+ });
32
35
  const tracked = new Map();
33
- function published(referrer) {
34
- const named = houseReferrer(referrer, tracked);
35
- return named === null || !tracked.has(named.locator) ? null : named;
36
- }
36
+ const read = new Map();
37
+ const rooms = roomStreams('site');
37
38
  function fileRoute(request) {
38
- const named = published(request.kind === 'path' ? { room: request.locator, path: request.path } : request.referrer);
39
- if (named === null) {
40
- return { kind: 'rejected', code: 'referrer_unknown', fields: {}, retryable: false };
41
- }
42
- if (request.fileRef === undefined)
43
- return unfound();
44
- const { room_ref, path, record } = named.referrer;
45
- return `${SITE_FILES}${room_ref}/${encodeURIComponent(request.fileRef)}/${path === undefined
46
- ? `${SITE_FILE_RECORD}/${encodeURIComponent(record)}`
47
- : encodeURIComponent(path)}`;
39
+ const entry = tracked.get(request.locator);
40
+ if (entry === undefined)
41
+ return { kind: 'room-not-readable', locator: request.locator };
42
+ return `${SITE_FILES}${entry.authority}/${encodeURIComponent(request.revision)}/${encodeURIComponent(request.path)}`;
48
43
  }
49
- async function enumerated(entry, from, page) {
50
- const answer = await transport.door(SITE_DOOR_ENUMERATE, {
51
- positions: [
52
- page === undefined
53
- ? presented(entry.authority, from)
54
- : {
55
- ...presented(entry.authority, { position: page.after, logEpoch: from.logEpoch }),
56
- page: { through: page.through, read: page.read },
57
- },
58
- ],
59
- });
60
- const classified = classifyRead(answer.status, answer.body, entry.locator);
61
- if (classified !== null)
62
- return classified;
63
- const found = parseEnumeration(answer.body, 'site');
64
- if (found === null)
65
- return UNCERTAIN;
66
- return viewOf(found, entry) ?? { kind: 'room-not-readable', locator: entry.locator };
44
+ async function openedRoom(entry, arriving, signal) {
45
+ const cut = read.get(entry.locator);
46
+ const answered = await transport.streamed(`${houseSiteRoute('bootstrap')}/${entry.authority}${cut === undefined ? '' : `?expired=${cut}`}`, signal ?? new AbortController().signal);
47
+ if (answered.room === null)
48
+ return classifyDoor(answered.status, answered.body, entry.locator) ?? UNCERTAIN;
49
+ const head = await rooms.head(entry, answered.room, arriving, signal);
50
+ const at = head.kind === 'bootstrap' ? logPosition(head.roomVersion) : null;
51
+ if (at !== null)
52
+ read.set(entry.locator, at.position);
53
+ return head;
67
54
  }
68
- async function enumeration(entry, from) {
69
- const first = await enumerated(entry, from);
70
- return 'kind' in first ? first : wholeView(first, (page) => enumerated(entry, from, page));
71
- }
72
- async function bootstrapPart(entry, after, offset) {
73
- const query = [
74
- ...(after === null ? [] : [`after=${encodeURIComponent(after)}`]),
75
- ...(offset === null ? [] : [`offset=${offset}`]),
76
- ];
77
- const answer = await transport.read(`${SITE_DOOR_BOOTSTRAP}${entry.authority}${query.length === 0 ? '' : `?${query.join('&')}`}`);
78
- const classified = classifyDoor(answer.status, answer.body, entry.locator);
79
- if (classified !== null)
80
- return { failure: classified };
81
- const scope = await stepped(parseSiteBootstrap(answer.body));
82
- return scope === null ? { failure: UNCERTAIN } : { body: answer.body, scope };
83
- }
84
- async function assembled(entry, scope, path, offset) {
85
- const opened = scope.contents.at(-1);
86
- if (opened === undefined || opened.path !== path || opened.content.length !== offset) {
87
- return UNCERTAIN;
88
- }
89
- let text = opened.content;
90
- for (;;) {
91
- const part = await bootstrapPart(entry, path, text.length);
92
- if ('failure' in part)
93
- return part.failure;
94
- const [piece] = part.scope.contents;
95
- if (piece === undefined ||
96
- part.scope.contents.length !== 1 ||
97
- piece.path !== path ||
98
- piece.content.length === 0) {
99
- return UNCERTAIN;
100
- }
101
- text += piece.content;
102
- const more = siteBootstrapOffset(part.body);
103
- if (more === null)
104
- break;
105
- if (more !== text.length)
106
- return UNCERTAIN;
107
- }
108
- const file = scope.files.find((held) => held.path === path);
109
- if (file === undefined || (await sha256Hex(text)) !== file.version)
110
- return UNCERTAIN;
111
- return { ...scope, contents: [...scope.contents.slice(0, -1), { path, content: text }] };
112
- }
113
- async function caughtUp(entry, after) {
114
- const part = await bootstrapPart(entry, after, null);
115
- if ('failure' in part)
55
+ async function streamedPage(entry, token) {
56
+ const part = await rooms.part(entry, token);
57
+ if ('kind' in part)
116
58
  return part;
117
- const next = siteBootstrapNext(part.body);
118
- const offset = siteBootstrapOffset(part.body);
119
- if (offset !== null) {
120
- if (next === null)
121
- return { failure: UNCERTAIN };
122
- const scope = await assembled(entry, part.scope, next, offset);
123
- return 'kind' in scope ? { failure: scope } : { scope, next };
124
- }
125
- const { scope } = part;
126
- if (after !== null || next !== null || roomVersion(scope) === roomVersion(entry)) {
127
- return { scope, next };
128
- }
129
- const view = await enumeration(entry, scope);
130
- if ('kind' in view)
131
- return { failure: settled(view) };
132
- return { scope: await stepped(advancedScope(scope, view)), next: null };
59
+ const { folders: _folders, ...page } = await stepped(bootstrapping(part.scope));
60
+ return {
61
+ ...page,
62
+ ...(part.models === undefined ? {} : { models: part.models }),
63
+ ...(part.pageToken === undefined ? {} : { pageToken: part.pageToken }),
64
+ bytes: part.bytes,
65
+ };
133
66
  }
134
- async function refreshed(entry, fromRoomVersion) {
135
- const from = logPosition(fromRoomVersion);
136
- if (from === null)
137
- return { kind: 'beyond-horizon' };
138
- const streamed = await stepped(notifications.advance(entry, from));
139
- if (streamed !== null) {
140
- const models = await streamedModels(streamed.schemas, entry, 'site');
141
- return models === undefined ? streamed.page : { ...streamed.page, models };
142
- }
143
- const view = await enumeration(entry, from);
144
- if ('kind' in view)
145
- return view;
146
- notifications.reach(entry.authority, view);
147
- const answer = await refreshFrom(view, fromRoomVersion);
148
- if (answer.kind !== 'net-advancement')
149
- return answer;
150
- const models = await viewModels(view, entry);
151
- return models === undefined ? answer : { ...answer, models };
67
+ async function discovered() {
68
+ const answer = await transport.read(houseSiteRoute('discover'));
69
+ const failed = discoveryFailure(answer.status, answer.body);
70
+ if (failed !== null)
71
+ return failed;
72
+ const document = parseSiteDiscovery(answer.body);
73
+ if (document === null)
74
+ return UNCERTAIN;
75
+ const entries = siteTracked(document);
76
+ tracked.clear();
77
+ for (const entry of entries)
78
+ tracked.set(entry.locator, entry);
79
+ notifications.track(entries);
80
+ return mapDiscovery(document, entries.map(siteRoom), siteCapabilities());
152
81
  }
153
82
  return {
154
83
  localSaves: true,
155
- exhaustiveDiscovery: true,
84
+ roomTypes: HOUSE_ROOM_TYPES,
85
+ nameableRoomTypes: HOUSE_NAMEABLE_ROOM_TYPES,
86
+ typedPaths: HOUSE_TYPED_PATHS,
156
87
  async authorize() {
157
88
  return {};
158
89
  },
159
- async discover() {
160
- const answer = await transport.read(SITE_DOOR_DISCOVER);
161
- const failed = discoveryFailure(answer.status, answer.body);
162
- if (failed !== null)
163
- return failed;
164
- const document = parseSiteDiscovery(answer.body);
165
- if (document === null)
166
- return UNCERTAIN;
167
- const entries = siteTracked(document);
168
- tracked.clear();
169
- for (const entry of entries)
170
- tracked.set(entry.locator, entry);
171
- notifications.track(entries);
172
- return mapDiscovery(document, entries.map(siteRoom), siteCapabilities());
173
- },
174
90
  async bootstrap(_authorization, request) {
175
91
  const entry = tracked.get(request.locator);
176
92
  if (entry === undefined)
177
93
  return { kind: 'room-not-readable', locator: request.locator };
178
- const continued = request.pageToken === undefined
179
- ? null
180
- : JSON.parse(request.pageToken);
181
- const held = await caughtUp(entry, continued?.after ?? null);
182
- if ('failure' in held)
183
- return held.failure;
184
- notifications.reach(entry.authority, held.scope);
185
- const seen = [
186
- ...(continued?.seen ?? []),
187
- ...entry.schemas.filter((schema) => held.scope.files.some((file) => file.path === schema)),
188
- ];
189
- const models = await partModels(held.scope, entry, new Set(seen), held.next === null);
190
- const continuation = held.next === null ? null : { after: held.next, seen };
191
- return {
192
- ...(await stepped(bootstrapping(held.scope))),
193
- ...(models === undefined ? {} : { models }),
194
- ...(continuation === null ? {} : { pageToken: JSON.stringify(continuation) }),
195
- };
94
+ return request.pageToken === undefined
95
+ ? openedRoom(entry, request.arriving, request.signal)
96
+ : streamedPage(entry, request.pageToken);
196
97
  },
197
- async refresh(_authorization, request) {
198
- const entry = tracked.get(request.locator);
199
- if (entry === undefined)
200
- return { kind: 'room-not-readable', locator: request.locator };
201
- if (request.pageToken !== undefined)
202
- return UNCERTAIN;
203
- if (snapshot) {
204
- return request.fromRoomVersion === roomVersion(entry)
205
- ? { kind: 'still-current' }
206
- : { kind: 'beyond-horizon' };
98
+ ...(snapshot
99
+ ? {
100
+ discover: discovered,
101
+ async refresh(_authorization, request) {
102
+ const entry = tracked.get(request.locator);
103
+ if (entry === undefined)
104
+ return { kind: 'room-not-readable', locator: request.locator };
105
+ if (request.pageToken !== undefined)
106
+ return UNCERTAIN;
107
+ return request.fromRoomVersion === roomVersion(entry)
108
+ ? { kind: 'still-current' }
109
+ : { kind: 'beyond-horizon' };
110
+ },
207
111
  }
208
- return refreshed(entry, request.fromRoomVersion);
209
- },
112
+ : {}),
210
113
  async mutate() {
211
114
  return { kind: 'rejected', affected: {}, code: 'write-grant', fields: {}, retryable: false };
212
115
  },
116
+ async attach() {
117
+ return { kind: 'rejected', code: 'write-grant', fields: {}, retryable: false };
118
+ },
213
119
  async history() {
214
120
  return { kind: 'no-history' };
215
121
  },
@@ -220,8 +126,8 @@ export function createSiteAdapter(transport, snapshot = false) {
220
126
  const read = await transport.download(route);
221
127
  if (read.status === 404)
222
128
  return unfound();
223
- return read.status === 200 && read.blob !== null
224
- ? { kind: 'binary', blob: read.blob }
129
+ return read.status === 200 && read.body !== null
130
+ ? { kind: 'binary', body: read.body, mediaType: read.mediaType }
225
131
  : UNCERTAIN;
226
132
  },
227
133
  async link(_authorization, request) {
@@ -234,13 +140,6 @@ export function createSiteAdapter(transport, snapshot = false) {
234
140
  name: request.path.slice(request.path.lastIndexOf('/') + 1),
235
141
  };
236
142
  },
237
- referrerScope(referrer) {
238
- const named = published(referrer);
239
- if (named === null)
240
- return null;
241
- const { path } = named.referrer;
242
- return { kind: 'room', locator: named.locator, ...(path === undefined ? {} : { path }) };
243
- },
244
143
  reached(locator, version, target) {
245
144
  return reachedVersion(version, target, tracked.get(locator)?.logEpoch);
246
145
  },
@@ -248,7 +147,7 @@ export function createSiteAdapter(transport, snapshot = false) {
248
147
  return followsVersion(version, next);
249
148
  },
250
149
  async action(_authorization, request) {
251
- if (request.name !== SITE_SUBMIT) {
150
+ if (request.name !== SITE_SUBMISSION) {
252
151
  return {
253
152
  kind: 'rejected',
254
153
  code: 'action-unimplemented',
@@ -258,7 +157,7 @@ export function createSiteAdapter(transport, snapshot = false) {
258
157
  }
259
158
  if (!transport.online())
260
159
  return UNCERTAIN;
261
- return actionOutcome(await transport.door(SITE_DOOR_SUBMIT, request.input), SITE_SUBMIT);
160
+ return actionOutcome(await transport.door(SITE_DOOR_SUBMIT, request.input), SITE_SUBMISSION, request.input);
262
161
  },
263
162
  roomStream: snapshot
264
163
  ? undefined
@@ -267,19 +166,10 @@ export function createSiteAdapter(transport, snapshot = false) {
267
166
  notifications.send(rooms.flatMap((room) => {
268
167
  const entry = tracked.get(room.locator);
269
168
  const at = logPosition(room.roomVersion);
270
- if (entry === undefined || at === null)
271
- return [];
272
- notifications.reach(entry.authority, at);
273
- return [presented(entry.authority, at)];
169
+ return entry === undefined || at === null ? [] : [presented(entry.authority, at)];
274
170
  }));
275
171
  },
276
- reopen: notifications.reopen,
277
172
  observe: notifications.observe,
278
- published(locator, roomVersion) {
279
- const at = logPosition(roomVersion);
280
- if (at !== null)
281
- notifications.published(locator, at);
282
- },
283
173
  },
284
174
  serviceMode: notifications.serviceMode,
285
175
  };
package/dist/stream.d.ts CHANGED
@@ -1,23 +1,23 @@
1
- import { type ConversationStream, type RefreshPage, type RoomStreamEvent, type ServiceModeSignal, type StreamReceipts } from '@agentshouse/mdruntime';
1
+ import { type AuthorityChange, type Discovery, type ConversationStream, type RoomStreamEvent, type ServiceModeSignal } from '@agentshouse/mdruntime';
2
2
  import type { TrackedAuthority } from './discovery.js';
3
- import type { HouseDoor, HouseHeldRoom, HouseMode, HouseStreamEntry } from './transport.js';
4
- import { type LogPosition } from './version.js';
5
- export type HeldRevision = (path: string, revision: string) => boolean;
6
- export type StreamedAdvancement = {
7
- page: RefreshPage;
8
- schemas: ReadonlyMap<string, HouseStreamEntry>;
3
+ import { type HouseDoor, type HouseHeldRoom, type HouseMode, type HouseStreamFrame } from './transport.js';
4
+ export type HouseOpeningFrame = Extract<HouseStreamFrame, {
5
+ type: 'opening';
6
+ }>;
7
+ export type HouseAuthorityChangedFrame = Extract<HouseStreamFrame, {
8
+ type: 'authority_changed';
9
+ }>;
10
+ export type HouseDiscovering = {
11
+ opened(frame: HouseOpeningFrame | null): Promise<Discovery | null>;
12
+ changed(frame: HouseAuthorityChangedFrame): AuthorityChange | null;
9
13
  };
10
14
  export type HouseNotifications = {
11
15
  runtime: string;
12
- observe(notify: (event: RoomStreamEvent) => void, receipts: StreamReceipts): () => void;
16
+ observe(notify: (event: RoomStreamEvent) => void): () => void;
13
17
  send(rooms: readonly HouseHeldRoom[]): void;
14
- published(locator: string, at: LogPosition): void;
15
- reopen(): void;
16
18
  track(authorities: readonly TrackedAuthority[]): void;
17
- reach(authority: string, at: LogPosition): void;
18
- advance(tracked: TrackedAuthority, from: LogPosition, holds?: HeldRevision): Generator<number, StreamedAdvancement | null, void>;
19
19
  conversations: ConversationStream;
20
20
  serviceMode: ServiceModeSignal;
21
21
  };
22
- export declare function houseNotifications(transport: HouseDoor, mode: HouseMode): HouseNotifications;
22
+ export declare function houseNotifications(transport: HouseDoor, mode: HouseMode, discovering: HouseDiscovering): HouseNotifications;
23
23
  //# sourceMappingURL=stream.d.ts.map