@agentshouse/mdruntime-adapter 0.1.0-alpha.3 → 0.1.0-alpha.30

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 (49) hide show
  1. package/AGENTS.md +122 -22
  2. package/dist/actions.d.ts +3 -11
  3. package/dist/actions.d.ts.map +1 -1
  4. package/dist/actions.js +119 -224
  5. package/dist/actions.js.map +1 -1
  6. package/dist/adapter.d.ts +3 -1
  7. package/dist/adapter.d.ts.map +1 -1
  8. package/dist/adapter.js +48 -104
  9. package/dist/adapter.js.map +1 -1
  10. package/dist/discovery.d.ts +35 -8
  11. package/dist/discovery.d.ts.map +1 -1
  12. package/dist/discovery.js +136 -32
  13. package/dist/discovery.js.map +1 -1
  14. package/dist/index.d.ts +1 -1
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js.map +1 -1
  17. package/dist/model.d.ts +19 -0
  18. package/dist/model.d.ts.map +1 -0
  19. package/dist/model.js +64 -0
  20. package/dist/model.js.map +1 -0
  21. package/dist/read.d.ts +20 -15
  22. package/dist/read.d.ts.map +1 -1
  23. package/dist/read.js +118 -39
  24. package/dist/read.js.map +1 -1
  25. package/dist/refusal.d.ts +4 -1
  26. package/dist/refusal.d.ts.map +1 -1
  27. package/dist/refusal.js +13 -0
  28. package/dist/refusal.js.map +1 -1
  29. package/dist/site.d.ts +4 -0
  30. package/dist/site.d.ts.map +1 -0
  31. package/dist/site.js +205 -0
  32. package/dist/site.js.map +1 -0
  33. package/dist/stream.d.ts +6 -5
  34. package/dist/stream.d.ts.map +1 -1
  35. package/dist/stream.js +107 -119
  36. package/dist/stream.js.map +1 -1
  37. package/dist/transport.d.ts +16 -25
  38. package/dist/transport.d.ts.map +1 -1
  39. package/dist/transport.js +5 -1
  40. package/dist/transport.js.map +1 -1
  41. package/dist/version.d.ts +14 -0
  42. package/dist/version.d.ts.map +1 -0
  43. package/dist/version.js +29 -0
  44. package/dist/version.js.map +1 -0
  45. package/dist/write.d.ts +6 -0
  46. package/dist/write.d.ts.map +1 -1
  47. package/dist/write.js +24 -7
  48. package/dist/write.js.map +1 -1
  49. package/package.json +3 -3
