@astrosheep/keiyaku 4.5.18 → 4.5.19

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.
@@ -1,10 +1,10 @@
1
1
  import { parsePublicHistoryId } from "../identity.js";
2
2
  import { answeredTurnFact, endBodyFact, finishBodyFact, insertActivityFact, insertTurnEndFact, insertTurnStartFact, insertPauseControl, insertSessionFact, insertStopControl, killFactForBody, latestBodyFact, latestKillFact, latestSessionFact, lastAnsweredTurnFact, pauseFact, sessionFactForCoordinate, stopFact, turnFact, } from "./rows.js";
3
- import { insertTellDeliveryFact, dispositionSnapshotProven, insertTellBindingFact, insertTellDispositionSnapshot, insertTellFact, insertTellReceiptFact, insertUndeliveredTellReceipts, latestOpenTellDisposition, openBoundTurns, openTellDispositionIds, pendingTellFacts, resolveTellDispositionSnapshot, successorBodyHoldingDisposition, tellFact, tellIdsForFence, } from "./tells.js";
4
- import { activityFactSlice, lastActivityAt as readLastActivityAt, pruneActivityFacts, } from "./timeline.js";
5
- import { isHeartAbsent, readSealFromLeash, readTransaction, transaction, withHeart, withReadOnlyHeart, } from "./storage.js";
3
+ import { insertTellDeliveryFact, dispositionSnapshotProven, insertTellBindingFact, insertTellDispositionSnapshot, insertTellFact, insertTellReceiptFact, insertUndeliveredTellReceipts, latestOpenTellDisposition, openBoundTurns, openTellDispositionIds, pendingTellFacts, hasPendingTell, resolveTellDispositionSnapshot, tellDispositionResolved, tellFact, tellIdsForFence, } from "./tells.js";
4
+ import { activityFactSlice, statusFacts, lastActivityAt as readLastActivityAt, pruneActivityFacts, } from "./timeline.js";
5
+ import { isHeartAbsent, readSealFromLeash, withHeartTransaction, withReadOnlyHeart } from "./storage.js";
6
6
  import { soulFact } from "./soul.js";
7
- import { AkumaBusyError } from "./facts.js";
7
+ import { AkumaBusyError, HeartAuthorityCorruptionError } from "./facts.js";
8
8
  export { HeartAbsentError, HeldAkumaLeash, classifyHeartSchema, initializeHeart, isHeartAbsent, probeLeash, } from "./storage.js";
9
9
  export { admitRequest, beginRequest, isRequestInputConflict, readNonterminalRequests, readRequest, refuseRequest, reserveRequest, serveRequest, serveUpstreamRequest, unproveRequest, voidRequest, } from "./request-authority.js";
10
10
  export { AkumaBusyError, life, lifeAt, projectTell } from "./facts.js";
@@ -12,7 +12,7 @@ export { drainPendingTells } from "./tells.js";
12
12
  const ACTIVITY_LIMIT = 5_000;
