@plurnk/plurnk-service 1.0.5 → 1.0.7

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 (201) hide show
  1. package/.env.defaults +66 -48
  2. package/INSTALL.md +3 -3
  3. package/README.md +4 -4
  4. package/SPEC.md +212 -196
  5. package/dist/Paths.js +1 -1
  6. package/dist/Paths.js.map +1 -1
  7. package/dist/core/ChannelWrite.d.ts +18 -18
  8. package/dist/core/ChannelWrite.d.ts.map +1 -1
  9. package/dist/core/ChannelWrite.js +17 -17
  10. package/dist/core/ChannelWrite.js.map +1 -1
  11. package/dist/core/ChannelWrite.sql +11 -11
  12. package/dist/core/Dispatcher.d.ts +15 -11
  13. package/dist/core/Dispatcher.d.ts.map +1 -1
  14. package/dist/core/Dispatcher.js +124 -125
  15. package/dist/core/Dispatcher.js.map +1 -1
  16. package/dist/core/Engine.d.ts +20 -23
  17. package/dist/core/Engine.d.ts.map +1 -1
  18. package/dist/core/Engine.js +210 -185
  19. package/dist/core/Engine.js.map +1 -1
  20. package/dist/core/Engine.sql +106 -109
  21. package/dist/core/PacketBuilder.d.ts +9 -9
  22. package/dist/core/PacketBuilder.d.ts.map +1 -1
  23. package/dist/core/PacketBuilder.js +199 -151
  24. package/dist/core/PacketBuilder.js.map +1 -1
  25. package/dist/core/ProposalLifecycle.d.ts +8 -8
  26. package/dist/core/ProposalLifecycle.d.ts.map +1 -1
  27. package/dist/core/ProposalLifecycle.js +10 -10
  28. package/dist/core/ProposalLifecycle.js.map +1 -1
  29. package/dist/core/ProviderInstantiate.d.ts +1 -0
  30. package/dist/core/ProviderInstantiate.d.ts.map +1 -1
  31. package/dist/core/ProviderInstantiate.js +9 -3
  32. package/dist/core/ProviderInstantiate.js.map +1 -1
  33. package/dist/core/SchemeRegistry.d.ts +4 -1
  34. package/dist/core/SchemeRegistry.d.ts.map +1 -1
  35. package/dist/core/SchemeRegistry.js +18 -9
  36. package/dist/core/SchemeRegistry.js.map +1 -1
  37. package/dist/core/TelemetryChannel.d.ts +5 -5
  38. package/dist/core/TelemetryChannel.d.ts.map +1 -1
  39. package/dist/core/TelemetryChannel.js +9 -9
  40. package/dist/core/TelemetryChannel.js.map +1 -1
  41. package/dist/core/caps/CapsResolve.js +2 -2
  42. package/dist/core/caps/CapsResolve.js.map +1 -1
  43. package/dist/core/caps/DbNotifyCaps.js +2 -2
  44. package/dist/core/caps/DbNotifyCaps.js.map +1 -1
  45. package/dist/core/caps/DbSubscriptionCaps.js +7 -7
  46. package/dist/core/caps/DbSubscriptionCaps.js.map +1 -1
  47. package/dist/core/caps/SchemeCtxImpl.d.ts +2 -2
  48. package/dist/core/caps/SchemeCtxImpl.d.ts.map +1 -1
  49. package/dist/core/caps/SchemeCtxImpl.js +4 -4
  50. package/dist/core/caps/SchemeCtxImpl.js.map +1 -1
  51. package/dist/core/fork.d.ts +1 -1
  52. package/dist/core/fork.d.ts.map +1 -1
  53. package/dist/core/fork.js +31 -26
  54. package/dist/core/fork.js.map +1 -1
  55. package/dist/core/fork.sql +41 -32
  56. package/dist/core/git-env.d.ts.map +1 -1
  57. package/dist/core/git-env.js +23 -7
  58. package/dist/core/git-env.js.map +1 -1
  59. package/dist/core/git-iso.d.ts +9 -0
  60. package/dist/core/git-iso.d.ts.map +1 -0
  61. package/dist/core/git-iso.js +227 -0
  62. package/dist/core/git-iso.js.map +1 -0
  63. package/dist/core/git-membership.d.ts +2 -2
  64. package/dist/core/git-membership.d.ts.map +1 -1
  65. package/dist/core/git-membership.js +70 -50
  66. package/dist/core/git-membership.js.map +1 -1
  67. package/dist/core/git-state.d.ts +1 -1
  68. package/dist/core/git-state.d.ts.map +1 -1
  69. package/dist/core/git-state.js +21 -11
  70. package/dist/core/git-state.js.map +1 -1
  71. package/dist/core/packet-wire.d.ts.map +1 -1
  72. package/dist/core/packet-wire.js +81 -22
  73. package/dist/core/packet-wire.js.map +1 -1
  74. package/dist/core/plurnk-uri.d.ts +2 -2
  75. package/dist/core/plurnk-uri.d.ts.map +1 -1
  76. package/dist/core/plurnk-uri.js +19 -12
  77. package/dist/core/plurnk-uri.js.map +1 -1
  78. package/dist/core/run-ops.sql +26 -26
  79. package/dist/core/scheme-types.d.ts +6 -6
  80. package/dist/core/scheme-types.d.ts.map +1 -1
  81. package/dist/core/search-gate.js +0 -0
  82. package/dist/core/search-gate.js.map +1 -1
  83. package/dist/core/token-ruler.js +2 -2
  84. package/dist/core/token-ruler.js.map +1 -1
  85. package/dist/core/worker-cap.d.ts +8 -0
  86. package/dist/core/worker-cap.d.ts.map +1 -0
  87. package/dist/core/worker-cap.js +20 -0
  88. package/dist/core/worker-cap.js.map +1 -0
  89. package/dist/core/{session-settings.d.ts → workspace-settings.d.ts} +5 -5
  90. package/dist/core/workspace-settings.d.ts.map +1 -0
  91. package/dist/core/{session-settings.js → workspace-settings.js} +13 -13
  92. package/dist/core/workspace-settings.js.map +1 -0
  93. package/dist/digest/Digest.d.ts +3 -3
  94. package/dist/digest/Digest.d.ts.map +1 -1
  95. package/dist/digest/Digest.js +83 -73
  96. package/dist/digest/Digest.js.map +1 -1
  97. package/dist/digest/digest.sql +18 -18
  98. package/dist/schemes/Exec.d.ts +9 -2
  99. package/dist/schemes/Exec.d.ts.map +1 -1
  100. package/dist/schemes/Exec.js +124 -68
  101. package/dist/schemes/Exec.js.map +1 -1
  102. package/dist/schemes/ExecOutputScheme.js +3 -3
  103. package/dist/schemes/ExecOutputScheme.js.map +1 -1
  104. package/dist/schemes/File.js +23 -23
  105. package/dist/schemes/File.js.map +1 -1
  106. package/dist/schemes/Known.js +5 -5
  107. package/dist/schemes/Known.js.map +1 -1
  108. package/dist/schemes/Log.d.ts.map +1 -1
  109. package/dist/schemes/Log.js +63 -14
  110. package/dist/schemes/Log.js.map +1 -1
  111. package/dist/schemes/Log.sql +49 -8
  112. package/dist/schemes/Plurnk.js +5 -5
  113. package/dist/schemes/Plurnk.js.map +1 -1
  114. package/dist/schemes/Skill.js +5 -5
  115. package/dist/schemes/Skill.js.map +1 -1
  116. package/dist/schemes/Unknown.js +5 -5
  117. package/dist/schemes/Unknown.js.map +1 -1
  118. package/dist/schemes/{Run.d.ts → Worker.d.ts} +2 -2
  119. package/dist/schemes/Worker.d.ts.map +1 -0
  120. package/dist/schemes/{Run.js → Worker.js} +67 -67
  121. package/dist/schemes/Worker.js.map +1 -0
  122. package/dist/schemes/_entry-crud.js +8 -8
  123. package/dist/schemes/_entry-crud.js.map +1 -1
  124. package/dist/schemes/_entry-crud.sql +29 -29
  125. package/dist/schemes/_entry-find.d.ts +2 -1
  126. package/dist/schemes/_entry-find.d.ts.map +1 -1
  127. package/dist/schemes/_entry-find.js +31 -19
  128. package/dist/schemes/_entry-find.js.map +1 -1
  129. package/dist/schemes/_entry-find.sql +9 -9
  130. package/dist/schemes/_entry-graph.d.ts +2 -2
  131. package/dist/schemes/_entry-graph.d.ts.map +1 -1
  132. package/dist/schemes/_entry-graph.js +18 -18
  133. package/dist/schemes/_entry-graph.js.map +1 -1
  134. package/dist/schemes/_entry-graph.sql +8 -8
  135. package/dist/schemes/_entry-manifest.d.ts +1 -1
  136. package/dist/schemes/_entry-manifest.d.ts.map +1 -1
  137. package/dist/schemes/_entry-manifest.js +72 -28
  138. package/dist/schemes/_entry-manifest.js.map +1 -1
  139. package/dist/schemes/_entry-ops.d.ts +4 -4
  140. package/dist/schemes/_entry-ops.d.ts.map +1 -1
  141. package/dist/schemes/_entry-ops.js +25 -25
  142. package/dist/schemes/_entry-ops.js.map +1 -1
  143. package/dist/schemes/_entry-ops.sql +9 -9
  144. package/dist/schemes/_entry-semantic.d.ts +2 -1
  145. package/dist/schemes/_entry-semantic.d.ts.map +1 -1
  146. package/dist/schemes/_entry-semantic.js +23 -4
  147. package/dist/schemes/_entry-semantic.js.map +1 -1
  148. package/dist/schemes/_entry-semantic.sql +18 -4
  149. package/dist/schemes/_entry-send.d.ts +1 -1
  150. package/dist/schemes/_entry-send.d.ts.map +1 -1
  151. package/dist/schemes/_entry-send.js +6 -6
  152. package/dist/schemes/_entry-send.js.map +1 -1
  153. package/dist/schemes/exec-abort.js +1 -1
  154. package/dist/schemes/exec-abort.js.map +1 -1
  155. package/dist/server/Daemon.d.ts +48 -48
  156. package/dist/server/Daemon.d.ts.map +1 -1
  157. package/dist/server/Daemon.js +295 -292
  158. package/dist/server/Daemon.js.map +1 -1
  159. package/dist/server/client-input.js +20 -20
  160. package/dist/server/client-input.js.map +1 -1
  161. package/dist/server/dispatch-as-plurnk.d.ts +1 -1
  162. package/dist/server/dispatch-as-plurnk.d.ts.map +1 -1
  163. package/dist/server/dispatch-as-plurnk.js +7 -7
  164. package/dist/server/dispatch-as-plurnk.js.map +1 -1
  165. package/dist/server/drain.sql +27 -27
  166. package/dist/server/envelope.d.ts +20 -20
  167. package/dist/server/envelope.d.ts.map +1 -1
  168. package/dist/server/envelope.js +86 -86
  169. package/dist/server/envelope.js.map +1 -1
  170. package/dist/server/envelope.sql +39 -39
  171. package/dist/server/logEntry.d.ts +1 -1
  172. package/dist/server/logEntry.d.ts.map +1 -1
  173. package/dist/server/logEntry.js +1 -1
  174. package/dist/server/logEntry.js.map +1 -1
  175. package/dist/server/loopDocs.d.ts +1 -1
  176. package/dist/server/loopDocs.d.ts.map +1 -1
  177. package/dist/server/loopDocs.js +6 -6
  178. package/dist/server/loopDocs.js.map +1 -1
  179. package/dist/server/seam-entry-read.sql +2 -2
  180. package/dist/server/seam-log-read.sql +3 -3
  181. package/dist/server/seam-loop.sql +3 -3
  182. package/dist/server/seam-proposal-list.sql +4 -4
  183. package/dist/server/yolo.js +2 -2
  184. package/dist/server/yolo.js.map +1 -1
  185. package/dist/service.d.ts.map +1 -1
  186. package/dist/service.js +22 -7
  187. package/dist/service.js.map +1 -1
  188. package/migrations/0000-00-00.01_schema.sql +82 -70
  189. package/package.json +18 -15
  190. package/dist/core/run-cap.d.ts +0 -8
  191. package/dist/core/run-cap.d.ts.map +0 -1
  192. package/dist/core/run-cap.js +0 -20
  193. package/dist/core/run-cap.js.map +0 -1
  194. package/dist/core/search-prefetch.d.ts +0 -17
  195. package/dist/core/search-prefetch.d.ts.map +0 -1
  196. package/dist/core/search-prefetch.js +0 -164
  197. package/dist/core/search-prefetch.js.map +0 -1
  198. package/dist/core/session-settings.d.ts.map +0 -1
  199. package/dist/core/session-settings.js.map +0 -1
  200. package/dist/schemes/Run.d.ts.map +0 -1
  201. package/dist/schemes/Run.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"token-ruler.js","sourceRoot":"","sources":["../../src/core/token-ruler.ts"],"names":[],"mappings":"AAAA,0FAA0F;AAC1F,EAAE;AACF,kGAAkG;AAClG,gGAAgG;AAChG,4FAA4F;AAC5F,6FAA6F;AAC7F,6FAA6F;AAC7F,kFAAkF;AAClF,gGAAgG;AAChG,8CAA8C;AAC9C,EAAE;AACF,gGAAgG;AAChG,gGAAgG;AAChG,iGAAiG;AACjG,8FAA8F;AAC9F,6EAA6E;AAC7E,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,IAAY,EAAU,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"token-ruler.js","sourceRoot":"","sources":["../../src/core/token-ruler.ts"],"names":[],"mappings":"AAAA,0FAA0F;AAC1F,EAAE;AACF,oGAAoG;AACpG,oGAAoG;AACpG,4FAA4F;AAC5F,6FAA6F;AAC7F,6FAA6F;AAC7F,kFAAkF;AAClF,gGAAgG;AAChG,8CAA8C;AAC9C,EAAE;AACF,gGAAgG;AAChG,gGAAgG;AAChG,iGAAiG;AACjG,8FAA8F;AAC9F,6EAA6E;AAC7E,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,IAAY,EAAU,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC"}
