@plurnk/plurnk-service 1.5.0 → 1.6.1

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 (248) hide show
  1. package/.env.defaults +22 -27
  2. package/INSTALL.md +22 -17
  3. package/README.md +1 -1
  4. package/SPEC.md +505 -362
  5. package/dist/build-info.json +1 -1
  6. package/dist/content/edit-collision.d.ts +5 -0
  7. package/dist/content/edit-collision.d.ts.map +1 -0
  8. package/dist/content/edit-collision.js +14 -0
  9. package/dist/content/edit-collision.js.map +1 -0
  10. package/dist/content/edit-receipt.d.ts +3 -2
  11. package/dist/content/edit-receipt.d.ts.map +1 -1
  12. package/dist/content/edit-receipt.js +58 -13
  13. package/dist/content/edit-receipt.js.map +1 -1
  14. package/dist/content/index.d.ts +4 -1
  15. package/dist/content/index.d.ts.map +1 -1
  16. package/dist/content/index.js +3 -1
  17. package/dist/content/index.js.map +1 -1
  18. package/dist/content/line-anchors.d.ts +41 -0
  19. package/dist/content/line-anchors.d.ts.map +1 -0
  20. package/dist/content/line-anchors.js +191 -0
  21. package/dist/content/line-anchors.js.map +1 -0
  22. package/dist/content/read-projector.d.ts +7 -1
  23. package/dist/content/read-projector.d.ts.map +1 -1
  24. package/dist/content/read-projector.js +46 -10
  25. package/dist/content/read-projector.js.map +1 -1
  26. package/dist/content/read-resolve.js +3 -3
  27. package/dist/content/read-resolve.js.map +1 -1
  28. package/dist/core/BudgetReadout.d.ts.map +1 -1
  29. package/dist/core/BudgetReadout.js +9 -2
  30. package/dist/core/BudgetReadout.js.map +1 -1
  31. package/dist/core/ChannelWrite.js +1 -1
  32. package/dist/core/ChannelWrite.js.map +1 -1
  33. package/dist/core/ChannelWrite.sql +3 -3
  34. package/dist/core/CoreSchemeServices.d.ts +1 -1
  35. package/dist/core/CoreSchemeServices.d.ts.map +1 -1
  36. package/dist/core/CoreSchemeServices.js +1 -1
  37. package/dist/core/CoreSchemeServices.js.map +1 -1
  38. package/dist/core/Dispatcher.d.ts +15 -5
  39. package/dist/core/Dispatcher.d.ts.map +1 -1
  40. package/dist/core/Dispatcher.js +220 -978
  41. package/dist/core/Dispatcher.js.map +1 -1
  42. package/dist/core/DurableStatement.d.ts.map +1 -1
  43. package/dist/core/DurableStatement.js +2 -0
  44. package/dist/core/DurableStatement.js.map +1 -1
  45. package/dist/core/Engine.d.ts +18 -43
  46. package/dist/core/Engine.d.ts.map +1 -1
  47. package/dist/core/Engine.js +90 -1554
  48. package/dist/core/Engine.js.map +1 -1
  49. package/dist/core/Engine.sql +192 -112
  50. package/dist/core/ExecutableTools.d.ts +10 -0
  51. package/dist/core/ExecutableTools.d.ts.map +1 -0
  52. package/dist/core/ExecutableTools.js +41 -0
  53. package/dist/core/ExecutableTools.js.map +1 -0
  54. package/dist/core/ExecutorRegistry.d.ts +3 -3
  55. package/dist/core/ExecutorRegistry.d.ts.map +1 -1
  56. package/dist/core/ExecutorRegistry.js +2 -2
  57. package/dist/core/ExecutorRegistry.js.map +1 -1
  58. package/dist/core/LogBody.d.ts +3 -0
  59. package/dist/core/LogBody.d.ts.map +1 -1
  60. package/dist/core/LogBody.js +21 -5
  61. package/dist/core/LogBody.js.map +1 -1
  62. package/dist/core/LogEntryProjection.d.ts +14 -0
  63. package/dist/core/LogEntryProjection.d.ts.map +1 -0
  64. package/dist/core/LogEntryProjection.js +38 -0
  65. package/dist/core/LogEntryProjection.js.map +1 -0
  66. package/dist/core/ModelCall.d.ts +27 -0
  67. package/dist/core/ModelCall.d.ts.map +1 -0
  68. package/dist/core/ModelCall.js +127 -0
  69. package/dist/core/ModelCall.js.map +1 -0
  70. package/dist/core/PacketBuilder.d.ts +14 -25
  71. package/dist/core/PacketBuilder.d.ts.map +1 -1
  72. package/dist/core/PacketBuilder.js +170 -244
  73. package/dist/core/PacketBuilder.js.map +1 -1
  74. package/dist/core/ProblemLog.d.ts +1 -1
  75. package/dist/core/ProblemLog.d.ts.map +1 -1
  76. package/dist/core/ProblemLog.js +15 -3
  77. package/dist/core/ProblemLog.js.map +1 -1
  78. package/dist/core/ProposalLifecycle.d.ts +2 -2
  79. package/dist/core/ProposalLifecycle.d.ts.map +1 -1
  80. package/dist/core/ProposalLifecycle.js +17 -9
  81. package/dist/core/ProposalLifecycle.js.map +1 -1
  82. package/dist/core/ProviderInstantiate.d.ts.map +1 -1
  83. package/dist/core/ProviderInstantiate.js +25 -8
  84. package/dist/core/ProviderInstantiate.js.map +1 -1
  85. package/dist/core/ResourceMutations.d.ts +73 -0
  86. package/dist/core/ResourceMutations.d.ts.map +1 -0
  87. package/dist/core/ResourceMutations.js +1142 -0
  88. package/dist/core/ResourceMutations.js.map +1 -0
  89. package/dist/core/SchemeRegistry.js +10 -10
  90. package/dist/core/SchemeRegistry.js.map +1 -1
  91. package/dist/core/StoredPacket.d.ts +2 -2
  92. package/dist/core/StoredPacket.js +7 -7
  93. package/dist/core/StrikeRail.d.ts +0 -1
  94. package/dist/core/StrikeRail.d.ts.map +1 -1
  95. package/dist/core/StrikeRail.js +6 -4
  96. package/dist/core/StrikeRail.js.map +1 -1
  97. package/dist/core/TurnRunner.d.ts +72 -0
  98. package/dist/core/TurnRunner.d.ts.map +1 -0
  99. package/dist/core/TurnRunner.js +1730 -0
  100. package/dist/core/TurnRunner.js.map +1 -0
  101. package/dist/core/caps/CapsResolve.js +1 -1
  102. package/dist/core/caps/CapsResolve.js.map +1 -1
  103. package/dist/core/caps/DbChannelCaps.js +4 -4
  104. package/dist/core/caps/DbChannelCaps.js.map +1 -1
  105. package/dist/core/caps/DbEntryCaps.d.ts +2 -1
  106. package/dist/core/caps/DbEntryCaps.d.ts.map +1 -1
  107. package/dist/core/caps/DbEntryCaps.js +8 -5
  108. package/dist/core/caps/DbEntryCaps.js.map +1 -1
  109. package/dist/core/caps/DbProjectionCaps.d.ts +1 -0
  110. package/dist/core/caps/DbProjectionCaps.d.ts.map +1 -1
  111. package/dist/core/caps/DbProjectionCaps.js +29 -0
  112. package/dist/core/caps/DbProjectionCaps.js.map +1 -1
  113. package/dist/core/caps/SchemeCtxImpl.d.ts +5 -2
  114. package/dist/core/caps/SchemeCtxImpl.d.ts.map +1 -1
  115. package/dist/core/caps/SchemeCtxImpl.js +6 -4
  116. package/dist/core/caps/SchemeCtxImpl.js.map +1 -1
  117. package/dist/core/content-hash.js +2 -2
  118. package/dist/core/content-hash.js.map +1 -1
  119. package/dist/core/content-weight.d.ts +2 -0
  120. package/dist/core/content-weight.d.ts.map +1 -0
  121. package/dist/core/content-weight.js +4 -0
  122. package/dist/core/content-weight.js.map +1 -0
  123. package/dist/core/content_weight.d.ts +4 -0
  124. package/dist/core/content_weight.d.ts.map +1 -0
  125. package/dist/core/content_weight.js +6 -0
  126. package/dist/core/content_weight.js.map +1 -0
  127. package/dist/core/fork.d.ts.map +1 -1
  128. package/dist/core/fork.js +23 -7
  129. package/dist/core/fork.js.map +1 -1
  130. package/dist/core/fork.sql +38 -17
  131. package/dist/core/git-membership.d.ts.map +1 -1
  132. package/dist/core/git-membership.js +1 -2
  133. package/dist/core/git-membership.js.map +1 -1
  134. package/dist/core/git-state.js +1 -1
  135. package/dist/core/git-state.js.map +1 -1
  136. package/dist/core/packet-wire.d.ts +6 -3
  137. package/dist/core/packet-wire.d.ts.map +1 -1
  138. package/dist/core/packet-wire.js +257 -99
  139. package/dist/core/packet-wire.js.map +1 -1
  140. package/dist/core/provider-accounting.d.ts +24 -0
  141. package/dist/core/provider-accounting.d.ts.map +1 -0
  142. package/dist/core/provider-accounting.js +76 -0
  143. package/dist/core/provider-accounting.js.map +1 -0
  144. package/dist/core/scheme-types.d.ts +1 -1
  145. package/dist/core/scheme-types.d.ts.map +1 -1
  146. package/dist/core/search-gate.d.ts +2 -2
  147. package/dist/core/search-gate.d.ts.map +1 -1
  148. package/dist/core/search-gate.js +8 -8
  149. package/dist/core/search-gate.js.map +1 -1
  150. package/dist/core/turn-scheduler.js +1 -1
  151. package/dist/core/turn-scheduler.js.map +1 -1
  152. package/dist/digest/Digest.d.ts.map +1 -1
  153. package/dist/digest/Digest.js +221 -110
  154. package/dist/digest/Digest.js.map +1 -1
  155. package/dist/digest/digest.sql +40 -18
  156. package/dist/schemes/Exec.d.ts.map +1 -1
  157. package/dist/schemes/Exec.js +136 -110
  158. package/dist/schemes/Exec.js.map +1 -1
  159. package/dist/schemes/ExecOutputScheme.js +1 -1
  160. package/dist/schemes/ExecOutputScheme.js.map +1 -1
  161. package/dist/schemes/File.d.ts +5 -4
  162. package/dist/schemes/File.d.ts.map +1 -1
  163. package/dist/schemes/File.js +25 -23
  164. package/dist/schemes/File.js.map +1 -1
  165. package/dist/schemes/Log.d.ts +11 -0
  166. package/dist/schemes/Log.d.ts.map +1 -1
  167. package/dist/schemes/Log.js +172 -107
  168. package/dist/schemes/Log.js.map +1 -1
  169. package/dist/schemes/Log.sql +48 -32
  170. package/dist/schemes/Prompt.d.ts +3 -3
  171. package/dist/schemes/Prompt.d.ts.map +1 -1
  172. package/dist/schemes/Prompt.js +4 -1
  173. package/dist/schemes/Prompt.js.map +1 -1
  174. package/dist/schemes/Skill.d.ts +4 -4
  175. package/dist/schemes/Skill.d.ts.map +1 -1
  176. package/dist/schemes/Skill.js +3 -0
  177. package/dist/schemes/Skill.js.map +1 -1
  178. package/dist/schemes/Worker.d.ts +4 -4
  179. package/dist/schemes/Worker.d.ts.map +1 -1
  180. package/dist/schemes/Worker.js +11 -7
  181. package/dist/schemes/Worker.js.map +1 -1
  182. package/dist/schemes/_entry-crud.d.ts +0 -1
  183. package/dist/schemes/_entry-crud.d.ts.map +1 -1
  184. package/dist/schemes/_entry-crud.js +19 -13
  185. package/dist/schemes/_entry-crud.js.map +1 -1
  186. package/dist/schemes/_entry-crud.sql +4 -16
  187. package/dist/schemes/_entry-find.d.ts +17 -16
  188. package/dist/schemes/_entry-find.d.ts.map +1 -1
  189. package/dist/schemes/_entry-find.js +58 -52
  190. package/dist/schemes/_entry-find.js.map +1 -1
  191. package/dist/schemes/_entry-find.sql +2 -21
  192. package/dist/schemes/_entry-graph.sql +1 -1
  193. package/dist/schemes/_entry-manifest.d.ts +5 -17
  194. package/dist/schemes/_entry-manifest.d.ts.map +1 -1
  195. package/dist/schemes/_entry-manifest.js +32 -28
  196. package/dist/schemes/_entry-manifest.js.map +1 -1
  197. package/dist/schemes/_entry-ops.d.ts +5 -4
  198. package/dist/schemes/_entry-ops.d.ts.map +1 -1
  199. package/dist/schemes/_entry-ops.js +89 -49
  200. package/dist/schemes/_entry-ops.js.map +1 -1
  201. package/dist/schemes/_entry-ops.sql +30 -4
  202. package/dist/schemes/_entry-send.js +4 -4
  203. package/dist/schemes/_entry-send.js.map +1 -1
  204. package/dist/schemes/_search-index.d.ts.map +1 -1
  205. package/dist/schemes/_search-index.js +10 -2
  206. package/dist/schemes/_search-index.js.map +1 -1
  207. package/dist/schemes/exec-abort.js +2 -2
  208. package/dist/schemes/exec-abort.js.map +1 -1
  209. package/dist/server/Daemon.d.ts +7 -42
  210. package/dist/server/Daemon.d.ts.map +1 -1
  211. package/dist/server/Daemon.js +139 -900
  212. package/dist/server/Daemon.js.map +1 -1
  213. package/dist/server/DrainSupervisor.d.ts +117 -0
  214. package/dist/server/DrainSupervisor.d.ts.map +1 -0
  215. package/dist/server/DrainSupervisor.js +802 -0
  216. package/dist/server/DrainSupervisor.js.map +1 -0
  217. package/dist/server/drain.sql +2 -2
  218. package/dist/server/envelope.d.ts +0 -2
  219. package/dist/server/envelope.d.ts.map +1 -1
  220. package/dist/server/envelope.js.map +1 -1
  221. package/dist/server/envelope.sql +2 -2
  222. package/dist/server/lifecycle-recovery.sql +28 -15
  223. package/dist/server/logEntry.d.ts +2 -1
  224. package/dist/server/logEntry.d.ts.map +1 -1
  225. package/dist/server/logEntry.js +14 -2
  226. package/dist/server/logEntry.js.map +1 -1
  227. package/dist/server/logEntry.sql +5 -1
  228. package/dist/server/seam-entry-read.sql +2 -2
  229. package/dist/service.js +1 -1
  230. package/dist/service.js.map +1 -1
  231. package/migrations/001_schema.sql +622 -216
  232. package/package.json +39 -34
  233. package/dist/core/BudgetOverflow.d.ts +0 -20
  234. package/dist/core/BudgetOverflow.d.ts.map +0 -1
  235. package/dist/core/BudgetOverflow.js +0 -49
  236. package/dist/core/BudgetOverflow.js.map +0 -1
  237. package/dist/core/caps/DbTagCaps.d.ts +0 -10
  238. package/dist/core/caps/DbTagCaps.d.ts.map +0 -1
  239. package/dist/core/caps/DbTagCaps.js +0 -46
  240. package/dist/core/caps/DbTagCaps.js.map +0 -1
  241. package/dist/core/ruler_count.d.ts +0 -4
  242. package/dist/core/ruler_count.d.ts.map +0 -1
  243. package/dist/core/ruler_count.js +0 -7
  244. package/dist/core/ruler_count.js.map +0 -1
  245. package/dist/core/token-ruler.d.ts +0 -2
  246. package/dist/core/token-ruler.d.ts.map +0 -1
  247. package/dist/core/token-ruler.js +0 -5
  248. package/dist/core/token-ruler.js.map +0 -1