package/dist/site.js ADDED
@@ -0,0 +1,205 @@
1
+ import { actionOutcome } from './actions.js';
2
+ import { mapDiscovery, parseSiteDiscovery, siteCapabilities, siteSlots, siteTracked } from './discovery.js';
3
+ import { MODEL_ABSENT, failedModel, heldModel, modelKey, modelShelf } from './model.js';
4
+ import { advancedScope, authorityRoot, bootstrapOf, definitionsOf, parseEnumeration, parseSiteBootstrap, refreshFrom, selectionRoots } from './read.js';
5
+ import { classifyDoor, discoveryFailure } from './refusal.js';
6
+ import { houseNotifications } from './stream.js';
7
+ import { PRIVATE_LOCATOR, SITE_DOOR_BOOTSTRAP, SITE_DOOR_DISCOVER, SITE_DOOR_ENUMERATE, SITE_DOOR_SUBMIT } from './transport.js';
8
+ import { logPosition, presented, roomVersion } from './version.js';
9
+ const UNCERTAIN = {
10
+ kind: 'transport-uncertainty'
11
+ };
12
+ function siteRoom(tracked, attachedModel) {
13
+ const read = selectionRoots(tracked);
14
+ return {
15
+ name: tracked.handle,
16
+ locator: tracked.locator,
17
+ collectionRoot: tracked.collectionRoot,
18
+ roomVersion: roomVersion(tracked),
19
+ operations: {
20
+ read: [
21
+ ...read
22
+ ],
23
+ sync: [
24
+ ...read
25
+ ],
26
+ write: [],
27
+ delete: []
28
+ },
29
+ replica: {
30
+ selection: tracked.locator,
31
+ admission: 'admitted'
32
+ },
33
+ attachedModel,
34
+ providerOwnedPaths: []
35
+ };
36
+ }
37
+ function settled(refused) {
38
+ return refused.kind === 'beyond-horizon' ? UNCERTAIN : refused;
39
+ }
40
+ export function createSiteAdapter(transport) {
41
+ const notifications = houseNotifications(transport, 'site');
42
+ const tracked = new Map();
43
+ const models = modelShelf();
44
+ const acquired = new Map();
45
+ let caught = new Map();
46
+ async function enumeration(entry, from) {
47
+ const answer = await transport.door(SITE_DOOR_ENUMERATE, {
48
+ positions: [
49
+ presented(entry.authority, from)
50
+ ]
51
+ });
52
+ const classified = classifyDoor(answer.status, answer.body, entry.locator);
53
+ if (classified !== null) return classified;
54
+ return parseEnumeration(answer.body, 'site') ?? UNCERTAIN;
55
+ }
56
+ async function caughtUp(entry) {
57
+ const answer = await transport.read(`${SITE_DOOR_BOOTSTRAP}${entry.authority}`);
58
+ const classified = classifyDoor(answer.status, answer.body, entry.locator);
59
+ if (classified !== null) return {
60
+ failure: settled(classified)
61
+ };
62
+ const scope = parseSiteBootstrap(answer.body);
63
+ if (scope === null) return {
64
+ failure: UNCERTAIN
65
+ };
66
+ if (roomVersion(scope) === roomVersion(entry)) return {
67
+ scope
68
+ };
69
+ const found = await enumeration(entry, scope);
70
+ if ('kind' in found) return {
71
+ failure: settled(found)
72
+ };
73
+ const view = found.authorities.find((held)=>held.authority === entry.authority);
74
+ if (view === undefined) return {
75
+ failure: {
76
+ kind: 'room-not-readable',
77
+ locator: entry.locator
78
+ }
79
+ };
80
+ return {
81
+ scope: advancedScope(scope, view)
82
+ };
83
+ }
84
+ async function refreshed(entry, fromRoomVersion) {
85
+ const from = logPosition(fromRoomVersion);
86
+ if (from === null) return {
87
+ kind: 'beyond-horizon'
88
+ };
89
+ const streamed = notifications.advance(entry, from);
90
+ if (streamed !== null) return streamed;
91
+ const found = await enumeration(entry, from);
92
+ if ('kind' in found) return found;
93
+ return refreshFrom(found, entry, fromRoomVersion);
94
+ }
95
+ async function acquire(entry, held) {
96
+ if ('failure' in held) return {
97
+ snapshot: failedModel(held.failure),
98
+ identity: null
99
+ };
100
+ const model = await heldModel(definitionsOf(held.scope, entry.modelRoot), `${authorityRoot(entry.handle)}/${entry.modelRoot}`, MODEL_ABSENT);
101
+ acquired.set(entry.authority, {
102
+ roomVersion: roomVersion(held.scope),
103
+ model
104
+ });
105
+ return model;
106
+ }
107
+ return {
108
+ async authorize () {
109
+ return {};
110
+ },
111
+ async model (_authorization, request) {
112
+ return models.answer(request);
113
+ },
114
+ async discover () {
115
+ const answer = await transport.read(SITE_DOOR_DISCOVER);
116
+ const classified = classifyDoor(answer.status, answer.body, PRIVATE_LOCATOR);
117
+ if (classified !== null) return discoveryFailure(classified);
118
+ const document = parseSiteDiscovery(answer.body);
119
+ if (document === null) return UNCERTAIN;
120
+ const entries = siteTracked(document);
121
+ tracked.clear();
122
+ for (const entry of entries)tracked.set(entry.locator, entry);
123
+ notifications.track(entries);
124
+ const published = new Set(entries.map((entry)=>entry.authority));
125
+ for (const authority of [
126
+ ...acquired.keys()
127
+ ]){
128
+ if (!published.has(authority)) acquired.delete(authority);
129
+ }
130
+ const fresh = new Map();
131
+ const current = new Map();
132
+ for (const entry of entries){
133
+ const kept = acquired.get(entry.authority);
134
+ if (kept !== undefined && kept.roomVersion === roomVersion(entry)) {
135
+ current.set(entry.authority, kept.model);
136
+ continue;
137
+ }
138
+ const held = await caughtUp(entry);
139
+ fresh.set(entry.authority, held);
140
+ current.set(entry.authority, await acquire(entry, held));
141
+ }
142
+ caught = fresh;
143
+ const rooms = entries.map((entry)=>{
144
+ const shelved = models.retain(modelKey(entry.locator, 'collection'), current.get(entry.authority));
145
+ return siteRoom(entry, shelved?.identity ?? null);
146
+ });
147
+ models.keep(entries.map((entry)=>entry.locator));
148
+ return {
149
+ ...mapDiscovery(document, rooms, siteCapabilities()),
150
+ slots: siteSlots(document)
151
+ };
152
+ },
153
+ async bootstrap (_authorization, request) {
154
+ const entry = tracked.get(request.locator);
155
+ if (entry === undefined) return {
156
+ kind: 'room-not-readable',
157
+ locator: request.locator
158
+ };
159
+ if (request.pageToken !== undefined) return UNCERTAIN;
160
+ const held = caught.get(entry.authority) ?? await caughtUp(entry);
161
+ if ('failure' in held) return held.failure;
162
+ return bootstrapOf(held.scope, entry);
163
+ },
164
+ async refresh (_authorization, request) {
165
+ const entry = tracked.get(request.locator);
166
+ if (entry === undefined) return {
167
+ kind: 'room-not-readable',
168
+ locator: request.locator
169
+ };
170
+ if (request.pageToken !== undefined) return UNCERTAIN;
171
+ const result = await refreshed(entry, request.fromRoomVersion);
172
+ if (result.kind === 'beyond-horizon') caught.delete(entry.authority);
173
+ return result;
174
+ },
175
+ async mutate () {
176
+ return {
177
+ kind: 'rejected',
178
+ affected: {},
179
+ code: 'write-grant',
180
+ fields: {}
181
+ };
182
+ },
183
+ async history () {
184
+ return {
185
+ kind: 'no-history'
186
+ };
187
+ },
188
+ async action (_authorization, request) {
189
+ if (request.name !== 'submit') {
190
+ return {
191
+ kind: 'rejected',
192
+ code: 'action-unimplemented',
193
+ fields: {
194
+ name: request.name
195
+ }
196
+ };
197
+ }
198
+ if (!transport.online()) return UNCERTAIN;
199
+ return actionOutcome(await transport.door(SITE_DOOR_SUBMIT, request.input), 'submit');
200
+ },
201
+ roomVersions: notifications
202
+ };
203
+ }
204
+
205
+ //# sourceMappingURL=site.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/workspace/packages/mdruntime-adapter/src/site.ts"],"sourcesContent":["import type {\n Adapter,\n AdapterFailure,\n BootstrapResult,\n DiscoverResult,\n DiscoveredRoom,\n ModelResult,\n RefreshResult,\n} from '@agentshouse/mdruntime';\nimport { actionOutcome } from './actions.js';\nimport {\n mapDiscovery,\n parseSiteDiscovery,\n siteCapabilities,\n siteSlots,\n siteTracked,\n type SiteTracked,\n} from './discovery.js';\nimport {\n MODEL_ABSENT,\n failedModel,\n heldModel,\n modelKey,\n modelShelf,\n type HeldModel,\n} from './model.js';\nimport {\n advancedScope,\n authorityRoot,\n bootstrapOf,\n definitionsOf,\n parseEnumeration,\n parseSiteBootstrap,\n refreshFrom,\n selectionRoots,\n type HouseEnumeration,\n type HouseScope,\n} from './read.js';\nimport { classifyDoor, discoveryFailure } from './refusal.js';\nimport { houseNotifications } from './stream.js';\nimport {\n PRIVATE_LOCATOR,\n SITE_DOOR_BOOTSTRAP,\n SITE_DOOR_DISCOVER,\n SITE_DOOR_ENUMERATE,\n SITE_DOOR_SUBMIT,\n type HouseSiteTransport,\n} from './transport.js';\nimport { logPosition, presented, roomVersion, type LogPosition } from './version.js';\n\ntype Refused = AdapterFailure | { kind: 'beyond-horizon' };\n\ntype CaughtUp = { scope: HouseScope } | { failure: AdapterFailure };\n\ntype Acquired = { roomVersion: string; model: HeldModel };\n\nconst UNCERTAIN = { kind: 'transport-uncertainty' } as const;\n\nfunction siteRoom(\n tracked: SiteTracked,\n attachedModel: DiscoveredRoom['attachedModel'],\n): DiscoveredRoom {\n const read = selectionRoots(tracked);\n return {\n name: tracked.handle,\n locator: tracked.locator,\n collectionRoot: tracked.collectionRoot,\n roomVersion: roomVersion(tracked),\n operations: { read: [...read], sync: [...read], write: [], delete: [] },\n replica: { selection: tracked.locator, admission: 'admitted' },\n attachedModel,\n providerOwnedPaths: [],\n };\n}\n\nfunction settled(refused: Refused): AdapterFailure {\n return refused.kind === 'beyond-horizon' ? UNCERTAIN : refused;\n}\n\nexport function createSiteAdapter(transport: HouseSiteTransport): Adapter {\n const notifications = houseNotifications(transport, 'site');\n const tracked = new Map<string, SiteTracked>();\n const models = modelShelf();\n const acquired = new Map<string, Acquired>();\n let caught = new Map<string, CaughtUp>();\n\n async function enumeration(\n entry: SiteTracked,\n from: LogPosition,\n ): Promise<HouseEnumeration | Refused> {\n const answer = await transport.door(SITE_DOOR_ENUMERATE, {\n positions: [presented(entry.authority, from)],\n });\n const classified = classifyDoor(answer.status, answer.body, entry.locator);\n if (classified !== null) return classified;\n return parseEnumeration(answer.body, 'site') ?? UNCERTAIN;\n }\n\n async function caughtUp(entry: SiteTracked): Promise<CaughtUp> {\n const answer = await transport.read(`${SITE_DOOR_BOOTSTRAP}${entry.authority}`);\n const classified = classifyDoor(answer.status, answer.body, entry.locator);\n if (classified !== null) return { failure: settled(classified) };\n const scope = parseSiteBootstrap(answer.body);\n if (scope === null) return { failure: UNCERTAIN };\n if (roomVersion(scope) === roomVersion(entry)) return { scope };\n const found = await enumeration(entry, scope);\n if ('kind' in found) return { failure: settled(found) };\n const view = found.authorities.find((held) => held.authority === entry.authority);\n if (view === undefined)\n return { failure: { kind: 'room-not-readable', locator: entry.locator } };\n return { scope: advancedScope(scope, view) };\n }\n\n async function refreshed(entry: SiteTracked, fromRoomVersion: string): Promise<RefreshResult> {\n const from = logPosition(fromRoomVersion);\n if (from === null) return { kind: 'beyond-horizon' };\n const streamed = notifications.advance(entry, from);\n if (streamed !== null) return streamed;\n const found = await enumeration(entry, from);\n if ('kind' in found) return found;\n return refreshFrom(found, entry, fromRoomVersion);\n }\n\n async function acquire(entry: SiteTracked, held: CaughtUp): Promise<HeldModel> {\n if ('failure' in held) return { snapshot: failedModel(held.failure), identity: null };\n const model = await heldModel(\n definitionsOf(held.scope, entry.modelRoot),\n `${authorityRoot(entry.handle)}/${entry.modelRoot}`,\n MODEL_ABSENT,\n );\n acquired.set(entry.authority, { roomVersion: roomVersion(held.scope), model });\n return model;\n }\n\n return {\n async authorize() {\n return {};\n },\n\n async model(_authorization, request): Promise<ModelResult> {\n return models.answer(request);\n },\n\n async discover(): Promise<DiscoverResult> {\n const answer = await transport.read(SITE_DOOR_DISCOVER);\n const classified = classifyDoor(answer.status, answer.body, PRIVATE_LOCATOR);\n if (classified !== null) return discoveryFailure(classified);\n const document = parseSiteDiscovery(answer.body);\n if (document === null) return UNCERTAIN;\n const entries = siteTracked(document);\n tracked.clear();\n for (const entry of entries) tracked.set(entry.locator, entry);\n notifications.track(entries);\n const published = new Set(entries.map((entry) => entry.authority));\n for (const authority of [...acquired.keys()]) {\n if (!published.has(authority)) acquired.delete(authority);\n }\n const fresh = new Map<string, CaughtUp>();\n const current = new Map<string, HeldModel>();\n for (const entry of entries) {\n const kept = acquired.get(entry.authority);\n if (kept !== undefined && kept.roomVersion === roomVersion(entry)) {\n current.set(entry.authority, kept.model);\n continue;\n }\n const held = await caughtUp(entry);\n fresh.set(entry.authority, held);\n current.set(entry.authority, await acquire(entry, held));\n }\n caught = fresh;\n const rooms = entries.map((entry) => {\n const shelved = models.retain(\n modelKey(entry.locator, 'collection'),\n current.get(entry.authority),\n );\n return siteRoom(entry, shelved?.identity ?? null);\n });\n models.keep(entries.map((entry) => entry.locator));\n return { ...mapDiscovery(document, rooms, siteCapabilities()), slots: siteSlots(document) };\n },\n\n async bootstrap(_authorization, request): Promise<BootstrapResult> {\n const entry = tracked.get(request.locator);\n if (entry === undefined) return { kind: 'room-not-readable', locator: request.locator };\n if (request.pageToken !== undefined) return UNCERTAIN;\n const held = caught.get(entry.authority) ?? (await caughtUp(entry));\n if ('failure' in held) return held.failure;\n return bootstrapOf(held.scope, entry);\n },\n\n async refresh(_authorization, request): Promise<RefreshResult> {\n const entry = tracked.get(request.locator);\n if (entry === undefined) return { kind: 'room-not-readable', locator: request.locator };\n if (request.pageToken !== undefined) return UNCERTAIN;\n const result = await refreshed(entry, request.fromRoomVersion);\n if (result.kind === 'beyond-horizon') caught.delete(entry.authority);\n return result;\n },\n\n async mutate() {\n return { kind: 'rejected', affected: {}, code: 'write-grant', fields: {} };\n },\n\n async history() {\n return { kind: 'no-history' };\n },\n\n async action(_authorization, request) {\n if (request.name !== 'submit') {\n return { kind: 'rejected', code: 'action-unimplemented', fields: { name: request.name } };\n }\n if (!transport.online()) return UNCERTAIN;\n return actionOutcome(await transport.door(SITE_DOOR_SUBMIT, request.input), 'submit');\n },\n\n roomVersions: notifications,\n };\n}\n"],"names":["actionOutcome","mapDiscovery","parseSiteDiscovery","siteCapabilities","siteSlots","siteTracked","MODEL_ABSENT","failedModel","heldModel","modelKey","modelShelf","advancedScope","authorityRoot","bootstrapOf","definitionsOf","parseEnumeration","parseSiteBootstrap","refreshFrom","selectionRoots","classifyDoor","discoveryFailure","houseNotifications","PRIVATE_LOCATOR","SITE_DOOR_BOOTSTRAP","SITE_DOOR_DISCOVER","SITE_DOOR_ENUMERATE","SITE_DOOR_SUBMIT","logPosition","presented","roomVersion","UNCERTAIN","kind","siteRoom","tracked","attachedModel","read","name","handle","locator","collectionRoot","operations","sync","write","delete","replica","selection","admission","providerOwnedPaths","settled","refused","createSiteAdapter","transport","notifications","Map","models","acquired","caught","enumeration","entry","from","answer","door","positions","authority","classified","status","body","caughtUp","failure","scope","found","view","authorities","find","held","undefined","refreshed","fromRoomVersion","streamed","advance","acquire","snapshot","identity","model","modelRoot","set","authorize","_authorization","request","discover","document","entries","clear","track","published","Set","map","keys","has","fresh","current","kept","get","rooms","shelved","retain","keep","slots","bootstrap","pageToken","refresh","result","mutate","affected","code","fields","history","action","online","input","roomVersions"],"mappings":"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"}
package/dist/stream.d.ts CHANGED
@@ -1,10 +1,11 @@
1
- import type { RefreshPage, SelectedRooms, ViewedConversations } from '@agentshouse/mdruntime';
1
+ import type { ConversationStream, RefreshPage, SelectedRooms } from '@agentshouse/mdruntime';
2
2
  import type { TrackedAuthority } from './discovery.js';