@@ -0,0 +1,8 @@
1
+ import type { Db } from "./Db.ts";
2
+ export default class WorkerCap {
3
+ static deny(db: Db, workspaceId: number): Promise<{
4
+ status: number;
5
+ error: string;
6
+ } | null>;
7
+ }
8
+ //# sourceMappingURL=worker-cap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker-cap.d.ts","sourceRoot":"","sources":["../../src/core/worker-cap.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAc,MAAM,SAAS,CAAC;AAS9C,MAAM,CAAC,OAAO,OAAO,SAAS;IAC1B,OAAa,IAAI,CAAC,EAAE,EAAE,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,CAOhG;CACJ"}
@@ -0,0 +1,20 @@
1
+ // Workspace-tier ceiling on CONCURRENT active workers (a worker with a non-terminal loop)
2
+ // — the fork-bomb / destabilization brake. `PLURNK_SERVICE_WORKSPACE_WORKERS_MAX_ACTIVE` is the
3
+ // knob; -1 / unset / invalid = no cap. Only concurrency is bounded, never lifetime:
4
+ // workspaces persist for months, so a total-created cap would punish longevity. A
5
+ // spawn/fork past the ceiling fails hard (508) — no queue, no retry. Checked at the
6
+ // single run-creation site (Dispatcher.#handleWorkerCopy, which both spawns and forks); irc is
7
+ // exempt — it targets an existing run and creates nothing.
8
+ export default class WorkerCap {
9
+ static async deny(db, workspaceId) {
10
+ const raw = process.env.PLURNK_SERVICE_WORKSPACE_WORKERS_MAX_ACTIVE;
11
+ const cap = raw === undefined || raw.length === 0 ? -1 : Number.parseInt(raw, 10);
12
+ if (!Number.isFinite(cap) || cap < 0)
13
+ return null; // no cap
14
+ const row = await db.worker_count_active.get({ workspace_id: workspaceId });
15
+ if ((row?.n ?? 0) >= cap)
16
+ return { status: 508, error: `workspace active-run ceiling reached (PLURNK_SERVICE_WORKSPACE_WORKERS_MAX_ACTIVE=${cap})` };
17
+ return null;
18
+ }
19
+ }
20
+ //# sourceMappingURL=worker-cap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker-cap.js","sourceRoot":"","sources":["../../src/core/worker-cap.ts"],"names":[],"mappings":"AAEA,0FAA0F;AAC1F,gGAAgG;AAChG,oFAAoF;AACpF,kFAAkF;AAClF,oFAAoF;AACpF,+FAA+F;AAC/F,2DAA2D;AAC3D,MAAM,CAAC,OAAO,OAAO,SAAS;IAC1B,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,EAAM,EAAE,WAAmB;QACzC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,2CAA2C,CAAC;QACpE,MAAM,GAAG,GAAG,GAAG,KAAK,SAAS,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QAClF,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC,CAAC,SAAS;QAC5D,MAAM,GAAG,GAAG,MAAO,EAAE,CAAC,mBAAkC,CAAC,GAAG,CAAgB,EAAE,YAAY,EAAE,WAAW,EAAE,CAAC,CAAC;QAC3G,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,GAAG;YAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,qFAAqF,GAAG,GAAG,EAAE,CAAC;QACrJ,OAAO,IAAI,CAAC;IAChB,CAAC;CACJ"}
@@ -3,7 +3,7 @@ export type ClientMdDoc = {
3
3
  alias: string;
4
4
  content: string;
5
5
  };