@@ -36,13 +36,16 @@ import { dirname, join, resolve } from "node:path";
36
36
  import { randomUUID } from "node:crypto";
37
37
  import { fileURLToPath } from "node:url";
38
38
  import { homedir } from "node:os";
39
+ import { isDeepStrictEqual } from "node:util";
39
40
  import SqlRiteSync from "@possumtech/sqlrite/sync";
40
41
  import { observedSync } from "../observe/spans.js";
41
42
  import PacketWire from "../core/packet-wire.js";
43
+ import LogBody from "../core/LogBody.js";
42
44
  import StoredPacket from "../core/StoredPacket.js";
43
45
  import { renderTarget } from "../core/plurnk-uri.js";
44
46
  import ProviderInstantiate from "../core/ProviderInstantiate.js";
45
- import { ProviderError, providerCostFor, providerCostUsd, validateProviderCost } from "@plurnk/plurnk-providers";
47
+ import { aggregateProviderAccounting, ProviderError, validateProviderRequestAccounting, } from "@plurnk/plurnk-providers";
48
+ import { providerRequestFromStorageRow, } from "../core/provider-accounting.js";
46
49
  import { Validator, } from "@plurnk/plurnk-contracts";
47
50
  // The requiem prompt ({§digest-requiem}): the model's exit interview. Absolution up front - the system is
