@sema-agent/server 7.144.0 → 7.146.0

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 (305) hide show
  1. package/MIGRATION.md +621 -0
  2. package/README.md +3 -0
  3. package/README.zh-CN.md +1 -0
  4. package/USAGE.md +233 -54
  5. package/deploy/facts/CHANGES.json +25 -0
  6. package/deploy/facts/README.md +57 -0
  7. package/deploy/facts/lanes.json +54 -0
  8. package/deploy/facts/lanes.schema.json +57 -0
  9. package/deploy/facts/sandbox-rbac.json +113 -0
  10. package/deploy/facts/sandbox-rbac.schema.json +74 -0
  11. package/deploy/facts/sealed-key.json +9 -0
  12. package/deploy/facts/sealed-key.schema.json +52 -0
  13. package/deploy/sema-up/chart/templates/_helpers.tpl +19 -0
  14. package/deploy/sema-up/chart/templates/sandbox-rbac.yaml +47 -7
  15. package/deploy/sema-up/chart/templates/server-v4pro.yaml +4 -0
  16. package/deploy/sema-up/chart/templates/server.yaml +4 -0
  17. package/deploy/sema-up/chart/values.yaml +7 -0
  18. package/deploy/sema-up/stack/sema-stack-cluster.yaml +30 -14
  19. package/dist/adoption/import-limits.d.ts +6 -0
  20. package/dist/adoption/import-limits.js +7 -0
  21. package/dist/adoption/import.d.ts +62 -0
  22. package/dist/adoption/import.js +107 -0
  23. package/dist/adoption/wire.d.ts +52 -0
  24. package/dist/adoption/wire.js +9 -0
  25. package/dist/approval-ask-machine.d.ts +2 -2
  26. package/dist/approval-card.d.ts +3 -12
  27. package/dist/approval-card.js +0 -4
  28. package/dist/approval-gate-currency.d.ts +1 -1
  29. package/dist/approval-gate-currency.js +1 -1
  30. package/dist/approval-reconciler.d.ts +1 -1
  31. package/dist/audit.d.ts +2 -21
  32. package/dist/audit.js +0 -9
  33. package/dist/boot/deployment-mods.d.ts +11 -1
  34. package/dist/boot/deployment-mods.js +15 -2
  35. package/dist/boot/device-lane.js +2 -0
  36. package/dist/boot/engine-lease.d.ts +193 -21
  37. package/dist/boot/engine-lease.js +146 -20
  38. package/dist/boot/execution-env.d.ts +4 -0
  39. package/dist/boot/execution-env.js +12 -0
  40. package/dist/boot/exit-boundary.d.ts +50 -0
  41. package/dist/boot/exit-boundary.js +32 -0
  42. package/dist/boot/exit-last.d.ts +20 -4
  43. package/dist/boot/exit-last.js +21 -4
  44. package/dist/boot/governance-seams.js +1 -0
  45. package/dist/boot/memory-consolidation.d.ts +29 -17
  46. package/dist/boot/memory-consolidation.js +26 -15
  47. package/dist/boot/reapers.d.ts +12 -4
  48. package/dist/boot/reapers.js +39 -15
  49. package/dist/boot/resolve-spec.d.ts +2 -0
  50. package/dist/boot/resolve-spec.js +38 -15
  51. package/dist/boot/runner-deps.js +1 -1
  52. package/dist/boot/session-faces.d.ts +1 -1
  53. package/dist/boot/session-faces.js +6 -4
  54. package/dist/boot/shutdown.js +27 -9
  55. package/dist/boot/stage-01-config.js +1 -1
  56. package/dist/boot/stage-03-store-backend.d.ts +1 -0
  57. package/dist/boot/stage-03-store-backend.js +2 -2
  58. package/dist/boot/stage-04-stores.d.ts +1 -0
  59. package/dist/boot/stage-05-execution-env.d.ts +3 -0
  60. package/dist/boot/stage-05-execution-env.js +3 -3
  61. package/dist/boot/stage-06-runners.d.ts +3 -0
  62. package/dist/boot/stage-06-runners.js +11 -10
  63. package/dist/boot/stage-07-capability-layer.d.ts +3 -0
  64. package/dist/boot/stage-07-capability-layer.js +7 -2
  65. package/dist/boot/stage-08-reapers.d.ts +3 -0
  66. package/dist/boot/stage-08-reapers.js +3 -3
  67. package/dist/boot/stage-09-leader.d.ts +4 -1
  68. package/dist/boot/stage-10-http-server.d.ts +4 -1
  69. package/dist/boot/stage-10-http-server.js +4 -3
  70. package/dist/boot/stores.d.ts +1 -0
  71. package/dist/boot/stores.js +35 -19
  72. package/dist/boot/workflow-orchestration.d.ts +1 -1
  73. package/dist/boot/workflow-orchestration.js +2 -1
  74. package/dist/capabilities/composite-members.d.ts +3 -1
  75. package/dist/capabilities/composite-members.js +3 -1
  76. package/dist/capabilities/scenarios.d.ts +2 -2
  77. package/dist/capability-facts.d.ts +18 -1
  78. package/dist/capability-facts.js +25 -7
  79. package/dist/checkpoint-ctx-seal-m28.d.ts +1 -1
  80. package/dist/config-catalog.js +13 -4
  81. package/dist/config-d-migration.d.ts +3 -2
  82. package/dist/config-provider.d.ts +2 -2
  83. package/dist/config-types.d.ts +43 -4
  84. package/dist/config.d.ts +6 -1
  85. package/dist/config.js +67 -11
  86. package/dist/deleted-session-ids.d.ts +19 -0
  87. package/dist/deleted-session-ids.js +26 -0
  88. package/dist/deploy-facts.d.ts +155 -0
  89. package/dist/deploy-facts.js +94 -0
  90. package/dist/device-ws-hub.d.ts +48 -3
  91. package/dist/device-ws-hub.js +48 -2
  92. package/dist/error-code-family.d.ts +35 -0
  93. package/dist/error-code-family.js +26 -0
  94. package/dist/fleet/drain-entry.d.ts +39 -3
  95. package/dist/fleet/drain-entry.js +6 -0
  96. package/dist/fleet/fleet-bus.d.ts +15 -4
  97. package/dist/fleet/fleet-bus.js +17 -3
  98. package/dist/hooks/branch-transcript.d.ts +1 -1
  99. package/dist/hooks/branch-transcript.js +1 -0
  100. package/dist/hooks/classic-stations.d.ts +25 -8
  101. package/dist/hooks/classic-stations.js +29 -3
  102. package/dist/hooks/hook-runner.d.ts +33 -16
  103. package/dist/hooks/hook-runner.js +166 -73
  104. package/dist/hooks/hook-source.d.ts +14 -2
  105. package/dist/hooks/hook-source.js +8 -0
  106. package/dist/http/active-run-conflict.d.ts +1 -1
  107. package/dist/http/active-run-conflict.js +1 -1
  108. package/dist/http/admission.js +13 -2
  109. package/dist/http/checkpoint-ctx.d.ts +60 -71
  110. package/dist/http/checkpoint-ctx.js +35 -40
  111. package/dist/http/dispatch.js +18 -2
  112. package/dist/http/durable-run-leg.d.ts +5 -0
  113. package/dist/http/durable-run-leg.js +8 -6
  114. package/dist/http/execution-context.d.ts +4 -0
  115. package/dist/http/execution-context.js +9 -0
  116. package/dist/http/health-self-report.d.ts +6 -2
  117. package/dist/http/health-self-report.js +3 -2
  118. package/dist/http/memory-compliance-failure-reply.d.ts +1 -1
  119. package/dist/http/memory-compliance-failure-reply.js +2 -15
  120. package/dist/http/memory-control-plane-corrupt-reply.d.ts +25 -0
  121. package/dist/http/memory-control-plane-corrupt-reply.js +19 -0
  122. package/dist/http/principal-gate.d.ts +35 -12
  123. package/dist/http/principal-gate.js +16 -12
  124. package/dist/http/request-guards.d.ts +1 -1
  125. package/dist/http/request-guards.js +5 -5
  126. package/dist/http/resume-legs.d.ts +22 -0
  127. package/dist/http/resume-legs.js +31 -20
  128. package/dist/http/route-ctx.d.ts +26 -10
  129. package/dist/http/route-table.js +2 -0
  130. package/dist/http/routes/adoption.d.ts +11 -0
  131. package/dist/http/routes/adoption.js +80 -6
  132. package/dist/http/routes/agents-roster.js +1 -0
  133. package/dist/http/routes/approvals-assistant.d.ts +20 -1
  134. package/dist/http/routes/approvals-assistant.js +29 -7
  135. package/dist/http/routes/background.js +1 -0
  136. package/dist/http/routes/capabilities.js +4 -1
  137. package/dist/http/routes/diagnostics.d.ts +9 -14
  138. package/dist/http/routes/diagnostics.js +10 -20
  139. package/dist/http/routes/engine.d.ts +31 -0
  140. package/dist/http/routes/engine.js +81 -0
  141. package/dist/http/routes/memory-bundle.js +3 -0
  142. package/dist/http/routes/memory-consolidation.js +3 -5
  143. package/dist/http/routes/memory-origin.d.ts +1 -1
  144. package/dist/http/routes/memory-origin.js +2 -4
  145. package/dist/http/routes/memory-policy.js +5 -1
  146. package/dist/http/routes/observability.js +3 -6
  147. package/dist/http/routes/rules.d.ts +1 -1
  148. package/dist/http/routes/rules.js +5 -2
  149. package/dist/http/routes/runs.js +7 -3
  150. package/dist/http/routes/session-sync.js +24 -25
  151. package/dist/http/routes/sessions-list.d.ts +1 -1
  152. package/dist/http/routes/sessions.js +4 -0
  153. package/dist/http/routes/tasks.js +35 -14
  154. package/dist/http/routes/trace-usage.js +1 -1
  155. package/dist/http/run-concurrency-cap.d.ts +86 -0
  156. package/dist/http/run-concurrency-cap.js +54 -0
  157. package/dist/http/run-meta.d.ts +14 -7
  158. package/dist/http/run-meta.js +5 -2
  159. package/dist/http/server.d.ts +27 -16
  160. package/dist/http/server.js +21 -3
  161. package/dist/http/stored-body-replay.d.ts +9 -6
  162. package/dist/http/stored-body-replay.js +19 -5
  163. package/dist/http/submit-ctx.d.ts +12 -0
  164. package/dist/http/submit-ctx.js +23 -0
  165. package/dist/http/wire-types.d.ts +8 -3
  166. package/dist/json-body-limit.d.ts +8 -0
  167. package/dist/json-body-limit.js +2 -0
  168. package/dist/leader/diffout.d.ts +1 -1
  169. package/dist/local-data-root.d.ts +2 -2
  170. package/dist/memory-operator-faces.d.ts +9 -4
  171. package/dist/memory-scope-stores.d.ts +9 -7
  172. package/dist/memory-scope-stores.js +3 -14
  173. package/dist/memory-scope.d.ts +36 -0
  174. package/dist/memory-scope.js +32 -1
  175. package/dist/net-address.d.ts +53 -0
  176. package/dist/net-address.js +111 -0
  177. package/dist/observability/fail-open.d.ts +28 -12
  178. package/dist/observability/fail-open.js +28 -12
  179. package/dist/orchestration/workflow-notify-journal.js +2 -3
  180. package/dist/org-delivery-gate.js +1 -1
  181. package/dist/peer-directory.d.ts +6 -12
  182. package/dist/peer-directory.js +5 -1
  183. package/dist/plugins/approval-ask-store-file.d.ts +16 -2
  184. package/dist/plugins/approval-ask-store-file.js +21 -1
  185. package/dist/plugins/approval-ask-store-memory.d.ts +8 -2
  186. package/dist/plugins/approval-ask-store-memory.js +11 -8
  187. package/dist/plugins/caching-session-store.d.ts +6 -1
  188. package/dist/plugins/caching-session-store.js +6 -0
  189. package/dist/plugins/checkpoint-store-face.d.ts +426 -0
  190. package/dist/plugins/checkpoint-store-face.js +152 -0
  191. package/dist/plugins/file-run-store.d.ts +13 -7
  192. package/dist/plugins/file-run-store.js +12 -13
  193. package/dist/plugins/fork-routing-session-store.d.ts +6 -2
  194. package/dist/plugins/fork-routing-session-store.js +2 -2
  195. package/dist/plugins/imported-layer-root-retirement.d.ts +10 -8
  196. package/dist/plugins/local-checkpoint-store.d.ts +16 -5
  197. package/dist/plugins/local-checkpoint-store.js +25 -6
  198. package/dist/plugins/local-session-store.d.ts +80 -21
  199. package/dist/plugins/local-session-store.js +79 -22
  200. package/dist/plugins/memory-engine-pg.d.ts +29 -6
  201. package/dist/plugins/memory-engine-pg.js +96 -214
  202. package/dist/plugins/memory-engine-sql-apply.d.ts +121 -0
  203. package/dist/plugins/memory-engine-sql-apply.js +358 -0
  204. package/dist/plugins/memory-engine-tidb.d.ts +35 -9
  205. package/dist/plugins/memory-engine-tidb.js +97 -227
  206. package/dist/plugins/memory-key-guards.d.ts +1 -1
  207. package/dist/plugins/memory-origin-law.d.ts +3 -3
  208. package/dist/plugins/memory-run-store.d.ts +2 -0
  209. package/dist/plugins/memory-run-store.js +6 -0
  210. package/dist/plugins/one-time-migrations.d.ts +32 -56
  211. package/dist/plugins/one-time-migrations.js +27 -119
  212. package/dist/plugins/permission-rule-store-file.d.ts +17 -3
  213. package/dist/plugins/permission-rule-store-file.js +8 -1
  214. package/dist/plugins/pg-pool.js +1 -1
  215. package/dist/plugins/pg-session-storage.d.ts +17 -4
  216. package/dist/plugins/pg-session-storage.js +8 -4
  217. package/dist/plugins/run-store-sql.d.ts +4 -0
  218. package/dist/plugins/run-store-sql.js +5 -2
  219. package/dist/plugins/schema-ensure-legs.js +2 -2
  220. package/dist/plugins/session-fork.d.ts +31 -9
  221. package/dist/plugins/session-fork.js +3 -2
  222. package/dist/plugins/session-placement.d.ts +4 -2
  223. package/dist/plugins/session-store.d.ts +1 -1
  224. package/dist/plugins/sql-json-column.d.ts +5 -1
  225. package/dist/plugins/store-backend.d.ts +11 -8
  226. package/dist/plugins/store-backend.js +8 -7
  227. package/dist/plugins/store-contracts.d.ts +1 -11
  228. package/dist/plugins/store-contracts.js +0 -9
  229. package/dist/plugins/store-sweep.d.ts +63 -0
  230. package/dist/plugins/store-sweep.js +48 -0
  231. package/dist/plugins/tidb-pool.js +1 -1
  232. package/dist/plugins/tidb-session-store.d.ts +16 -3
  233. package/dist/plugins/tidb-session-store.js +8 -4
  234. package/dist/prompt-screen.d.ts +18 -0
  235. package/dist/prompt-screen.js +5 -0
  236. package/dist/rules-consent.d.ts +56 -51
  237. package/dist/rules-consent.js +86 -14
  238. package/dist/run-acceptance.d.ts +40 -10
  239. package/dist/run-acceptance.js +38 -5
  240. package/dist/run-cancel-context.d.ts +4 -1
  241. package/dist/run-cancel-context.js +2 -1
  242. package/dist/runs.js +2 -2
  243. package/dist/sandbox-egress-policy.js +6 -11
  244. package/dist/sandbox-rbac-rules.d.ts +88 -0
  245. package/dist/sandbox-rbac-rules.js +80 -0
  246. package/dist/sealed-key-layout.d.ts +17 -0
  247. package/dist/sealed-key-layout.js +4 -0
  248. package/dist/sealed-key.js +5 -5
  249. package/dist/security.d.ts +48 -9
  250. package/dist/security.js +9 -2
  251. package/dist/session-agent.d.ts +39 -0
  252. package/dist/session-agent.js +16 -0
  253. package/dist/session-error-wire.d.ts +4 -4
  254. package/dist/session-notify-door.d.ts +5 -3
  255. package/dist/session-resident-background.d.ts +57 -0
  256. package/dist/session-resident-background.js +20 -0
  257. package/dist/session-sync-kernel.d.ts +53 -14
  258. package/dist/session-sync-kernel.js +70 -1
  259. package/dist/session-sync.d.ts +5 -3
  260. package/dist/session-workspace.d.ts +1 -0
  261. package/dist/session-workspace.js +1 -0
  262. package/dist/sighup-idle.d.ts +9 -7
  263. package/dist/sighup-idle.js +4 -5
  264. package/dist/spec-fields.d.ts +16 -0
  265. package/dist/spec-fields.js +7 -0
  266. package/dist/stored-request-env-credentials-m38.d.ts +3 -0
  267. package/dist/stored-request-env-credentials-m38.js +8 -0
  268. package/dist/stored-request-prompt-profile-m30.d.ts +1 -1
  269. package/dist/{plugins → stores-sql}/checkpoint-store-sql.d.ts +33 -429
  270. package/dist/{plugins → stores-sql}/checkpoint-store-sql.js +70 -209
  271. package/dist/{plugins → stores-sql}/mailbox-store-sql.d.ts +2 -2
  272. package/dist/{plugins → stores-sql}/mailbox-store-sql.js +4 -4
  273. package/dist/{plugins → stores-sql}/roster-store-sql.d.ts +2 -2
  274. package/dist/{plugins → stores-sql}/roster-store-sql.js +1 -1
  275. package/dist/{plugins → stores-sql}/tool-result-store-sql.d.ts +2 -2
  276. package/dist/{plugins → stores-sql}/tool-result-store-sql.js +6 -6
  277. package/dist/{plugins → stores-sql}/workflow-journal-store-sql.d.ts +7 -12
  278. package/dist/{plugins → stores-sql}/workflow-journal-store-sql.js +3 -8
  279. package/dist/task-settings.d.ts +59 -4
  280. package/dist/task-settings.js +50 -4
  281. package/dist/tool-approval.d.ts +2 -2
  282. package/dist/trace/core-keyset-guard.d.ts +8 -7
  283. package/dist/trace/engine-notice-wire.d.ts +1 -1
  284. package/dist/trace/engine-notice-wire.js +2 -0
  285. package/dist/trace/exit-seat.d.ts +6 -0
  286. package/dist/trace/exit-seat.js +1 -0
  287. package/dist/trace/model-segment.d.ts +3 -2
  288. package/dist/trace/model-segment.js +16 -8
  289. package/dist/trace/project.js +9 -0
  290. package/dist/trace/redact.d.ts +79 -4
  291. package/dist/trace/redact.js +523 -123
  292. package/dist/trace/self-minted-wire.d.ts +391 -1
  293. package/dist/trace/self-minted-wire.js +145 -0
  294. package/dist/trace/wire-projection-faces.d.ts +1 -1
  295. package/dist/trace/wire-projection-faces.js +1 -1
  296. package/dist/usage-analytics.d.ts +4 -0
  297. package/package.json +12 -4
  298. package/scripts/drain-check-local.mjs +8 -29
  299. package/scripts/drain-check.mysql.sql +3 -24
  300. package/scripts/drain-check.pg.sql +3 -24
  301. package/skills/update-config.md +1 -1
  302. package/wire-changes/server@7.145.0.json +146 -0
  303. package/wire-changes/server@7.146.0.json +3221 -0
  304. package/dist/approval-batch-census.d.ts +0 -27
  305. package/dist/approval-batch-census.js +0 -27