3
- import { type HouseTransport } from './transport.js';
3
+ import type { HouseDoor, HouseMode } from './transport.js';
4
+ import { type LogPosition } from './version.js';
4
5
  export type HouseNotifications = SelectedRooms & {
5
6
  track(authorities: readonly TrackedAuthority[]): void;
6
- advance(tracked: TrackedAuthority, fromRoomVersion: string): RefreshPage | null;
7
- conversations: ViewedConversations;
7
+ advance(tracked: TrackedAuthority, from: LogPosition): RefreshPage | null;
8
+ conversations: ConversationStream;
8
9
  };
9
- export declare function houseNotifications(transport: HouseTransport): HouseNotifications;
10
+ export declare function houseNotifications(transport: HouseDoor, mode: HouseMode): HouseNotifications;
10
11
  //# sourceMappingURL=stream.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../src/stream.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,WAAW,EAEX,aAAa,EACb,mBAAmB,EACpB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEvD,OAAO,EAAgD,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAUnG,MAAM,MAAM,kBAAkB,GAAG,aAAa,GAAG;IAC/C,KAAK,CAAC,WAAW,EAAE,SAAS,gBAAgB,EAAE,GAAG,IAAI,CAAC;IACtD,OAAO,CAAC,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAAC;IAChF,aAAa,EAAE,mBAAmB,CAAC;CACpC,CAAC;AAaF,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,cAAc,GAAG,kBAAkB,CA0MhF"}