48
51
  // under test, not the model - so RLHF'd self-blame doesn't crowd out the system indictment. The
@@ -53,7 +56,9 @@ const isRecord = (value) => typeof value === "object" && value !== null && !Arra
53
56
  const requiemResponseEvidence = (response) => {
54
57
  if (!isRecord(response))
55
58
  return response;
56
- const { rawBody: _rawBody, ...withoutRawBody } = response;
59
+ const { rawBody: _rawBody, accounting: _accounting, ...withoutRawBody } = response;
60
+ void _rawBody;
61
+ void _accounting;
57
62
  if (!isRecord(withoutRawBody.assistantRaw))
58
63
  return withoutRawBody;
59
64
  const { rawBody: _nestedRawBody, ...assistantRaw } = withoutRawBody.assistantRaw;
@@ -110,20 +115,27 @@ export default class Digest {
110
115
  return fallback;
111
116
  }
112
117
  }
113
- static #providerCosts(raw, subject) {
114
- const parsed = Digest.#parseJson(raw);
115
- if (!Array.isArray(parsed))
116
- throw new TypeError(`digest: ${subject} monetary evidence is not an array`);
117
- return parsed.map((cost) => validateProviderCost(cost));
118
- }
119
- static #providerCost(raw, subject) {
120
- return validateProviderCost(Digest.#parseJson(raw));
118
+ static #requestAccounting(row) {
119
+ if (row.state !== "settled" || row.outcome === null || row.cost_kind === null) {
120
+ throw new TypeError(`digest: provider request ${row.id} is not settled`);
121
+ }
122
+ return providerRequestFromStorageRow(row);
121
123
  }