@@ -1,14 +1,14 @@
1
1
  import { BaseSessionStorage, type AcquiredSession, type PlacedSessionRow, type SessionHead, type SessionMetadata, type SessionPlacementRecord, type SessionStore, type SessionTreeEntry, type SessionWriteOptions } from "@sema-agent/core";
2
2
  import type { Pool, PoolClient } from "pg";
3
3
  import type { SessionSummary } from "./store-contracts.js";
4
- import { type ClientSessionRowWrite, type ReplaySessionRowWrite, type ReplaySessionRowWriteOutcome, type SessionRowPatch, type SessionRowWriteOutcome } from "../session-row.js";
4
+ import { type ClientSessionRowWrite, type ReplaySessionRowWrite, type ReplaySessionRowWriteOutcome, type SessionRowAnswer, type SessionRowPatch, type SessionRowWriteOutcome } from "../session-row.js";
5
5
  import { type PlacementAcquireOpts } from "./session-placement.js";
6
6
  import { type StagingHandle } from "../session-sync-kernel.js";
7
7
  import { type InstructionSourceBaseline } from "../instruction-source-notice.js";
8
8
  import type { SessionWorkspace } from "../session-workspace.js";
9
9
  import type { RetentionDeclaration } from "@sema-agent/core";
10
10
  import { type IndexSpec } from "./ensure-index.js";