1
+ {"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../src/stream.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,kBAAkB,EAClB,WAAW,EAEX,aAAa,EACd,MAAM,wBAAwB,CAAC;AAEhC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEvD,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAsC,MAAM,gBAAgB,CAAC;AAC/F,OAAO,EAAe,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAc7D,MAAM,MAAM,kBAAkB,GAAG,aAAa,GAAG;IAC/C,KAAK,CAAC,WAAW,EAAE,SAAS,gBAAgB,EAAE,GAAG,IAAI,CAAC;IACtD,OAAO,CAAC,OAAO,EAAE,gBAAgB,EAAE,IAAI,EAAE,WAAW,GAAG,WAAW,GAAG,IAAI,CAAC;IAC1E,aAAa,EAAE,kBAAkB,CAAC;CACnC,CAAC;AAOF,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,GAAG,kBAAkB,CA4L5F"}
package/dist/stream.js CHANGED
@@ -1,53 +1,32 @@
1
- import { actionOutcome, HISTORY_PAGE, readConversationHistory } from './actions.js';
1
+ import { acceptedMessage, actionOutcome } from './actions.js';
2
2
  import { selects } from './read.js';
3
- import { PRIVATE_AUTHORITY } from './transport.js';
3
+ import { roomVersion } from './version.js';
4
4
  const HELD_POSITIONS_MAX = 256;