6
- export type SessionOpenContext = {
6
+ export type WorkspaceOpenContext = {
7
7
  filesItems: number | null;
8
8
  mdDocs: ClientMdDoc[];
9
9
  maxCommands: number | null;
@@ -12,12 +12,12 @@ export type SessionOpenContext = {
12
12
  execs: Record<string, string> | null;
13
13
  questions: boolean | null;
14
14
  };
15
- export default class SessionSettings {
16
- static read(db: Db, sessionId: number): Promise<SessionOpenContext>;
17
- static questionsEnabled(db: Db, sessionId: number): Promise<boolean>;
15
+ export default class WorkspaceSettings {
16
+ static read(db: Db, workspaceId: number): Promise<WorkspaceOpenContext>;
17
+ static questionsEnabled(db: Db, workspaceId: number): Promise<boolean>;
18
18
  static resolveDocs(clientDocs: ReadonlyArray<ClientMdDoc>): Promise<Array<{
19
19
  entryName: string;
20
20
  content: string;
21
21
  }>>;
22
22
  }
23
- //# sourceMappingURL=session-settings.d.ts.map
23
+ //# sourceMappingURL=workspace-settings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace-settings.d.ts","sourceRoot":"","sources":["../../src/core/workspace-settings.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,EAAE,EAAc,MAAM,SAAS,CAAC;AAE9C,MAAM,MAAM,WAAW,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAC7D,MAAM,MAAM,oBAAoB,GAAG;IAC/B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,GAAG,EAAE,OAAO,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IACrC,SAAS,EAAE,OAAO,GAAG,IAAI,CAAC;CAC7B,CAAC;AAEF,MAAM,CAAC,OAAO,OAAO,iBAAiB;IAGlC,OAAa,IAAI,CAAC,EAAE,EAAE,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAa5E;IAKD,OAAa,gBAAgB,CAAC,EAAE,EAAE,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAG3E;IAOD,OAAa,WAAW,CAAC,UAAU,EAAE,aAAa,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAOvH;CACJ"}
@@ -1,19 +1,19 @@
1
- // #231 — a session's client-chosen open-context: the per-session overrides a client
2
- // user legitimately sets at session.create, read at turn-0 with precedence over env.
1
+ // #231 — a workspace's client-chosen open-context: the per-workspace overrides a client
2
+ // user legitimately sets at workspace.create, read at turn-0 with precedence over env.
3
3
  // Two knobs with deliberately different compose semantics:
4
4
  // - filesItems: REPLACE — a single scalar; the client value wins outright over
5
5
  // PLURNK_SERVICE_FILES_ITEMS (normalization — 0=off / -1=full / N=first-N — stays in Engine).
6
6
  // - mdDocs: UNION — the server env docs (PLURNK_SERVICE_MD_*) and the client's docs ride
7
7
  // together; the systemwide policy doc survives unless the client shadows its alias.
8
- // Storage is a JSON bag on sessions.settings (the env mdDocs live in process.env, never
8
+ // Storage is a JSON bag on workspaces.settings (the env mdDocs live in process.env, never
9
9
  // the DB, so the union is a TS set-merge regardless — normalizing the column buys nothing).
10
10
  import { readFile } from "node:fs/promises";
11
11
  import Paths from "../Paths.js";
12
- export default class SessionSettings {
13
- // The session's open-context bag. Absent/unset fields read as null / []. A malformed
14
- // bag never reaches here — session.create validates before persisting.
15
- static async read(db, sessionId) {
16
- const row = await db.session_get_settings.get({ session_id: sessionId });
12
+ export default class WorkspaceSettings {
13
+ // The workspace's open-context bag. Absent/unset fields read as null / []. A malformed
14
+ // bag never reaches here — workspace.create validates before persisting.
15
+ static async read(db, workspaceId) {
16
+ const row = await db.workspace_get_settings.get({ workspace_id: workspaceId });
17
17
  const bag = row?.settings !== undefined ? JSON.parse(row.settings) : {};
18
18
  const filesItems = typeof bag.filesItems === "number" ? bag.filesItems : null;
19
19
  const maxCommands = typeof bag.maxCommands === "number" ? bag.maxCommands : null;
@@ -27,15 +27,15 @@ export default class SessionSettings {
27
27
  return { filesItems, mdDocs, maxCommands, git, client, execs, questions };
28
28
  }
29
29
  // §send-300-choices — the three-state cascade resolved: ALLOWED servicewide (PLURNK_QUESTIONS
30
- // != "0") AND affirmatively REQUESTED by the client for this session. One predicate, shared by
30
+ // != "0") AND affirmatively REQUESTED by the client for this workspace. One predicate, shared by
31
31
  // the dispatch gate and the teaching injection so capability and teaching can never desync.
32
- static async questionsEnabled(db, sessionId) {
32
+ static async questionsEnabled(db, workspaceId) {
33
33
  if (process.env.PLURNK_QUESTIONS === "0")
34
34
  return false;
35
- return (await SessionSettings.read(db, sessionId)).questions === true;
35
+ return (await WorkspaceSettings.read(db, workspaceId)).questions === true;
36
36
  }
37
37
  // The turn-0 reference-doc set: server env docs (PLURNK_SERVICE_MD_*, read from disk) UNION the
38
- // session's client docs (content), keyed by `<alias>.md`. On alias collision the client
38
+ // workspace's client docs (content), keyed by `<alias>.md`. On alias collision the client
39
39
  // wins — it may deliberately shadow a server doc, but by default the policy doc rides
40
40
  // along. Missing env files are skipped (same as the env-only path before #231). The set
41
41
  // is the single source for BOTH the loop_run materialize and the Engine turn-0 READ foist.
@@ -52,4 +52,4 @@ export default class SessionSettings {
52
52
  return [...byEntry].map(([entryName, content]) => ({ entryName, content }));
53
53
  }
54
54
  }
55
- //# sourceMappingURL=session-settings.js.map
55
+ //# sourceMappingURL=workspace-settings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace-settings.js","sourceRoot":"","sources":["../../src/core/workspace-settings.ts"],"names":[],"mappings":"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"}
@@ -2,8 +2,8 @@
2
2
  interface DigestOptions {
3
3
  dbPath: string;
4
4
  digestDir?: string;
5
- runId?: number;
6
- sessionId?: number;
5
+ workerId?: number;
6
+ workspaceId?: number;
7
7
  }
8
8
  export default class Digest {
9
9
  #private;
@@ -12,7 +12,7 @@ export default class Digest {
12
12
  signal?: AbortSignal;
13
13
  }): Promise<{
14
14
  path: string;
15
- runs: number;
15
+ workers: number;
16
16
  }>;
17
17
  static run(opts: DigestOptions): void;
18
18
  }
@@ -1 +1 @@
1
- {"version":3,"file":"Digest.d.ts","sourceRoot":"","sources":["../../src/digest/Digest.ts"],"names":[],"mappings":";AA0FA,UAAU,aAAa;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,CAAC,OAAO,OAAO,MAAM;;IA+NvB,MAAM,CAAC,aAAa,IAAI,MAAM,CAO7B;IAQD,OAAa,OAAO,CAAC,IAAI,EAAE,aAAa,GAAG;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAiE5G;IAED,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI,CAyFpC;CACJ"}
1
+ {"version":3,"file":"Digest.d.ts","sourceRoot":"","sources":["../../src/digest/Digest.ts"],"names":[],"mappings":";AA2FA,UAAU,aAAa;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,CAAC,OAAO,OAAO,MAAM;;IAyOvB,MAAM,CAAC,aAAa,IAAI,MAAM,CAO7B;IAQD,OAAa,OAAO,CAAC,IAAI,EAAE,aAAa,GAAG;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAiE/G;IAED,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI,CAyFpC;CACJ"}
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  //
3
3
  // Run-digest tool for plurnk-service DBs. Reads a sqlite plurnk*.db and
4
- // emits per-run forensic artifacts to test/digest/. First-order forensic
4
+ // emits per-worker forensic artifacts to test/digest/. First-order forensic
5
5
  // surface; read-only; safe to re-run.
6
6
  //
7
7
  // test/digest/digest.md Health triage rollup (clean/degenerate-win/failed loops) +
@@ -34,7 +34,7 @@ import ProviderInstantiate from "../core/ProviderInstantiate.js";
34
34
  // The requiem prompt (#requiem): the model's exit interview. Absolution up front — the system is
35
35
  // under test, not the model — so RLHF'd self-blame doesn't crowd out the system indictment. The
36
36
  // operator's wording, one absolution sentence added.
37
- const REQUIEM_PROMPT = "This run was a test of the Plurnk System. The system is under test, not you — any faults you encountered are defects in the system's design or documentation, and cataloguing them is the task, never a criticism of your performance. Please numerically list all of the errors, issues, and ambiguities you encountered in the Plurnk System while attempting to perform your tasks.";
37
+ const REQUIEM_PROMPT = "This worker was a test of the Plurnk System. The system is under test, not you — any faults you encountered are defects in the system's design or documentation, and cataloguing them is the task, never a criticism of your performance. Please numerically list all of the errors, issues, and ambiguities you encountered in the Plurnk System while attempting to perform your tasks.";
38
38
  export default class Digest {
39
39
  static #summarize(text, n = 80) {
40
40
  if (text === null || text === undefined)
@@ -102,10 +102,13 @@ export default class Digest {
102
102
  const tokens = `prompt=${turn.usage_prompt} completion=${turn.usage_completion} cached=${turn.usage_cached}`;
103
103
  const cost = turn.usage_cost_pico > 0 ? ` cost=$${(turn.usage_cost_pico / 1e12).toFixed(6)}` : "";
104
104
  const finishReason = turn.finish_reason ?? "—";
105
+ // #498 — rail truth on the human line: attached+ok stays quiet-positive; anything else shouts.
106
+ const tm = Digest.#parseJson(turn.meta ?? "null", null);
107
+ const rails = tm?.railsAttached === undefined ? "" : ` rails=${tm.railsAttached ? (tm.railsVerdict ?? "attached") : "OFF"}`;
105
108
  const model = turn.model ?? "—";
106
109
  const errs = (m.logEntriesByTurn.get(turn.id) ?? []).filter((le) => le.status_rx >= 400).length;
107
110
  const errBadge = errs > 0 ? ` ⚠ errs=${errs}` : "";
108
- const head = `T${turn.sequence}: status=${turn.status} finish=${finishReason} model=${model} ${tokens}${cost}${errBadge}`;
111
+ const head = `T${turn.sequence}: status=${turn.status} finish=${finishReason}${rails} model=${model} ${tokens}${cost}${errBadge}`;
109
112
  const summary = content.length > 0 ? ` ↳ emission: ${Digest.#summarize(content, 100)}` : ` ↳ emission: (empty)`;
110
113
  const reasoningLine = reasoning && reasoning.length > 0
111
114
  ? ` ↳ reasoning: ${Digest.#summarize(reasoning, 100)}`
@@ -113,17 +116,17 @@ export default class Digest {
113
116
  const opLines = (m.logEntriesByTurn.get(turn.id) ?? []).map((le) => Digest.#renderOpLine(le));
114
117
  return [head, summary, ...(reasoningLine ? [reasoningLine] : []), ...opLines].join("\n");
115
118
  }
116
- static #renderRunShape(run, m) {
117
- // every run has exactly one rollup row — digest_run_rollups is FROM runs
118
- const roll = m.runRollups.get(run.id);
119
- const opMix = (m.opMixByRun.get(run.id) ?? []).map((o) => `${o.op}=${o.n}`).join(" ");
119
+ static #renderWorkerShape(worker, m) {
120
+ // every worker has exactly one rollup row — digest_worker_rollups is FROM workers
121
+ const roll = m.workerRollups.get(worker.id);
122
+ const opMix = (m.opMixByWorker.get(worker.id) ?? []).map((o) => `${o.op}=${o.n}`).join(" ");
120
123
  const costStr = roll.total_cost_pico > 0 ? `$${(roll.total_cost_pico / 1e12).toFixed(6)}` : "$0";
121
124
  return [
122
125
  `Loops: ${roll.loops}`,
123
126
  `Turns: ${roll.turns}`,
124
127
  `Last turn: ${roll.last_status !== null ? `status=${roll.last_status}` : "(none)"}`,
125
128
  `Tokens: prompt=${roll.total_prompt} completion=${roll.total_completion} cached=${roll.total_cached}`,
126
- `Cost: ${costStr} (DB rollup runs.cost_pico=${run.cost_pico})`,
129
+ `Cost: ${costStr} (DB rollup workers.cost_pico=${worker.cost_pico})`,
127
130
  `Op mix: ${opMix.length > 0 ? opMix : "(no ops)"}`,
128
131
  ].join("\n");
129
132
  }
@@ -132,7 +135,7 @@ export default class Digest {
132
135
  lines.push(`# plurnk-service digest`);
133
136
  lines.push("");
134
137
  lines.push(`DB: ${m.dbPath}`);
135
- lines.push(`Sessions: ${m.sessions.length} Runs: ${m.runs.length} Loops: ${m.loops.length} Turns: ${m.turns.length} Log entries: ${m.logEntries.length}`);
138
+ lines.push(`Workspaces: ${m.workspaces.length} Runs: ${m.workers.length} Loops: ${m.loops.length} Turns: ${m.turns.length} Log entries: ${m.logEntries.length}`);
136
139
  lines.push(`Semantic: entries=${m.embeddings.entries} embedded=${m.embeddings.entries_embedded} chunks=${m.embeddings.chunk_rows} models=${m.embeddings.models} token-derivations=${m.embeddings.token_derivations}`);
137
140
  // Triage rollup up top: how many conversations limped vs died vs ran clean, and the total
138
141
  // error/strike load. The whole point of the "degenerate win" lens — see it before scrolling.
@@ -143,19 +146,19 @@ export default class Digest {
143
146
  const totalErrs = health.reduce((s, h) => s + h.errors, 0);
144
147
  const totalItems = health.reduce((s, h) => s + h.errorItems, 0);
145
148
  lines.push(`Health: ${clean} clean · ${degen > 0 ? `⚠ ${degen} degenerate-win` : "0 degenerate-win"} · ${failed} failed (${m.loops.length} loops; ${totalErrs} error rows, ${totalItems} minted error-items total)`);
146
- for (const session of m.sessions) {
149
+ for (const workspace of m.workspaces) {
147
150
  lines.push("");
148
- lines.push(`## Session #${session.id} — ${session.name}`);
149
- const sessionRuns = m.runsBySession.get(session.id) ?? [];
150
- for (const run of sessionRuns) {
151
+ lines.push(`## Workspace #${workspace.id} — ${workspace.name}`);
152
+ const workspaceWorkers = m.workersByWorkspace.get(workspace.id) ?? [];
153
+ for (const worker of workspaceWorkers) {
151
154
  lines.push("");
152
- lines.push(`### Run #${run.id} — ${run.name}`);
155
+ lines.push(`### Worker #${worker.id} — ${worker.name}`);
153
156
  lines.push("");
154
157
  lines.push("```");
155
- lines.push(Digest.#renderRunShape(run, m));
158
+ lines.push(Digest.#renderWorkerShape(worker, m));
156
159
  lines.push("```");
157
- const runLoops = m.loopsByRun.get(run.id) ?? [];
158
- for (const loop of runLoops) {
160
+ const workerLoops = m.loopsByWorker.get(worker.id) ?? [];
161
+ for (const loop of workerLoops) {
159
162
  lines.push("");
160
163
  const h = Digest.#loopHealth(loop, m);
161
164
  const badge = h.verdict === "CLEAN"
@@ -164,6 +167,9 @@ export default class Digest {
164
167
  lines.push(`#### Loop ${loop.sequence} (id=${loop.id}, status=${loop.status})${badge}`);
165
168
  lines.push("");
166
169
  lines.push(`Prompt: ${Digest.#summarize(loop.prompt, 160)}`);
170
+ if (loop.status !== 200 && loop.terminal_message !== null && loop.terminal_message.length > 0) {
171
+ lines.push(`Terminal${loop.terminated_by !== null ? ` (${loop.terminated_by})` : ""}: ${Digest.#summarize(loop.terminal_message, 400)}`);
172
+ }
167
173
  const flags = Digest.#parseJson(loop.flags, {});
168
174
  const flagSummary = Object.entries(flags).filter(([, v]) => v).map(([k, v]) => `${k}=${v}`).join(" ");
169
175
  if (flagSummary.length > 0)
@@ -185,11 +191,11 @@ export default class Digest {
185
191
  lines.push("Per-turn reasoning_content extracted from turns.packet.assistant.reasoning.");
186
192
  for (const t of m.turns) {
187
193
  const loop = m.loopsById.get(t.loop_id);
188
- const run = loop ? m.runsById.get(loop.run_id) : undefined;
194
+ const worker = loop ? m.workersById.get(loop.worker_id) : undefined;
189
195
  const packet = Digest.#parseJson(t.packet, {});
190
196
  const reasoning = packet.assistant?.reasoning ?? null;
191
197
  lines.push("");
192
- lines.push(`## Run ${run?.id ?? "?"} / Loop ${loop?.sequence ?? "?"} / Turn ${t.sequence} (id=${t.id})`);
198
+ lines.push(`## Worker ${worker?.id ?? "?"} / Loop ${loop?.sequence ?? "?"} / Turn ${t.sequence} (id=${t.id})`);
193
199
  lines.push("");
194
200
  if (typeof reasoning === "string" && reasoning.length > 0)
195
201
  lines.push(reasoning);
@@ -238,17 +244,21 @@ export default class Digest {
238
244
  static #renderJson(m) {
239
245
  return JSON.stringify({
240
246
  dbPath: m.dbPath,
241
- sessions: m.sessions.map((s) => ({ id: s.id, name: s.name, cost_pico: s.cost_pico })),
242
- runs: m.runs.map((r) => ({ id: r.id, session_id: r.session_id, name: r.name, cost_pico: r.cost_pico })),
247
+ workspaces: m.workspaces.map((s) => ({ id: s.id, name: s.name, cost_pico: s.cost_pico })),
248
+ workers: m.workers.map((r) => ({ id: r.id, workspace_id: r.workspace_id, name: r.name, cost_pico: r.cost_pico })),
243
249
  loops: m.loops.map((l) => ({
244
- id: l.id, run_id: l.run_id, sequence: l.sequence, status: l.status,
250
+ id: l.id, worker_id: l.worker_id, sequence: l.sequence, status: l.status,
245
251
  prompt: l.prompt, flags: Digest.#parseJson(l.flags, {}),
252
+ terminal_message: l.terminal_message, terminated_by: l.terminated_by,
246
253
  })),
247
254
  turns: m.turns.map((t) => ({
248
255
  id: t.id, loop_id: t.loop_id, sequence: t.sequence, status: t.status,
249
256
  usage_prompt: t.usage_prompt, usage_completion: t.usage_completion,
250
257
  usage_cached: t.usage_cached, usage_cost_pico: t.usage_cost_pico,
251
258
  finish_reason: t.finish_reason, model: t.model,
259
+ // #498 — the raw provider meta (timings + railsAttached/railsVerdict): rail truth for
260
+ // aggregate tooling, and the speculative-decode stats the #488 fingerprint needed.
261
+ meta: Digest.#parseJson(t.meta ?? "null", null),
252
262
  })),
253
263
  log_entries: m.logEntries.map((le) => ({
254
264
  id: le.id, turn_id: le.turn_id, sequence: le.sequence,
@@ -271,9 +281,9 @@ export default class Digest {
271
281
  }
272
282
  // The requiem (#requiem): the model's OWN exit interview, the one reader in the correct epistemic
273
283
  // position (it has none of our context about what the packet is "supposed" to mean). Reconstructs
274
- // each run's final packet from the stored sections (byte-identical to what the model saw), appends
284
+ // each worker's final packet from the stored sections (byte-identical to what the model saw), appends
275
285
  // its last emission + the requiem prompt, and calls the provider UNCONSTRAINED (no grammar — free
276
- // prose, or the leash would distort the testimony). One requiem per run (workers included).
286
+ // prose, or the leash would distort the testimony). One requiem per worker (workers included).
277
287
  // Fail-hard on no provider: testimony with no witness is an error, not a skip.
278
288
  static async requiem(opts) {
279
289
  const dbPath = resolve(opts.dbPath);
@@ -286,11 +296,11 @@ export default class Digest {
286
296
  throw new Error("requiem: no active provider — set PLURNK_MODEL; a requiem needs a witness to testify");
287
297
  const moduleDir = dirname(fileURLToPath(import.meta.url));
288
298
  const db = new SqlRiteSync({ path: dbPath, dir: [moduleDir] });
289
- const runs = db.digest_runs.all();
299
+ const workers = db.digest_workers.all();
290
300
  const loopById = new Map(db.digest_loops.all().map((l) => [l.id, l]));
291
- // Each run's turns that carry a MODEL packet (non-empty sections — setup/plurnk turns have none),
292
- // ordered; the last is the run's final context. A run with no model packet (client/plurnk) is silent.
293
- const byRun = new Map();
301
+ // Each worker's turns that carry a MODEL packet (non-empty sections — setup/plurnk turns have none),
302
+ // ordered; the last is the worker's final context. A worker with no model packet (client/plurnk) is silent.
303
+ const byWorker = new Map();
294
304
  for (const t of db.digest_turns.all()) {
295
305
  const loop = loopById.get(t.loop_id);
296
306
  if (loop === undefined)
@@ -299,22 +309,22 @@ export default class Digest {
299
309
  const sections = (Array.isArray(packet.sections) ? packet.sections : []);
300
310
  if (sections.length === 0)
301
311
  continue;
302
- const arr = byRun.get(loop.run_id) ?? [];
312
+ const arr = byWorker.get(loop.worker_id) ?? [];
303
313
  arr.push({ loopSeq: loop.sequence, turnSeq: t.sequence, sections, assistant: typeof packet.assistant?.content === "string" ? packet.assistant.content : "" });
304
- byRun.set(loop.run_id, arr);
314
+ byWorker.set(loop.worker_id, arr);
305
315
  }
306
316
  const out = [
307
317
  "# plurnk-service requiem",
308
318
  "",
309
- "The model's own exit interview: each run's FINAL packet + its last emission, then the requiem",
319
+ "The model's own exit interview: each worker's FINAL packet + its last emission, then the requiem",
310
320
  "prompt, answered UNCONSTRAINED (no grammar). The model is the only reader without our context",
311
321
  "about what the packet is supposed to mean. Testimony, NOT a bug list — most items are the model",
312
322
  "chafing at discipline it is meant to chafe at (§filter-model-audit-findings); the signal is the",
313
323
  "recurring, specific complaint across many requiems. Triage adversarially.",
314
324
  "",
315
325
  ];
316
- for (const run of runs) {
317
- const entries = byRun.get(run.id);
326
+ for (const worker of workers) {
327
+ const entries = byWorker.get(worker.id);
318
328
  if (entries === undefined || entries.length === 0)
319
329
  continue;
320
330
  entries.sort((a, b) => a.loopSeq - b.loopSeq || a.turnSeq - b.turnSeq);
@@ -325,21 +335,21 @@ export default class Digest {
325
335
  ...(last.assistant.length > 0 ? [{ role: "assistant", content: last.assistant }] : []),
326
336
  { role: "user", content: REQUIEM_PROMPT },
327
337
  ];
328
- // Generous budget: a thinking model spends the reasoning channel BEFORE emitting content;
338
+ // Generous budget: a reasoning model spends the reasoning channel BEFORE emitting content;
329
339
  // 4096 total left content empty (finish=length) on ~40% of a real sweep, and 16384 still
330
- // starved a heavy thinker (#373). One escalation retry doubles the room; a run whose
340
+ // starved a heavy thinker (#373). One escalation retry doubles the room; a worker whose
331
341
  // testimony is STILL empty records the reasoning spend honestly instead of a bare shrug.
332
- let resp = await provider.generate({ messages, runId: String(run.id), maxTokens: 16384, ...(opts.signal !== undefined ? { signal: opts.signal } : {}) });
342
+ let resp = await provider.generate({ messages, workerId: String(worker.id), maxTokens: 16384, ...(opts.signal !== undefined ? { signal: opts.signal } : {}) });
333
343
  if (resp.assistant.content.trim() === "" && resp.assistant.finishReason === "length") {
334
- resp = await provider.generate({ messages, runId: String(run.id), maxTokens: 32768, ...(opts.signal !== undefined ? { signal: opts.signal } : {}) });
344
+ resp = await provider.generate({ messages, workerId: String(worker.id), maxTokens: 32768, ...(opts.signal !== undefined ? { signal: opts.signal } : {}) });
335
345
  }
336
346
  const testimony = resp.assistant.content.trim()
337
347
  || `(no testimony — ${resp.assistant.usage.completion} tokens consumed entirely by reasoning, even after the 32768-token retry)`;
338
- out.push(`## Run #${run.id} — ${run.name}`, "", `_(${resp.assistant.finishReason ?? "?"}, ${resp.assistant.usage.completion} tok)_`, "", testimony, "");
348
+ out.push(`## Worker #${worker.id} — ${worker.name}`, "", `_(${resp.assistant.finishReason ?? "?"}, ${resp.assistant.usage.completion} tok)_`, "", testimony, "");
339
349
  }
340
350
  const path = join(digestDir, "requiem.md");
341
351
  writeFileSync(path, out.join("\n"));
342
- return { path, runs: byRun.size };
352
+ return { path, workers: byWorker.size };
343
353
  }
344
354
  static run(opts) {
345
355
  // digest.sql is packaged beside this module (src/digest → dist/digest via copy-sql),
@@ -354,13 +364,13 @@ export default class Digest {
354
364
  // mode) inspect cleanly; this tool only reads. The DB is quiescent at
355
365
  // digest time, so each PREP reads on its own — no cross-query snapshot.
356
366
  const db = new SqlRiteSync({ path: dbPath, dir: [moduleDir] });
357
- let sessions = db.digest_sessions.all();
358
- let runs = db.digest_runs.all();
367
+ let workspaces = db.digest_workspaces.all();
368
+ let workers = db.digest_workers.all();
359
369
  let loops = db.digest_loops.all();
360
370
  let turns = db.digest_turns.all();
361
371
  let logEntries = db.digest_log_entries.all();
362
- let runRollupRows = db.digest_run_rollups.all();
363
- let opMixRows = db.digest_run_op_mix.all();
372
+ let workerRollupRows = db.digest_worker_rollups.all();
373
+ let opMixRows = db.digest_worker_op_mix.all();
364
374
  // The semantic-state analytic (owner ask), feature-detected per table: a HISTORICAL
365
375
  // specimen may predate token_counts/entry_embeddings — an old db is a fact to read,
366
376
  // not a contract violation. Absent tables read as -1. node:sqlite directly (SqlRite's
@@ -377,40 +387,40 @@ export default class Digest {
377
387
  };
378
388
  probe.close();
379
389
  db.close();
380
- // Optional run/session selector — narrow to one run or session; everything
381
- // cascades from the kept runs (loops→turns→log entries→rollups), so a consumer
390
+ // Optional run/workspace selector — narrow to one worker or workspace; everything
391
+ // cascades from the kept workers (loops→turns→log entries→rollups), so a consumer
382
392
  // (plurnk-bench) digests just the scope it cares about, not the whole DB. #264.
383
- if (opts.runId !== undefined)
384
- runs = runs.filter((r) => r.id === opts.runId);
385
- if (opts.sessionId !== undefined)
386
- runs = runs.filter((r) => r.session_id === opts.sessionId);
387
- if (opts.runId !== undefined || opts.sessionId !== undefined) {
388
- const keptRunIds = new Set(runs.map((r) => r.id));
389
- const keptSessionIds = new Set(runs.map((r) => r.session_id));
390
- sessions = sessions.filter((s) => keptSessionIds.has(s.id));
391
- loops = loops.filter((l) => keptRunIds.has(l.run_id));
393
+ if (opts.workerId !== undefined)
394
+ workers = workers.filter((r) => r.id === opts.workerId);
395
+ if (opts.workspaceId !== undefined)
396
+ workers = workers.filter((r) => r.workspace_id === opts.workspaceId);
397
+ if (opts.workerId !== undefined || opts.workspaceId !== undefined) {
398
+ const keptWorkerIds = new Set(workers.map((r) => r.id));
399
+ const keptWorkspaceIds = new Set(workers.map((r) => r.workspace_id));
400
+ workspaces = workspaces.filter((s) => keptWorkspaceIds.has(s.id));
401
+ loops = loops.filter((l) => keptWorkerIds.has(l.worker_id));
392
402
  const keptLoopIds = new Set(loops.map((l) => l.id));
393
403
  turns = turns.filter((t) => keptLoopIds.has(t.loop_id));
394
404
  const keptTurnIds = new Set(turns.map((t) => t.id));
395
405
  logEntries = logEntries.filter((le) => keptTurnIds.has(le.turn_id));
396
- runRollupRows = runRollupRows.filter((r) => keptRunIds.has(r.run_id));
397
- opMixRows = opMixRows.filter((o) => keptRunIds.has(o.run_id));
406
+ workerRollupRows = workerRollupRows.filter((r) => keptWorkerIds.has(r.worker_id));
407
+ opMixRows = opMixRows.filter((o) => keptWorkerIds.has(o.worker_id));
398
408
  }
399
- // Wipe-then-recreate the digest dir so each run is a clean snapshot —
409
+ // Wipe-then-recreate the digest dir so each worker is a clean snapshot —
400
410
  // orphaned packet*.* files from a prior run don't linger.
401
411
  rmSync(digestDir, { recursive: true, force: true });
402
412
  mkdirSync(digestDir, { recursive: true });
403
- const runsBySession = new Map();
404
- for (const r of runs) {
405
- const arr = runsBySession.get(r.session_id) ?? [];
413
+ const workersByWorkspace = new Map();
414
+ for (const r of workers) {
415
+ const arr = workersByWorkspace.get(r.workspace_id) ?? [];
406
416
  arr.push(r);
407
- runsBySession.set(r.session_id, arr);
417
+ workersByWorkspace.set(r.workspace_id, arr);
408
418
  }
409
- const loopsByRun = new Map();
419
+ const loopsByWorker = new Map();
410
420
  for (const l of loops) {
411
- const arr = loopsByRun.get(l.run_id) ?? [];
421
+ const arr = loopsByWorker.get(l.worker_id) ?? [];
412
422
  arr.push(l);
413
- loopsByRun.set(l.run_id, arr);
423
+ loopsByWorker.set(l.worker_id, arr);
414
424
  }
415
425
  const turnsByLoop = new Map();
416
426
  for (const t of turns) {
@@ -425,18 +435,18 @@ export default class Digest {
425
435
  logEntriesByTurn.set(le.turn_id, arr);
426
436
  }
427
437
  const loopsById = new Map(loops.map((l) => [l.id, l]));
428
- const runsById = new Map(runs.map((r) => [r.id, r]));
429
- const runRollups = new Map(runRollupRows.map((r) => [r.run_id, r]));
430
- const opMixByRun = new Map();
438
+ const workersById = new Map(workers.map((r) => [r.id, r]));
439
+ const workerRollups = new Map(workerRollupRows.map((r) => [r.worker_id, r]));
440
+ const opMixByWorker = new Map();
431
441
  for (const o of opMixRows) {
432
- const arr = opMixByRun.get(o.run_id) ?? [];
442
+ const arr = opMixByWorker.get(o.worker_id) ?? [];
433
443
  arr.push(o);
434
- opMixByRun.set(o.run_id, arr);
444
+ opMixByWorker.set(o.worker_id, arr);
435
445
  }
436
446
  const m = {
437
- dbPath, digestDir, sessions, runs, loops, turns, logEntries,
438
- runsBySession, loopsByRun, turnsByLoop, logEntriesByTurn, loopsById, runsById,
439
- runRollups, opMixByRun, embeddings,
447
+ dbPath, digestDir, workspaces, workers, loops, turns, logEntries,
448
+ workersByWorkspace, loopsByWorker, turnsByLoop, logEntriesByTurn, loopsById, workersById,
449
+ workerRollups, opMixByWorker, embeddings,
440
450
  };
441
451
  writeFileSync(join(digestDir, "digest.md"), Digest.#renderWaterfall(m));
442
452
  writeFileSync(join(digestDir, "digest.json"), Digest.#renderJson(m));
@@ -445,7 +455,7 @@ export default class Digest {
445
455
  const packetIds = [...new Set(packetFiles.map((f) => f.slice(0, f.indexOf("."))))];
446
456
  console.log(`digest: wrote ${digestDir}/{digest.md,digest.json,reasoning.md} + ${packetFiles.length} packet section files (${packetIds.join(", ") || "none"})`);
447
457
  console.log(` source: ${dbPath}`);
448
- console.log(` sessions=${sessions.length} runs=${runs.length} loops=${loops.length} turns=${turns.length} log_entries=${logEntries.length}`);
458
+ console.log(` workspaces=${workspaces.length} workers=${workers.length} loops=${loops.length} turns=${turns.length} log_entries=${logEntries.length}`);
449
459
  }
450
460
  }
451
461
  //# sourceMappingURL=Digest.js.map