@agentshouse/mdruntime 0.1.0-cbbae1fff01c6 → 0.1.0-cc00e9289ddf4

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 (200) hide show
  1. package/README.md +17 -15
  2. package/dist/acceptance.d.ts.map +1 -1
  3. package/dist/acceptance.js +14 -23
  4. package/dist/act-lanes.d.ts +30 -0
  5. package/dist/act-lanes.d.ts.map +1 -0
  6. package/dist/act-lanes.js +225 -0
  7. package/dist/actions.d.ts +8 -2
  8. package/dist/actions.d.ts.map +1 -1
  9. package/dist/actions.js +114 -63
  10. package/dist/adapter.d.ts +30 -12
  11. package/dist/adapter.d.ts.map +1 -1
  12. package/dist/attached-model.d.ts +0 -1
  13. package/dist/attached-model.d.ts.map +1 -1
  14. package/dist/attached-model.js +0 -5
  15. package/dist/chat-types.d.ts +0 -2
  16. package/dist/chat-types.d.ts.map +1 -1
  17. package/dist/chat-types.js +0 -2
  18. package/dist/client.d.ts +8 -18
  19. package/dist/client.d.ts.map +1 -1
  20. package/dist/client.js +27 -15
  21. package/dist/compile.d.ts +2 -0
  22. package/dist/compile.d.ts.map +1 -1
  23. package/dist/compile.js +17 -4
  24. package/dist/connection.d.ts.map +1 -1
  25. package/dist/connection.js +15 -13
  26. package/dist/conversation-live.d.ts +23 -8
  27. package/dist/conversation-live.d.ts.map +1 -1
  28. package/dist/conversation-live.js +21 -9
  29. package/dist/conversation-row.d.ts +2 -0
  30. package/dist/conversation-row.d.ts.map +1 -1
  31. package/dist/conversation-row.js +2 -0
  32. package/dist/conversation.d.ts +27 -5
  33. package/dist/conversation.d.ts.map +1 -1
  34. package/dist/conversation.js +279 -168
  35. package/dist/discovery.d.ts.map +1 -1
  36. package/dist/discovery.js +8 -6
  37. package/dist/durability.d.ts +4 -0
  38. package/dist/durability.d.ts.map +1 -1
  39. package/dist/durability.js +4 -0
  40. package/dist/entry.js +1 -1
  41. package/dist/expectation.d.ts +25 -14
  42. package/dist/expectation.d.ts.map +1 -1
  43. package/dist/expectation.js +51 -20
  44. package/dist/failure.d.ts +2 -0
  45. package/dist/failure.d.ts.map +1 -1
  46. package/dist/failure.js +3 -0
  47. package/dist/figures.d.ts.map +1 -1
  48. package/dist/figures.js +4 -2
  49. package/dist/file.d.ts.map +1 -1
  50. package/dist/file.js +2 -1
  51. package/dist/host.d.ts +1 -0
  52. package/dist/host.d.ts.map +1 -1
  53. package/dist/host.js +39 -11
  54. package/dist/index.d.ts +11 -8
  55. package/dist/index.d.ts.map +1 -1
  56. package/dist/index.js +9 -6
  57. package/dist/install.d.ts +10 -4
  58. package/dist/install.d.ts.map +1 -1
  59. package/dist/install.js +45 -46
  60. package/dist/lifecycle.d.ts.map +1 -1
  61. package/dist/lifecycle.js +22 -16
  62. package/dist/listing.d.ts +7 -3
  63. package/dist/listing.d.ts.map +1 -1
  64. package/dist/listing.js +26 -13
  65. package/dist/live.d.ts +5 -3
  66. package/dist/live.d.ts.map +1 -1
  67. package/dist/live.js +71 -30
  68. package/dist/locks.d.ts.map +1 -1
  69. package/dist/locks.js +2 -3
  70. package/dist/lost.d.ts.map +1 -1
  71. package/dist/lost.js +2 -3
  72. package/dist/media.d.ts.map +1 -1
  73. package/dist/media.js +7 -10
  74. package/dist/memory.d.ts +3 -1
  75. package/dist/memory.d.ts.map +1 -1
  76. package/dist/memory.js +50 -22
  77. package/dist/messages.d.ts +12 -3
  78. package/dist/messages.d.ts.map +1 -1
  79. package/dist/messages.js +24 -10
  80. package/dist/models.d.ts.map +1 -1
  81. package/dist/models.js +5 -8
  82. package/dist/observation.d.ts +27 -12
  83. package/dist/observation.d.ts.map +1 -1
  84. package/dist/observation.js +173 -69
  85. package/dist/opening.d.ts.map +1 -1
  86. package/dist/opening.js +3 -9
  87. package/dist/outcome.d.ts +7 -0
  88. package/dist/outcome.d.ts.map +1 -1
  89. package/dist/outcome.js +23 -2
  90. package/dist/overlay.d.ts +3 -0
  91. package/dist/overlay.d.ts.map +1 -1
  92. package/dist/overlay.js +8 -4
  93. package/dist/page-metadata.d.ts +8 -0
  94. package/dist/page-metadata.d.ts.map +1 -0
  95. package/dist/page-metadata.js +19 -0
  96. package/dist/parts.d.ts +2 -9
  97. package/dist/parts.d.ts.map +1 -1
  98. package/dist/parts.js +6 -14
  99. package/dist/preparation.d.ts +27 -6
  100. package/dist/preparation.d.ts.map +1 -1
  101. package/dist/preparation.js +137 -56
  102. package/dist/presence.d.ts +7 -1
  103. package/dist/presence.d.ts.map +1 -1
  104. package/dist/presence.js +22 -9
  105. package/dist/queries.d.ts +2 -2
  106. package/dist/queries.d.ts.map +1 -1
  107. package/dist/queries.js +62 -24
  108. package/dist/questions.d.ts +0 -5
  109. package/dist/questions.d.ts.map +1 -1
  110. package/dist/questions.js +0 -7
  111. package/dist/queue.d.ts +2 -2
  112. package/dist/queue.d.ts.map +1 -1
  113. package/dist/queue.js +25 -30
  114. package/dist/reach.d.ts +2 -3
  115. package/dist/reach.d.ts.map +1 -1
  116. package/dist/reach.js +75 -21
  117. package/dist/react.d.ts +7 -5
  118. package/dist/react.d.ts.map +1 -1
  119. package/dist/react.js +28 -5
  120. package/dist/readiness.d.ts +9 -3
  121. package/dist/readiness.d.ts.map +1 -1
  122. package/dist/readiness.js +3 -5
  123. package/dist/reads.d.ts +3 -1
  124. package/dist/reads.d.ts.map +1 -1
  125. package/dist/reads.js +5 -1
  126. package/dist/recovery.d.ts +3 -1
  127. package/dist/recovery.d.ts.map +1 -1
  128. package/dist/recovery.js +29 -7
  129. package/dist/refusal.d.ts.map +1 -1
  130. package/dist/refusal.js +1 -3
  131. package/dist/retried.d.ts +30 -0
  132. package/dist/retried.d.ts.map +1 -0
  133. package/dist/retried.js +72 -0
  134. package/dist/room-draft.d.ts +2 -2
  135. package/dist/room-draft.d.ts.map +1 -1
  136. package/dist/room-draft.js +26 -21
  137. package/dist/rows.d.ts +2 -0
  138. package/dist/rows.d.ts.map +1 -1
  139. package/dist/rows.js +3 -0
  140. package/dist/runtime.d.ts +2 -2
  141. package/dist/runtime.d.ts.map +1 -1
  142. package/dist/save.d.ts +1 -0
  143. package/dist/save.d.ts.map +1 -1
  144. package/dist/save.js +105 -55
  145. package/dist/saves.d.ts.map +1 -1
  146. package/dist/saves.js +3 -6
  147. package/dist/seed.d.ts.map +1 -1
  148. package/dist/seed.js +2 -2
  149. package/dist/shown.d.ts +3 -2
  150. package/dist/shown.d.ts.map +1 -1
  151. package/dist/shown.js +58 -39
  152. package/dist/socket/lanes.d.ts +12 -4
  153. package/dist/socket/lanes.d.ts.map +1 -1
  154. package/dist/socket/lanes.js +34 -8
  155. package/dist/socket.d.ts +3 -3
  156. package/dist/socket.d.ts.map +1 -1
  157. package/dist/socket.js +3 -9
  158. package/dist/source.d.ts +0 -7
  159. package/dist/source.d.ts.map +1 -1
  160. package/dist/store/installs.d.ts +2 -2
  161. package/dist/store/installs.d.ts.map +1 -1
  162. package/dist/store/installs.js +10 -4
  163. package/dist/store/readiness.d.ts +5 -4
  164. package/dist/store/readiness.d.ts.map +1 -1
  165. package/dist/store/readiness.js +28 -12
  166. package/dist/store/records.d.ts +11 -4
  167. package/dist/store/records.d.ts.map +1 -1
  168. package/dist/store/records.js +56 -20
  169. package/dist/store/rooms-shown.d.ts +8 -0
  170. package/dist/store/rooms-shown.d.ts.map +1 -0
  171. package/dist/store/rooms-shown.js +13 -0
  172. package/dist/store.d.ts +9 -6
  173. package/dist/store.d.ts.map +1 -1
  174. package/dist/store.js +11 -2
  175. package/dist/stored.d.ts +24 -13
  176. package/dist/stored.d.ts.map +1 -1
  177. package/dist/stored.js +73 -17
  178. package/dist/surface.d.ts +4 -3
  179. package/dist/surface.d.ts.map +1 -1
  180. package/dist/surface.js +12 -13
  181. package/dist/sync.d.ts.map +1 -1
  182. package/dist/sync.js +31 -17
  183. package/dist/tables.d.ts +14 -7
  184. package/dist/tables.d.ts.map +1 -1
  185. package/dist/tables.js +36 -15
  186. package/dist/tallies.d.ts.map +1 -1
  187. package/dist/tallies.js +1 -2
  188. package/dist/wire.d.ts +40 -3
  189. package/dist/wire.d.ts.map +1 -1
  190. package/dist/wire.js +11 -1
  191. package/package.json +3 -3
  192. package/dist/attempt.d.ts +0 -16
  193. package/dist/attempt.d.ts.map +0 -1
  194. package/dist/attempt.js +0 -70
  195. package/dist/job.d.ts +0 -5
  196. package/dist/job.d.ts.map +0 -1
  197. package/dist/job.js +0 -7
  198. package/dist/log.d.ts +0 -6
  199. package/dist/log.d.ts.map +0 -1
  200. package/dist/log.js +0 -14