5
5
  function position(value) {
6
6
  if (!/^(?:0|[1-9][0-9]*)$/.test(value)) return null;
7
7
  return BigInt(value);
8
8
  }
9
- function messageTurn(message) {
10
- if (typeof message !== 'object' || message === null) return null;
11
- const turn = message.message_id;
12
- return typeof turn === 'string' ? turn : null;
13
- }
14
- export function houseNotifications(transport) {
9
+ export function houseNotifications(transport, mode) {
15
10
  const stream = transport.stream;
16
- const held = new Map();
11
+ const lines = new Map();
17
12
  const locators = new Map();
18
- const authorities = new Map();
19
13
  const observers = new Set();
20
14
  const conversationObservers = new Set();
21
- const subscribedConversations = new Set();
22
- let selected = [];
15
+ let replicated = [];
23
16
  let viewed = [];
24
17
  let listening = false;
25
- function subscribe() {
26
- const named = [];
27
- for (const locator of selected){
28
- const authority = authorities.get(locator);
29
- if (authority === undefined || authority === PRIVATE_AUTHORITY) continue;
30
- named.push({
31
- kind: 'room',
32
- room: authority
33
- });
34
- }
35
- if (authorities.has(PRIVATE_AUTHORITY)) named.push({
36
- kind: 'private'
37
- });
38
- for (const conversation of viewed){
39
- named.push({
40
- kind: 'conversation',
41
- conversation
42
- });
43
- }
44
- stream.subscribe(named);
18
+ let catching = false;
19
+ function listen() {
20
+ if (listening) return;
21
+ listening = true;
22
+ stream.observe(receive);
45
23
  }
46
24
  function emitConversation(event) {
47
- if (!viewed.includes(event.conversation)) return;
25
+ const delivered = event.kind === 'accepted-message' || event.kind === 'gap' ? replicated : viewed;
26
+ if (!delivered.includes(event.conversation)) return;
48
27
  for (const observer of conversationObservers)observer(event);
49
28
  }
50
- async function catchUp(conversation, history) {
29
+ async function summarize(conversation) {
51
30
  const viewedAnswer = await transport.door('/door/conversations/view', {
52
31
  conversation_id: conversation
53
32
  });
@@ -59,23 +38,30 @@ export function houseNotifications(transport) {
59
38
  summary: structuredClone('session' in summary.result ? summary.result.session : null)
60
39
  });
61
40
  }
62
- if (!history) return;
63
- const heldHistory = await readConversationHistory(transport, conversation);
64
- if ('outcome' in heldHistory) return;
65
- const newest = heldHistory.messages.slice(-HISTORY_PAGE);
66
- for (const entry of newest){
67
- const turn = messageTurn(entry);
68
- if (turn === null) return;
69
- emitConversation({
70
- kind: 'accepted-message',
71
- conversation,
72
- message: {
73
- turn,
74
- message: structuredClone(entry)
75
- }
41
+ }
42
+ function reopened() {
43
+ for (const conversation of replicated)emitConversation({
44
+ kind: 'gap',
45
+ conversation
46
+ });
47
+ for (const conversation of viewed)void summarize(conversation);
48
+ }
49
+ function announce(authority, at) {
50
+ const version = roomVersion(at);
51
+ for (const locator of locators.get(authority) ?? []){
52
+ for (const observer of observers)observer({
53
+ locator,
54
+ roomVersion: version
76
55
  });
77
56
  }
78
57
  }
58
+ function interrupt(authority) {
59
+ const line = lines.get(authority);
60
+ if (line !== undefined) lines.set(authority, {
61
+ logEpoch: line.logEpoch,
62
+ frames: []
63
+ });
64
+ }
79
65
  function receive(frame) {
80
66
  if (frame.type === 'draft') {
81
67
  emitConversation({
@@ -87,15 +73,12 @@ export function houseNotifications(transport) {
87
73
  return;
88
74
  }
89
75
  if (frame.type === 'message') {
90
- const turn = messageTurn(frame.message);
91
- if (turn === null) return;
76
+ const message = acceptedMessage(frame.message);
77
+ if (message === null) return;
92
78
  emitConversation({
93
79
  kind: 'accepted-message',
94
80
  conversation: frame.conversation_id,
95
- message: {
96
- turn,
97
- message: structuredClone(frame.message)
98
- }
81
+ message
99
82
  });
100
83
  return;
101
84
  }
@@ -107,61 +90,60 @@ export function houseNotifications(transport) {
107
90
  });
108
91
  return;
109
92
  }
110
- if (frame.type === 'subscribed' && viewed.includes(frame.authority)) {
111
- const reconnect = subscribedConversations.has(frame.authority);
112
- subscribedConversations.add(frame.authority);
113
- void catchUp(frame.authority, reconnect);
114
- return;
115
- }
116
- if (frame.type !== 'entries') {
117
- held.delete(frame.authority);
118
- return;
119
- }
93
+ if (frame.type !== 'entries') return;
120
94
  const at = position(frame.position);
121
95
  if (at === null) {
122
- held.delete(frame.authority);
96
+ interrupt(frame.authority);
123
97
  return;
124
98
  }
125
- const run = held.get(frame.authority) ?? [];
126
- const last = run.at(-1);
127
- const continued = last !== undefined && last.position + 1n === at;
128
- const next = continued ? [
129
- ...run,
130
- {
131
- position: at,
132
- entries: frame.entries
133
- }
134
- ] : [
135
- {
136
- position: at,
137
- entries: frame.entries
138
- }
139
- ];
140
- held.set(frame.authority, next.length > HELD_POSITIONS_MAX ? [] : next);
141
- const locator = locators.get(frame.authority);
142
- if (locator === undefined) return;
143
- for (const observer of observers)observer({
144
- locator,
145
- roomVersion: frame.position
99
+ const line = lines.get(frame.authority);
100
+ if (line?.logEpoch === frame.log_epoch) {
101
+ const last = line.frames.at(-1);
102
+ const continued = last !== undefined && last.position + 1n === at;
103
+ const next = continued ? [
104
+ ...line.frames,
105
+ {
106
+ position: at,
107
+ entries: frame.entries
108
+ }
109
+ ] : [
110
+ {
111
+ position: at,
112
+ entries: frame.entries
113
+ }
114
+ ];
115
+ lines.set(frame.authority, {
116
+ logEpoch: line.logEpoch,
117
+ frames: next.length > HELD_POSITIONS_MAX ? [] : next
118
+ });
119
+ } else {
120
+ interrupt(frame.authority);
121
+ }
122
+ announce(frame.authority, {
123
+ position: frame.position,
124
+ logEpoch: frame.log_epoch
146
125
  });
147
126
  }
148
127
  return {
149
128
  conversations: {
129
+ replicate (next) {
130
+ replicated = [
131
+ ...next
132
+ ];
133
+ },
150
134
  view (next) {
151
- const open = new Set(next);
152
- for (const conversation of subscribedConversations){
153
- if (!open.has(conversation)) subscribedConversations.delete(conversation);
154
- }
135
+ const entering = next.filter((conversation)=>!viewed.includes(conversation));
155
136
  viewed = [
156
137
  ...next
157
138
  ];
158
- subscribe();
139
+ for (const conversation of entering)void summarize(conversation);
159
140
  },
160
141
  observe (notify) {
161
142
  conversationObservers.add(notify);
162
- if (!listening) {
163
- listening = true;
164
- stream.observe(receive);
143
+ listen();
144
+ if (!catching) {
145
+ catching = true;
146
+ stream.opened(reopened);
165
147
  }
166
148
  return ()=>{
167
149
  conversationObservers.delete(notify);
@@ -170,38 +152,37 @@ export function houseNotifications(transport) {
170
152
  },
171
153
  track (entries) {
172
154
  locators.clear();
173
- authorities.clear();
174
155
  for (const entry of entries){
175
- locators.set(entry.authority, entry.locator);
176
- authorities.set(entry.locator, entry.authority);
156
+ locators.set(entry.authority, [
157
+ ...locators.get(entry.authority) ?? [],
158
+ entry.locator
159
+ ]);
160
+ if (lines.get(entry.authority)?.logEpoch !== entry.logEpoch) {
161
+ lines.set(entry.authority, {
162
+ logEpoch: entry.logEpoch,
163
+ frames: []
164
+ });
165
+ }
177
166
  }
178
- for (const authority of held.keys()){
179
- if (!locators.has(authority)) held.delete(authority);
167
+ for (const authority of lines.keys()){
168
+ if (!locators.has(authority)) lines.delete(authority);
180
169
  }
181
170
  },
182
- select (named) {
183
- selected = [
184
- ...named
185
- ];
186
- subscribe();
187
- },
171
+ select () {},
188
172
  observe (notify) {
189
173
  observers.add(notify);
190
- if (!listening) {
191
- listening = true;
192
- stream.observe(receive);
193
- }
174
+ listen();
194
175
  return ()=>{
195
176
  observers.delete(notify);
196
177
  };
197
178
  },
198
- advance (tracked, fromRoomVersion) {
199
- const run = held.get(tracked.authority);
200
- const from = position(fromRoomVersion);
201
- if (run === undefined || from === null) return null;
202
- const at = run.findIndex((frame)=>frame.position === from + 1n);
179
+ advance (tracked, from) {
180
+ const line = lines.get(tracked.authority);
181
+ const start = position(from.position);
182
+ if (line === undefined || start === null || line.logEpoch !== from.logEpoch) return null;
183
+ const at = line.frames.findIndex((frame)=>frame.position === start + 1n);
203
184
  if (at === -1) return null;
204
- const pending = run.slice(at);
185
+ const pending = line.frames.slice(at);
205
186
  const documents = {};
206
187
  const revisions = {};
207
188
  const removals = [];
@@ -214,19 +195,26 @@ export function houseNotifications(transport) {
214
195
  if (!removals.includes(entry.path)) removals.push(entry.path);
215
196
  continue;
216
197
  }
217
- if (entry.text === undefined || entry.revision === undefined) return null;
198
+ if (entry.text === undefined) return null;
199
+ if (mode === 'credential' && entry.revision === undefined) return null;
218
200
  documents[entry.path] = entry.text;
219
- revisions[entry.path] = entry.revision;
201
+ if (entry.revision !== undefined) revisions[entry.path] = entry.revision;
220
202
  const removed = removals.indexOf(entry.path);
221
203
  if (removed !== -1) removals.splice(removed, 1);
222
204
  }
223
205
  }
224
206
  const last = pending.at(-1);
225
207
  if (last === undefined) return null;
226
- held.set(tracked.authority, pending);
208
+ lines.set(tracked.authority, {
209
+ logEpoch: line.logEpoch,
210
+ frames: pending
211
+ });
227
212
  return {
228
213
  kind: 'net-advancement',
229
- roomVersion: last.position.toString(),
214
+ roomVersion: roomVersion({
215
+ position: last.position.toString(),
216
+ logEpoch: line.logEpoch
217
+ }),
230
218
  documents,
231
219
  revisions,
232
220
  binaryTargets: [],