13
13
  export async function readSoul(paths) {
14
14
  try {
15
- return await withHeart(paths, soulFact);
15
+ return await withReadOnlyHeart(paths, soulFact);
16
16
  }
17
17
  catch (error) {
18
18
  if (isHeartAbsent(error))
@@ -22,7 +22,7 @@ export async function readSoul(paths) {
22
22
  }
23
23
  export async function heartExists(paths) {
24
24
  try {
25
- return await withHeart(paths, () => true);
25
+ return await withReadOnlyHeart(paths, () => true);
26
26
  }
27
27
  catch (error) {
28
28
  if (isHeartAbsent(error))
@@ -41,23 +41,43 @@ export async function readSeal(paths) {
41
41
  }
42
42
  }
43
43
  export async function recordSession(paths, input) {
44
- return await withHeart(paths, (heart) => ({ sequence: insertSessionFact(heart, input), ...input }));
44
+ return await withHeartTransaction(paths, (heart) => ({ sequence: insertSessionFact(heart, input), ...input }));
45
45
  }
46
46
  export async function appendActivity(paths, input) {
47
- return await withHeart(paths, (heart) => transaction(heart, () => {
47
+ return await withHeartTransaction(paths, (heart) => {
48
48
  const sequence = insertActivityFact(heart, input);
49
49
  pruneActivityFacts(heart, ACTIVITY_LIMIT);
50
50
  return sequence;
51
- }));
51
+ });
52
52
  }
53
53
  export async function activitySlice(paths) {
54
- return await withHeart(paths, (heart) => readTransaction(heart, () => activityFactSlice(heart)));
54
+ return await withReadOnlyHeart(paths, (heart) => activityFactSlice(heart));
55
+ }
56
+ export async function readStatusFacts(paths, input) {
57
+ return await withReadOnlyHeart(paths, (heart) => statusFacts(heart, input));
58
+ }
59
+ /** Lifecycle probes do not load narration, Tell bodies, or delivery histories. */
60
+ export async function readLifeSnapshot(paths) {
61
+ try {
62
+ return await withReadOnlyHeart(paths, (heart) => ({
63
+ soul: soulFact(heart),
64
+ latestBody: latestBodyFact(heart),
65
+ latestSession: latestSessionFact(heart),
66
+ latestKill: latestKillFact(heart),
67
+ hasPendingTell: hasPendingTell(heart),
68
+ }));
69
+ }
70
+ catch (error) {
71
+ if (isHeartAbsent(error))
72
+ return { soul: null, latestBody: null, latestSession: null, latestKill: null, hasPendingTell: false };
73
+ throw error;
74
+ }
55
75
  }
56
76
  function sameTellInput(existing, tell) {
57
77
  return (existing.body === tell.body && existing.recordedAt === tell.recordedAt && existing.schemaJson === tell.schemaJson);
58
78
  }
59
79
  export async function recordTell(paths, tell, options = {}) {
60
- return await withHeart(paths, (heart) => transaction(heart, () => {
80
+ return await withHeartTransaction(paths, (heart) => {
61
81
  if (soulFact(heart) === null)
62
82
  return { kind: "not-born" };
63
83
  const existing = tellFact(heart, tell.id);
@@ -73,37 +93,40 @@ export async function recordTell(paths, tell, options = {}) {
73
93
  const sequence = insertTellFact(heart, tell);
74
94
  pruneActivityFacts(heart, ACTIVITY_LIMIT);
75
95
  return { kind: "recorded", tell: { sequence, ...tell, state: "pending", deliveries: [] } };
76
- }));
96
+ });
77
97
  }
78
98
  export async function readTell(paths, id) {
79
- return await withHeart(paths, (heart) => readTransaction(heart, () => tellFact(heart, id)));
99
+ return await withReadOnlyHeart(paths, (heart) => tellFact(heart, id));
80
100
  }
81
101
  export async function recordTellDeliveries(paths, inputs) {
82
- await withHeart(paths, (heart) => transaction(heart, () => {
102
+ await withHeartTransaction(paths, (heart) => {
83
103
  for (const input of inputs) {
84
104
  const current = tellFact(heart, input.tellId);
85
105
  if (current === null)
86
106
  throw new Error(`unknown tell ${input.tellId}`);
87
107
  }
108
+ let changed = false;
88
109
  for (const input of inputs)
89
- insertTellDeliveryFact(heart, input);
90
- pruneActivityFacts(heart, ACTIVITY_LIMIT);
91
- }));
110
+ changed = insertTellDeliveryFact(heart, input) || changed;
111
+ pruneActivityFacts(heart, ACTIVITY_LIMIT, changed);
112
+ });
92
113
  }
93
114
  export async function recordTellReceipt(paths, input) {
94
- await withHeart(paths, (heart) => transaction(heart, () => {
115
+ await withHeartTransaction(paths, (heart) => {
95
116
  const tellIds = input.evidence === "exact" ? [input.tellId] : tellIdsForFence(heart, input.turnSequence, input.fence);
96
117
  if (tellIds.length === 0)
97
118
  throw new Error("tell receipt has no delivery mapping");
98
- insertTellReceiptFact(heart, input);
99
- pruneActivityFacts(heart, ACTIVITY_LIMIT);
100
- }));
119
+ const changed = insertTellReceiptFact(heart, input);
120
+ pruneActivityFacts(heart, ACTIVITY_LIMIT, changed);
121
+ });
101
122
  }
102
123
  export async function decidePendingTellDisposition(paths, input) {
103
- return await withHeart(paths, (heart) => transaction(heart, () => {
124
+ return await withHeartTransaction(paths, (heart) => {
104
125
  const body = latestBodyFact(heart);
105
126
  if (body === null || body.sequence !== input.bodySequence)
106
127
  return null;
128
+ if (tellDispositionResolved(heart, input.bodySequence) === true)
129
+ return null;
107
130
  const existing = openTellDispositionIds(heart, input.bodySequence);
108
131
  if (existing !== null)
109
132
  return { bodySequence: input.bodySequence, tellIds: existing };
@@ -122,46 +145,33 @@ export async function decidePendingTellDisposition(paths, input) {
122
145
  insertTellDispositionSnapshot(heart, input.bodySequence, tellIds, input.at);
123
146
  pruneActivityFacts(heart, ACTIVITY_LIMIT);
124
147
  return { bodySequence: input.bodySequence, tellIds };
125
- }));
148
+ });
126
149
  }
127
150
  export async function readOpenPendingTellDisposition(paths) {
128
- return await withHeart(paths, (heart) => readTransaction(heart, () => latestOpenTellDisposition(heart)));
129
- }
130
- export async function resolvePendingTellDisposition(paths, bodySequence, at) {
131
- await withHeart(paths, (heart) => transaction(heart, () => {
151
+ return await withReadOnlyHeart(paths, (heart) => latestOpenTellDisposition(heart));
152
+ }
153
+ /** Prove and consume the persisted decision in one transaction, never a caller's stale member list. */
154
+ export async function resolvePendingTellDisposition(paths, bodySequence, at, outcome = "custody") {
155
+ return await withHeartTransaction(paths, (heart) => {
156
+ if (tellDispositionResolved(heart, bodySequence) === true)
157
+ return true;
158
+ const tellIds = openTellDispositionIds(heart, bodySequence);
159
+ if (tellIds === null)
160
+ throw new HeartAuthorityCorruptionError(`Akuma disposition ${bodySequence} is missing`);
161
+ if (outcome === "undelivered")
162
+ insertUndeliveredTellReceipts(heart, tellIds, at);
163
+ if (!dispositionSnapshotProven(heart, tellIds))
164
+ return false;
132
165
  resolveTellDispositionSnapshot(heart, bodySequence, at);
133
- pruneActivityFacts(heart, ACTIVITY_LIMIT);
134
- }));
135
- }
136
- /**
137
- * Heart-owned disposition custody proof. Sequence growth, spawn resolution, and
138
- * an unqualified held leash are never proof. Returns the successor Body sequence
139
- * when that exact Body took the frozen Tell-id snapshot by delivery, or true when
140
- * the snapshot is already fully settled (no longer pending).
141
- */
142
- export async function provePendingTellDispositionCustody(paths, disposition) {
143
- return await withHeart(paths, (heart) => readTransaction(heart, () => {
144
- if (!dispositionSnapshotProven(heart, disposition))
145
- return { kind: "unproven" };
146
- const successorBodySequence = successorBodyHoldingDisposition(heart, disposition);
147
- return successorBodySequence === null
148
- ? { kind: "proven" }
149
- : { kind: "proven", successorBodySequence };
150
- }));
151
- }
152
- export async function recordUndeliveredPendingTells(paths, at, tellIds) {
153
- if (tellIds.length === 0)
154
- return;
155
- await withHeart(paths, (heart) => transaction(heart, () => {
156
- insertUndeliveredTellReceipts(heart, tellIds, at);
157
- pruneActivityFacts(heart, ACTIVITY_LIMIT);
158
- }));
166
+ pruneActivityFacts(heart, ACTIVITY_LIMIT, true);
167
+ return true;
168
+ });
159
169
  }
160
170
  export async function readKill(paths, bodySequence) {
161
- return await withHeart(paths, (heart) => killFactForBody(heart, bodySequence));
171
+ return await withReadOnlyHeart(paths, (heart) => killFactForBody(heart, bodySequence));
162
172
  }
163
- export async function requestStop(paths, at) {
164
- return await withHeart(paths, (heart) => transaction(heart, () => {
173
+ export async function requestStop(paths, at, signal) {
174
+ return await withHeartTransaction(paths, (heart) => {
165
175
  const body = latestBodyFact(heart);
166
176
  if (body === null)
167
177
  throw new Error("Akuma has no Body to kill");
@@ -175,10 +185,10 @@ export async function requestStop(paths, at) {
175
185
  }
176
186
  insertStopControl(heart, body.sequence, at);
177
187
  return { kind: "requested", body };
178
- }));
188
+ }, signal);
179
189
  }
180
- export async function requestPause(paths, at) {
181
- return await withHeart(paths, (heart) => transaction(heart, () => {
190
+ export async function requestPause(paths, at, signal) {
191
+ return await withHeartTransaction(paths, (heart) => {
182
192
  if (soulFact(heart) === null)
183
193
  return { kind: "not-born" };
184
194
  const body = latestBodyFact(heart);
@@ -186,45 +196,45 @@ export async function requestPause(paths, at) {
186
196
  throw new Error("Akuma has no Body to interrupt");
187
197
  insertPauseControl(heart, at);
188
198
  return { kind: "requested", body };
189
- }));
199
+ }, signal);
190
200
  }
191
201
  export async function stopRequested(paths, bodySequence) {
192
- return await withHeart(paths, (heart) => {
202
+ return await withReadOnlyHeart(paths, (heart) => {
193
203
  const target = stopFact(heart);
194
204
  return target !== null && (bodySequence === undefined || target.bodySequence === bodySequence);
195
205
  });
196
206
  }
197
207
  export async function pauseRequested(paths, bodySequence) {
198
- return await withHeart(paths, (heart) => {
208
+ return await withReadOnlyHeart(paths, (heart) => {
199
209
  const target = pauseFact(heart);
200
210
  return target !== null && (bodySequence === undefined || target.bodySequence === bodySequence);
201
211
  });
202
212
  }
203
213
  export async function breakBody(paths, input) {
204
- await withHeart(paths, (heart) => endBodyFact(heart, input));
214
+ await withHeartTransaction(paths, (heart) => endBodyFact(heart, input));
205
215
  }
206
216
  export async function beginTurn(paths, input) {
207
- return await withHeart(paths, (heart) => transaction(heart, () => {
217
+ return await withHeartTransaction(paths, (heart) => {
208
218
  const fact = insertTurnStartFact(heart, input);
209
219
  pruneActivityFacts(heart, ACTIVITY_LIMIT);
210
220
  return fact;
211
- }));
221
+ });
212
222
  }
213
223
  export async function bindTellsToTurn(paths, input) {
214
- await withHeart(paths, (heart) => transaction(heart, () => {
224
+ await withHeartTransaction(paths, (heart) => {
215
225
  for (const tellId of input.tellIds) {
216
226
  if (tellFact(heart, tellId) === null)
217
227
  throw new Error(`unknown tell ${tellId}`);
218
228
  insertTellBindingFact(heart, { tellId, turnSequence: input.turnSequence, boundAt: input.boundAt });
219
229
  }
220
230
  pruneActivityFacts(heart, ACTIVITY_LIMIT);
221
- }));
231
+ });
222
232
  }
223
233
  export async function readOpenBoundTurns(paths, bodySequence) {
224
- return await withHeart(paths, (heart) => readTransaction(heart, () => openBoundTurns(heart, bodySequence)));
234
+ return await withReadOnlyHeart(paths, (heart) => openBoundTurns(heart, bodySequence));
225
235
  }
226
236
  export async function failOpenBoundTurns(paths, input) {
227
- return await withHeart(paths, (heart) => transaction(heart, () => {
237
+ return await withHeartTransaction(paths, (heart) => {
228
238
  const sequences = openBoundTurns(heart, input.bodySequence);
229
239
  for (const turnSequence of sequences) {
230
240
  insertTurnEndFact(heart, {
@@ -234,22 +244,22 @@ export async function failOpenBoundTurns(paths, input) {
234
244
  completedAt: input.completedAt,
235
245
  });
236
246
  }
237
- pruneActivityFacts(heart, ACTIVITY_LIMIT);
247
+ pruneActivityFacts(heart, ACTIVITY_LIMIT, sequences.length > 0);
238
248
  return sequences;
239
- }));
249
+ });
240
250
  }
241
251
  export async function readTurn(paths, sequence) {
242
- return await withHeart(paths, (heart) => readTransaction(heart, () => turnFact(heart, sequence)));
252
+ return await withReadOnlyHeart(paths, (heart) => turnFact(heart, sequence));
243
253
  }
244
254
  export async function endTurn(paths, input) {
245
- return await withHeart(paths, (heart) => transaction(heart, () => {
255
+ return await withHeartTransaction(paths, (heart) => {
246
256
  const fact = insertTurnEndFact(heart, { kind: "turn-end", ...input });
247
- pruneActivityFacts(heart, ACTIVITY_LIMIT);
257
+ pruneActivityFacts(heart, ACTIVITY_LIMIT, true);
248
258
  return fact;
249
- }));
259
+ });
250
260
  }
251
261
  export async function finishBodyIfIdle(paths, input) {
252
- return await withHeart(paths, (heart) => transaction(heart, () => {
262
+ return await withHeartTransaction(paths, (heart) => {
253
263
  if (stopFact(heart)?.bodySequence === input.sequence || pauseFact(heart)?.bodySequence === input.sequence) {
254
264
  endBodyFact(heart, { ...input, end: "put-down" });
255
265
  return { kind: "controlled" };
@@ -259,11 +269,11 @@ export async function finishBodyIfIdle(paths, input) {
259
269
  return { kind: "pending", tells: pending.map((tell) => tell.id) };
260
270
  finishBodyFact(heart, input);
261
271
  return { kind: "finished" };
262
- }));
272
+ });
263
273
  }
264
274
  export async function readHeart(paths) {
265
275
  try {
266
- return await withReadOnlyHeart(paths, (heart) => readTransaction(heart, () => ({
276
+ return await withReadOnlyHeart(paths, (heart) => ({
267
277
  soul: soulFact(heart),
268
278
  latestBody: latestBodyFact(heart),
269
279
  latestSession: latestSessionFact(heart),
@@ -272,7 +282,7 @@ export async function readHeart(paths) {
272
282
  stop: stopFact(heart),
273
283
  pause: pauseFact(heart),
274
284
  lastActivityAt: readLastActivityAt(heart),
275
- })));
285
+ }));
276
286
  }
277
287
  catch (error) {
278
288
  if (isHeartAbsent(error)) {
@@ -291,14 +301,14 @@ export async function readHeart(paths) {
291
301
  }
292
302
  }
293
303
  export async function readLastAnsweredTurn(paths) {
294
- return await withHeart(paths, lastAnsweredTurnFact);
304
+ return await withReadOnlyHeart(paths, lastAnsweredTurnFact);
295
305
  }
296
306
  export async function readForkPoint(paths, historyId) {
297
307
  const turnSequence = parsePublicHistoryId(historyId);
298
308
  if (turnSequence === null)
299
309
  return null;
300
310
  try {
301
- return await withHeart(paths, (heart) => {
311
+ return await withReadOnlyHeart(paths, (heart) => {
302
312
  const turn = answeredTurnFact(heart, turnSequence);
303
313
  const outcome = turn?.end?.outcome;
304
314
  if (outcome?.kind !== "answered" || outcome.historyId === undefined)
@@ -1,6 +1,6 @@
1
1
  import { parseAkuId } from "../identity.js";
2
2
  import { insertRequestFact, nonterminalRequestFacts, requestFact, updateRequestRefused, updateRequestBegun, updateRequestReserved, updateRequestServed, updateRequestUnproven, updateRequestVoided, updateUpstreamRequestServed, } from "./request-rows.js";
3
- import { transaction, withHeart } from "./storage.js";
3
+ import { withHeartTransaction, withReadOnlyHeart } from "./storage.js";
4
4
  import { soulFact } from "./soul.js";
5
5
  import { requestPayloadJson } from "./request-rows.js";
6
6
  // This stays out of the public Heart index: only transport service projects it as a refusal.
@@ -14,7 +14,7 @@ export function isRequestInputConflict(error) {
14
14
  return error instanceof RequestInputConflictError;
15
15
  }
16
16
  export async function admitRequest(paths, input) {
17
- return await withHeart(paths, (heart) => transaction(heart, () => {
17
+ return await withHeartTransaction(paths, (heart) => {
18
18
  const soul = soulFact(heart);
19
19
  if (soul === null)
20
20
  throw new Error("Akuma request admission requires a born Soul");
@@ -34,10 +34,10 @@ export async function admitRequest(paths, input) {
34
34
  throw new RequestInputConflictError(input.id);
35
35
  }
36
36
  return fact;
37
- }));
37
+ });
38
38
  }
39
39
  export async function reserveRequest(paths, id, child) {
40
- return await withHeart(paths, (heart) => transaction(heart, () => {
40
+ return await withHeartTransaction(paths, (heart) => {
41
41
  const before = requestFact(heart, id);
42
42
  if (before === null)
43
43
  throw new Error(`unknown Akuma request ${id}`);
@@ -49,13 +49,13 @@ export async function reserveRequest(paths, id, child) {
49
49
  throw new Error(`Akuma request ${id} cannot reserve ${child}`);
50
50
  }
51
51
  return requestFact(heart, id);
52
- }));
52
+ });
53
53
  }
54
54
  export async function serveRequest(paths, id, child) {
55
55
  const parsed = parseAkuId(child);
56
56
  if (parsed.id !== child)
57
57
  throw new Error(`Akuma request ${id} has a noncanonical child`);
58
- return await withHeart(paths, (heart) => transaction(heart, () => {
58
+ return await withHeartTransaction(paths, (heart) => {
59
59
  const before = requestFact(heart, id);
60
60
  if (before === null)
61
61
  throw new Error(`unknown Akuma request ${id}`);
@@ -65,10 +65,10 @@ export async function serveRequest(paths, id, child) {
65
65
  throw new Error(`Akuma request ${id} cannot serve ${child}`);
66
66
  }
67
67
  return requestFact(heart, id);
68
- }));
68
+ });
69
69
  }
70
70
  export async function serveUpstreamRequest(paths, id, serviceJson) {
71
- return await withHeart(paths, (heart) => transaction(heart, () => {
71
+ return await withHeartTransaction(paths, (heart) => {
72
72
  const before = requestFact(heart, id);
73
73
  if (before === null)
74
74
  throw new Error(`unknown Akuma request ${id}`);
@@ -78,10 +78,10 @@ export async function serveUpstreamRequest(paths, id, serviceJson) {
78
78
  throw new Error(`Akuma request ${id} cannot be served with this service reference`);
79
79
  }
80
80
  return requestFact(heart, id);
81
- }));
81
+ });
82
82
  }
83
83
  export async function refuseRequest(paths, id, diagnostic) {
84
- return await withHeart(paths, (heart) => transaction(heart, () => {
84
+ return await withHeartTransaction(paths, (heart) => {
85
85
  const before = requestFact(heart, id);
86
86
  if (before === null)
87
87
  throw new Error(`unknown Akuma request ${id}`);
@@ -91,10 +91,10 @@ export async function refuseRequest(paths, id, diagnostic) {
91
91
  throw new Error(`Akuma request ${id} cannot be refused`);
92
92
  }
93
93
  return requestFact(heart, id);
94
- }));
94
+ });
95
95
  }
96
96
  export async function voidRequest(paths, id, evidence) {
97
- return await withHeart(paths, (heart) => transaction(heart, () => {
97
+ return await withHeartTransaction(paths, (heart) => {
98
98
  const before = requestFact(heart, id);
99
99
  if (before === null)
100
100
  throw new Error(`unknown Akuma request ${id}`);
@@ -105,10 +105,10 @@ export async function voidRequest(paths, id, evidence) {
105
105
  throw new Error(`Akuma request ${id} cannot be voided`);
106
106
  }
107
107
  return requestFact(heart, id);
108
- }));
108
+ });
109
109
  }
110
110
  export async function beginRequest(paths, id) {
111
- return await withHeart(paths, (heart) => transaction(heart, () => {
111
+ return await withHeartTransaction(paths, (heart) => {
112
112
  const before = requestFact(heart, id);
113
113
  if (before === null)
114
114
  throw new Error(`unknown Akuma request ${id}`);
@@ -117,10 +117,10 @@ export async function beginRequest(paths, id) {
117
117
  else if (before.state !== "begun")
118
118
  throw new Error(`Akuma request ${id} cannot begin`);
119
119
  return requestFact(heart, id);
120
- }));
120
+ });
121
121
  }
122
122
  export async function unproveRequest(paths, id, evidence) {
123
- return await withHeart(paths, (heart) => transaction(heart, () => {
123
+ return await withHeartTransaction(paths, (heart) => {
124
124
  const before = requestFact(heart, id);
125
125
  if (before === null)
126
126
  throw new Error(`unknown Akuma request ${id}`);
@@ -130,11 +130,11 @@ export async function unproveRequest(paths, id, evidence) {
130
130
  throw new Error(`Akuma request ${id} cannot be marked unproven`);
131
131
  }
132
132
  return requestFact(heart, id);
133
- }));
133
+ });
134
134
  }
135
135
  export async function readRequest(paths, id) {
136
- return await withHeart(paths, (heart) => requestFact(heart, id));
136
+ return await withReadOnlyHeart(paths, (heart) => requestFact(heart, id));
137
137
  }
138
138
  export async function readNonterminalRequests(paths) {
139
- return await withHeart(paths, nonterminalRequestFacts);
139
+ return await withReadOnlyHeart(paths, nonterminalRequestFacts);
140
140
  }
@@ -2,5 +2,5 @@ import type { DatabaseSync } from "node:sqlite";
2
2
  export declare function assertHeartSchemaVersion(database: DatabaseSync): void;
3
3
  export declare function assertLeashSchemaVersion(database: DatabaseSync): void;
4
4
  export declare function heartSchemaIsCurrent(database: DatabaseSync): boolean;
5
- export declare const HEART_SCHEMA = "\n CREATE TABLE IF NOT EXISTS akuma_schema (\n singleton INTEGER PRIMARY KEY CHECK (singleton = 1),\n version INTEGER NOT NULL CHECK (version = 25)\n ) STRICT;\n INSERT OR IGNORE INTO akuma_schema(singleton, version) VALUES (1, 25);\n CREATE TABLE IF NOT EXISTS soul (\n singleton INTEGER PRIMARY KEY CHECK (singleton = 1),\n soul_json TEXT NOT NULL CHECK (json_valid(soul_json))\n ) STRICT;\n CREATE TABLE IF NOT EXISTS bodies (\n sequence INTEGER PRIMARY KEY AUTOINCREMENT,\n leash_taken_at TEXT NOT NULL,\n hung_diagnostic TEXT,\n hung_at TEXT,\n end TEXT CHECK (end IN ('exited', 'broke-off', 'put-down')),\n ended_at TEXT,\n CHECK ((hung_diagnostic IS NULL AND hung_at IS NULL)\n OR (hung_diagnostic IS NOT NULL AND hung_at IS NOT NULL))\n ) STRICT;\n CREATE TABLE IF NOT EXISTS sessions (\n sequence INTEGER PRIMARY KEY AUTOINCREMENT,\n provider TEXT NOT NULL,\n coordinate_json TEXT NOT NULL CHECK (json_valid(coordinate_json)),\n cwd TEXT NOT NULL,\n options_json TEXT NOT NULL CHECK (json_valid(options_json)),\n admitted_at TEXT NOT NULL\n ) STRICT;\n CREATE TABLE IF NOT EXISTS turns (\n sequence INTEGER PRIMARY KEY REFERENCES timeline(sequence) ON DELETE CASCADE,\n body_sequence INTEGER NOT NULL REFERENCES bodies(sequence),\n started_at TEXT NOT NULL,\n end_sequence INTEGER UNIQUE REFERENCES timeline(sequence) ON DELETE SET NULL,\n outcome TEXT CHECK (outcome IN ('answered', 'failed', 'invalid-output')),\n history_id TEXT UNIQUE,\n session_json TEXT CHECK (session_json IS NULL OR json_valid(session_json)),\n answer TEXT,\n answer_json TEXT CHECK (answer_json IS NULL OR json_valid(answer_json)),\n schema_json TEXT CHECK (schema_json IS NULL OR json_valid(schema_json)),\n diagnostic TEXT,\n completed_at TEXT,\n CHECK (\n (outcome = 'answered' AND session_json IS NOT NULL AND answer IS NOT NULL AND diagnostic IS NULL\n AND ((schema_json IS NULL AND answer_json IS NULL) OR (schema_json IS NOT NULL AND answer_json IS NOT NULL)))\n OR (outcome = 'failed' AND history_id IS NULL AND session_json IS NULL AND answer IS NULL\n AND answer_json IS NULL AND diagnostic IS NOT NULL)\n OR (outcome = 'invalid-output' AND answer IS NOT NULL AND answer_json IS NULL\n AND diagnostic IS NOT NULL AND schema_json IS NOT NULL)\n OR (outcome IS NULL AND end_sequence IS NULL AND history_id IS NULL AND session_json IS NULL\n AND answer IS NULL AND answer_json IS NULL AND diagnostic IS NULL AND completed_at IS NULL)\n )\n ) STRICT;\n CREATE TABLE IF NOT EXISTS timeline (\n sequence INTEGER PRIMARY KEY AUTOINCREMENT,\n kind TEXT NOT NULL CHECK (kind IN ('turn-start', 'call', 'activity', 'tell', 'turn-end'))\n ) STRICT;\n CREATE TABLE IF NOT EXISTS calls (\n sequence INTEGER PRIMARY KEY REFERENCES timeline(sequence) ON DELETE CASCADE,\n turn_sequence INTEGER NOT NULL REFERENCES turns(sequence) ON DELETE CASCADE,\n body TEXT NOT NULL,\n at TEXT NOT NULL\n ) STRICT;\n CREATE TABLE IF NOT EXISTS activity (\n sequence INTEGER PRIMARY KEY REFERENCES timeline(sequence) ON DELETE CASCADE,\n turn_sequence INTEGER NOT NULL REFERENCES turns(sequence) ON DELETE CASCADE,\n event_json TEXT NOT NULL CHECK (json_valid(event_json)),\n at TEXT NOT NULL\n ) STRICT;\n CREATE TABLE IF NOT EXISTS tells (\n id TEXT PRIMARY KEY,\n sequence INTEGER NOT NULL UNIQUE REFERENCES timeline(sequence) ON DELETE CASCADE,\n body TEXT NOT NULL,\n schema_json TEXT CHECK (schema_json IS NULL OR json_valid(schema_json)),\n recorded_at TEXT NOT NULL\n ) STRICT;\n CREATE TABLE IF NOT EXISTS tell_bindings (\n sequence INTEGER PRIMARY KEY AUTOINCREMENT,\n tell_id TEXT NOT NULL REFERENCES tells(id) ON DELETE CASCADE,\n turn_sequence INTEGER NOT NULL REFERENCES turns(sequence) ON DELETE CASCADE,\n bound_at TEXT NOT NULL,\n UNIQUE (tell_id, turn_sequence)\n ) STRICT;\n CREATE TABLE IF NOT EXISTS tell_deliveries (\n sequence INTEGER PRIMARY KEY AUTOINCREMENT,\n tell_id TEXT NOT NULL REFERENCES tells(id) ON DELETE CASCADE,\n route TEXT NOT NULL CHECK (route IN ('launch', 'live')),\n turn_sequence INTEGER NOT NULL REFERENCES turns(sequence) ON DELETE CASCADE,\n fence TEXT NOT NULL,\n receipt TEXT CHECK (receipt IN ('unavailable', 'required')),\n delivered_at TEXT NOT NULL,\n CHECK ((route = 'launch' AND receipt IS NULL) OR (route = 'live' AND receipt IS NOT NULL)),\n UNIQUE (tell_id, turn_sequence, fence)\n ) STRICT;\n CREATE TABLE IF NOT EXISTS tell_receipts (\n sequence INTEGER PRIMARY KEY AUTOINCREMENT,\n evidence TEXT NOT NULL CHECK (evidence IN ('exact', 'fence')),\n tell_id TEXT REFERENCES tells(id) ON DELETE CASCADE,\n turn_sequence INTEGER REFERENCES turns(sequence) ON DELETE CASCADE,\n fence TEXT,\n kind TEXT NOT NULL,\n received_at TEXT NOT NULL,\n CHECK (\n (evidence = 'exact' AND tell_id IS NOT NULL AND turn_sequence IS NULL AND fence IS NULL)\n OR (evidence = 'fence' AND tell_id IS NULL AND turn_sequence IS NOT NULL AND fence IS NOT NULL)\n )\n ) STRICT;\n CREATE UNIQUE INDEX IF NOT EXISTS tell_receipts_exact\n ON tell_receipts(tell_id, kind) WHERE evidence = 'exact';\n CREATE UNIQUE INDEX IF NOT EXISTS tell_receipts_fence\n ON tell_receipts(turn_sequence, fence, kind) WHERE evidence = 'fence';\n CREATE TABLE IF NOT EXISTS tell_dispositions (\n body_sequence INTEGER NOT NULL REFERENCES bodies(sequence),\n tell_id TEXT NOT NULL REFERENCES tells(id),\n decided_at TEXT NOT NULL,\n resolved_at TEXT,\n PRIMARY KEY (body_sequence, tell_id)\n ) STRICT;\n CREATE TABLE IF NOT EXISTS requests (\n sequence INTEGER PRIMARY KEY AUTOINCREMENT,\n id TEXT NOT NULL UNIQUE,\n requester TEXT NOT NULL,\n action TEXT NOT NULL,\n payload_json TEXT NOT NULL CHECK (json_valid(payload_json)),\n admitted_at TEXT NOT NULL,\n state TEXT NOT NULL CHECK (state IN ('admitted', 'reserved', 'begun', 'served', 'refused', 'voided', 'unproven')),\n child TEXT,\n service_json TEXT CHECK (service_json IS NULL OR json_valid(service_json)),\n diagnostic TEXT,\n evidence TEXT,\n CHECK (\n (state = 'admitted' AND child IS NULL AND service_json IS NULL AND diagnostic IS NULL AND evidence IS NULL)\n OR (state = 'reserved' AND child IS NOT NULL AND service_json IS NULL\n AND diagnostic IS NULL AND evidence IS NULL)\n OR (state = 'begun' AND child IS NULL AND service_json IS NULL AND diagnostic IS NULL AND evidence IS NULL)\n OR (state = 'served'\n AND ((child IS NOT NULL AND service_json IS NULL)\n OR (child IS NULL AND service_json IS NOT NULL))\n AND diagnostic IS NULL AND evidence IS NULL)\n OR (state = 'refused' AND child IS NULL AND service_json IS NULL AND diagnostic IS NOT NULL AND evidence IS NULL)\n OR (state = 'voided' AND child IS NULL AND service_json IS NULL AND diagnostic IS NULL AND evidence IS NOT NULL)\n OR (state = 'unproven' AND child IS NULL AND service_json IS NULL AND diagnostic IS NULL AND evidence IS NOT NULL)\n )\n ) STRICT;\n CREATE TABLE IF NOT EXISTS control (\n kind TEXT PRIMARY KEY CHECK (kind IN ('stop', 'pause')),\n value_json TEXT NOT NULL CHECK (json_valid(value_json)),\n at TEXT NOT NULL\n ) STRICT;\n CREATE TABLE IF NOT EXISTS kills (\n sequence INTEGER PRIMARY KEY AUTOINCREMENT,\n body_sequence INTEGER NOT NULL UNIQUE REFERENCES bodies(sequence),\n evidence TEXT NOT NULL CHECK (evidence = 'killed'),\n at TEXT NOT NULL\n ) STRICT;\n";
5
+ export declare const HEART_SCHEMA = "\n CREATE TABLE IF NOT EXISTS akuma_schema (\n singleton INTEGER PRIMARY KEY CHECK (singleton = 1),\n version INTEGER NOT NULL CHECK (version = 28)\n ) STRICT;\n INSERT OR IGNORE INTO akuma_schema(singleton, version) VALUES (1, 28);\n CREATE TABLE IF NOT EXISTS soul (\n singleton INTEGER PRIMARY KEY CHECK (singleton = 1),\n soul_json TEXT NOT NULL CHECK (json_valid(soul_json))\n ) STRICT;\n CREATE TABLE IF NOT EXISTS bodies (\n sequence INTEGER PRIMARY KEY AUTOINCREMENT,\n leash_taken_at TEXT NOT NULL,\n hung_diagnostic TEXT,\n hung_at TEXT,\n end TEXT CHECK (end IN ('exited', 'broke-off', 'put-down')),\n ended_at TEXT,\n CHECK ((hung_diagnostic IS NULL AND hung_at IS NULL)\n OR (hung_diagnostic IS NOT NULL AND hung_at IS NOT NULL))\n ) STRICT;\n CREATE TABLE IF NOT EXISTS sessions (\n sequence INTEGER PRIMARY KEY AUTOINCREMENT,\n provider TEXT NOT NULL,\n coordinate_json TEXT NOT NULL CHECK (json_valid(coordinate_json)),\n cwd TEXT NOT NULL,\n options_json TEXT NOT NULL CHECK (json_valid(options_json)),\n admitted_at TEXT NOT NULL\n ) STRICT;\n CREATE TABLE IF NOT EXISTS turns (\n sequence INTEGER PRIMARY KEY REFERENCES timeline(sequence) ON DELETE CASCADE,\n body_sequence INTEGER NOT NULL REFERENCES bodies(sequence),\n started_at TEXT NOT NULL,\n end_sequence INTEGER UNIQUE REFERENCES timeline(sequence) ON DELETE SET NULL,\n outcome TEXT CHECK (outcome IN ('answered', 'failed', 'invalid-output')),\n history_id TEXT UNIQUE,\n session_json TEXT CHECK (session_json IS NULL OR json_valid(session_json)),\n answer TEXT,\n answer_json TEXT CHECK (answer_json IS NULL OR json_valid(answer_json)),\n schema_json TEXT CHECK (schema_json IS NULL OR json_valid(schema_json)),\n diagnostic TEXT,\n completed_at TEXT,\n CHECK (\n (outcome = 'answered' AND session_json IS NOT NULL AND answer IS NOT NULL AND diagnostic IS NULL\n AND ((schema_json IS NULL AND answer_json IS NULL) OR (schema_json IS NOT NULL AND answer_json IS NOT NULL)))\n OR (outcome = 'failed' AND history_id IS NULL AND session_json IS NULL AND answer IS NULL\n AND answer_json IS NULL AND diagnostic IS NOT NULL)\n OR (outcome = 'invalid-output' AND answer IS NOT NULL AND answer_json IS NULL\n AND diagnostic IS NOT NULL AND schema_json IS NOT NULL)\n OR (outcome IS NULL AND end_sequence IS NULL AND history_id IS NULL AND session_json IS NULL\n AND answer IS NULL AND answer_json IS NULL AND diagnostic IS NULL AND completed_at IS NULL)\n )\n ) STRICT;\n CREATE TABLE IF NOT EXISTS timeline (\n sequence INTEGER PRIMARY KEY AUTOINCREMENT,\n kind TEXT NOT NULL CHECK (kind IN ('turn-start', 'call', 'activity', 'tell', 'turn-end'))\n ) STRICT;\n CREATE TABLE IF NOT EXISTS activity_retention (\n singleton INTEGER PRIMARY KEY CHECK (singleton = 1),\n checked_sequence INTEGER NOT NULL\n ) STRICT;\n INSERT OR IGNORE INTO activity_retention(singleton, checked_sequence) VALUES (1, 0);\n CREATE TABLE IF NOT EXISTS calls (\n sequence INTEGER PRIMARY KEY REFERENCES timeline(sequence) ON DELETE CASCADE,\n turn_sequence INTEGER NOT NULL REFERENCES turns(sequence) ON DELETE CASCADE,\n body TEXT NOT NULL,\n at TEXT NOT NULL\n ) STRICT;\n CREATE TABLE IF NOT EXISTS activity (\n sequence INTEGER PRIMARY KEY REFERENCES timeline(sequence) ON DELETE CASCADE,\n turn_sequence INTEGER NOT NULL REFERENCES turns(sequence) ON DELETE CASCADE,\n event_json TEXT NOT NULL CHECK (json_valid(event_json)),\n at TEXT NOT NULL\n ) STRICT;\n CREATE TABLE IF NOT EXISTS tells (\n id TEXT PRIMARY KEY,\n sequence INTEGER NOT NULL UNIQUE REFERENCES timeline(sequence) ON DELETE CASCADE,\n body TEXT NOT NULL,\n schema_json TEXT CHECK (schema_json IS NULL OR json_valid(schema_json)),\n recorded_at TEXT NOT NULL\n ) STRICT;\n CREATE TABLE IF NOT EXISTS tell_bindings (\n sequence INTEGER PRIMARY KEY AUTOINCREMENT,\n tell_id TEXT NOT NULL REFERENCES tells(id) ON DELETE CASCADE,\n turn_sequence INTEGER NOT NULL REFERENCES turns(sequence) ON DELETE CASCADE,\n bound_at TEXT NOT NULL,\n UNIQUE (tell_id, turn_sequence)\n ) STRICT;\n CREATE TABLE IF NOT EXISTS tell_deliveries (\n sequence INTEGER PRIMARY KEY AUTOINCREMENT,\n tell_id TEXT NOT NULL REFERENCES tells(id) ON DELETE CASCADE,\n route TEXT NOT NULL CHECK (route IN ('launch', 'live')),\n turn_sequence INTEGER NOT NULL REFERENCES turns(sequence) ON DELETE CASCADE,\n fence TEXT NOT NULL,\n receipt TEXT CHECK (receipt IN ('unavailable', 'required')),\n delivered_at TEXT NOT NULL,\n CHECK ((route = 'launch' AND receipt IS NULL) OR (route = 'live' AND receipt IS NOT NULL)),\n UNIQUE (tell_id, turn_sequence, fence)\n ) STRICT;\n CREATE TABLE IF NOT EXISTS tell_receipts (\n sequence INTEGER PRIMARY KEY AUTOINCREMENT,\n evidence TEXT NOT NULL CHECK (evidence IN ('exact', 'fence')),\n tell_id TEXT REFERENCES tells(id) ON DELETE CASCADE,\n turn_sequence INTEGER REFERENCES turns(sequence) ON DELETE CASCADE,\n fence TEXT,\n kind TEXT NOT NULL,\n received_at TEXT NOT NULL,\n CHECK (\n (evidence = 'exact' AND tell_id IS NOT NULL AND turn_sequence IS NULL AND fence IS NULL)\n OR (evidence = 'fence' AND tell_id IS NULL AND turn_sequence IS NOT NULL AND fence IS NOT NULL)\n )\n ) STRICT;\n CREATE UNIQUE INDEX IF NOT EXISTS tell_receipts_exact\n ON tell_receipts(tell_id, kind);\n CREATE UNIQUE INDEX IF NOT EXISTS tell_receipts_fence\n ON tell_receipts(turn_sequence, fence, kind);\n CREATE TABLE IF NOT EXISTS tell_dispositions (\n body_sequence INTEGER PRIMARY KEY REFERENCES bodies(sequence),\n decided_at TEXT NOT NULL,\n resolved_at TEXT\n ) STRICT;\n CREATE TABLE IF NOT EXISTS tell_disposition_members (\n body_sequence INTEGER NOT NULL REFERENCES tell_dispositions(body_sequence) ON DELETE CASCADE,\n tell_id TEXT NOT NULL REFERENCES tells(id),\n PRIMARY KEY (body_sequence, tell_id)\n ) STRICT;\n CREATE INDEX IF NOT EXISTS calls_by_turn ON calls(turn_sequence);\n CREATE INDEX IF NOT EXISTS activity_by_turn ON activity(turn_sequence);\n CREATE INDEX IF NOT EXISTS tell_bindings_by_turn ON tell_bindings(turn_sequence);\n CREATE INDEX IF NOT EXISTS tell_deliveries_by_turn ON tell_deliveries(turn_sequence);\n CREATE INDEX IF NOT EXISTS tell_disposition_members_by_tell ON tell_disposition_members(tell_id);\n CREATE TABLE IF NOT EXISTS requests (\n sequence INTEGER PRIMARY KEY AUTOINCREMENT,\n id TEXT NOT NULL UNIQUE,\n requester TEXT NOT NULL,\n action TEXT NOT NULL,\n payload_json TEXT NOT NULL CHECK (json_valid(payload_json)),\n admitted_at TEXT NOT NULL,\n state TEXT NOT NULL CHECK (state IN ('admitted', 'reserved', 'begun', 'served', 'refused', 'voided', 'unproven')),\n child TEXT,\n service_json TEXT CHECK (service_json IS NULL OR json_valid(service_json)),\n diagnostic TEXT,\n evidence TEXT,\n CHECK (\n (state = 'admitted' AND child IS NULL AND service_json IS NULL AND diagnostic IS NULL AND evidence IS NULL)\n OR (state = 'reserved' AND child IS NOT NULL AND service_json IS NULL\n AND diagnostic IS NULL AND evidence IS NULL)\n OR (state = 'begun' AND child IS NULL AND service_json IS NULL AND diagnostic IS NULL AND evidence IS NULL)\n OR (state = 'served'\n AND ((child IS NOT NULL AND service_json IS NULL)\n OR (child IS NULL AND service_json IS NOT NULL))\n AND diagnostic IS NULL AND evidence IS NULL)\n OR (state = 'refused' AND child IS NULL AND service_json IS NULL AND diagnostic IS NOT NULL AND evidence IS NULL)\n OR (state = 'voided' AND child IS NULL AND service_json IS NULL AND diagnostic IS NULL AND evidence IS NOT NULL)\n OR (state = 'unproven' AND child IS NULL AND service_json IS NULL AND diagnostic IS NULL AND evidence IS NOT NULL)\n )\n ) STRICT;\n CREATE TABLE IF NOT EXISTS control (\n kind TEXT PRIMARY KEY CHECK (kind IN ('stop', 'pause')),\n value_json TEXT NOT NULL CHECK (json_valid(value_json)),\n at TEXT NOT NULL\n ) STRICT;\n CREATE TABLE IF NOT EXISTS kills (\n sequence INTEGER PRIMARY KEY AUTOINCREMENT,\n body_sequence INTEGER NOT NULL UNIQUE REFERENCES bodies(sequence),\n evidence TEXT NOT NULL CHECK (evidence = 'killed'),\n at TEXT NOT NULL\n ) STRICT;\n";
6
6
  export declare const LEASH_SCHEMA = "\n PRAGMA journal_mode=DELETE;\n CREATE TABLE IF NOT EXISTS leash_schema (\n singleton INTEGER PRIMARY KEY CHECK (singleton = 1),\n version INTEGER NOT NULL CHECK (version = 4)\n ) STRICT;\n INSERT OR IGNORE INTO leash_schema(singleton, version) VALUES (1, 4);\n CREATE TABLE IF NOT EXISTS seal (\n singleton INTEGER PRIMARY KEY CHECK (singleton = 1),\n evidence TEXT NOT NULL,\n at TEXT NOT NULL\n ) STRICT;\n";
@@ -1,4 +1,4 @@
1
- const HEART_SCHEMA_VERSION = 25;
1
+ const HEART_SCHEMA_VERSION = 28;
2
2
  const LEASH_SCHEMA_VERSION = 4;
3
3
  function assertSchemaVersion(database, table, expected) {
4
4
  const row = database.prepare(`SELECT version FROM ${table} WHERE singleton = 1`).get();
@@ -76,6 +76,11 @@ export const HEART_SCHEMA = `
76
76
  sequence INTEGER PRIMARY KEY AUTOINCREMENT,
77
77
  kind TEXT NOT NULL CHECK (kind IN ('turn-start', 'call', 'activity', 'tell', 'turn-end'))
78
78
  ) STRICT;
79
+ CREATE TABLE IF NOT EXISTS activity_retention (
80
+ singleton INTEGER PRIMARY KEY CHECK (singleton = 1),
81
+ checked_sequence INTEGER NOT NULL
82
+ ) STRICT;
83
+ INSERT OR IGNORE INTO activity_retention(singleton, checked_sequence) VALUES (1, 0);
79
84
  CREATE TABLE IF NOT EXISTS calls (
80
85
  sequence INTEGER PRIMARY KEY REFERENCES timeline(sequence) ON DELETE CASCADE,
81
86
  turn_sequence INTEGER NOT NULL REFERENCES turns(sequence) ON DELETE CASCADE,
@@ -127,16 +132,24 @@ export const HEART_SCHEMA = `
127
132
  )
128
133
  ) STRICT;
129
134
  CREATE UNIQUE INDEX IF NOT EXISTS tell_receipts_exact
130
- ON tell_receipts(tell_id, kind) WHERE evidence = 'exact';
135
+ ON tell_receipts(tell_id, kind);
131
136
  CREATE UNIQUE INDEX IF NOT EXISTS tell_receipts_fence
132
- ON tell_receipts(turn_sequence, fence, kind) WHERE evidence = 'fence';
137
+ ON tell_receipts(turn_sequence, fence, kind);
133
138
  CREATE TABLE IF NOT EXISTS tell_dispositions (
134
- body_sequence INTEGER NOT NULL REFERENCES bodies(sequence),
135
- tell_id TEXT NOT NULL REFERENCES tells(id),
139
+ body_sequence INTEGER PRIMARY KEY REFERENCES bodies(sequence),
136
140
  decided_at TEXT NOT NULL,
137
- resolved_at TEXT,
141
+ resolved_at TEXT
142
+ ) STRICT;
143
+ CREATE TABLE IF NOT EXISTS tell_disposition_members (
144
+ body_sequence INTEGER NOT NULL REFERENCES tell_dispositions(body_sequence) ON DELETE CASCADE,
145
+ tell_id TEXT NOT NULL REFERENCES tells(id),
138
146
  PRIMARY KEY (body_sequence, tell_id)
139
147
  ) STRICT;
148
+ CREATE INDEX IF NOT EXISTS calls_by_turn ON calls(turn_sequence);
149
+ CREATE INDEX IF NOT EXISTS activity_by_turn ON activity(turn_sequence);
150
+ CREATE INDEX IF NOT EXISTS tell_bindings_by_turn ON tell_bindings(turn_sequence);
151
+ CREATE INDEX IF NOT EXISTS tell_deliveries_by_turn ON tell_deliveries(turn_sequence);
152
+ CREATE INDEX IF NOT EXISTS tell_disposition_members_by_tell ON tell_disposition_members(tell_id);
140
153
  CREATE TABLE IF NOT EXISTS requests (
141
154
  sequence INTEGER PRIMARY KEY AUTOINCREMENT,
142
155
  id TEXT NOT NULL UNIQUE,
@@ -8,10 +8,8 @@ export declare class HeartAbsentError extends Error {
8
8
  }
9
9
  export declare function isHeartAbsent(error: unknown): error is HeartAbsentError;
10
10
  export declare function classifyHeartSchema(paths: AkumaPaths): Promise<"current" | "unsupported">;
11
- export declare function withHeart<T>(paths: AkumaPaths, body: (database: DatabaseSync) => T): Promise<T>;
12
- export declare function withReadOnlyHeart<T>(paths: AkumaPaths, body: (database: DatabaseSync) => T): Promise<T>;
13
- export declare function transaction<T>(database: DatabaseSync, body: () => T): T;
14
- export declare function readTransaction<T>(database: DatabaseSync, body: () => T): T;
11
+ export declare function withReadOnlyHeart<T>(paths: AkumaPaths, body: (database: DatabaseSync) => T, signal?: AbortSignal): Promise<T>;
12
+ export declare function withHeartTransaction<T>(paths: AkumaPaths, body: (database: DatabaseSync) => T, signal?: AbortSignal): Promise<T>;
15
13
  export declare function initializeHeart(paths: AkumaPaths): Promise<void>;
16
14
  export declare class HeldAkumaLeash {
17
15
  private readonly database;