11
- import { type ForkTarget, type ForkToOutcome } from "./session-fork.js";
11
+ import { type ForkPlacement, type ForkTarget, type ForkToOutcome } from "./session-fork.js";
12
12
  import { type ExecutionPlacementSeat } from "../execution-placement.js";
13
13
  /**
14
14
  * PG-dialect DDL for the L1 session log, translated from tidb-pool.ts SCHEMA_STATEMENTS:
@@ -118,6 +118,17 @@ export declare class PgSessionStore implements SessionStore {
118
118
  limit: number;
119
119
  q?: string;
120
120
  }): Promise<SessionSummary[]>;
121
+ /** @see TiDBSessionStore.listSessionRows — the PG twin. */
122
+ listSessionRows(opts: {
123
+ owner?: string;
124
+ includeUnowned?: boolean;
125
+ cursor?: {
126
+ lastActivityAt: string;
127
+ sessionId: string;
128
+ };
129
+ limit: number;
130
+ q?: string;
131
+ }): Promise<Array<SessionRowAnswer<SessionSummary>>>;
121
132
  /** @see TiDBSessionStore.sessionSummaryOf — the PG twin (same projection, run-preview subqueries scoped to the id). */
122
133
  sessionSummaryOf(sessionId: string): Promise<SessionSummary | undefined>;
123
134
  /** @see TiDBSessionStore.listRowOf — the PG twin. */
@@ -143,8 +154,10 @@ export declare class PgSessionStore implements SessionStore {
143
154
  /** @see TiDBSessionStore.recordSessionWorkspace — the PG twin (whole-cell overwrite fenced to the occupant; `pgSafeJsonStringify` + `::jsonb`, like every PG JSON write;
144
155
  * the general query surface — the pool-wide `DB_QUERY_TIMEOUT_MS` server-side `statement_timeout` bounds it, no per-call client timer). */
145
156
  recordSessionWorkspace(sessionId: string, incarnation: string, workspace: SessionWorkspace): Promise<void>;
146
- /** @see TiDBSessionStore.fork — core 契约 `fork(sourceId, owner?)` = {@link forkTo} 的投影(新铸 uuidv7 目标,`forkByFreshId`;三店同一只)。 */
147
- fork(sourceId: string, owner?: string | null): Promise<string | null>;
157
+ /** @see TiDBSessionStore.fork — core 契约 `fork(sourceId, owner?, opts?)` = {@link forkTo} 的投影(新铸 uuidv7 目标,`forkByFreshId`;三店同一只;`opts.placement` 进委派分区)。 */
158
+ fork(sourceId: string, owner?: string | null, opts?: {
159
+ placement?: ForkPlacement;
160
+ }): Promise<string | null>;
148
161
  /** @see TiDBSessionStore.forkTo — fork 续写的店契约(PG 孪生逐字同义):目标行 meta 先、主键排他;撞已在 ⇒ 回滚后在新视图读目标按属主与 fork 键分类;
149
162
  * 读不到 ⇒ 整笔重跑一次,再不行 ⇒ {@link ForkTargetUnsettledError}。 */
150
163
  forkTo(sourceId: string, owner: string | null, target: ForkTarget): Promise<ForkToOutcome | null>;
@@ -320,6 +320,9 @@ export class PgSessionStore {
320
320
  }
321
321
  }