package/dist/live.js CHANGED
@@ -1,27 +1,25 @@
1
- import { changeOf, digestOf, } from './observation.js';
1
+ import { changeOf, digestOf, keyedAt, keyedInto, samePending, unchanged, } from './observation.js';
2
2
  import { uncaught } from './failure.js';
3
- import { roomOfKey } from './rows.js';
3
+ import { roomsOfReach } from './rows.js';
4
4
  function narrowed(plan) {
5
5
  return (plan.paths !== null &&
6
6
  plan.hop === null &&
7
7
  plan.backlinks === null &&
8
8
  !(plan.shape.kind === 'documents' && plan.shape.backlinks));
9
9
  }
10
- function reads(plan, locator) {
11
- if (plan.locked.includes(locator))
12
- return false;
13
- if (plan.rooms !== null && !plan.rooms.includes(locator))
14
- return false;
15
- if (plan.reach === null)
16
- return true;
17
- return (plan.reach.rooms.includes(locator) ||
18
- plan.reach.documents.some((document) => roomOfKey(document) === locator));
10
+ export function readerOf(plan) {
11
+ const locked = new Set(plan.locked);
12
+ const named = plan.rooms === null ? null : new Set(plan.rooms);
13
+ const reached = plan.reach === null ? null : roomsOfReach(plan.reach);
14
+ return (locator) => !locked.has(locator) &&
15
+ (named === null || named.has(locator)) &&
16
+ (reached === null || reached.has(locator));
19
17
  }
20
- function affected(plan, set) {
21
- if (set.rooms.some((locator) => reads(plan, locator)))
18
+ function affected({ plan, reads }, set) {
19
+ if (set.rooms.some(reads))
22
20
  return true;
23
21
  const paths = narrowed(plan) ? new Set(plan.paths) : null;
24
- return set.documents.some(({ locator, path }) => reads(plan, locator) && (paths === null || path === null || paths.has(path)));
22
+ return set.documents.some(({ locator, path }) => reads(locator) && (paths === null || path === null || paths.has(path)));
25
23
  }
26
24
  function shown(entry, held) {
27
25
  const cover = entry.cover(held);
@@ -30,11 +28,13 @@ function shown(entry, held) {
30
28
  export function createLiveQueries(run, holding) {
31
29
  const entries = new Map();
32
30
  let closed = false;
33
- let recovering = false;
34
- let recoverAgain = false;
31
+ let recoveringNow = false;
32
+ let recoveringAll = false;
33
+ const recovering = new Set();
35
34
  function deliver(entry, next) {
36
35
  const previous = entry.digest;
37
36
  entry.digest = digestOf(next);
37
+ entry.pending = 'pending' in entry.digest ? entry.digest.pending : null;
38
38
  entry.covered = JSON.stringify('pending' in next.result ? next.result : next.result.coverage);
39
39
  const change = previous === undefined ? null : changeOf(previous, next);
40
40
  if (change !== null) {
@@ -87,58 +87,99 @@ export function createLiveQueries(run, holding) {
87
87
  }
88
88
  })();
89
89
  }
90
- function recovered() {
91
- if (recovering) {
92
- recoverAgain = true;
93
- return;
90
+ function pendingMoved(entry, held, rooms) {
91
+ const { pending } = entry;
92
+ if (pending === null || pending.size === 0 || entry.running || entry.fresh.size > 0) {
93
+ return false;
94
94
  }
95
- recovering = true;
95
+ const at = entry.pendingAt(held, rooms);
96
+ const change = at === null ? null : keyedAt(pending, at, samePending);
97
+ if (change === null || change.removed.length === pending.size)
98
+ return false;
99
+ keyedInto(pending, change);
100
+ entry.covered = '';
101
+ if (unchanged(change))
102
+ return true;
103
+ for (const listener of entry.ready)
104
+ listener({ pending: change });
105
+ return true;
106
+ }
107
+ function recovered(locators) {
108
+ if (locators === undefined)
109
+ recoveringAll = true;
110
+ else
111
+ for (const locator of locators)
112
+ recovering.add(locator);
113
+ if (recoveringNow)
114
+ return;
115
+ recoveringNow = true;
96
116
  void (async () => {
97
117
  try {
98
- do {
99
- recoverAgain = false;
100
- for (const entry of entries.values()) {
118
+ while ((recoveringAll || recovering.size > 0) && !closed) {
119
+ const all = recoveringAll;
120
+ const touched = [...recovering];
121
+ recoveringAll = false;
122
+ recovering.clear();
123
+ const reached = [...entries.values()].filter((entry) => all || touched.some(entry.reads));
124
+ for (const entry of reached) {
101
125
  if (entry.running)
102
126
  entry.dirty = true;
103
127
  }
104
- const waiting = [...entries.values()].filter((entry) => !entry.running);
105
- if (waiting.length === 0 || closed)
128
+ const waiting = reached.filter((entry) => !entry.running);
129
+ if (waiting.length === 0)
106
130
  continue;
107
131
  const held = await holding();
108
132
  for (const entry of waiting) {
109
133
  if (entry.running)
110
134
  continue;
135
+ const read = all ? [] : touched.filter(entry.reads);
136
+ if (read.length > 0 && pendingMoved(entry, held, read))
137
+ continue;
111
138
  if (entry.digest === undefined || shown(entry, held) !== entry.covered)
112
139
  schedule(entry);
113
140
  }
114
- } while (recoverAgain && !closed);
141
+ }
115
142
  }
116
143
  catch (error) {
117
144
  uncaught(error);
118
145
  }
119
146
  finally {
120
- recovering = false;
147
+ recoveringNow = false;
121
148
  }
122
149
  })();
123
150
  }
124
151
  return {
125
152
  touched(set) {
153
+ const pending = new Set();
126
154
  for (const entry of entries.values()) {
127
- if (affected(entry.plan, set))
155
+ if (!affected(entry, set))
156
+ continue;
157
+ if (entry.pending === null || entry.running) {
128
158
  schedule(entry);
159
+ continue;
160
+ }
161
+ for (const locator of set.rooms)
162
+ pending.add(locator);
163
+ for (const { locator } of set.documents)
164
+ pending.add(locator);
129
165
  }
166
+ if (pending.size > 0)
167
+ recovered([...pending]);
130
168
  },
131
169
  recover: recovered,
132
- watch(plan, identity, cover, listener, failed) {
170
+ watch(plan, identity, cover, pendingAt, listener, failed) {
133
171
  let entry = entries.get(identity);
134
172
  if (entry === undefined) {
135
173
  entry = {
136
174
  plan,
175
+ reads: readerOf(plan),
137
176
  cover,
177
+ pendingAt,
138
178
  listeners: new Set(),
139
179
  fresh: new Map(),
140
180
  ready: new Set(),
141
181
  digest: undefined,
182
+ pending: null,
142
183
  covered: '',
143
184
  dirty: false,
144
185
  running: false,
@@ -1 +1 @@
1
- {"version":3,"file":"locks.d.ts","sourceRoot":"","sources":["../src/locks.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAG5C,wBAAsB,MAAM,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAE5F;AAED,wBAAsB,WAAW,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,OAAO,GAAG,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,CAK1F;AAED,wBAAsB,QAAQ,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,MAAM,EAAE,iBAMzE;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,QAU9C;AAED,wBAAgB,UAAU,CAAC,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAE7E"}
1
+ {"version":3,"file":"locks.d.ts","sourceRoot":"","sources":["../src/locks.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAG5C,wBAAsB,MAAM,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAE5F;AAED,wBAAsB,WAAW,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,OAAO,GAAG,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,CAG1F;AAED,wBAAsB,QAAQ,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,MAAM,EAAE,iBAKzE;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,QAU9C;AAED,wBAAgB,UAAU,CAAC,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAE7E"}
package/dist/locks.js CHANGED
@@ -4,14 +4,13 @@ export async function locked({ store, presence }, locator) {
4
4
  return presence.locked(locator, async () => (await store.open()).offline(locator));
5
5
  }
6
6
  export async function lockedRooms({ store, presence }) {
7
- const held = (await store.covering()).rooms;
8
- return presence.lockedAmong(new Map([...held].map(([locator, { offline }]) => [locator, offline])));
7
+ const { rooms, locking } = await store.covering();
8
+ return presence.lockedAmong(rooms, locking);
9
9
  }
10
10
  export async function relocked(runtime, before) {
11
11
  const after = await lockedRooms(runtime);
12
12
  if (JSON.stringify(after) === JSON.stringify(before))
13
13
  return;
14
- runtime.journal.record(`locked ${after.length}`);
15
14
  emitRooms(runtime);
16
15
  runtime.store.tell(LOCKS_TOPIC, undefined);
17
16
  }
@@ -1 +1 @@
1
- {"version":3,"file":"lost.d.ts","sourceRoot":"","sources":["../src/lost.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAK5C,wBAAsB,WAAW,CAAC,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,MAAM,EAAE,iBAIhF;AAED,wBAAgB,SAAS,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,MAAM,EAAE,QAOtE;AAED,wBAAsB,SAAS,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,MAAM,EAAE,iBA+B5E"}
1
+ {"version":3,"file":"lost.d.ts","sourceRoot":"","sources":["../src/lost.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAK5C,wBAAsB,WAAW,CAAC,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,MAAM,EAAE,iBAIhF;AAED,wBAAgB,SAAS,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,MAAM,EAAE,QAMtE;AAED,wBAAsB,SAAS,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,MAAM,EAAE,iBA+B5E"}
package/dist/lost.js CHANGED
@@ -10,12 +10,11 @@ export async function eraseFenced({ store }, locators) {
10
10
  }
11
11
  }
12
12
  export function fenceLive(runtime, locators) {
13
- const { store, socket, presence, journal } = runtime;
13
+ const { store, socket, presence } = runtime;
14
14
  for (const locator of new Set(locators)) {
15
15
  if (!presence.lose(locator, socket.session.now().discoveries))
16
16
  continue;
17
17
  store.forget(locator);
18
- journal.record(`room lost ${locator}`);
19
18
  }
20
19
  }
21
20
  export async function loseRooms(runtime, locators) {
@@ -43,7 +42,7 @@ export async function loseRooms(runtime, locators) {
43
42
  queue: true,
44
43
  drafts: held.filter((draft) => draft.locator === locator).map(({ path }) => path),
45
44
  }))));
46
- recoverReads(runtime);
45
+ recoverReads(runtime, fresh);
47
46
  }
48
47
  await unpair(runtime);
49
48
  await eraseFenced(runtime, fresh);
@@ -1 +1 @@
1
- {"version":3,"file":"media.d.ts","sourceRoot":"","sources":["../src/media.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAa,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3D,eAAO,MAAM,UAAU,eAAe,CAAC;AAwBvC,KAAK,IAAI,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;AAIpF,wBAAgB,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,eAAe,GAAG,eAAe,CAG5E;AAsBD,wBAAgB,eAAe,CAAC,MAAM,EAAE,OAAO,CAAC,eAAe,CAAC,GAAG,eAAe,GAAG,IAAI,CA0LxF"}
1
+ {"version":3,"file":"media.d.ts","sourceRoot":"","sources":["../src/media.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAqB,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAEnE,eAAO,MAAM,UAAU,eAAe,CAAC;AAwBvC,KAAK,IAAI,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;AAIpF,wBAAgB,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,eAAe,GAAG,eAAe,CAG5E;AAsBD,wBAAgB,eAAe,CAAC,MAAM,EAAE,OAAO,CAAC,eAAe,CAAC,GAAG,eAAe,GAAG,IAAI,CAsLxF"}
package/dist/media.js CHANGED
@@ -1,4 +1,5 @@
1
- import { UNCERTAIN } from './outcome.js';
1
+ import { isAudioType, isImageType, isVideoType } from '@agentshouse/mdmodel';
2
+ import { blobOf, UNCERTAIN } from './outcome.js';
2
3
  export const ROOM_MEDIA = 'room-media';
3
4
  const ATTRIBUTES = ['room', 'path', 'revision', 'width', 'height', 'alt'];
4
5
  const LOADING = new Set(['room', 'path', 'revision']);
@@ -29,11 +30,11 @@ function refusedForGood(shown) {
29
30
  return (shown.kind === 'rejected' || shown.kind === 'scoped-refusal') && !shown.retryable;
30
31
  }
31
32
  function drawnFor(mediaType) {
32
- if (mediaType.startsWith('image/'))
33
+ if (isImageType(mediaType))
33
34
  return 'img';
34
- if (mediaType.startsWith('video/'))
35
+ if (isVideoType(mediaType))
35
36
  return 'video';
36
- if (mediaType.startsWith('audio/'))
37
+ if (isAudioType(mediaType))
37
38
  return 'audio';
38
39
  return null;
39
40
  }
@@ -129,11 +130,7 @@ export function defineRoomMedia(client) {
129
130
  this.#unshown(asked);
130
131
  return;
131
132
  }
132
- const copy = await new Response(shown.stream, {
133
- headers: { 'content-type': shown.mediaType },
134
- })
135
- .blob()
136
- .catch(() => null);
133
+ const copy = await blobOf(shown).catch(() => null);
137
134
  if (asked !== this.#asked)
138
135
  return;
139
136
  if (copy === null)
@@ -179,7 +176,7 @@ export function defineRoomMedia(client) {
179
176
  if (drawn instanceof HTMLMediaElement) {
180
177
  drawn.controls = true;
181
178
  drawn.preload = 'metadata';
182
- if (drawn instanceof HTMLVideoElement)
179
+ if (drawn instanceof HTMLVideoElement && 'playsInline' in drawn)
183
180
  drawn.playsInline = true;
184
181
  }
185
182
  drawn.addEventListener(drawn instanceof HTMLImageElement ? 'load' : 'loadedmetadata', () => {
package/dist/memory.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { type VerifiedModel } from '@agentshouse/mdmodel';
1
2
  import type { ModelResult } from './model.js';
2
3
  import { type AcceptedMessage, type ActionResult, type Adapter, type BinaryFile, type Capability, type ConversationAction, type ConversationEvent, type DiscoveredRoom, type SavedFacts, type HeadChange, type ListedAgent, type ListedConversation, type Member, type ProviderLimit, type Retention, type ServiceMode, type TypedPath, type TypedSelection } from './adapter.js';
3
4
  export type MemoryRoom = Omit<DiscoveredRoom, 'typedSelection' | 'authoredPaths'> & {
@@ -31,7 +32,7 @@ export type MemoryAdapterOptions = {
31
32
  retention?: Retention;
32
33
  member?: Member;
33
34
  unopened?: boolean;
34
- roomTypes?: string;
35
+ roomTypes?: VerifiedModel;
35
36
  typedPaths?: readonly TypedPath[];
36
37
  };
37
38
  export type MemoryAdapter = Adapter & {
@@ -39,6 +40,7 @@ export type MemoryAdapter = Adapter & {
39
40
  refresh: NonNullable<Adapter['refresh']>;
40
41
  setRooms(rooms: readonly MemoryRoom[]): void;
41
42
  setConversations(conversations: readonly MemoryConversation[]): void;
43
+ setAgents(agents: readonly ListedAgent[]): void;
42
44
  script(operation: keyof Adapter, answer: unknown): void;
43
45
  open(): void;
44
46
  close(closing?: {
@@ -1 +1 @@
1
- {"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../src/memory.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../src/memory.ts"],"names":[],"mappings":"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"}
package/dist/memory.js CHANGED
@@ -1,5 +1,7 @@
1
1
  import { definitionAt, documentFacts, parse } from '@agentshouse/mdmodel';
2
2
  import { NO_ROOM_TYPES, schemaTypes } from './attached-model.js';
3
+ import { selected } from './collection.js';
4
+ import { declaredType } from './outcome.js';
3
5
  import { sameChange } from './overlay.js';
4
6
  const ATTACHMENTS = 'attachments';
5
7
  function lastName(path) {
@@ -53,7 +55,7 @@ export function createMemoryAdapter(options = {}) {
53
55
  const listeners = new Set();
54
56
  const modeObservers = new Set();
55
57
  const scripted = new Map();
56
- const sentTurns = new Map();
58
+ const repeats = new Map();
57
59
  const historyPageSize = options.historyPageSize;
58
60
  const attached = new Map();
59
61
  const linked = new WeakMap();
@@ -63,6 +65,7 @@ export function createMemoryAdapter(options = {}) {
63
65
  const declaredRooms = new Set();
64
66
  let replicated = [];
65
67
  let heldCursor = null;
68
+ let agents = structuredClone(options.agents ?? []);
66
69
  let issued = 0;
67
70
  function scriptConversations(entries) {
68
71
  scripted.clear();
@@ -104,8 +107,11 @@ export function createMemoryAdapter(options = {}) {
104
107
  }
105
108
  }
106
109
  const saved = new WeakMap();
110
+ const roomTypes = options.roomTypes ?? NO_ROOM_TYPES;
107
111
  function factsAt(room, path, content) {
108
- const types = schemaTypes(room.model?.kind === 'model' ? room.model.snapshot.schema : null, NO_ROOM_TYPES);
112
+ const types = selected({ selection: room.typedSelection ?? null }, path)
113
+ ? roomTypes.types
114
+ : schemaTypes(room.model?.kind === 'model' ? room.model.snapshot.schema : null, roomTypes);
109
115
  let held = saved.get(types);
110
116
  if (held === undefined) {
111
117
  held = new Map();
@@ -261,16 +267,14 @@ export function createMemoryAdapter(options = {}) {
261
267
  }
262
268
  function pageOf(conversation, after) {
263
269
  const messages = conversation.messages ?? [];
264
- const first = typeof after === 'string' ? messages.findIndex((message) => message.position > after) : 0;
270
+ const first = typeof after === 'string' ? messages.findIndex((message) => message.change > after) : 0;
265
271
  const from = first === -1 ? messages.length : first;
266
272
  const page = messages.slice(from, from + (historyPageSize ?? messages.length));
267
273
  const last = page.at(-1);
268
274
  return {
269
275
  conversation: conversation.conversation,
270
276
  messages: page,
271
- ...(last !== undefined && from + page.length < messages.length
272
- ? { next: last.position }
273
- : {}),
277
+ ...(last !== undefined && from + page.length < messages.length ? { next: last.change } : {}),
274
278
  };
275
279
  }
276
280
  function historiesOf(request) {
@@ -290,11 +294,20 @@ export function createMemoryAdapter(options = {}) {
290
294
  function conversationAction(request) {
291
295
  const name = request.name;
292
296
  const input = (request.input ?? {});
297
+ const operation = typeof input.operation_id === 'string' ? `${name}\u0000${input.operation_id}` : null;
298
+ const first = (answer) => {
299
+ if (operation !== null)
300
+ repeats.set(operation, answer);
301
+ return answer;
302
+ };
303
+ const repeated = operation === null ? undefined : repeats.get(operation);
293
304
  if (name === 'conversations/create') {
305
+ if (repeated !== undefined)
306
+ return repeated;
294
307
  issued += 1;
295
308
  const conversation = `conversation-${issued}`;
296
309
  scripted.set(conversation, { conversation });
297
- return { kind: 'action', result: { conversation_id: conversation } };
310
+ return first({ kind: 'action', result: { conversation_id: conversation } });
298
311
  }
299
312
  const named = typeof input.conversation_id === 'string' ? input.conversation_id : '';
300
313
  const conversation = scripted.get(named);
@@ -310,26 +323,39 @@ export function createMemoryAdapter(options = {}) {
310
323
  if (outcome !== undefined)
311
324
  return outcome;
312
325
  if (name === 'conversations/send') {
313
- const operation = typeof input.operation_id === 'string' ? input.operation_id : null;
314
- const answered = operation === null ? undefined : sentTurns.get(operation);
315
- if (answered !== undefined)
316
- return { kind: 'action', result: { message_id: answered } };
326
+ if (repeated !== undefined)
327
+ return repeated;
317
328
  issued += 1;
318
- const turn = `turn-${issued}`;
319
- if (operation !== null)
320
- sentTurns.set(operation, turn);
321
- return { kind: 'action', result: { message_id: turn } };
329
+ return first({ kind: 'action', result: { message_id: `turn-${issued}` } });
322
330
  }
323
331
  if (name === 'conversations/delete')
324
332
  scripted.delete(named);
325
333
  return { kind: 'action', result: { conversation_id: named } };
326
334
  }
335
+ function decided(request) {
336
+ const { request: asked, message, decision, } = (request.input ?? {});
337
+ return {
338
+ kind: 'action',
339
+ result: {
340
+ request: asked,
341
+ ...(message === undefined ? {} : { message }),
342
+ state: decision === 'approve' ? 'started' : 'declined',
343
+ },
344
+ };
345
+ }
327
346
  function announceList() {
328
347
  const entries = [...scripted.values()].flatMap(({ conversation, listed, messages = [] }) => listed === undefined
329
348
  ? []
330
- : [{ conversation, ...listed, newest: messages.at(-1)?.position ?? null }]);
349
+ : [{ conversation, ...listed, newest: messages.at(-1)?.change ?? null }]);
331
350
  for (const listener of listeners) {
332
- listener({ kind: 'list', entries, whole: true, more: false, cursor: null });
351
+ listener({
352
+ kind: 'list',
353
+ agents: structuredClone(agents),
354
+ entries,
355
+ whole: true,
356
+ more: false,
357
+ cursor: null,
358
+ });
333
359
  }
334
360
  }
335
361
  function discovery() {
@@ -385,6 +411,9 @@ export function createMemoryAdapter(options = {}) {
385
411
  setConversations(next) {
386
412
  scriptConversations(next);
387
413
  },
414
+ setAgents(next) {
415
+ agents = structuredClone(next);
416
+ },
388
417
  open() {
389
418
  carried.clear();
390
419
  announceList();
@@ -447,9 +476,6 @@ export function createMemoryAdapter(options = {}) {
447
476
  };
448
477
  },
449
478
  history: (request) => Promise.resolve(historiesOf(request)),
450
- ...(options.agents === undefined
451
- ? {}
452
- : { agents: () => Promise.resolve(structuredClone(options.agents)) }),
453
479
  },
454
480
  serviceMode: {
455
481
  observe(notify) {
@@ -602,7 +628,7 @@ export function createMemoryAdapter(options = {}) {
602
628
  const blob = heldBlob(room, request.path);
603
629
  return blob === undefined
604
630
  ? { kind: 'rejected', code: 'not_found', fields: {}, retryable: false }
605
- : { kind: 'binary', body: blob.stream(), mediaType: blob.type };
631
+ : { kind: 'binary', body: blob.stream(), mediaType: declaredType(blob) };
606
632
  }, request);
607
633
  },
608
634
  async link(_authorization, request) {
@@ -616,7 +642,7 @@ export function createMemoryAdapter(options = {}) {
616
642
  }
617
643
  const url = linked.get(blob) ?? URL.createObjectURL(blob);
618
644
  linked.set(blob, url);
619
- return { kind: 'link', url, name: lastName(request.path), mediaType: blob.type };
645
+ return { kind: 'link', url, name: lastName(request.path), mediaType: declaredType(blob) };
620
646
  }, request);
621
647
  },
622
648
  async attach(_authorization, request) {
@@ -633,6 +659,8 @@ export function createMemoryAdapter(options = {}) {
633
659
  return resolveScript(scripts.action, () => {
634
660
  if (request.name.startsWith('conversations/'))
635
661
  return conversationAction(request);
662
+ if (request.name === 'agent-functions/decide')
663
+ return decided(request);
636
664
  return { kind: 'rejected', code: 'unscripted-action', fields: {}, retryable: false };
637
665
  }, request).then((answer) => {
638
666
  if (answer.kind === 'action' && answer.at !== undefined)
@@ -1,7 +1,7 @@
1
1
  import type { AcceptedMessage } from './adapter.js';
2
2
  import type { Connection } from './tables.js';
3
3
  export declare const MESSAGE_TABLES: readonly string[];
4
- export declare const ROWS = "WITH chosen AS (\n SELECT listed.value AS conversation,\n (SELECT id FROM conversation_messages WHERE conversation = listed.value AND json_extract(message, '$.role') = 'assistant' ORDER BY position DESC, identity DESC LIMIT 1) AS answer,\n (SELECT id FROM conversation_messages WHERE conversation = listed.value AND text <> '' ORDER BY position DESC, identity DESC LIMIT 1) AS said,\n (SELECT id FROM conversation_messages WHERE conversation = listed.value AND json_extract(message, '$.role') = 'house' ORDER BY position, identity LIMIT 1) AS opened,\n (SELECT id FROM conversation_messages WHERE conversation = listed.value AND json_extract(message, '$.role') = 'house' AND json_extract(message, '$.waiting') = 1 ORDER BY position, identity LIMIT 1) AS asking\n FROM json_each(?) AS listed\n )\n SELECT chosen.conversation AS conversation,\n substr(said.text, 1, 512) AS said_text,\n json_extract(said.message, '$.role') AS said_role,\n json_extract(said.message, '$.accepted_at') AS said_at,\n substr(json_extract(answer.message, '$.failure'), 1, 512) AS failure,\n json_extract(opened.message, '$.source') AS opened_source,\n json_extract(opened.message, '$.sender') AS opened_sender,\n asking.identity AS asking_identity,\n asking.text AS asking_text,\n json_extract(asking.message, '$.accepted_at') AS asking_at,\n json_extract(asking.message, '$.source') AS asking_source,\n json_extract(asking.message, '$.sender') AS asking_sender\n FROM chosen\n LEFT JOIN conversation_messages said ON said.id = chosen.said\n LEFT JOIN conversation_messages answer ON answer.id = chosen.answer\n LEFT JOIN conversation_messages opened ON opened.id = chosen.opened\n LEFT JOIN conversation_messages asking ON asking.id = chosen.asking";
4
+ export declare const ROWS = "WITH chosen AS (\n SELECT listed.value AS conversation,\n (SELECT id FROM conversation_messages WHERE conversation = listed.value AND json_extract(message, '$.role') = 'assistant' ORDER BY position DESC, identity DESC LIMIT 1) AS answer,\n (SELECT id FROM conversation_messages WHERE conversation = listed.value AND text <> '' ORDER BY position DESC, identity DESC LIMIT 1) AS said,\n (SELECT id FROM conversation_messages WHERE conversation = listed.value AND json_extract(message, '$.role') = 'house' ORDER BY position, identity LIMIT 1) AS opened,\n (SELECT id FROM conversation_messages WHERE conversation = listed.value AND json_extract(message, '$.role') = 'house' AND json_extract(message, '$.waiting') = 1 ORDER BY position, identity LIMIT 1) AS asking\n FROM json_each(?) AS listed\n )\n SELECT chosen.conversation AS conversation,\n substr(said.text, 1, 512) AS said_text,\n json_extract(said.message, '$.role') AS said_role,\n json_extract(said.message, '$.accepted_at') AS said_at,\n answer.turn AS answered,\n substr(json_extract(answer.message, '$.failure'), 1, 512) AS failure,\n (SELECT value FROM conversation_facts WHERE conversation = chosen.conversation AND name = 'openedTurn') AS opened_turn,\n json_extract(opened.message, '$.source') AS opened_source,\n json_extract(opened.message, '$.sender') AS opened_sender,\n asking.identity AS asking_identity,\n asking.text AS asking_text,\n json_extract(asking.message, '$.accepted_at') AS asking_at,\n json_extract(asking.message, '$.source') AS asking_source,\n json_extract(asking.message, '$.sender') AS asking_sender\n FROM chosen\n LEFT JOIN conversation_messages said ON said.id = chosen.said\n LEFT JOIN conversation_messages answer ON answer.id = chosen.answer\n LEFT JOIN conversation_messages opened ON opened.id = chosen.opened\n LEFT JOIN conversation_messages asking ON asking.id = chosen.asking";
5
5
  export type HeldConversation = {
6
6
  readonly conversation: string;
7
7
  readonly read: string | null | undefined;
@@ -22,6 +22,8 @@ export type HeldRow = {
22
22
  readonly role: string | null;
23
23
  readonly at: string | null;
24
24
  } | null;
25
+ readonly answered: string | null;
26
+ readonly openedTurn: string | null;
25
27
  readonly failure: string | null;
26
28
  readonly opened: HeldSource | null;
27
29
  readonly asking: (HeldSource & {
@@ -30,6 +32,12 @@ export type HeldRow = {
30
32
  readonly at: string | null;
31
33
  }) | null;
32
34
  };
35
+ export type ConversationFact = 'openedTurn' | 'dismissedSuggestion';
36
+ export type HeldFact = {
37
+ readonly conversation: string;
38
+ readonly name: ConversationFact;
39
+ readonly value: string;
40
+ };
33
41
  export type ConversationWrite = {
34
42
  readonly conversation: string;
35
43
  readonly messages: readonly AcceptedMessage[];
@@ -44,6 +52,7 @@ export declare function messageWindow(connection: Connection, conversation: stri
44
52
  export declare function messageCount(connection: Connection, conversation: string, phrase: string | null): number;
45
53
  export declare function conversationRows(connection: Connection, conversations: readonly string[]): HeldRow[];
46
54
  export declare function turnArrived(connection: Connection, conversation: string, turn: string): boolean;
47
- export declare function droppedMessages(connection: Connection, conversations: readonly string[]): void;
48
- export declare function forgottenMessages(connection: Connection): void;
55
+ export declare function conversationFacts(connection: Connection): HeldFact[];
56
+ export declare function keptFact(connection: Connection, { conversation, name, value }: HeldFact): null;
57
+ export declare function forgottenConversations(connection: Connection): void;
49
58
  //# sourceMappingURL=messages.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"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"}
package/dist/messages.js CHANGED
@@ -1,14 +1,16 @@
1
1
  import { sql } from '@agentshouse/mdmodel';
2
2
  import { EXCERPT } from './read.js';
3
+ import { tablesOf } from './stored.js';
3
4
  const SAID_BY_HOUSE = `json_extract(message, '$.role') = 'house'`;
4
5
  const ANSWERS_THE_TURN = `json_extract(message, '$.role') = 'assistant'`;
5
6
  export const MESSAGE_TABLES = [
6
7
  'CREATE TABLE conversation_reads (conversation TEXT PRIMARY KEY, read TEXT, partial INTEGER NOT NULL) WITHOUT ROWID',
7
- 'CREATE TABLE conversation_messages (id INTEGER PRIMARY KEY, conversation TEXT NOT NULL, identity TEXT NOT NULL, turn TEXT NOT NULL, position TEXT NOT NULL, message TEXT NOT NULL, text TEXT NOT NULL)',
8
+ 'CREATE TABLE conversation_messages (id INTEGER PRIMARY KEY, conversation TEXT NOT NULL, identity TEXT NOT NULL, turn TEXT NOT NULL, position TEXT NOT NULL, change TEXT NOT NULL, message TEXT NOT NULL, text TEXT NOT NULL)',
8
9
  'CREATE UNIQUE INDEX conversation_messages_identity ON conversation_messages (conversation, identity)',
9
10
  'CREATE INDEX conversation_messages_position ON conversation_messages (conversation, position, identity)',
10
11
  'CREATE INDEX conversation_messages_turn ON conversation_messages (conversation, turn)',
11
12
  `CREATE INDEX conversation_messages_house ON conversation_messages (conversation, position, identity) WHERE ${SAID_BY_HOUSE}`,
13
+ 'CREATE TABLE conversation_facts (conversation TEXT NOT NULL, name TEXT NOT NULL, value TEXT NOT NULL, PRIMARY KEY (conversation, name)) WITHOUT ROWID',
12
14
  ];
13
15
  const NEWEST_FIRST = 'ORDER BY position DESC, identity DESC LIMIT 1';
14
16
  const OLDEST_FIRST = 'ORDER BY position, identity LIMIT 1';
@@ -25,7 +27,9 @@ export const ROWS = `WITH chosen AS (
25
27
  substr(said.text, 1, ${EXCERPT}) AS said_text,
26
28
  json_extract(said.message, '$.role') AS said_role,
27
29
  json_extract(said.message, '$.accepted_at') AS said_at,
30
+ answer.turn AS answered,
28
31
  substr(json_extract(answer.message, '$.failure'), 1, ${EXCERPT}) AS failure,
32
+ (SELECT value FROM conversation_facts WHERE conversation = chosen.conversation AND name = 'openedTurn') AS opened_turn,
29
33
  json_extract(opened.message, '$.source') AS opened_source,
30
34
  json_extract(opened.message, '$.sender') AS opened_sender,
31
35
  asking.identity AS asking_identity,
@@ -63,11 +67,12 @@ export function heldConversations(connection, conversations) {
63
67
  }
64
68
  export function keptMessages(connection, { conversation, messages, read }) {
65
69
  for (const message of messages) {
66
- connection.run('INSERT INTO conversation_messages (conversation, identity, turn, position, message, text) VALUES (?, ?, ?, ?, ?, ?) ON CONFLICT (conversation, identity) DO UPDATE SET turn = excluded.turn, position = excluded.position, message = excluded.message, text = excluded.text', [
70
+ connection.run('INSERT INTO conversation_messages (conversation, identity, turn, position, change, message, text) VALUES (?, ?, ?, ?, ?, ?, ?) ON CONFLICT (conversation, identity) DO UPDATE SET turn = excluded.turn, position = excluded.position, change = excluded.change, message = excluded.message, text = excluded.text', [
67
71
  conversation,
68
72
  message.identity,
69
73
  message.turn,
70
74
  message.position,
75
+ message.change,
71
76
  JSON.stringify(message.message ?? null),
72
77
  message.text,
73
78
  ]);
@@ -82,6 +87,7 @@ function accepted(row) {
82
87
  identity: text(row.identity),
83
88
  turn: text(row.turn),
84
89
  position: text(row.position),
90
+ change: text(row.change),
85
91
  text: text(row.text),
86
92
  message: JSON.parse(text(row.message)),
87
93
  };
@@ -93,7 +99,7 @@ function holding(phrase) {
93
99
  }
94
100
  export function messageWindow(connection, conversation, limit, before, phrase) {
95
101
  const held = holding(phrase);
96
- const rows = connection.rows(`SELECT identity, turn, position, message, text FROM conversation_messages WHERE conversation = ?${held.sql}${before === null ? '' : ' AND (position < ? OR (position = ? AND identity < ?))'} ORDER BY position DESC, identity DESC LIMIT ?`, before === null
102
+ const rows = connection.rows(`SELECT identity, turn, position, change, message, text FROM conversation_messages WHERE conversation = ?${held.sql}${before === null ? '' : ' AND (position < ? OR (position = ? AND identity < ?))'} ORDER BY position DESC, identity DESC LIMIT ?`, before === null
97
103
  ? [conversation, ...held.bind, limit + 1]
98
104
  : [conversation, ...held.bind, before[0], before[0], before[1], limit + 1]);
99
105
  return { messages: rows.slice(0, limit).map(accepted).reverse(), more: rows.length > limit };
@@ -122,6 +128,8 @@ export function conversationRows(connection, conversations) {
122
128
  role: textOrNull(row.said_role),
123
129
  at: textOrNull(row.said_at),
124
130
  },
131
+ answered: textOrNull(row.answered),
132
+ openedTurn: textOrNull(row.opened_turn),
125
133
  failure: textOrNull(row.failure),
126
134
  opened: row.opened_source === null ? null : heldSource(row.opened_source, row.opened_sender),
127
135
  asking: row.asking_identity === null
@@ -137,12 +145,18 @@ export function conversationRows(connection, conversations) {
137
145
  export function turnArrived(connection, conversation, turn) {
138
146
  return (connection.rows('SELECT 1 FROM conversation_messages WHERE conversation = ? AND turn = ? LIMIT 1', [conversation, turn]).length > 0);
139
147
  }
140
- export function droppedMessages(connection, conversations) {
141
- const named = [JSON.stringify(conversations)];
142
- connection.run('DELETE FROM conversation_messages WHERE conversation IN (SELECT value FROM json_each(?))', named);
143
- connection.run('DELETE FROM conversation_reads WHERE conversation IN (SELECT value FROM json_each(?))', named);
148
+ export function conversationFacts(connection) {
149
+ return connection.rows('SELECT conversation, name, value FROM conversation_facts').map((row) => ({
150
+ conversation: text(row.conversation),
151
+ name: text(row.name),
152
+ value: text(row.value),
153
+ }));
154
+ }
155
+ export function keptFact(connection, { conversation, name, value }) {
156
+ connection.run('INSERT OR REPLACE INTO conversation_facts (conversation, name, value) VALUES (?, ?, ?)', [conversation, name, value]);
157
+ return null;
144
158
  }
145
- export function forgottenMessages(connection) {
146
- connection.run('DELETE FROM conversation_messages');
147
- connection.run('DELETE FROM conversation_reads');
159
+ export function forgottenConversations(connection) {
160
+ for (const table of tablesOf(MESSAGE_TABLES))
161
+ connection.run(`DELETE FROM ${table}`);
148
162
  }