122
- static #costProjection(costs) {
123
- const values = costs.map(providerCostUsd);
124
- return values.some((value) => value === null)
124
+ static #accounting(rows) {
125
+ return rows.some((row) => row.state !== "settled")
125
126
  ? null
126
- : values.reduce((total, value) => total + (value ?? 0), 0);
127
+ : aggregateProviderAccounting(rows.map((row) => Digest.#requestAccounting(row)));
128
+ }
129
+ static #usageSummary(accounting) {
130
+ if (accounting === null)
131
+ return "accounting=incomplete";
132
+ const usage = accounting.usage;
133
+ return [
134
+ `input=${usage?.inputTokens ?? "unknown"}`,
135
+ `output=${usage?.outputTokens ?? "unknown"}`,
136
+ `reasoning=${usage?.outputTokenDetails?.reasoningTokens ?? "unknown"}`,
137
+ `cache-read=${usage?.inputTokenDetails?.cacheReadTokens ?? "unknown"}`,
138
+ ].join(" ");
127
139
  }
128
140
  static #operationResult(raw, subject) {
129
141
  try {
@@ -173,8 +185,13 @@ export default class Digest {
173
185
  static #renderGroupedOpLine(row) {
174
186
  const attrs = Digest.#parseJson(row.attrs, {});
175
187
  const materialized = row.origin === "plurnk" && row.op === "EDIT" && attrs.kind === "entry_materialized";
176
- const modelEmission = row.op === null && attrs.kind === "model_emission";
177
- return Digest.#renderOpLine(row, materialized ? "materialized entry" : modelEmission ? "model emission" : row.op ?? "actionless row");
188
+ const actionlessKind = row.op === null
189
+ ? LogBody.actionlessKind({ op: row.op, attrs })
190
+ : null;
191
+ const label = actionlessKind === "model_emission"
192
+ ? "model emission"
193
+ : actionlessKind ?? row.op ?? "actionless row";
194
+ return Digest.#renderOpLine(row, materialized ? "materialized entry" : label);
178
195
  }
179
196
  // Human triage is not a row dump. Preserve every row in digest.json, but
180
197
  // collapse identical rendered outcomes in the Markdown waterfall. Using the
@@ -223,11 +240,11 @@ export default class Digest {
223
240
  const assistant = packet !== null && StoredPacket.isAdmitted(packet) ? packet.assistant : null;
224
241
  const content = assistant?.content ?? "";
225
242
  const reasoning = assistant?.reasoning ?? null;
226
- const tokens = `prompt=${turn.usage_prompt} completion=${turn.usage_completion} reasoning=${turn.usage_reasoning} cached=${turn.usage_cached}`;
227
- Digest.#providerCosts(turn.usage_cost, `turn ${turn.id}`);
228
- const cost = turn.usage_cost_usd === null
243
+ const accounting = Digest.#accounting(m.requestsByTurn.get(turn.id) ?? []);
244
+ const tokens = Digest.#usageSummary(accounting);
245
+ const cost = accounting === null || accounting.costUsd === null
229
246
  ? " cost=unavailable"
230
- : ` cost=$${turn.usage_cost_usd.toFixed(6)}`;
247
+ : ` cost=$${accounting.costUsd}`;
231
248
  const finishReason = turn.finish_reason ?? "—";
232
249
  // Render only observed constraint metadata; absence makes no claim
233
250
  // about endpoint-owned settings. {§rail-truth-engine-verdict}
@@ -270,13 +287,14 @@ export default class Digest {
270
287
  // every worker has exactly one rollup row — digest_worker_rollups is FROM workers
271
288
  const roll = m.workerRollups.get(worker.id);
272
289
  const opMix = (m.opMixByWorker.get(worker.id) ?? []).map((o) => `${o.op}=${o.n}`).join(" ");
273
- const costStr = roll.total_cost_usd === null ? "unknown" : `$${roll.total_cost_usd.toFixed(6)}`;
290
+ const accounting = Digest.#accounting(m.requestsByWorker.get(worker.id) ?? []);
291
+ const costStr = accounting === null || accounting.costUsd === null ? "unknown" : `$${accounting.costUsd}`;
274
292
  return [
275
293
  `Loops: ${roll.loops}`,
276
294
  `Turns: ${roll.turns}`,
277
295
  `Last turn: ${roll.last_status !== null ? `status=${roll.last_status}` : "(none)"}`,
278
- `Tokens: prompt=${roll.total_prompt} completion=${roll.total_completion} reasoning=${roll.total_reasoning} cached=${roll.total_cached}`,
279
- `Cost: ${costStr} (DB rollup workers.cost_usd=${worker.cost_usd})`,
296
+ `Tokens: ${Digest.#usageSummary(accounting)}`,
297
+ `Cost: ${costStr}`,
280
298
  `Op mix: ${opMix.length > 0 ? opMix : "(no ops)"}`,
281
299
  ].join("\n");
282
300
  }
@@ -286,9 +304,11 @@ export default class Digest {
286
304
  lines.push("");
287
305
  lines.push(`DB: ${m.dbPath}`);
288
306
  const rejectedAttempts = m.turnAttempts.filter((attempt) => attempt.accepted === 0).length;
289
- const erroredAttempts = m.turnAttempts.filter((attempt) => attempt.state === "error").length;
290
- const pendingAttempts = m.turnAttempts.filter((attempt) => attempt.state === "pending").length;
291
- lines.push(`Workspaces: ${m.workspaces.length} Workers: ${m.workers.length} Loops: ${m.loops.length} Turns: ${m.turns.length} Provider attempts: ${m.turnAttempts.length} (${rejectedAttempts} rejected, ${erroredAttempts} errored, ${pendingAttempts} open) Log entries: ${m.logEntries.length}`);
307
+ const erroredCalls = m.modelCalls.filter((call) => call.state === "error").length;
308
+ const pendingCalls = m.modelCalls.filter((call) => call.state === "pending").length;
309
+ const bareCalls = m.modelCalls.filter((call) => call.kind === "bare").length;
310
+ const pendingRequests = m.providerRequests.filter((request) => request.state === "pending").length;
311
+ lines.push(`Workspaces: ${m.workspaces.length} Workers: ${m.workers.length} Loops: ${m.loops.length} Turns: ${m.turns.length} Model calls: ${m.modelCalls.length} (${bareCalls} BARE, ${erroredCalls} errored, ${pendingCalls} open) Emission attempts: ${m.turnAttempts.length} (${rejectedAttempts} rejected) Provider requests: ${m.providerRequests.length} (${pendingRequests} open) Log entries: ${m.logEntries.length}`);
292
312
  lines.push(`Semantic: body=${m.embeddings.body_entries} attached=${m.embeddings.derivation_complete} (vector=${m.embeddings.vector_complete} lexical=${m.embeddings.lexical} excluded=${m.embeddings.excluded} nonsemantic=${m.embeddings.nonsemantic} failed=${m.embeddings.failed}) unattached=${m.embeddings.unfinished} artifacts=${m.embeddings.derivation_artifacts_complete} complete/${m.embeddings.derivation_artifacts_building} building chunks=${m.embeddings.chunk_rows} models=${m.embeddings.models} token-derivations=${m.embeddings.token_derivations}`);
293
313
  if (m.embeddings.dispositions.length > 0) {
294
314
  lines.push("Semantic dispositions:");
@@ -472,59 +492,86 @@ export default class Digest {
472
492
  return JSON.stringify({
473
493
  dbPath: m.dbPath,
474
494
  semantic: m.embeddings,
475
- workspaces: m.workspaces.map((s) => ({ id: s.id, name: s.name, cost_usd: s.cost_usd })),
476
- workers: m.workers.map((r) => ({ id: r.id, workspace_id: r.workspace_id, name: r.name, cost_usd: r.cost_usd })),
495
+ workspaces: m.workspaces.map((s) => ({
496
+ id: s.id,
497
+ name: s.name,
498
+ accounting: Digest.#accounting(m.requestsByWorkspace.get(s.id) ?? []),
499
+ })),
500
+ workers: m.workers.map((r) => ({
501
+ id: r.id,
502
+ workspace_id: r.workspace_id,
503
+ name: r.name,
504
+ accounting: Digest.#accounting(m.requestsByWorker.get(r.id) ?? []),
505
+ })),
477
506
  loops: m.loops.map((l) => ({
478
507
  id: l.id, worker_id: l.worker_id, sequence: l.sequence, status: l.status,
479
508
  prompt: l.prompt, flags: Digest.#parseJson(l.flags, {}),
480
509
  terminated_by: l.terminated_by,
481
510
  result: Digest.#terminalResult(l),
511
+ accounting: Digest.#accounting(m.requestsByLoop.get(l.id) ?? []),
512
+ })),
513
+ turns: m.turns.map((t) => ({
514
+ id: t.id, loop_id: t.loop_id, sequence: t.sequence, status: t.status,
515
+ accounting: Digest.#accounting(m.requestsByTurn.get(t.id) ?? []),
516
+ finish_reason: t.finish_reason, model: t.model,
517
+ attributions: t.packet?.attributions ?? [],
518
+ // Preserve the opaque provider and engine metadata for aggregate
519
+ // tooling. {§meta-passthrough}, {§rail-truth-engine-verdict}
520
+ meta: Digest.#parseJson(t.meta ?? "null", null),
521
+ })),
522
+ model_calls: m.modelCalls.map((call) => ({
523
+ id: call.id,
524
+ turn_id: call.turn_id,
525
+ sequence: call.sequence,
526
+ kind: call.kind,
527
+ state: call.state,
528
+ response: Digest.#parseJson(call.response),
529
+ failure: Digest.#parseJson(call.failure),
530
+ attributions: Digest.#parseJson(call.attributions, []),
531
+ accounting: Digest.#accounting(m.requestsByModelCall.get(call.id) ?? []),
532
+ finish_reason: call.finish_reason,
533
+ model: call.model,
534
+ timestamp: call.timestamp,
535
+ completed_at: call.completed_at,
536
+ turn_attempt_id: call.turn_attempt_id,
537
+ accepted: call.accepted === null ? null : call.accepted === 1,
538
+ parse_errors: Digest.#parseJson(call.parse_errors, []),
539
+ log_entry_id: call.log_entry_id,
540
+ })),
541
+ turn_attempts: m.turnAttempts.map((attempt) => ({
542
+ id: attempt.id,
543
+ turn_id: attempt.turn_id,
544
+ sequence: attempt.sequence,
545
+ state: attempt.state,
546
+ accepted: attempt.accepted === null ? null : attempt.accepted === 1,
547
+ response: Digest.#parseJson(attempt.response),
548
+ failure: Digest.#parseJson(attempt.failure),
549
+ parse_errors: Digest.#parseJson(attempt.parse_errors, []),
550
+ attributions: Digest.#parseJson(attempt.attributions, []),
551
+ accounting: Digest.#accounting(m.requestsByAttempt.get(attempt.id) ?? []),
552
+ finish_reason: attempt.finish_reason,
553
+ model: attempt.model,
554
+ timestamp: attempt.timestamp,
555
+ completed_at: attempt.completed_at,
556
+ })),
557
+ provider_requests: m.providerRequests.map((request) => ({
558
+ id: request.id,
559
+ model_call_id: request.model_call_id,
560
+ turn_attempt_id: request.turn_attempt_id,
561
+ kind: request.kind,
562
+ sequence: request.sequence,
563
+ state: request.state,
564
+ accounting: request.state === "settled"
565
+ ? Digest.#requestAccounting(request)
566
+ : null,
567
+ started_at: request.started_at,
568
+ completed_at: request.completed_at,
482
569
  })),
483
- turns: m.turns.map((t) => {
484
- const costs = Digest.#providerCosts(t.usage_cost, `turn ${t.id}`);
485
- return {
486
- id: t.id, loop_id: t.loop_id, sequence: t.sequence, status: t.status,
487
- usage_prompt: t.usage_prompt, usage_completion: t.usage_completion,
488
- usage_reasoning: t.usage_reasoning, usage_cached: t.usage_cached,
489
- usage_cost: costs,
490
- usage_cost_usd: t.usage_cost_usd,
491
- finish_reason: t.finish_reason, model: t.model,
492
- attributions: t.packet?.attributions ?? [],
493
- // Preserve the opaque provider and engine metadata for aggregate
494
- // tooling. {§meta-passthrough}, {§rail-truth-engine-verdict}
495
- meta: Digest.#parseJson(t.meta ?? "null", null),
496
- };
497
- }),
498
- turn_attempts: m.turnAttempts.map((attempt) => {
499
- const cost = attempt.usage_cost === null
500
- ? null
501
- : Digest.#providerCost(attempt.usage_cost, `attempt ${attempt.id}`);
502
- return {
503
- id: attempt.id,
504
- turn_id: attempt.turn_id,
505
- sequence: attempt.sequence,
506
- state: attempt.state,
507
- accepted: attempt.accepted === null ? null : attempt.accepted === 1,
508
- response: Digest.#parseJson(attempt.response),
509
- failure: Digest.#parseJson(attempt.failure),
510
- parse_errors: Digest.#parseJson(attempt.parse_errors, []),
511
- attributions: Digest.#parseJson(attempt.attributions, []),
512
- usage_prompt: attempt.usage_prompt,
513
- usage_completion: attempt.usage_completion,
514
- usage_reasoning: attempt.usage_reasoning,
515
- usage_cached: attempt.usage_cached,
516
- usage_cost: cost,
517
- usage_cost_usd: attempt.usage_cost_usd,
518
- finish_reason: attempt.finish_reason,
519
- model: attempt.model,
520
- timestamp: attempt.timestamp,
521
- completed_at: attempt.completed_at,
522
- };
523
- }),
524
570
  log_entries: m.logEntries.map((le) => ({
525
571
  id: le.id, worker_id: le.worker_id, loop_id: le.loop_id,
526
572
  turn_id: le.turn_id, sequence: le.sequence, origin: le.origin,
527
- source: le.source, attrs: Digest.#parseJson(le.attrs, {}),
573
+ source: le.source, model_call_id: le.model_call_id,
574
+ attrs: Digest.#parseJson(le.attrs, {}),
528
575
  op: le.op, target: Digest.#renderTarget(le),
529
576
  status_rx: le.status_rx, state: le.state, outcome: le.outcome,
530
577
  ...(Digest.#renderStream(le) === null
@@ -532,6 +579,11 @@ export default class Digest {
532
579
  : { stream: Digest.#renderStream(le) }),
533
580
  ...(le.status_rx >= 400 ? { problem: Digest.#rowProblem(le) } : {}),
534
581
  })),
582
+ log_curation_effects: m.curationEffects.map(({ tags_added, tags_removed, ...effect }) => ({
583
+ ...effect,
584
+ tags_added: Digest.#parseJson(tags_added, []),
585
+ tags_removed: Digest.#parseJson(tags_removed, []),
586
+ })),
535
587
  }, null, 2);
536
588
  }
537
589
  // Default DB path — mirrors the service (`Service.#expandHome(PLURNK_SERVICE_DB_PATH)`; the
@@ -648,33 +700,20 @@ export default class Digest {
648
700
  content: `# Verbatim worker evidence\n\n${JSON.stringify(evidence, null, 2)}\n\n# Audit request\n\n${REQUIEM_PROMPT}`,
649
701
  },
650
702
  ];
651
- const id = String(worker.id);
703
+ const id = worker.provider_identity;
652
704
  const report = {
653
705
  workerId: worker.id,
654
706
  workerName: worker.name,
655
707
  messages,
656
708
  responses: [],
657
709
  calls: [],
658
- usage: { prompt: 0, completion: 0, reasoning: 0, cached: 0, total: 0 },
659
- costs: [],
660
- costUsd: null,
710
+ accounting: aggregateProviderAccounting([]),
661
711
  testimony: null,
662
712
  };
663
713
  reports.push(report);
664
714
  const updateObservedTotals = () => {
665
- report.usage = report.calls.reduce((total, call) => {
666
- if (call.usage === null)
667
- return total;
668
- return {
669
- prompt: total.prompt + call.usage.prompt,
670
- completion: total.completion + call.usage.completion,
671
- reasoning: total.reasoning + call.usage.reasoning,
672
- cached: total.cached + call.usage.cached,
673
- total: total.total + call.usage.total,
674
- };
675
- }, { prompt: 0, completion: 0, reasoning: 0, cached: 0, total: 0 });
676
- report.costs = report.calls.map((call) => call.cost);
677
- report.costUsd = Digest.#costProjection(report.costs);
715
+ const requests = report.calls.flatMap((call) => call.requests.flatMap((request) => request.accounting === null ? [] : [request.accounting]));
716
+ report.accounting = aggregateProviderAccounting(requests);
678
717
  persistReports();
679
718
  };
680
719
  const issue = async (outputTokens) => {
@@ -682,25 +721,62 @@ export default class Digest {
682
721
  openedAt: new Date().toISOString(),
683
722
  completedAt: null,
684
723
  state: "open",
685
- usage: null,
686
- cost: { kind: "unknown", reason: "requiem provider call is open" },
724
+ requests: [],
687
725
  failure: null,
688
726
  };
689
727
  report.calls.push(call);
690
728
  updateObservedTotals();
729
+ const observeRequest = async (identity) => {
730
+ if (identity.provider.length === 0 || identity.model.length === 0) {
731
+ throw new TypeError("requiem provider request identity is incomplete");
732
+ }
733
+ const request = {
734
+ provider: identity.provider,
735
+ model: identity.model,
736
+ openedAt: new Date().toISOString(),
737
+ completedAt: null,
738
+ state: "open",
739
+ accounting: null,
740
+ };
741
+ call.requests.push(request);
742
+ updateObservedTotals();
743
+ return async (value) => {
744
+ if (request.state !== "open")
745
+ throw new Error("requiem provider request settled more than once");
746
+ const accounting = validateProviderRequestAccounting(value);
747
+ if (accounting.provider !== identity.provider || accounting.model !== identity.model) {
748
+ throw new TypeError("requiem provider request settlement changed its identity");
749
+ }
750
+ request.state = "settled";
751
+ request.completedAt = new Date().toISOString();
752
+ request.accounting = accounting;
753
+ updateObservedTotals();
754
+ };
755
+ };
756
+ const assertObserved = (expected) => {
757
+ const observedRequests = call.requests.map((request) => {
758
+ if (request.accounting === null) {
759
+ throw new TypeError("requiem provider returned while a physical request remained open");
760
+ }
761
+ return request.accounting;
762
+ });
763
+ if (!isDeepStrictEqual(expected.map(validateProviderRequestAccounting), observedRequests)) {
764
+ throw new TypeError("requiem provider accounting differs from its observed physical requests");
765
+ }
766
+ };
691
767
  try {
692
768
  const response = await provider.generate({
693
769
  messages,
694
770
  workerId: id,
695
771
  primaryWorkerId: id,
696
- maxTokens: outputTokens,
772
+ maxOutputTokens: outputTokens,
773
+ observeRequest,
697
774
  ...(opts.signal !== undefined ? { signal: opts.signal } : {}),
698
775
  });
776
+ assertObserved(response.accounting);
699
777
  call.state = "response";
700
778
  call.completedAt = new Date().toISOString();
701
- call.usage = response.assistant.usage;
702
- call.cost = providerCostFor(provider, response.assistant.usage, response.charge);
703
- report.responses.push(response);
779
+ report.responses.push(requiemResponseEvidence(response));
704
780
  updateObservedTotals();
705
781
  return response;
706
782
  }
@@ -712,16 +788,11 @@ export default class Digest {
712
788
  : cause instanceof Error
713
789
  ? { name: cause.name, message: cause.message }
714
790
  : String(cause);
715
- if (cause instanceof ProviderError && cause.attempt !== undefined) {
716
- call.usage = cause.attempt.assistant.usage;
717
- call.cost = providerCostFor(provider, cause.attempt.assistant.usage, cause.attempt.charge);
718
- report.responses.push(cause.attempt);
719
- }
720
- else {
721
- call.cost = {
722
- kind: "unknown",
723
- reason: "requiem provider call failed without response-bearing charge evidence",
724
- };
791
+ if (cause instanceof ProviderError) {
792
+ assertObserved(cause.accounting);
793
+ if (cause.attempt !== undefined) {
794
+ report.responses.push(requiemResponseEvidence(cause.attempt));
795
+ }
725
796
  }
726
797
  updateObservedTotals();
727
798
  throw cause;
@@ -743,13 +814,14 @@ export default class Digest {
743
814
  if (resp === undefined)
744
815
  throw new Error(`requiem worker ${worker.id} completed without a provider response`);
745
816
  const testimony = resp.assistant.content.trim()
746
- || `(no testimony - ${resp.assistant.usage.completion} visible completion tokens after ${report.calls.length} provider attempt(s))`;
817
+ || `(no testimony - ${report.accounting.usage?.outputTokens ?? "unknown"} output tokens after ${report.calls.length} provider call(s))`;
747
818
  report.testimony = testimony;
748
819
  persistReports();
749
- const costSummary = report.costUsd === null
820
+ const costSummary = report.accounting.costUsd === null
750
821
  ? "cost USD unavailable"
751
- : `cost USD ${report.costUsd}`;
752
- out.push(`## Worker #${worker.id} - ${worker.name}`, "", `_(${resp.assistant.model}, ${resp.assistant.finishReason ?? "?"}, attempts ${report.calls.length}, prompt ${report.usage.prompt}, completion ${report.usage.completion}, reasoning ${report.usage.reasoning}, cached ${report.usage.cached}, ${costSummary})_`, "", testimony, "");
822
+ : `cost USD ${report.accounting.costUsd}`;
823
+ const physicalRequests = report.calls.reduce((total, call) => total + call.requests.length, 0);
824
+ out.push(`## Worker #${worker.id} - ${worker.name}`, "", `_(${resp.assistant.model}, ${resp.assistant.finishReason ?? "?"}, provider calls ${report.calls.length}, physical requests ${physicalRequests}, ${Digest.#usageSummary(report.accounting)}, ${costSummary})_`, "", testimony, "");
753
825
  }
754
826
  const path = join(digestDir, "requiem.md");
755
827
  writeFileSync(path, out.join("\n"));
@@ -782,8 +854,11 @@ export default class Digest {
782
854
  ...turn,
783
855
  packet: StoredPacket.parse(turn.packet, `digest turn ${turn.id}`),
784
856
  }));
857
+ let modelCalls = db.digest_model_calls.all();
785
858
  let turnAttempts = db.digest_turn_attempts.all();
859
+ let providerRequests = db.digest_provider_requests.all();
786
860
  let logEntries = db.digest_log_entries.all();
861
+ let curationEffects = [];
787
862
  let workerRollupRows = db.digest_worker_rollups.all();
788
863
  let opMixRows = db.digest_worker_op_mix.all();
789
864
  // The semantic-state analytic (owner ask), feature-detected per table: a HISTORICAL
@@ -795,6 +870,14 @@ export default class Digest {
795
870
  const hasColumn = (table, column) => probe.prepare(`SELECT 1 FROM pragma_table_info(?) WHERE name=?`).get(table, column) !== undefined;
796
871
  const one = (q) => Number(probe.prepare(q).get().n);
797
872
  const semanticStateAvailable = has("entries") && has("entry_channels") && hasColumn("entries", "deep_hash");
873
+ if (has("log_curation_effects")) {
874
+ curationEffects = probe.prepare(`
875
+ SELECT operation_log_entry_id, target_log_entry_id, expanded_before,
876
+ tags_added, tags_removed
877
+ FROM log_curation_effects
878
+ ORDER BY operation_log_entry_id, target_log_entry_id
879
+ `).all();
880
+ }
798
881
  const body = semanticStateAvailable
799
882
  ? "FROM entries e JOIN entry_channels ec ON ec.entry_id=e.id AND ec.name='body'"
800
883
  : "";
@@ -837,8 +920,13 @@ export default class Digest {
837
920
  const keptLoopIds = new Set(loops.map((l) => l.id));
838
921
  turns = turns.filter((t) => keptLoopIds.has(t.loop_id));
839
922
  const keptTurnIds = new Set(turns.map((t) => t.id));
923
+ modelCalls = modelCalls.filter((call) => keptTurnIds.has(call.turn_id));
840
924
  turnAttempts = turnAttempts.filter((attempt) => keptTurnIds.has(attempt.turn_id));
925
+ providerRequests = providerRequests.filter((request) => keptTurnIds.has(request.turn_id));
841
926
  logEntries = logEntries.filter((le) => keptTurnIds.has(le.turn_id));
927
+ const keptLogEntryIds = new Set(logEntries.map((entry) => entry.id));
928
+ curationEffects = curationEffects.filter((effect) => keptLogEntryIds.has(effect.operation_log_entry_id)
929
+ && keptLogEntryIds.has(effect.target_log_entry_id));
842
930
  workerRollupRows = workerRollupRows.filter((r) => keptWorkerIds.has(r.worker_id));
843
931
  opMixRows = opMixRows.filter((o) => keptWorkerIds.has(o.worker_id));
844
932
  }
@@ -870,6 +958,27 @@ export default class Digest {
870
958
  arr.push(attempt);
871
959
  attemptsByTurn.set(attempt.turn_id, arr);
872
960
  }
961
+ const requestsByModelCall = new Map();
962
+ const requestsByAttempt = new Map();
963
+ const requestsByTurn = new Map();
964
+ const requestsByLoop = new Map();
965
+ const requestsByWorker = new Map();
966
+ const requestsByWorkspace = new Map();
967
+ const appendRequest = (map, id, request) => {
968
+ const rows = map.get(id) ?? [];
969
+ rows.push(request);
970
+ map.set(id, rows);
971
+ };
972
+ for (const request of providerRequests) {
973
+ appendRequest(requestsByModelCall, request.model_call_id, request);
974
+ if (request.turn_attempt_id !== null) {
975
+ appendRequest(requestsByAttempt, request.turn_attempt_id, request);
976
+ }
977
+ appendRequest(requestsByTurn, request.turn_id, request);
978
+ appendRequest(requestsByLoop, request.loop_id, request);
979
+ appendRequest(requestsByWorker, request.worker_id, request);
980
+ appendRequest(requestsByWorkspace, request.workspace_id, request);
981
+ }
873
982
  const logEntriesByTurn = new Map();
874
983
  for (const le of logEntries) {
875
984
  const arr = logEntriesByTurn.get(le.turn_id) ?? [];
@@ -886,8 +995,10 @@ export default class Digest {
886
995
  opMixByWorker.set(o.worker_id, arr);
887
996
  }
888
997
  const m = {
889
- dbPath, digestDir, workspaces, workers, loops, turns, turnAttempts, logEntries,
890
- workersByWorkspace, loopsByWorker, turnsByLoop, attemptsByTurn, logEntriesByTurn, loopsById, workersById,
998
+ dbPath, digestDir, workspaces, workers, loops, turns, modelCalls, turnAttempts, providerRequests, logEntries, curationEffects,
999
+ workersByWorkspace, loopsByWorker, turnsByLoop, attemptsByTurn,
1000
+ requestsByModelCall, requestsByAttempt, requestsByTurn, requestsByLoop, requestsByWorker, requestsByWorkspace,
1001
+ logEntriesByTurn, loopsById, workersById,
891
1002
  workerRollups, opMixByWorker, embeddings,
892
1003
  };
893
1004
  writeFileSync(join(digestDir, "digest.md"), Digest.#renderWaterfall(m));
@@ -897,7 +1008,7 @@ export default class Digest {
897
1008
  const packetIds = [...new Set(packetFiles.map((f) => f.slice(0, f.indexOf("."))))];
898
1009
  console.log(`digest: wrote ${digestDir}/{digest.md,digest.json,reasoning.md} + ${packetFiles.length} packet section files (${packetIds.join(", ") || "none"})`);
899
1010
  console.log(` source: ${dbPath}`);
900
- console.log(` workspaces=${workspaces.length} workers=${workers.length} loops=${loops.length} turns=${turns.length} turn_attempts=${turnAttempts.length} log_entries=${logEntries.length}`);
1011
+ console.log(` workspaces=${workspaces.length} workers=${workers.length} loops=${loops.length} turns=${turns.length} model_calls=${modelCalls.length} turn_attempts=${turnAttempts.length} provider_requests=${providerRequests.length} log_entries=${logEntries.length} log_curation_effects=${curationEffects.length}`);
901
1012
  }
902
1013
  }
903
1014
  //# sourceMappingURL=Digest.js.map