322
322
  async listSessions(opts) {
323
+ return (await this.listSessionRows(opts)).map((a) => a.row);
324
+ }
325
+ async listSessionRows(opts) {
323
326
  const params = [];
324
327
  const ph = (v) => {
325
328
  params.push(v);
@@ -336,7 +339,7 @@ export class PgSessionStore {
336
339
  const sid = ph(opts.cursor.sessionId);
337
340
  where.push(`(sm.updated_at < ${a} OR (sm.updated_at = ${b} AND sm.session_id < ${sid}))`);
338
341
  }
339
- return (await this.selectListRows({ where, params, limit: opts.limit })).map((a) => a.row);
342
+ return this.selectListRows({ where, params, limit: opts.limit });
340
343
  }
341
344
  async sessionSummaryOf(sessionId) {
342
345
  return (await this.listRowOf(sessionId))?.row;
@@ -419,8 +422,8 @@ export class PgSessionStore {
419
422
  async recordSessionWorkspace(sessionId, incarnation, workspace) {
420
423
  await this.pool.query("UPDATE session_meta SET workspace = $1::jsonb WHERE session_id = $2 AND incarnation = $3", [encodeSessionWorkspaceCell(workspace, pgSafeJsonStringify, sessionId), sessionId, incarnation]);
421
424
  }
422
- async fork(sourceId, owner) {
423
- return forkByFreshId((s, o, t) => this.forkTo(s, o, t), sourceId, owner);
425
+ async fork(sourceId, owner, opts) {
426
+ return forkByFreshId((s, o, t) => this.forkTo(s, o, t), sourceId, owner, opts);
424
427
  }
425
428
  async forkTo(sourceId, owner, target) {
426
429
  for (let attempt = 0; attempt < 2; attempt++) {
@@ -448,7 +451,8 @@ export class PgSessionStore {
448
451
  try {
449
452
  await conn.query("BEGIN");
450
453
  try {
451
- await conn.query("INSERT INTO session_meta (session_id, owner, created_at, updated_at, leaf_id, leaf_seq, incarnation, fork_key) VALUES ($1,$2,$3,$4,$5,$6,$7,$8)", [target.id, forkOwner, now, now, forkLeafId, rows.length, mintSessionIncarnation(), target.key ?? null]);
454
+ await conn.query("INSERT INTO session_meta (session_id, owner, created_at, updated_at, leaf_id, leaf_seq, incarnation, fork_key, " +
455
+ "placement_kind, placement_scope, placement_parent_session_id, placement_root_session_id, placement_handle, placed_at) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14)", [target.id, forkOwner, now, now, forkLeafId, rows.length, mintSessionIncarnation(), target.key ?? null, ...placementInsertValues(target.placement, now)]);
452
456
  }
453
457
  catch (err) {
454
458
  if (!isDupKey(err))
@@ -52,6 +52,10 @@ export declare class SqlRunStore {
52
52
  * 续跑与 wake 复用 checkpoint 的会话 —— 所以零行只在「受理之后被删」这一种竞争里出现。
53
53
  */
54
54
  createRun(taskId: string, sessionId: string, owner: string | null, instanceId: string, meta?: NewRunMeta): Promise<CreateRunOutcome>;
55
+ /** run 行预览的**写一次补写**(腿上装着 prompt 屏时建行不写预览,引擎交付可写的那一份之后补;律的属主 = `run-acceptance.ts`):
56
+ * 列仍是 NULL(没建过预览)才写 —— 建行写过的、已经补过的一律不动,所以补写永远盖不掉任何一份既有预览。不碰 `updated_at`(预览不是状态变化,
57
+ * 收割与列表的时序都不该因它移动)。行不在 ⇒ 0 行,无事。PG 臂过 `pgSanitizeText`(与建行写同一条:预览是模型可控标量)。 */
58
+ fillObjectivePreview(taskId: string, preview: string): Promise<void>;
55
59
  /** Request cancel of a RUNNING run (durable, cross-replica). Sets the flag the owning instance's heartbeat
56
60
  * tick polls; a same-replica caller additionally aborts the in-flight controller for instant effect. Only
57
61
  * flags a `running` row (terminal/suspended → 0 affected = no-op). Returns whether a running row was flagged.
@@ -6,7 +6,7 @@ import { pgSanitizeText } from "./pg-safe-json.js";
6
6
  import { toIso as iso } from "./sql-row-helpers.js";
7
7
  import { recordFailOpen } from "../observability/fail-open.js";
8
8
  import { createObjectColumnReader, decodeJsonColumn, decodeNullableJsonColumn, foldJsonColumn, isJsonColumnBodyProblem, jsonColumnText, readJsonObjectColumn, readNonNullJsonValueColumn } from "./sql-json-column.js";
9
- import { RUN_HOLDER_DEATH_CODES, RUN_STALLED_ERROR, readLegacyHolderDeathCode, settledLegObservationOf } from "./store-contracts.js";
9
+ import { RUN_HOLDER_DEATH_CODES, RUN_STALLED_ERROR, settledLegObservationOf } from "./store-contracts.js";
10
10
  import { notifyRunTerminal, runTerminalLogLevel } from "../observability/run-terminal.js";
11
11
  import { mysqlDriver, pgDriver, dialectJsonEncoder, SQL_DB_NOW_MS, sqlDbNowMs } from "./sql-driver.js";
12
12
  import { isDupKeyError } from "./sql-errors.js";
@@ -141,6 +141,9 @@ export class SqlRunStore {
141
141
  conn.release();
142
142
  }
143
143
  }
144
+ async fillObjectivePreview(taskId, preview) {
145
+ await this.db.query(this.q("UPDATE task_run SET objective_preview = ? WHERE task_id = ? AND objective_preview IS NULL", "UPDATE task_run SET objective_preview = $1 WHERE task_id = $2 AND objective_preview IS NULL"), [this.db.dialect === "mysql" ? preview : pgSanitizeText(preview), taskId]);
146
+ }
144
147
  async requestCancel(taskId, owner) {
145
148
  const { affected } = await this.db.query(this.q("UPDATE task_run SET cancel_requested = 1 WHERE task_id = ? AND owner <=> ? AND status = 'running'", "UPDATE task_run SET cancel_requested = 1 WHERE task_id = $1 AND owner IS NOT DISTINCT FROM $2 AND status = 'running'"), [taskId, owner]);
146
149
  return affected > 0;
@@ -307,7 +310,7 @@ export class SqlRunStore {
307
310
  status: r.status,
308
311
  result: refusing("serve a finished run as if it had produced nothing", () => decodeResultColumn(r.result, String(r.task_id))) ?? null,
309
312
  error: r.error ?? null,
310
- errorCode: readLegacyHolderDeathCode({ status: String(r.status), error: r.error ?? null, errorCode: r.error_code ?? null }),
313
+ errorCode: r.error_code ?? null,
311
314
  jobId: r.job_id ?? null,
312
315
  source: r.source ?? null,
313
316
  identity: readRunIdentityCells(r.identity_source, r.identity_scope, r.identity_principal_inc),
@@ -5,9 +5,9 @@ import { ensurePgMemoryEngineSchema } from "./memory-engine-pg.js";
5
5
  import { ensurePgMemoryEmbedderMetaSchema } from "./memory-embedder-fingerprint.js";
6
6
  import { ensureTiDBMemorySyncSchema, ensureTiDBMemoryHistorySchema } from "./memory-sync-store-tidb.js";
7
7
  import { ensurePgMemorySyncSchema, ensurePgMemoryHistorySchema } from "./memory-sync-store-pg.js";
8
- import { ensureTiDBRosterSchema, ensurePgRosterSchema } from "./roster-store-sql.js";
8
+ import { ensureTiDBRosterSchema, ensurePgRosterSchema } from "../stores-sql/roster-store-sql.js";
9
9
  import { ensureTiDBBackgroundAgentSchema, ensurePgBackgroundAgentSchema } from "../stores-sql/background-agent-store-sql.js";
10
- import { ensureTiDBMailboxSchema, ensurePgMailboxSchema } from "./mailbox-store-sql.js";
10
+ import { ensureTiDBMailboxSchema, ensurePgMailboxSchema } from "../stores-sql/mailbox-store-sql.js";
11
11
  import { ensureTiDBTaskAttachmentSchema, ensurePgTaskAttachmentSchema } from "./task-attachment-store.js";
12
12
  import { ensureTiDBUsageWindowSchema, ensurePgUsageWindowSchema } from "../stores-sql/usage-window-store-sql.js";
13
13
  import { ensureTiDBTaskListSchema, ensurePgTaskListSchema } from "./task-list-store-sql.js";
@@ -13,10 +13,15 @@
13
13
  * 吃一只 `Session`):唤醒出来的会话 `getEntry` 在地板之下会回读持久日志,所以判官在压缩过的会话上也看得见整条叶链。
14
14
  *
15
15
  * ## fork 续写的店契约(本文件下半)
16
- * 三店同一只私有体 `forkTo(sourceId, owner, { id, key? })`:目标行原子新建(唯一创建点)· 撞已在按属主与 fork 键分类({@link classifyForkTarget})·
17
- * local 崩溃中间态 `forking` 只许同键重试补齐 · fork 键此后每个写携带(整段换装经 `carriedRowCells`)。core 契约 `fork(sourceId, owner?)` = {@link forkByFreshId}。
16
+ * 三店同一只私有体 `forkTo(sourceId, owner, target)`:目标行原子新建(唯一创建点)· 撞已在按属主与 fork 键分类({@link classifyForkTarget})·
17
+ * local 崩溃中间态 `forking` 只许同键重试补齐 · fork 键此后每个写携带(整段换装经 `carriedRowCells`)。core 契约 `fork(sourceId, owner?, opts?)` = {@link forkByFreshId}。
18
+ *
19
+ * ## fork 进委派分区(core `SessionStore.fork` 的 `opts.placement`)
20
+ * fork 的创建那一笔就是一次建行:带元组的 fork 与带 placement 的 acquire 落**同一组**元组(六列 / 存档行头,first-write 不可变,`placedAt` 由店铸),
21
+ * 此后这一行欠 placed 行的每一条义务(不上宿主列表 · claim 形 acquire 拒 · 进分区枚举 · release = 真删除)。元组不带属主:行的属主仍是 fork 的属主参数
22
+ * 按 `forkOwnerOf` 的那一读(core 句:元组没有属主)。没声明分区的店照常 fork、不落元组(core 句:忽略合法,拒 fork 不合法)。
18
23
  */
19
- import { type ForkPoint, type ForkRowShape, type SessionStorage } from "@sema-agent/core";
24
+ import { type ForkPoint, type ForkRowShape, type SessionStorage, type SessionStore } from "@sema-agent/core";
20
25
  /** 问引擎:这只(已唤醒的)源会话从哪里拷。SQL 孪生用它;local 店手里就是引擎的 `Session`,直接调 `settledForkOptions`。 */
21
26
  export declare function forkPointOf(storage: SessionStorage): Promise<ForkPoint>;
22
27
  /** 两只 SQL 孪生整份日志里的一行(两孪生选同一组列):父链两键交引擎挑行,其余三列原样搬进新会话、不读内容。 */
@@ -33,13 +38,27 @@ export interface ForkLogRow {
33
38
  */
34
39
  export declare const FORK_LOG_ROW_SHAPE: ForkRowShape<ForkLogRow>;
35
40
  /**
36
- * `forkTo` 的目标:调用方给的新会话 id 与(可选)fork 键。`key` 是服务端拼的不透明串(路由按「源 + 调用方 nonce」派生),存进新行那一格
37
- * `fork_key`(SQL)/ `forkKey`(local):一个事实 = 「建这一行的那次 fork 请求的键」。缺 = 非键控 fork(core 契约的现铸形)。
41
+ * fork 进委派分区时落的元组 —— core `SessionStore.fork` 第三参 `opts.placement` 的那一形,**直接取 core 的型、不复述**(core 当前写作 `SessionPlacement` 去掉属主:行的属主是
42
+ * fork 的属主参数的那一读)。本仓里这个元组型只有这一处定义:三店(local · pg · tidb)、路由店、`forkByFreshId`、引擎面声明的 fork 形参,以及建行绑定值
43
+ * `placementInsertValues`(`session-placement.ts`)都读它 —— core 再改 `fork` 的第三参,型面在这里一处跟着动(`SessionStore.fork` 是可选成员,所以取参数前先 `NonNullable`)。
44
+ */
45
+ export type ForkPlacement = NonNullable<NonNullable<Parameters<NonNullable<SessionStore["fork"]>>[2]>["placement"]>;
46
+ /**
47
+ * `forkTo` 的目标:调用方给的新会话 id,外加两者之一(或都没有):
48
+ * · `key` —— 服务端拼的不透明串(路由按「源 + 调用方 nonce」派生),存进新行那一格 `fork_key`(SQL)/ `forkKey`(local):一个事实 = 「建这一行的那次 fork 请求的键」;
49
+ * · `placement` —— 新行落进委派分区的元组(见文件头「fork 进委派分区」)。
50
+ * 两者互斥由型面说:带键的是客户端经路由的 fork(一位用户续一只会话,永不 placed —— placement 只走引擎的可信内部链),placed 的是引擎的 fork(后台 fork 子代的转录,
51
+ * 现铸 id、永不带键)⇒ {@link classifyForkTarget} 的重放判(键相等)永远碰不到 placed 目标。都缺 = 非键控、普通的 fork(core 契约的现铸形)。
38
52
  */
39
- export interface ForkTarget {
53
+ export type ForkTarget = {
40
54
  readonly id: string;
41
55
  readonly key?: string;
42
- }
56
+ readonly placement?: undefined;
57
+ } | {
58
+ readonly id: string;
59
+ readonly placement: ForkPlacement;
60
+ readonly key?: undefined;
61
+ };
43
62
  /**
44
63
  * fork 键的**唯一**拼法(路由带键 ⇒ `forkTo(…, { id: forkId, key: forkKeyOf(源, forkNonce) })`):服务端拼的不透明串 —— 源 id 与调用方 nonce 的摘要,
45
64
  * 定长(远短于 `fork_key` 的 255),源进摘要 ⇒ 「异源同 nonce」拼出不同的键(撞同一只目标即 409,不当重放)。调用方不读它,只靠同键重试同答。
@@ -78,8 +97,11 @@ export declare class ForkTargetUnsettledError extends Error {
78
97
  /** 三店的 `forkTo` 形(私有体,经店面 `forkTo` 交出)。 */
79
98
  export type ForkToFn = (sourceId: string, owner: string | null, target: ForkTarget) => Promise<ForkToOutcome | null>;
80
99
  /**
81
- * core 契约 `fork(sourceId, owner?) → string | null` = `forkTo(…, { id: uuidv7() })` 的投影(三店同一只):`created` ⇒ 新 id;源不在 ⇒ `null`。
100
+ * core 契约 `fork(sourceId, owner?, opts?) → string | null` = `forkTo(…, { id: uuidv7(), placement? })` 的投影(三店同一只):`created` ⇒ 新 id;源不在 ⇒ `null`。
101
+ * `opts.placement` 原样进目标(落不落由店判:声明了分区的店落,没声明的忽略 —— 各店 `forkTo` 一处)。
82
102
  * 新铸的 uuidv7 撞上已在的行(`occupied` / `replayed`)不是一个答案,是不变量破 ⇒ 响亮抛 —— 无键请求永不答 `replayed`。
83
103
  */
84
- export declare function forkByFreshId(forkTo: ForkToFn, sourceId: string, owner: string | null | undefined): Promise<string | null>;
104
+ export declare function forkByFreshId(forkTo: ForkToFn, sourceId: string, owner: string | null | undefined, opts?: {
105
+ placement?: ForkPlacement;
106
+ }): Promise<string | null>;
85
107
  //# sourceMappingURL=session-fork.d.ts.map
@@ -26,9 +26,10 @@ export class ForkTargetUnsettledError extends Error {
26
26
  this.name = "ForkTargetUnsettledError";
27
27
  }
28
28
  }
29
- export async function forkByFreshId(forkTo, sourceId, owner) {
29
+ export async function forkByFreshId(forkTo, sourceId, owner, opts) {
30
30
  const id = uuidv7();
31
- const out = await forkTo(sourceId, owner ?? null, { id });
31
+ const placement = opts?.placement;
32
+ const out = await forkTo(sourceId, owner ?? null, placement !== undefined ? { id, placement } : { id });
32
33
  if (out === null)
33
34
  return null;
34
35
  switch (out.outcome) {
@@ -34,6 +34,7 @@
34
34
  * 店上,带 placement 的 acquire 必须回读到**已持久化**且同一元组身份的行,回读缺失/异组=响亮拒。
35
35
  */
36
36
  import { type PlacedSessionRow, type SessionPlacement, type SessionPlacementRecord, type SessionStore } from "@sema-agent/core";
37
+ import type { ForkPlacement } from "./session-fork.js";
37
38
  /**
38
39
  * 三只持久会话店(SQL 两孪生 + local 后端)的**会话耐久声明**(core `SessionStore.durability`:未 placed 的会话活不活过进程重启)——
39
40
  * **单源**:三处声明(两孪生类上的顶层 `durability`、`LocalBackend` 交给店的顶层 `durability`)与委派分区的 {@link SESSION_PLACEMENTS}
@@ -93,8 +94,9 @@ export declare function placementFromRow(row: PlacementColumns | undefined): Ses
93
94
  * 消费方对它 fail-closed(永不按年龄收割)。一个「无行」与「另一个租户有行」分不清的 join 不许删东西。
94
95
  */
95
96
  export declare function placedRowFromRow(sessionId: string, row: PlacementColumns, lastActivityMs?: number): PlacedSessionRow;
96
- /** 创建时一次落行的六个绑定值(顺序 = {@link PLACEMENT_SELECT_COLUMNS});无 placement ⇒ 六个 NULL。 */
97
- export declare function placementInsertValues(placement: SessionPlacement | undefined, placedAt: Date): [string | null, string | null, string | null, string | null, string | null, Date | null];
97
+ /** 创建时一次落行的六个绑定值(顺序 = {@link PLACEMENT_SELECT_COLUMNS});无 placement ⇒ 六个 NULL。读的只是元组(属主不在元组里 —— 它是行的属主格,建行那一笔另记):
98
+ * 带 placement 的 acquire 与带元组的 fork 两个建行点同调。 */
99
+ export declare function placementInsertValues(placement: ForkPlacement | undefined, placedAt: Date): [string | null, string | null, string | null, string | null, string | null, Date | null];
98
100
  /** `acquire` 的 opts 面(core `SessionStore.acquire` 的第二参,窄成本模块要判的两键)。 */
99
101
  export interface PlacementAcquireOpts {
100
102
  requireExisting?: boolean;
@@ -48,7 +48,7 @@ export declare function ensureSessionDurable(hostStore: SessionStore, sessionId:
48
48
  * durable 委派分区的部署上(stage-07:core `resolveSubagentTranscriptTier(...) === "full"`),那里 core 给每只新起的后台子代铸 placement、
49
49
  * 转录生来就在 host 店 —— 所以这里**只验、不搬、不判属主**:属主是引擎判的(core 7.62.0 [ref] L1:没人派生的 run 读自己那一行、按化身绑一次,原样带给每一层子代;
50
50
  * 复活的子代 = 它自己那一行的属主),placed 行建行那一笔原样记下了它(盒义务 7,三只持久会话店 + `session-ownership.ts` 的 `placedSessionOwnerOf`)。
51
- * 挂点不认领、不推断、不收委派树根见证([ref] P-5;本仓的委派继承律在这一写点退役 —— 存量无主 placed 行:SQL 两店由一次性迁移 M23 在窗内回填,本机文件店没有回填臂,见 `local-session-store.ts` 头注)。
51
+ * 挂点不认领、不推断、不收委派树根见证([ref] P-5;本仓的委派继承律在这一写点退役 —— 存量无主 placed 行:SQL 两店由一次性迁移 M23 在窗内回填,本机文件店没有回填臂;本机文件店的建行自 7.145.0 起格先、存档后,崩在两笔之间不再新生这一形,见 `local-session-store.ts` 头注)。
52
52
  * 0. 宿主店读不出属主(没有 `headOf`,唯一判法 `headReadOf`)⇒ 响亮拒,一次读都不发。
53
53
  * 1. 在不在、有没有真内容:{@link ensureSessionDurable}(`requireExisting` 打开 + 叶子非空 + forget 收尾)。任何失败(不在、空壳、读故障)⇒ 原样上抛 = 否决
54
54
  * (读故障也否决:说不准转录在不在,就不让一次审批押在它上面)。不判它是不是 placed 行:复活的是旧行时 id 取行上的会话(升级前停驻、当年晋升进来的普通行),
@@ -69,7 +69,7 @@ export interface JsonColumnMember {
69
69
  *
70
70
  * 🔴 存在的唯一理由是「别名位在那里不合法」,**不是**给读腿留第二条路:读腿一律用 {@link jsonColumnText}
71
71
  * (别名是它的产物的一部分,理由见那只函数)。今天整仓**三处**消费方,各自的「别名位不合法」都是真的:
72
- * · `one-time-migrations.ts` 的几条改写(M8 / M17 / M18 / M21 的候选扫描与按原文 CAS 回写)——
72
+ * · `one-time-migrations.ts` 的几条改写(M17 / M18 / M21 的候选扫描与按原文 CAS 回写)——
73
73
  * 它们要在 `WHERE` 里判这一列的**文本**,而 `WHERE` 没有别名位;
74
74
  * · `workflow-journal-store-sql.ts` 的 `loadPage`([ref] 车③a)—— 那一列的投影裹在一条
75
75
  * `CASE WHEN LENGTH(result) > ? THEN NULL ELSE <expr> END AS result_json` 里:**别名属于整条 CASE**
@@ -202,6 +202,10 @@ export declare class StoreCorruptError extends Error {
202
202
  /** 出事的坐标(`<table>.<column> for <identity>`,由 {@link SqlJsonColumnError.where} 原样带过来)——
203
203
  * 进 wire 的 `where` 字段,人按它去找那一行。 */
204
204
  readonly where: string;
205
+ /** 构造函数交进来的就是这一只列错误(类型面如实:折码只在体问题上产出本类,见 {@link foldJsonColumn});子类想在同一份因由上再带一格事实时按它再构造,不必断言。 */
206
+ readonly cause: SqlJsonColumnError & {
207
+ readonly problem: SqlJsonColumnBodyProblem;
208
+ };
205
209
  constructor(cause: SqlJsonColumnError & {
206
210
  readonly problem: SqlJsonColumnBodyProblem;
207
211
  }, sentence: string);
@@ -20,7 +20,7 @@ import { TiDBResumeAnchorStore, PgResumeAnchorStore } from "./resume-anchor-stor
20
20
  import { type SessionPolicyStoreLogger } from "../stores-sql/session-policy-store-sql.js";
21
21
  import type { ServiceConfig } from "../config-types.js";
22
22
  import { TiDBRunStore, PgRunStore } from "./run-store-sql.js";
23
- import { TiDBCheckpointStore, PgCheckpointStore } from "./checkpoint-store-sql.js";
23
+ import { TiDBCheckpointStore, PgCheckpointStore } from "../stores-sql/checkpoint-store-sql.js";
24
24
  import { TiDBImageIndex, PgImageIndex } from "./image-index-sql.js";
25
25
  import { TiDBImageBake, PgImageBake } from "./image-bake-store-sql.js";
26
26
  import { TiDBLeaderRunStore, PgLeaderRunStore } from "./leader-run-store-sql.js";
@@ -92,7 +92,7 @@ export type ServiceFileHistoryStore = FileHistoryStore & {
92
92
  * protected — never a synchronous external-store delete). Reaper-driven; SQL twins only. */
93
93
  sweepOrphanBlobs?(): Promise<number>;
94
94
  };
95
- /** SVC-2 (core CORE-7) WorkflowJournalStore — core's interface PLUS the service `deleteByRun` (GC extra). tidb/pg =
95
+ /** SVC-2 (core CORE-7) WorkflowJournalStore — core's interface PLUS the service read page and age sweep below. tidb/pg =
96
96
  * durable SQL twins (cross-replica resume journal); local = the service {@link FileWorkflowJournalStore}
97
97
  * (single-box but RESTART-durable crash-safe JSONL, replacing core's InMemory). The shared equivalence suite keeps
98
98
  * all three byte-matched (scope guard / idempotent-per-ordinal / oversize **refusal** —— [ref] / core 7.12.0
@@ -111,11 +111,14 @@ export type ServiceWorkflowJournalStore = WorkflowJournalStore & {
111
111
  resultJson: string | null;
112
112
  resultBytes: number;
113
113
  }>>;
114
- deleteByRun?(runId: string): Promise<number>;
115
- /** SVC-2 GC sweep (durable twins only): purge entries older than `maxAgeMs` — wired into the reaper to bound the
116
- * TaskResult-bearing table (the per-run deleteByRun has no run-store reap hook). Local InMemory omits it. */
114
+ /** SVC-2 GC sweep (durable twins only): purge entries older than `maxAgeMs` — the `workflow-journal-expired` leg of the backend sweep verb
115
+ * (`plugins/store-sweep.ts`, composed in `boot/stores.ts`) bounds the TaskResult-bearing table with it; the journal's one delete path (no per-run
116
+ * delete: the engine contract never had one). Local File / InMemory omit it. */
117
117
  reapExpired?(now: number, maxAgeMs: number): Promise<number>;
118
118
  };
119
+ /** 本部署有没有 workflow journal 店、是哪一只 —— 唯一读法:开了 self-orchestration 且有后端 ⇒ 后端的 journal 店,否则无。
120
+ * 两处读者同读它:workflow 编排段(交 Runner 与 journal HTTP 读面的那只)与持久层组合点(按龄清扫动词的 `workflow-journal-expired` 腿)。 */
121
+ export declare function workflowJournalOf(config: Pick<ServiceConfig, "selfOrchestrationEnabled">, backend: Pick<StoreBackend, "workflowJournal"> | undefined): ServiceWorkflowJournalStore | undefined;
119
122
  export type ImageIndex = TiDBImageIndex | PgImageIndex;
120
123
  export type ImageBake = TiDBImageBake | PgImageBake;
121
124
  /** [ref] 车4 件1:durable leader-run 登记表的双方言孪生(union 同族——TS 私有字段让具体类名义上不同)。 */
@@ -280,9 +283,9 @@ export interface StoreBackend {
280
283
  * until a 2026-07-25 doc-rot sweep caught it). core 1.145.0 added the seam this comment was waiting for:
281
284
  * `RunnerDeps.workflowJournalStore` (now `@sema-agent/core` `dist/core/types.d.ts`) + `journalStore` on the
282
285
  * run_workflow tool deps (`dist/orchestration/run-workflow-tool.d.ts`), and the boot chain consumes it
283
- * end-to-end: built in `boot/workflow-orchestration.ts` (`const workflowJournalStore = …backend.workflowJournal()`),
284
- * threaded into RunnerDeps (`boot/runner-deps.ts` 的 `workflowJournalStore`), reaped on the maintenance tick
285
- * (`boot/reapers.ts` 同名字段), and read by `GET /v1/workflows/:id/journal` (`http/routes/workflows.ts`, [ref]). */
286
+ * end-to-end: selected by {@link workflowJournalOf} (self-orchestration on ∧ a backend), built in `boot/workflow-orchestration.ts`,
287
+ * threaded into RunnerDeps (`boot/runner-deps.ts` 的 `workflowJournalStore`), aged out by the backend sweep verb's `workflow-journal-expired`
288
+ * leg (`boot/stores.ts`, same selector), and read by `GET /v1/workflows/:id/journal` (`http/routes/workflows.ts`, [ref]). */
286
289
  workflowJournal(): ServiceWorkflowJournalStore;
287
290
  /** [ref] §1 consumption sink for `RunnerDeps.onTaskOutcome` facts. REQUIRED on all backends:
288
291
  * tidb/pg = the outcome-ledger twins (SQL rows, mapped via coreOutcomeToLedgerRow + verbatim core_outcome
@@ -33,7 +33,7 @@ import { FileUserInputLedgerStore } from "./user-input-ledger-file.js";
33
33
  import { TiDBUserInputLedgerStore, PgUserInputLedgerStore } from "./user-input-ledger-sql.js";
34
34
  import { TiDBResumeAnchorStore, PgResumeAnchorStore } from "./resume-anchor-store-sql.js";
35
35
  import { TiDBSessionPolicyStore, PgSessionPolicyStore } from "../stores-sql/session-policy-store-sql.js";
36
- import { TiDBWorkflowJournalStore, PgWorkflowJournalStore } from "./workflow-journal-store-sql.js";
36
+ import { TiDBWorkflowJournalStore, PgWorkflowJournalStore } from "../stores-sql/workflow-journal-store-sql.js";
37
37
  import { TiDBFileHistoryStore, PgFileHistoryStore, FILE_HISTORY_BLOB_TABLE, FILE_HISTORY_BLOB_KEY_PREFIX } from "./file-history-store-sql.js";
38
38
  import { MinioBlobBackend, SqlBlobBackend } from "./blob-backend.js";
39
39
  import { buildDisplaySafeUrl } from "../trace/redact.js";
@@ -41,8 +41,8 @@ import { fileMemoryControlDirFor } from "../memory-scope.js";
41
41
  import { createTidbPool, ensureSchema as ensureTidbSchema } from "./tidb-pool.js";
42
42
  import { createPgPool, ensurePgSchema, pgPoolOptions } from "./pg-pool.js";
43
43
  import { TiDBRunStore, PgRunStore } from "./run-store-sql.js";
44
- import { TiDBCheckpointStore, PgCheckpointStore } from "./checkpoint-store-sql.js";
45
- import { TiDBToolResultStore, PgToolResultStore } from "./tool-result-store-sql.js";
44
+ import { TiDBCheckpointStore, PgCheckpointStore } from "../stores-sql/checkpoint-store-sql.js";
45
+ import { TiDBToolResultStore, PgToolResultStore } from "../stores-sql/tool-result-store-sql.js";
46
46
  import { TiDBImageIndex, PgImageIndex } from "./image-index-sql.js";
47
47
  import { TiDBImageBake, PgImageBake } from "./image-bake-store-sql.js";
48
48
  import { TiDBLeaderRunStore, PgLeaderRunStore } from "./leader-run-store-sql.js";
@@ -59,11 +59,14 @@ import { TiDBAdoptionLogStore, PgAdoptionLogStore } from "./adoption-log-sql.js"
59
59
  import { TiDBRetentionStore, PgRetentionStore, sqlStoreRetention } from "./retention-store-sql.js";
60
60
  import { TiDBRetentionLaneStore, PgRetentionLaneStore } from "./retention-lane-store-sql.js";
61
61
  import { createSqlPermissionRuleStores } from "./permission-rule-store-sql.js";
62
- import { FILE_PERMISSION_RULE_DIR, createFilePermissionRuleStores } from "./permission-rule-store-file.js";
62
+ import { FILE_PERMISSION_RULE_DIR, createFilePermissionRuleStores, fileRuleBucketOwners, isFileBucketDamaged } from "./permission-rule-store-file.js";
63
63
  import { retireImportedLayerRootAllows } from "./imported-layer-root-retirement.js";
64
64
  import { mysqlDriver, pgDriver } from "./sql-driver.js";
65
65
  import { storeDegradedReasonFor } from "../store-posture.js";
66
66
  import { openWithinWait } from "./store-open-wait.js";
67
+ export function workflowJournalOf(config, backend) {
68
+ return config.selfOrchestrationEnabled && backend ? backend.workflowJournal() : undefined;
69
+ }
67
70
  function historyBlobBackend(config, dialect, pool) {
68
71
  const s = config.snapshotBlobStore;
69
72
  if (!s)
@@ -298,17 +301,15 @@ class LocalBackend {
298
301
  await this.sessionStore.settleArchiveSnapshots();
299
302
  }
300
303
  async runOneTimeMigrations() {
301
- const holderDeathCodeRowsBackfilled = this.runStore.migrateLegacyHolderDeathCodes();
302
304
  const archives = await this.sessionStore.migrateLocalArchives();
303
305
  const placements = await backfillLocalExecutionPlacements(this.runStore.sessionIdsWithRuns(), this.sessionStore.executionPlacementBackfill, { lanes: this.executionLanes });
304
306
  const importedLayerRoot = { importedLayerRootAllowsRetired: [], importedLayerRootBucketsSkipped: [] };
305
307
  if (existsSync(join(this.fileBackend.root, FILE_PERMISSION_RULE_DIR))) {
306
308
  const rules = this.permissionRule();
307
- await retireImportedLayerRootAllows({ candidateOwners: () => rules.approvals.importRecordOwners(), durable: rules.durable, isWholeBucketRefusal: () => false, isVanishedBucket: () => false }, importedLayerRoot);
309
+ await retireImportedLayerRootAllows({ candidateOwners: () => fileRuleBucketOwners(rules), durable: rules.durable, isWholeBucketRefusal: isFileBucketDamaged, isVanishedBucket: () => false }, importedLayerRoot);
308
310
  }
309
311
  return {
310
312
  ...NO_SQL_MIGRATIONS,
311
- holderDeathCodeRowsBackfilled,
312
313
  executionPlacementsBackfilled: placements.backfilled,
313
314
  executionPlacementSessionsUnreadable: placements.unreadable,
314
315
  askExecutionLanesBackfilled: backfillLocalAskExecutionLanes(this.approvalAskStore, { lanes: this.executionLanes }),
@@ -168,20 +168,10 @@ export declare const RUN_HOLDER_DEATH_CODES: {
168
168
  readonly orphaned: "run.orphaned";
169
169
  };
170
170
  export type RunHolderDeathCode = (typeof RUN_HOLDER_DEATH_CODES)[keyof typeof RUN_HOLDER_DEATH_CODES];
171
- /**
172
- * 一次性迁移 **M6** 的判据(窗、删法与删后谁接住:窗口见 `plugins/one-time-migrations.ts` 登记表):7.116.0 之前写下的死亡判决行只有句子、码列为空 ——
173
- * 该行应有的码。两处用:local 腿的起服写回(`FileRunStore.migrateLegacyHolderDeathCodes`)与 SQL 两孪生行映射里的**读侧半场**(滚动升级窗里旧副本
174
- * 还会写这种行,起服写回覆盖不到;本函数随 M6 一起删)。只认两句落款的原字节(接管臂括注形按前缀认);码列已有值 / 非 `failed` / 别的句子 ⇒ 原样。
175
- */
176
- export declare function readLegacyHolderDeathCode(row: {
177
- readonly status: string;
178
- readonly error: string | null;
179
- readonly errorCode: string | null;
180
- }): string | null;
181
171
  /**
182
172
  * run 行上是不是一份**持有者死亡判决**:`failed`,且行上的码是 {@link RUN_HOLDER_DEATH_CODES} 之一。判决只说「持有它的那个进程死在这条腿收场之前」,
183
173
  * 不说是谁 —— 持有者是谁读行上的 `instanceId`(判决不改它)。读者 = 用户输入受理账本的收敛判词(`src/user-input-ledger.ts` `judgeOpen`)。
184
- * 旧行(只有句子)由一次性迁移 M6 补上码(起服写回 + SQL 滚动窗的读侧半场,{@link readLegacyHolderDeathCode}),所以这里不再按句子判。
174
+ * 只认码、不按句子判:7.116.0 起两只死亡判决的写者恒同写码;只有句子、码列为空的行(更早的写者铸的)不是死亡判决(随包 MIGRATION 写了)。
185
175
  * 正常失败(别的码 / 无码)/ park 过期收割(`approval.expired`:腿 park 过)都**不是**死亡判决。
186
176
  */
187
177
  export declare function isHolderDeathVerdict(run: Pick<RunRecord, "status" | "errorCode">): boolean;
@@ -8,15 +8,6 @@ export function buildRunOrphanedError(heldBy) {
8
8
  }
9
9
  export const RUN_HOLDER_DEATH_CODES = { stalled: "run.stalled", orphaned: "run.orphaned" };
10
10
  const RUN_HOLDER_DEATH_CODE_WORDS = new Set(Object.values(RUN_HOLDER_DEATH_CODES));
11
- export function readLegacyHolderDeathCode(row) {
12
- if (row.errorCode !== null || row.status !== "failed" || row.error === null)
13
- return row.errorCode;
14
- if (row.error === RUN_STALLED_ERROR)
15
- return RUN_HOLDER_DEATH_CODES.stalled;
16
- if (row.error === RUN_ORPHANED_ERROR || row.error.startsWith(`${RUN_ORPHANED_ERROR} (`))
17
- return RUN_HOLDER_DEATH_CODES.orphaned;
18
- return null;
19
- }
20
11
  export function isHolderDeathVerdict(run) {
21
12
  return run.status === "failed" && run.errorCode !== null && RUN_HOLDER_DEATH_CODE_WORDS.has(run.errorCode);
22
13
  }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * 后端级按龄清扫动词 —— 一个后端的按龄清扫只有一只动词 `sweep({ nowMs })`,答每条腿删了几行。
3
+ *
4
+ * ## 律(一条)
5
+ * 维护 tick 只调这一只动词,不逐店探「有没有某个按龄删的方法」;每条腿的界只有一种读法({@link buildStoreSweepBounds}:部署旋钮 → 引擎的词 `maxAgeMs`,
6
+ * 即工具结果店 `ToolResultStore.reap(now, { maxAgeMs })`、名册 `RosterGcOptions.maxAgeMs` 的那个词;journal 店的按龄删同收 `maxAgeMs`),**每次清扫现读** —— `TOOL_RESULT_TTL_SEC` 是热键,
7
+ * 改了下一拍就按新值删;没给界的腿,或本后端那只座没有按龄删的面,一行不删、答 0。
8
+ * 形照引擎 SQL 后端包的同名动词(`SqlStorageBackend.sweep` / `SqlSweepLeg` / `SqlSweepReceipt`):腿名逐字用它的词。
9
+ *
10
+ * ## 射程(如实)
11
+ * 今天的三条腿:`tool-results-aged`(工具结果店的契约成员 `reap`)、`roster-aged`(名册 SQL 孪生的按龄删;File / 内存名册没有这一面 ⇒ 0)与
12
+ * `workflow-journal-expired`(journal SQL 孪生的按龄删 `reapExpired`,见 {@link createJournalSweepLeg};只在开了 self-orchestration 且有后端时有这只店,
13
+ * core 的 File journal 店没有这一面 ⇒ 0)。引擎包的腿闭集另有 `file-history-orphan-blobs`,随它的座搬进 `src/stores-sql/` 时并进来;信箱的 `reap` 是按收件箱
14
+ * 作用域的契约成员,引擎包的清扫腿里没有它,本动词也不收。实现仍是各座自己的方法;目录搬进引擎包之后,SQL 后端的这只动词就是包的 `sweep`。
15
+ *
16
+ * ## 失败
17
+ * 腿互不牵连:一条腿抛(同步抛或拒)不拦别的腿。全腿成功 ⇒ 答回执;有腿失败 ⇒ 以 {@link StoreSweepError} 拒,带上成功腿的计数与失败腿的原因,
18
+ * 调用方按腿各自上报。
19
+ */
20
+ import type { ServiceConfig } from "../config.js";
21
+ /** 本后端跑的清扫腿(闭集;词与引擎 SQL 后端包的 `SqlSweepLeg` 同字)。 */
22
+ export declare const STORE_SWEEP_LEGS: readonly ["roster-aged", "tool-results-aged", "workflow-journal-expired"];
23
+ export type StoreSweepLeg = (typeof STORE_SWEEP_LEGS)[number];
24
+ /** 一次清扫每条腿删了几行。 */
25
+ export type StoreSweepReceipt = Readonly<Record<StoreSweepLeg, number>>;
26
+ /** 一条腿的界(部署陈述;词 = 引擎的 `maxAgeMs`)。 */
27
+ export interface StoreSweepBound {
28
+ readonly maxAgeMs: number;
29
+ }
30
+ /** 各腿的界(缺一条 = 那条腿不删)。 */
31
+ export type StoreSweepBounds = Readonly<Partial<Record<StoreSweepLeg, StoreSweepBound>>>;
32
+ /**
33
+ * 部署旋钮 → 各腿的界:本仓唯一的读法。`TOOL_RESULT_TTL_SEC`(秒,> 0,热键)→ `tool-results-aged`;`ROSTER_RETENTION_MS`(毫秒,下限 1 分钟)→ `roster-aged`;
34
+ * `WORKFLOW_JOURNAL_RETENTION_MS`(毫秒,下限 1 分钟)→ `workflow-journal-expired`。
35
+ * 组合点把它包成读法函数交给 {@link createStoreSweep},每次清扫现读一次 —— 交起服那一刻的值会让热发布的新界不生效。
36
+ */
37
+ export declare function buildStoreSweepBounds(config: Pick<ServiceConfig, "toolResultTtlSec" | "rosterRetentionMs" | "workflowJournalRetentionMs">): StoreSweepBounds;
38
+ /** 一条腿的实现:删掉存了超过 `bound.maxAgeMs` 的行,答删了几行。 */
39
+ export type StoreSweepLegRun = (nowMs: number, bound: StoreSweepBound) => Promise<number> | number;
40
+ /**
41
+ * journal 店的按龄删 → `workflow-journal-expired` 腿:截止线 = 动词交来的 `nowMs` − 动词交来的 `maxAgeMs`(界每次清扫现读,腿不自存)。
42
+ * 店没有这一面(core 的 File / 内存 journal 店)或没有店(self-orchestration 关 / 无后端)⇒ 无腿,动词答 0。
43
+ */
44
+ export declare function createJournalSweepLeg(store: {
45
+ reapExpired?(now: number, maxAgeMs: number): Promise<number>;
46
+ } | undefined): StoreSweepLegRun | undefined;
47
+ export interface StoreSweep {
48
+ sweep(input: {
49
+ readonly nowMs: number;
50
+ }): Promise<StoreSweepReceipt>;
51
+ }
52
+ /** 有腿失败的那次清扫:`receipt` 里失败腿记 0,`failed` 按腿带原因。 */
53
+ export declare class StoreSweepError extends Error {
54
+ readonly receipt: StoreSweepReceipt;
55
+ readonly failed: ReadonlyMap<StoreSweepLeg, unknown>;
56
+ constructor(receipt: StoreSweepReceipt, failed: ReadonlyMap<StoreSweepLeg, unknown>);
57
+ }
58
+ export declare function createStoreSweep(spec: {
59
+ readonly legs: Readonly<Partial<Record<StoreSweepLeg, StoreSweepLegRun>>>;
60
+ /** 界的读法:每次清扫调一次(只收函数 —— 起服那一刻的界对象交不进来)。 */
61
+ readonly bounds: () => StoreSweepBounds;
62
+ }): StoreSweep;
63
+ //# sourceMappingURL=store-sweep.d.ts.map
@@ -0,0 +1,48 @@
1
+ export const STORE_SWEEP_LEGS = ["roster-aged", "tool-results-aged", "workflow-journal-expired"];
2
+ export function buildStoreSweepBounds(config) {
3
+ return {
4
+ "roster-aged": { maxAgeMs: config.rosterRetentionMs },
5
+ "tool-results-aged": { maxAgeMs: config.toolResultTtlSec * 1000 },
6
+ "workflow-journal-expired": { maxAgeMs: config.workflowJournalRetentionMs },
7
+ };
8
+ }
9
+ export function createJournalSweepLeg(store) {
10
+ const reap = store?.reapExpired?.bind(store);
11
+ return reap === undefined ? undefined : (nowMs, { maxAgeMs }) => reap(nowMs, maxAgeMs);
12
+ }
13
+ export class StoreSweepError extends Error {
14
+ receipt;
15
+ failed;
16
+ constructor(receipt, failed) {
17
+ super(`store sweep: leg(s) ${[...failed.keys()].join(", ")} failed`);
18
+ this.receipt = receipt;
19
+ this.failed = failed;
20
+ this.name = "StoreSweepError";
21
+ }
22
+ }
23
+ export function createStoreSweep(spec) {
24
+ const runLeg = async (leg, nowMs, bounds) => {
25
+ const run = spec.legs[leg];
26
+ const bound = bounds[leg];
27
+ if (run === undefined || bound === undefined)
28
+ return { leg, removed: 0 };
29
+ try {
30
+ return { leg, removed: await run(nowMs, bound) };
31
+ }
32
+ catch (error) {
33
+ return { leg, error };
34
+ }
35
+ };
36
+ return {
37
+ async sweep({ nowMs }) {
38
+ const bounds = spec.bounds();
39
+ const settled = await Promise.all(STORE_SWEEP_LEGS.map((leg) => runLeg(leg, nowMs, bounds)));
40
+ const receipt = Object.fromEntries(settled.map((s) => [s.leg, "removed" in s ? s.removed : 0]));
41
+ const failed = new Map(settled.flatMap((s) => ("error" in s ? [[s.leg, s.error]] : [])));
42
+ if (failed.size > 0)
43
+ throw new StoreSweepError(receipt, failed);
44
+ return receipt;
45
+ },
46
+ };
47
+ }
48
+ //# sourceMappingURL=store-sweep.js.map
@@ -16,7 +16,7 @@ import { PRINCIPAL_MAX_UTF8_BYTES } from "../security.js";
16
16
  import { ensureIndexes } from "./ensure-index.js";
17
17
  import { CENTRAL_SCHEMA_INDEXES } from "./schema-indexes.js";
18
18
  import { SESSION_INDEXES } from "./pg-session-storage.js";
19
- import { TOOL_RESULT_INDEXES } from "./tool-result-store-sql.js";
19
+ import { TOOL_RESULT_INDEXES } from "../stores-sql/tool-result-store-sql.js";
20
20
  import { COST_QUOTA_INDEXES } from "./pg-cost-quota.js";
21
21
  import { RATE_LIMIT_INDEXES } from "./pg-rate-limiter.js";
22
22
  import { OUTCOME_LEDGER_INDEXES } from "./outcome-ledger-sql.js";
@@ -16,10 +16,10 @@ import { type InstructionSourceBaseline } from "../instruction-source-notice.js"
16
16
  import type { SessionWorkspace } from "../session-workspace.js";
17
17
  import type { RetentionDeclaration } from "@sema-agent/core";
18
18
  import type { SessionSummary } from "./store-contracts.js";
19
- import { type ClientSessionRowWrite, type ReplaySessionRowWrite, type ReplaySessionRowWriteOutcome, type SessionRowPatch, type SessionRowWriteOutcome } from "../session-row.js";
19
+ import { type ClientSessionRowWrite, type ReplaySessionRowWrite, type ReplaySessionRowWriteOutcome, type SessionRowAnswer, type SessionRowPatch, type SessionRowWriteOutcome } from "../session-row.js";
20
20
  import { type PlacementAcquireOpts } from "./session-placement.js";
21
21
  import { type StagingHandle } from "../session-sync-kernel.js";
22
- import { type ForkTarget, type ForkToOutcome } from "./session-fork.js";
22
+ import { type ForkPlacement, type ForkTarget, type ForkToOutcome } from "./session-fork.js";
23
23
  import { type ExecutionPlacementSeat } from "../execution-placement.js";
24
24
  export declare class TiDBSessionStore implements SessionStore {
25
25
  private readonly pool;
@@ -130,6 +130,17 @@ export declare class TiDBSessionStore implements SessionStore {
130
130
  limit: number;
131
131
  q?: string;
132
132
  }): Promise<SessionSummary[]>;
133
+ /** {@link listSessions} with each row's occupant (owner + incarnation) from the same SELECT — `OwnerAwareSessionStore.listSessionRows`; the incarnation never enters the row. */
134
+ listSessionRows(opts: {
135
+ owner?: string;
136
+ includeUnowned?: boolean;
137
+ cursor?: {
138
+ lastActivityAt: string;
139
+ sessionId: string;
140
+ };
141
+ limit: number;
142
+ q?: string;
143
+ }): Promise<Array<SessionRowAnswer<SessionSummary>>>;
133
144
  /** ONE session's list row — {@link listSessions}' projection (same SELECT, same mapper) scoped to one id; `undefined` = not a
134
145
  * row of the list face (no such session / placed). The run-preview subqueries are scoped to the same id (an index read on
135
146
  * `task_run.session_id`, not the whole-ledger aggregate the list pays). */
@@ -194,7 +205,9 @@ export declare class TiDBSessionStore implements SessionStore {
194
205
  * We instead SELECT the entire `session_event` log ordered by seq — every durable entry, compaction floor
195
206
  * bypassed — and re-seq it dense from 0 into the new session, ids/parents/payload byte-verbatim.
196
207
  */
197
- fork(sourceId: string, owner?: string | null): Promise<string | null>;
208
+ fork(sourceId: string, owner?: string | null, opts?: {
209
+ placement?: ForkPlacement;
210
+ }): Promise<string | null>;
198
211
  /**
199
212
  * **fork 续写的店契约**(三店同一只私有体;core `fork` 是它的投影):把源会话的整份历史拷进调用方给的目标 id。
200
213
  * 1. **原子目标创建**:事务首句 `INSERT INTO session_meta (…, fork_key) VALUES (:target, …)`,主键即排他(meta 先,随后拷事件、COMMIT);