@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
@@ -152,6 +152,9 @@ export class TiDBSessionStore {
152
152
  }
153
153
  }
154
154
  async listSessions(opts) {
155
+ return (await this.listSessionRows(opts)).map((a) => a.row);
156
+ }
157
+ async listSessionRows(opts) {
155
158
  const params = [];
156
159
  const where = ["sm.placement_kind IS NULL"];
157
160
  if (opts.owner !== undefined) {
@@ -166,7 +169,7 @@ export class TiDBSessionStore {
166
169
  where.push("(sm.updated_at < ? OR (sm.updated_at = ? AND sm.session_id < ?))");
167
170
  params.push(new Date(opts.cursor.lastActivityAt), new Date(opts.cursor.lastActivityAt), opts.cursor.sessionId);
168
171
  }
169
- return (await this.selectListRows({ where, params, limit: opts.limit })).map((a) => a.row);
172
+ return this.selectListRows({ where, params, limit: opts.limit });
170
173
  }
171
174
  async sessionSummaryOf(sessionId) {
172
175
  return (await this.listRowOf(sessionId))?.row;
@@ -246,8 +249,8 @@ export class TiDBSessionStore {
246
249
  async recordSessionWorkspace(sessionId, incarnation, workspace) {
247
250
  await this.pool.query("UPDATE session_meta SET workspace = ? WHERE session_id = ? AND incarnation = ?", [encodeSessionWorkspaceCell(workspace, JSON.stringify, sessionId), sessionId, incarnation]);
248
251
  }
249
- async fork(sourceId, owner) {
250
- return forkByFreshId((s, o, t) => this.forkTo(s, o, t), sourceId, owner);
252
+ async fork(sourceId, owner, opts) {
253
+ return forkByFreshId((s, o, t) => this.forkTo(s, o, t), sourceId, owner, opts);
251
254
  }
252
255
  async forkTo(sourceId, owner, target) {
253
256
  for (let attempt = 0; attempt < 2; attempt++) {
@@ -275,7 +278,8 @@ export class TiDBSessionStore {
275
278
  try {
276
279
  await conn.beginTransaction();
277
280
  try {
278
- await conn.query("INSERT INTO session_meta (session_id, owner, created_at, updated_at, leaf_id, leaf_seq, incarnation, fork_key) VALUES (?,?,?,?,?,?,?,?)", [target.id, forkOwner, now, now, forkLeafId, eventRows.length, mintSessionIncarnation(), target.key ?? null]);
281
+ await conn.query("INSERT INTO session_meta (session_id, owner, created_at, updated_at, leaf_id, leaf_seq, incarnation, fork_key, " +
282
+ "placement_kind, placement_scope, placement_parent_session_id, placement_root_session_id, placement_handle, placed_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)", [target.id, forkOwner, now, now, forkLeafId, eventRows.length, mintSessionIncarnation(), target.key ?? null, ...placementInsertValues(target.placement, now)]);
279
283
  }
280
284
  catch (err) {
281
285
  if (!isMysqlDupKeyError(err))
@@ -0,0 +1,18 @@
1
+ /**
2
+ * 「这条腿上有没有 prompt 屏」的**唯一读法** —— 提交时问一次,问的是这条腿两座注册**声明**的事件(部署座 `RunnerDeps.mods` + 任务座 `TaskSpec.mods`)。
3
+ *
4
+ * ## 为什么问它
5
+ * prompt 屏(引擎的 `prompt.submit` 扩展站,底层 `classic.UserPromptSubmit`)可以在交付模型之前改写或拦下一条目标。屏装着的腿上,目标的哪一份
6
+ * 会被交付,只有屏判完才知道;本服务存 / 转目标的每张面(run 行预览、续跑输入、标题器取材)因此只能在交付之后写交付的那一份
7
+ * (律的属主 = `run-acceptance.ts`)。屏**不在**的腿上没有「屏前」:交付的就是请求原文,提交那一刻就知道 —— 与没有屏这回事之前逐字同。
8
+ *
9
+ * ## 判据(一条,引擎的)
10
+ * 引擎的 `promptScreenDeclaredBy`:两座的注册并起来,任一张的 `uses.events`(名字或 `<前缀>.*`,按引擎准入的读法展开)选中屏听的任一站
11
+ * (引擎的 `PROMPT_SCREEN_STATIONS`)⇒ 有屏。引擎准入保证一张注册挂上的 handler 不越出它声明的事件(越出 = 准入拒),所以「声明里没有」⇒
12
+ * 这条腿的总线上**一定**没有屏 —— 判据只会多判有屏(声明了、`register` 时没挂),不会漏判。多判的代价是这条腿的那几张面等到交付才写
13
+ * (写的仍是交付的那一份),不是放行。站名表与前缀展开只住在引擎里:引擎给屏加一站,本服务同一拍就认。
14
+ */
15
+ import { type ModRegistration } from "@sema-agent/core";
16
+ /** 这条腿的两座(缺席的座 = 没有注册)里有没有一张注册声明了 prompt 屏。 */
17
+ export declare function promptScreenDeclared(seats: ReadonlyArray<ReadonlyArray<Pick<ModRegistration, "uses">> | undefined>): boolean;
18
+ //# sourceMappingURL=prompt-screen.d.ts.map
@@ -0,0 +1,5 @@
1
+ import { promptScreenDeclaredBy } from "@sema-agent/core";
2
+ export function promptScreenDeclared(seats) {
3
+ return promptScreenDeclaredBy(seats.flatMap((seat) => seat ?? []));
4
+ }
5
+ //# sourceMappingURL=prompt-screen.js.map
@@ -1,5 +1,5 @@
1
1
  import { type DirectWritableRuleBehavior, type RemovalLiveness } from "./permission-rule-vocab.js";
2
- import { type RuleOwner, type ImportPreview, type RedeemedBatchMember, type ImportedSettingsLayer, type PersistedRule, type RuleBehavior, type RuleCandidate, type EffectivePermissionRule, type RemoveResult, type RuleWriteCommitted, type EditedRuleTextPrecheck, type RuleScope, type RuleOffer } from "@sema-agent/core";
2
+ import { type RuleOwner, type ImportPreview, type ImportedSettingsLayer, type PersistedRule, type RuleBehavior, type RuleCandidate, type EffectivePermissionRule, type RemoveResult, type RuleRefusalCode, type RuleWriteCommitted, type EditedRuleTextPrecheck, type RuleScope, type RuleOffer } from "@sema-agent/core";
3
3
  import type { DurableRulePartitionProvider, PermissionRuleStoreProvider, RuleApprovalRecordStore } from "@sema-agent/core";
4
4
  import { type RuleImportTicket, type RuleTicketPurpose, type RuleTicketReclaimResult, type RuleTicketRedeemResult, type RuleTicketSnapshot } from "./plugins/permission-rule-store-sql.js";
5
5
  /**
@@ -142,11 +142,12 @@ export declare const MAX_LOCAL_IMPORT_BYTES: number;
142
142
  * 救回来",而不是等下一次 prepare。运维旋钮暂不开(没有部署形要求它可调)。
143
143
  */
144
144
  export declare const LOCAL_IMPORT_CLAIM_LEASE_MS = 60000;
145
- /** 一条规则的 wire 身份键(**态** + 规则文本 + {@link serializeRuleScope} 的判别式串)。两个面
146
- * (预览 / 兑付回执)用**同一把键**,客户端拿它把两张表 join 起来。
145
+ /** 一条规则的 wire 身份键(**态** + 规则文本 + {@link serializeRuleScope} 的判别式串)。local-import 的两个面
146
+ * (预览 / 兑付回执)用**同一把键**,客户端拿它把两张表 join 起来;cc-import 的兑付回执也是这一把(两只 redeem 口
147
+ * 同一张回执,见 {@link ImportOutcome}),scope 串与 `GET /v1/rules` 列举行逐字同形,对账直接按它比。
147
148
  * 🔴 `behavior` 自 7.67.0 起是这把键的一格(core 7.9.0 [ref]:规则的身份是三元组)—— 少了它,同文本
148
149
  * 同作用域的 deny 与 allow 两行会被本口判成 `duplicate_row`,而它们是两条不同的规则。 */
149
- export interface LocalImportRuleKey {
150
+ export interface ImportRuleKey {
150
151
  behavior: RuleBehavior;
151
152
  rule: string;
152
153
  scope: string;
@@ -189,65 +190,68 @@ export type LocalImportRefusalCode =
189
190
  /** 过了本层全部判据,却没能成为引擎的候选(= 本仓与 core 的判据分了家)。**响亮**而不是静默丢:
190
191
  * 它意味着两侧的规则语法判据漂了,那是要人去看的事。 */
191
192
  | "not_importable";
192
- /** 兑付面的**成员级终态**(闭集,机读;`detail` 是给人看的那一半)。
193
- *
194
- * 🔴 **`refused` 在本版的兑付面结构上到不了,这是如实登记而不是遗漏**:走到兑付这一步的成员,已经
195
- * 在 prepare 过了全部内容判据 —— 于是 core 交回的 `refused` 行只剩店侧成因(读不出店 / 写面抛 / OCC
196
- * 用尽),而那些**都是"不知道落没落"**,不是"这条规则不行"。core 的 `RedeemedBatchMember` 拒绝臂
197
- * 只带一个**自由文本** `reason`,把它按串去猜「这是裁定还是瞬时」正是本仓明令禁止的自造判读,
198
- * 所以一律落到 `indeterminate`(安全方向)。词仍留在表里是因为它进了**落盘回执的边界 schema**
199
- * (`LocalImportOutcomeSchema` 的 z.enum——`redeemed_outcome` 列回读同一张词表,旧行 / 别版本进程写下的
200
- * `refused` 必须读得回),不是留一个死枝;⚠️ 预览面**不用**这个词:那一面的拒绝形是
201
- * `admitted:false + LocalImportRefusalCode`({@link LocalImportPreviewMember}),两面词表**不同**。
202
- * 收窄需要 core 给拒绝臂一个**带类型的**
203
- * 瞬时/裁定判别位 —— 已作为上游请托登记(server 不在下游造第二份判据,[ref])。 */
204
- export type LocalImportMemberOutcome =
193
+ /** 兑付回执的**成员级终态**(闭集,机读;`detail` 是给人看的那一半)。两只 redeem 口(cc-import / local-import)同一张表。
194
+ *
195
+ * 🔴 **引擎拒的成员,结局只读引擎的两词 `committed`**(core 7.63.0 [ref],`RedeemedBatchMember` 拒臂必填):`no` ⇒ `refused`
196
+ * (引擎确知这一条没落 —— 裁定,如旧导入记录里从 `/` 写起的全局规则的 `unsupported.layer_root`;同批放行被扣下的 `withheld`)、
197
+ * `unknown` ⇒ `indeterminate`(写面抛 / 读回读不出 —— 那一条**可能已经落了**)。本仓不按串判,不另写第二份判据。
198
+ * 它进了**落盘回执的边界 schema**(`ImportOutcomeSchema`;`redeemed_outcome` 列回读同一张词表)。⚠️ 预览面**不用**这个词表:那一面的拒绝形是
199
+ * `admitted:false + LocalImportRefusalCode`({@link LocalImportPreviewMember}),两面词表**不同**。 */
200
+ export type ImportMemberOutcome =
205
201
  /** 这一次真的写进店里了。 */
206
202
  "persisted"
207
203
  /** 等价规则**已经在店**(引擎的 `deduped`)—— 同意早就生效,这次什么都不用做。 */
208
204
  | "no-op"
209
- /** 这条被拒了(落盘回执 schema 的历史词位——现行两面都不铸它:预览面用 admitted:false +
210
- * LocalImportRefusalCode,兑付面见上方登记恒折 indeterminate;留词只为旧行回读)。 */
205
+ /** 引擎拒了这一条,并答 `committed: "no"` —— 确知没落。重来同一条拒因(`reason`)会得同一个答,出路在 `detail`(引擎原话)。 */
211
206
  | "refused"
212
- /** **不知道落没落**:店在这一条上出了状况。如实编,禁猜(猜"成了"会谎报一次没发生的授权,猜"没成"
213
- * 会让人以为可以重来一次,而重来会撞上一条其实已经存在的规则)。 */
207
+ /** 引擎拒了这一条,并答 `committed: "unknown"` —— **不知道落没落**。如实编,禁猜。这一词**不进落定的回执**(落定律:任一成员
208
+ * 结局不知 ⇒ 不落定、503 同票重走,见 {@link unsettledDetailOf});词表留着它,是因为 7.145.x 及更早落定的票行(`redeemed_outcome`)
209
+ * 里有它,回放读那份存量。 */
214
210
  | "indeterminate";
215
- /** `indeterminate` 的成因(闭集)。 */
216
- export type LocalImportIndeterminateCode =
217
- /** 引擎在这一条上答了 refused,而拒因只有自由文本 ⇒ 落不了裁定,如实说"不知道"。 */
218
- "store_refused"
219
- /** 兑付走本身没走完(本层抛出/中断)⇒ 这一条连走都没走到。 */
220
- | "walk_failed";
221
211
  /** prepare 面的一行。判别式联合:被收下的行**没有**拒因字段可读,被拒的行**必然**带拒因 ——
222
212
  * 「admitted 却带 reason」这种矛盾态在类型上不可表达。
223
213
  * 收下的**收紧**(deny / ask)在 prepare 当场经直写接缝落店,行带它的结局 `outcome`(`persisted` = 这次写了 / `no-op` = 等价规则已在店);
224
214
  * 收下的**放行**没有 `outcome` —— 它的结局在 redeem 回执里(放行要同意,收紧不等同意;见 `prepareLocalImport`)。 */
225
215
  export type LocalImportPreviewMember = {
226
- key: LocalImportRuleKey;
216
+ key: ImportRuleKey;
227
217
  admitted: true;
228
218
  } | {
229
- key: LocalImportRuleKey;
219
+ key: ImportRuleKey;
230
220
  admitted: true;
231
221
  outcome: LocalImportTighteningOutcome;
232
222
  } | {
233
- key: LocalImportRuleKey;
223
+ key: ImportRuleKey;
234
224
  admitted: false;
235
225
  reason: LocalImportRefusalCode;
236
226
  detail: string;
237
227
  };
238
228
  /** 一条收紧在 prepare 当场落店的结局(与兑付回执的 `persisted` / `no-op` 同词同义;直写接缝 `addPersistedRule` 的 `added` / `no-op`)。 */
239
229
  export type LocalImportTighteningOutcome = "persisted" | "no-op";
240
- /** 兑付回执的一行。 */
241
- export interface LocalImportOutcomeMember {
242
- key: LocalImportRuleKey;
243
- outcome: LocalImportMemberOutcome;
244
- reason?: LocalImportIndeterminateCode;
230
+ /** 兑付回执的一行(两只 redeem 口同形;唯一铸点 {@link buildImportOutcomeMembers})。`reason` / `detail` 在场 ⇔ 引擎拒了这一条
231
+ * (`refused` / `indeterminate`):`reason` = 引擎的闭集拒因码(`RuleRefusalCode`,core 属主,逐字),`detail` = 引擎给人看的那句话。 */
232
+ export interface ImportOutcomeMember {
233
+ key: ImportRuleKey;
234
+ outcome: ImportMemberOutcome;
235
+ reason?: RuleRefusalCode;
245
236
  detail?: string;
246
237
  }
247
- /** 兑付回执整只 = **写进票行 `redeemed_outcome` 的逐字体**(回放同源:回放答的就是它)。 */
248
- export interface LocalImportOutcome {
249
- members: readonly LocalImportOutcomeMember[];
250
- /** 这只桶在兑付结束时的 OCC 版本(与 cc-import 的 `result.rev` 同义)。 */
238
+ /**
239
+ * 一次兑付的回执整只 —— **两只 redeem 口同一张**(cc-import / local-import 的 200 体都是 `{ members, rev }`,路由同一个铸点),
240
+ * local-import 另把它**逐字**写进票行 `redeemed_outcome`(回放同源:回放答的就是它)。
241
+ *
242
+ * 🔴 **落定律(一条,两口同读)**:兑付走完、拿到引擎的完整成员表、**且每个成员的结局都确定**(`persisted` / `no-op` / 引擎答
243
+ * `committed: "no"` 的 `refused`)⇒ 这次兑付落定,回执逐成员交出,票不放回、不答 503:cc-import 一次性用掉、答 200 回执;local-import 把回执
244
+ * 落进票行(SETTLED)后答 200 —— 落定这一跳答不成(落定写抛 / 认领被租约接管而新持有者还没落定)⇒ 照旧答 409
245
+ * `conflict.rule_import_in_flight`,同票再来读到的是权威的那一份回执(那一跳不改成员结局,只决定哪一份是权威)。**没有**完整
246
+ * 成员表(引擎在走之前整批拒 / confirm 输给并发写者 / 店在走之前抛 / 循环里仍抛出来的异常),或任一成员结局**不知**(引擎答
247
+ * `committed: "unknown"`:规则店的写抛 / 读回读不出,引擎 7.64.0 起还有成员循环里审批记录店的记 dot CAS / dot 分配失败)⇒ 不落定:放回认领、503
248
+ * `state.rule_import_retry`,同一张票重来就是重走一遍 —— 兑付按引擎的设计幂等(记录里的 dot 台账),重走交出完整表,
249
+ * 被打断的那一走已落的成员读 `no-op`。确知的成员拒**不**按票自动重试:一条永久被拒的成员(旧记录里的 `unsupported.layer_root`)
250
+ * 若走重试臂,客户端会 503 到票过期、临期放不回 ⇒ 404,已落成员不撤、被拒成员永不落 —— 7.144.0 及更早 cc-import 口就是这一形。
251
+ */
252
+ export interface ImportOutcome {
253
+ members: readonly ImportOutcomeMember[];
254
+ /** 这只桶在兑付结束时的 OCC 版本。 */
251
255
  rev: number;
252
256
  }
253
257
  /** A7 prepare 的产物。第二臂(`ticket` 整键缺席)= **零可导入行** —— 与 cc-import 的「预览无票」臂
@@ -288,7 +292,7 @@ export type LocalImportRedeemed =
288
292
  /** `replayed` = 这份终局不是本次走出来的,是从票行里逐字读回来的(⑥ 丢响应重试那条路)。 */
289
293
  {
290
294
  ok: true;
291
- outcome: LocalImportOutcome;
295
+ outcome: ImportOutcome;
292
296
  replayed: boolean;
293
297
  }
294
298
  /** 五类票拒(不存在 / 别人的 / 错车道 / 过期 / 已用过而无从回放)—— wire 面同形 404。 */
@@ -329,18 +333,19 @@ export type RuleImportPrepared = {
329
333
  reason: "too-many-candidates";
330
334
  candidates: number;
331
335
  };
332
- /** 导入 redeem 的结果。wire 口径写死(端点=http/routes/rules.ts):票拒五类(自造 / 别人的 / 错车道 /
333
- * 过期 / 已用过)与裁定性拒绝(record-unusable / payload-mismatch)同形 404(零存在性 oracle,顶注 ①);
334
- * `retryable === true` ⇒ 503 `state.rule_import_retry`;`state-unreadable` ⇒ 503
335
- * `state.rule_ticket_unreadable`。分类保留只为服务端日志/诊断与上述三分支的判据。 */
336
- export type RuleImportRedeemed = {
336
+ /** 导入 redeem 的结果。wire 口径写死(端点=http/routes/rules.ts):成功 ⇒ 200 回执 `{ members, rev }`(与 local-import 口
337
+ * 同一张、同一个铸点;落定律见 {@link ImportOutcome});票拒五类(自造 / 别人的 / 错车道 / 过期 / 已用过)与裁定性拒绝
338
+ * (record-unusable / payload-mismatch)同形 404(零存在性 oracle,顶注 ①);`retryable === true` ⇒ 503
339
+ * `state.rule_import_retry`;`state-unreadable` ⇒ 503 `state.rule_ticket_unreadable`。分类保留只为服务端日志/诊断与
340
+ * 上述三分支的判据。 */
341
+ export type RuleImportRedeemed =
342
+ /** 兑付走完、拿到完整成员表且每个成员结局确定 ⇒ 落定(引擎确知没落的成员也在这一臂,那几行 `refused`)。 */
343
+ {
337
344
  ok: true;
338
- result: {
339
- members: readonly RedeemedBatchMember[];
340
- rev: number;
341
- };
345
+ outcome: ImportOutcome;
342
346
  }
343
- /** `retryable` = 这次失败**没有裁定任何事**(store 抛错 / CAS 冲突),认领已放回,属主原样重试即可。
347
+ /** `retryable` = 这次兑付**没有落定**(store 抛错 / CAS 冲突 / 引擎走之前整批拒 / 循环里仍抛出来的异常 / 某个成员结局不知),
348
+ * 认领已放回,属主原样重试即重走。
344
349
  * 它与裁定性拒绝(载荷被改写、记录不存在)分家是承重的:把两者塌成一格,一次数据库抖动就会被
345
350
  * 客户端读成「这张票永远不能用了」。⚠️ wire 面**不再**折 404:`retryable === true` ⇒ 503
346
351
  * `state.rule_import_retry` + Retry-After(http/routes/rules.ts;走到这一步的调用方已赢下 principal
@@ -620,7 +625,7 @@ export type RuleBucketRef = string | RuleOwner;
620
625
  *
621
626
  * 🔴 三臂**刻意不塌成布尔**:「落了」「早就有了」「不知道落没落」对调用方是三种不同的处置,而把第三种
622
627
  * 折进前两种里的任一边都是编一个结论(猜"成了"会谎报一次没发生的写,猜"没成"会让人以为可以重来)。
623
- * 与导入面的成员级四态({@link LocalImportMemberOutcome})同一条纪律,词更少是因为本口只有一行。
628
+ * 与导入面的成员级四态({@link ImportMemberOutcome})同一条纪律,词更少是因为本口只有一行。
624
629
  */
625
630
  export type RuleWritten =
626
631
  /** `persisted` = 这一次**真的往店里写了**;`no-op` = 这条身份**此前就站在店里**,本次调用**一个字都没写**
@@ -5,7 +5,7 @@ import { createLogger } from "./observability/logger.js";
5
5
  import { ABSOLUTE_HOST_PATH_MAX, hasControlOrBidi } from "@sema-agent/settings-schema";
6
6
  import { isDirectWritableRuleBehavior } from "./permission-rule-vocab.js";
7
7
  import { hasBidiControls } from "./text-bidi.js";
8
- import { confirmRuleApproval, RULE_BEHAVIORS, parseRuleText, ruleReachesProgramRun, precheckEditedRuleText, prepareCardApproval, prepareCcImport, redeemRuleBatch, redeemRuleTicket, addPersistedRule, removePersistedRule, isValidDurableScope, effectiveOrThrow, readRowBehavior, readRuleOwner, } from "@sema-agent/core";
8
+ import { confirmRuleApproval, RULE_BEHAVIORS, RULE_REFUSAL_CODES, parseRuleText, ruleReachesProgramRun, precheckEditedRuleText, prepareCardApproval, prepareCcImport, redeemRuleBatch, redeemRuleTicket, addPersistedRule, removePersistedRule, isValidDurableScope, effectiveOrThrow, readRowBehavior, readRuleOwner, } from "@sema-agent/core";
9
9
  import { buildRulePayloadHash } from "./plugins/permission-rule-store-sql.js";
10
10
  export const RULE_IMPORT_TICKET_TTL_MS = 10 * 60_000;
11
11
  export const RULE_IMPORT_RETRY_AFTER_SEC = 2;
@@ -15,13 +15,13 @@ export const MAX_LOCAL_IMPORT_BYTES = 256 * 1024;
15
15
  export const LOCAL_IMPORT_CLAIM_LEASE_MS = 60_000;
16
16
  const LOCAL_IMPORT_CLAIM_WAIT_MS = RULE_IMPORT_RETRY_AFTER_SEC * 1000;
17
17
  const LOCAL_IMPORT_CLAIM_POLL_MS = 25;
18
- const LocalImportOutcomeSchema = z
18
+ const ImportOutcomeSchema = z
19
19
  .object({
20
20
  members: z.array(z
21
21
  .object({
22
22
  key: z.object({ behavior: z.preprocess((v) => readRowBehavior(v) ?? v, z.enum(RULE_BEHAVIORS)), rule: z.string().min(1), scope: z.string().min(1) }).strict(),
23
23
  outcome: z.enum(["persisted", "no-op", "refused", "indeterminate"]),
24
- reason: z.enum(["store_refused", "walk_failed"]).optional(),
24
+ reason: z.enum(RULE_REFUSAL_CODES).optional(),
25
25
  detail: z.string().optional(),
26
26
  })
27
27
  .strict()),
@@ -137,6 +137,52 @@ function confirmRefusalWord(reason) {
137
137
  }
138
138
  }
139
139
  }
140
+ function isRuleStoreFault(code) {
141
+ switch (code) {
142
+ case "store_read":
143
+ case "store_write":
144
+ return true;
145
+ case "unsupported.layer_root":
146
+ case "ticket_invalid":
147
+ case "record_missing":
148
+ case "wrong_principal":
149
+ case "record_schema_stale":
150
+ case "not_confirmed":
151
+ case "record_malformed":
152
+ case "not_chosen":
153
+ case "parse_rejected":
154
+ case "store_unavailable":
155
+ case "record_changed":
156
+ case "withheld":
157
+ case "session_ended":
158
+ case "occ":
159
+ case "dot_removed":
160
+ return false;
161
+ default:
162
+ return code;
163
+ }
164
+ }
165
+ class RuleStoreFaultError extends Error {
166
+ code;
167
+ constructor(refusal) {
168
+ super(refusal.reason);
169
+ this.name = "RuleStoreFaultError";
170
+ this.code = refusal.code;
171
+ }
172
+ }
173
+ function throwIfRuleStoreFault(refusal) {
174
+ const { code } = refusal;
175
+ if (!isRuleStoreFault(code))
176
+ return;
177
+ switch (refusal.committed) {
178
+ case "no":
179
+ throw new RuleStoreFaultError({ code, reason: refusal.reason });
180
+ case "unknown":
181
+ return;
182
+ default:
183
+ return refusal.committed;
184
+ }
185
+ }
140
186
  function refusedMembersOf(members) {
141
187
  const refused = [];
142
188
  for (const m of members) {
@@ -241,7 +287,7 @@ function buildLocalImportLayers(candidates) {
241
287
  return root === "" ? { layer: "userSettings", path: LOCAL_IMPORT_LAYER_PATH, readFile } : { layer: "projectSettings", path: `${LOCAL_IMPORT_LAYER_PATH}:${root}`, root, readFile };
242
288
  });
243
289
  }
244
- function buildLocalImportMembers(members) {
290
+ function buildImportOutcomeMembers(members) {
245
291
  return members.map((m) => {
246
292
  const key = { behavior: m.behavior, rule: m.rule, scope: serializeRuleScope(m.scope) };
247
293
  switch (m.status) {
@@ -250,7 +296,7 @@ function buildLocalImportMembers(members) {
250
296
  case "deduped":
251
297
  return { key, outcome: "no-op" };
252
298
  case "refused":
253
- return { key, outcome: "indeterminate", reason: "store_refused", detail: m.reason };
299
+ return { key, outcome: refusedOutcomeOf(m.committed), reason: m.code, detail: m.reason };
254
300
  default: {
255
301
  const unknown = m;
256
302
  throw new Error(`unknown redeemed-batch member status: ${JSON.stringify(unknown)}`);
@@ -258,6 +304,22 @@ function buildLocalImportMembers(members) {
258
304
  }
259
305
  });
260
306
  }
307
+ function unsettledDetailOf(members) {
308
+ const open = members.find((m) => m.outcome === "indeterminate");
309
+ return open === undefined ? undefined : `the outcome of ${open.key.rule} is not known (${open.reason ?? "unknown"}): ${open.detail ?? ""}`;
310
+ }
311
+ function refusedOutcomeOf(committed) {
312
+ switch (committed) {
313
+ case "no":
314
+ return "refused";
315
+ case "unknown":
316
+ return "indeterminate";
317
+ default: {
318
+ const unknown = committed;
319
+ throw new Error(`unknown redeemed-batch member committed word: ${JSON.stringify(unknown)}`);
320
+ }
321
+ }
322
+ }
261
323
  export function createRuleConsentLane(stores, opts) {
262
324
  const deps = { provider: stores.provider, approvals: stores.approvals, cardEdits: true };
263
325
  const ticketTtlMs = opts?.ticketTtlMs ?? RULE_IMPORT_TICKET_TTL_MS;
@@ -300,10 +362,15 @@ export function createRuleConsentLane(stores, opts) {
300
362
  if (!confirmed.ok)
301
363
  return { ok: false, reason: "confirm-refused", detail: confirmed.reason };
302
364
  const result = await redeemRuleBatch({ approvalId: prepared.approvalId, principal: input.principal, deps });
303
- if ("status" in result)
365
+ if ("status" in result) {
366
+ throwIfRuleStoreFault(result);
304
367
  return { ok: false, reason: "redeem-refused", detail: result.reason };
368
+ }
305
369
  const refused = refusedMembersOf(result.members);
306
370
  if (refused.length > 0) {
371
+ if (refused.length === result.members.length && refused.every((m) => m.committed === "no"))
372
+ for (const m of refused)
373
+ throwIfRuleStoreFault(m);
307
374
  return { ok: false, reason: "redeem-refused", detail: `partial batch: ${refused.length} member(s) refused (${refused.map((x) => x.reason).join("; ")})` };
308
375
  }
309
376
  return { ok: true, ...anchorBatchMembers(result.members, redemption.offer), rev: result.rev };
@@ -351,8 +418,10 @@ export function createRuleConsentLane(stores, opts) {
351
418
  ticket = minted.ticket;
352
419
  }
353
420
  const redeemed = await redeemRuleTicket({ ticket, principal: input.principal, deps });
354
- if (redeemed.status !== "redeemed")
421
+ if (redeemed.status !== "redeemed") {
422
+ throwIfRuleStoreFault(redeemed);
355
423
  return { ok: false, reason: "redeem-refused", detail: redeemed.reason };
424
+ }
356
425
  return { ok: true, kind: "single", rule: redeemed.rule, rev: redeemed.rev, alreadyRedeemed: redeemed.alreadyRedeemed };
357
426
  },
358
427
  async prepareImport(principal, layers) {
@@ -449,11 +518,11 @@ export function createRuleConsentLane(stores, opts) {
449
518
  if ("status" in result) {
450
519
  return await indeterminate(result.reason);
451
520
  }
452
- const refused = refusedMembersOf(result.members);
453
- if (refused.length > 0) {
454
- return await indeterminate(`partial import: ${refused.length} member(s) refused (${refused.map((x) => x.reason).join("; ")})`);
455
- }
456
- return { ok: true, result };
521
+ const members = buildImportOutcomeMembers(result.members);
522
+ const open = unsettledDetailOf(members);
523
+ if (open !== undefined)
524
+ return await indeterminate(open);
525
+ return { ok: true, outcome: { members, rev: result.rev } };
457
526
  }
458
527
  catch (err) {
459
528
  return await indeterminate(err instanceof Error ? err.message : String(err));
@@ -535,7 +604,7 @@ export function createRuleConsentLane(stores, opts) {
535
604
  },
536
605
  async redeemLocalImport(principal, ticket) {
537
606
  const readSettled = (raw) => {
538
- const parsed = LocalImportOutcomeSchema.safeParse(raw);
607
+ const parsed = ImportOutcomeSchema.safeParse(raw);
539
608
  if (!parsed.success) {
540
609
  throw new Error(`the settled outcome recorded on this import ticket does not carry a readable shape: ${parsed.error.message}`);
541
610
  }
@@ -609,8 +678,11 @@ export function createRuleConsentLane(stores, opts) {
609
678
  if ("status" in result) {
610
679
  return await transient(result.reason);
611
680
  }
612
- members = buildLocalImportMembers(result.members);
681
+ members = buildImportOutcomeMembers(result.members);
613
682
  rev = result.rev;
683
+ const open = unsettledDetailOf(members);
684
+ if (open !== undefined)
685
+ return await transient(open);
614
686
  }
615
687
  catch (err) {
616
688
  return await transient(err instanceof Error ? err.message : String(err));
@@ -1,27 +1,44 @@
1
1
  /**
2
- * **一次提交的「受理」读法** —— 标题器只在引擎**受理**这条 run 之后起(一条律,唯一属主 = 本文件)。
2
+ * **一次提交的「受理」与「交付」读法** —— 标题器只在引擎**受理**这条 run 之后起;本服务存 / 转这条 run 的目标的每一张面只取引擎**交付**的那一份
3
+ * (一条律,唯一属主 = 本文件)。
3
4
  *
4
5
  * ## 受理是哪一刻
5
6
  * 引擎把这次提交的 objective 收进会话的那一帧:`human_input{ carrier: "objective", delivery: "applied" }`。它是第一次模型调用
6
7
  * 之前的最后一帧;在它之前引擎能拒的一切 —— prepare 门(`config.tool_face_control` / `config.user_invoked_skill_unknown` /
7
- * `config.memory_capture_unsupported` …)、第一回合前的内联图片准入(`config.image_invalid`)、部署的 UserPromptSubmit 拦截、
8
- * 开跑前的预算 / 墙钟 —— 都还没让这次输入进会话。`wiring_manifest` 不是受理:它在 prepare 通过时就出了,图片准入与提示拦截
8
+ * `config.memory_capture_unsupported` …)、第一回合前的内联图片准入(`config.image_invalid`)、部署的 prompt 屏(`prompt.submit` 丢弃 / 抛错、
9
+ * `classic.UserPromptSubmit` 拦截)、开跑前的预算 / 墙钟 —— 都还没让这次输入进会话。`wiring_manifest` 不是受理:它在 prepare 通过时就出了,图片准入与提示拦截
9
10
  * 都在它之后(core `run-identity-wiring.js` 推 wiring_manifest,`run-leg.js` 才做图片准入、推 human_input、调模型)。
10
11
  *
11
12
  * 只回结果、不交流的腿(verify / cascade:core 在自己里面排空每条腿的流,宿主手里没有流)看不到那一帧。它们的受理证据是结果本身:
12
- * 结果里跑过 turn(`stats.turns > 0`)⇒ objective 进过会话、模型答过。**token 不是证据**:被 UserPromptSubmit 拦下的 objective 不进会话,
13
+ * 结果里跑过 turn(`stats.turns > 0`)⇒ objective 进过会话、模型答过。**token 不是证据**:被屏拦下的 objective 不进会话,
13
14
  * 而同一条 run 的收尾压缩可以在已有历史的会话上花 token(turns 0、tokens > 0)—— 按 token 判会把被拦下的输入送去起名。
14
15
  * 两种证据说的是同一件事,本文件把它们收成一只闩:先到的那一个算数,只算一次。
15
16
  *
17
+ * ## 交付的是哪一份(律:本服务的目标副本 = 引擎交付的那一份,屏判完之前一个字节都不存)
18
+ * prompt 屏(`prompt-screen.ts` 判这条腿有没有)可以改写或拦下目标:模型与会话转录只见屏后文本,调用方的原话没送达。本服务存 / 转目标的面 ——
19
+ * run 行预览(`objectivePreview`:`GET /v1/tasks` / 收件箱 `objective` / 运维任务视图 / 会话列表 / fleet 行名同读)、续跑输入(checkpoint ctx 体里的
20
+ * `objective`)、标题器取材 —— 于是只读本座交出的那一份,不读请求体:
21
+ * · **腿上没有屏** ⇒ 交付的就是请求原文,提交那一刻就知道({@link RunAcceptance.objectiveAtSubmit} 在场):各面在提交时照写,与没有屏这回事之前逐字同;
22
+ * · **腿上有屏** ⇒ 提交时什么都不写(`objectiveAtSubmit` 缺席:预览列 NULL、续跑输入标上 `promptScreened` 而 `objective` 留空、fleet 行不带名);屏判完、引擎交付时
23
+ * 本座把交付的那一份交给各面登记的补写口({@link RunAcceptance.onDelivered}:run 行预览、fleet 行名),持流的腿在那一帧上等补写落定再往下走。
24
+ * 续跑输入**不**登记补写口、一直留空,续跑腿的展示面只看它上面的标记(那一行在跑的时候还有别的写者,交付时整行重写会与它抢写;理由、代价、
25
+ * verify 形那份原文与留空的体记下的模型全文 `http/submit-ctx.ts`):
26
+ * - 受理帧不带 `rewrittenBy`(引擎:缺席 = 原样送达)⇒ 交付的 = 请求原文;
27
+ * - 受理帧带 `rewrittenBy`(屏改写过)⇒ 引擎今天不把屏后文本交给宿主 ⇒ **没有**可写的那一份:各面保持提交时的空,标题器不起。
28
+ * 引擎把屏后文本交给宿主之后,这一臂改读它 —— 读口换,律不变;
29
+ * - 没有受理帧(屏丢弃 / 抛错 / 受理前取消或失败)⇒ 没有交付,各面保持空;
30
+ * - 只回结果的腿:宿主看不到受理帧,也就分不出改写 ⇒ 有屏时没有可写的那一份(各面保持空、标题器不起);没屏时照提交时写好的。
31
+ * 不加第二份脱敏、不按内容猜:屏改没改由引擎的 `rewrittenBy` 说,不由本服务比字符串。
32
+ *
16
33
  * ## 受理之后 / 之前
17
- * · 受理 ⇒ 标题器起一次(会话没有标题时它才真去生成;判据在标题器里,本文件不重复)。
34
+ * · 受理 ⇒ 有交付的那一份时标题器起一次(会话没有标题时它才真去生成;判据在标题器里,本文件不重复)。
18
35
  * · 没受理就收场(零工作的拒、受理前取消 / 拦截)⇒ 标题器不起:被拒的输入不花一次模型调用,也不被送去起名。
19
36
  * 这次提交在提交门铸下的会话行照旧留下(无标题、零消息),与此前同 —— 「提交没让引擎受理就收场」的会话去留是另一件事,不在本文件。
20
37
  *
21
38
  * ## 谁喂它
22
- * 三条提交腿(同步 / 流式 / durable)各在两处喂同一只:持流的那一形把每一帧交给 {@link RunAcceptance.observe};终局落行之后调一次
23
- * {@link RunAcceptance.settle}。腿上不再有「什么时候起标题」的判断,标题器的唯一调用点是本文件的闩。座由提交门在 `prepareSpec` 里
24
- * 随 spec 一起铸(`PreparedTaskSubmission.acceptance`);不经提交门的内部车道铸一只不带标题器的座,也就不起标题。
39
+ * 三条提交腿(同步 / 流式 / durable)各在两处喂同一只:持流的那一形把每一帧交给 {@link RunAcceptance.observe}(并等它交回的那一拍 —— 有补写时
40
+ * 补写落定才往下);终局落行之后调一次 {@link RunAcceptance.settle}。腿上不再有「什么时候起标题」「目标写哪一份」的判断,标题器与补写的唯一调用点是本文件的闩。
41
+ * 座由提交门在 `prepareSpec` 里随 spec 一起铸(`PreparedTaskSubmission.acceptance`);不经提交门的内部车道铸一只不带标题器的座,也就不起标题。
25
42
  *
26
43
  * ## 标题写成之后(`session_titled` 帧,契约 §4a-ttl)
27
44
  * 标题器写成时把那一帧交回本座({@link RunAcceptanceDeps.sessionTitler} 的 `onTitled`);本座是这条 run 的流与标题器之间**唯一**的接缝:
@@ -45,21 +62,34 @@ export interface RunStreamSink {
45
62
  }
46
63
  export interface RunAcceptanceInput {
47
64
  sessionId?: string;
65
+ /** 请求里的目标原文 —— 本座是它在受理之后唯一的读者(交付的那一份由它与引擎的交付帧定,见头注「交付的是哪一份」)。 */
48
66
  objective: string;
67
+ /** 这条腿上有没有 prompt 屏(唯一读法 `prompt-screen.ts` 的 `promptScreenDeclared`;不经提交门的内部车道交自己负责的字,恒 `false`)。 */
68
+ screened: boolean;
49
69
  /** 本 turn 真实驱动的模型 id(标题器与 turn 同链路)。 */
50
70
  model?: string;
51
71
  }
72
+ /** 登记补写口的那张面(fail-open 留痕里点名哪一面没补上)。闭集:加一张面 = 加一个词。 */
73
+ export type DeliveryFace = "run-preview" | "fleet-name";
52
74
  export interface RunAcceptance {
53
- /** 持流的腿逐帧调用(判哪一帧算受理只在这里)。不抛、不等。 */
75
+ /** 持流的腿逐帧调用(判哪一帧算受理、交付的是哪一份只在这里)。不抛;交回的那一拍在有补写时等补写落定(补写失败不抛,留痕),腿 await 它再往下。 */
54
76
  observe(ev: {
55
77
  type: string;
56
- }): void;
78
+ }): Promise<void>;
57
79
  /** 终局落行之后:还没在流上见过受理帧、而结果里跑过 turn(只回结果的腿)⇒ 受理。只读 `stats.turns`。不抛、不等。 */
58
80
  settle(result: {
59
81
  readonly stats: {
60
82
  readonly turns: number;
61
83
  };
62
84
  }): void;
85
+ /** 提交那一刻就知道的交付文本(腿上没有屏 ⇒ 请求原文);腿上有屏 ⇒ 缺席:各面提交时不写,交付时经 {@link onDelivered} 补。 */
86
+ readonly objectiveAtSubmit: string | undefined;
87
+ /**
88
+ * 一张面在提交时按 {@link objectiveAtSubmit} 写过之后,登记它的补写口:腿上有屏、引擎交付了可写的那一份时,本座以交付的文本调它一次
89
+ * (持流的腿在受理帧那一拍等它落定)。腿上没有屏 ⇒ 提交时已写完,永不调。补写抛错 ⇒ 不抛给腿(那张面保持提交时的空),留一次 F 类痕。
90
+ * 须在腿起跑之前登记(交付之后才登记的口当场补,不等)。
91
+ */
92
+ onDelivered(face: DeliveryFace, fill: (objective: string) => Promise<void>): void;
63
93
  /** 持活流的腿开流时挂上它的写口(一次提交至多一条活流;重挂以最后一次为准)。不挂 = 本次提交没有可写的流,标题写成的帧丢弃。 */
64
94
  attachStream(sink: RunStreamSink): void;
65
95
  }
@@ -1,35 +1,68 @@
1
+ import { recordFailOpen } from "./observability/fail-open.js";
2
+ import { seatText } from "./trace/exit-seat.js";
1
3
  function isObjectiveApplied(ev) {
2
4
  if (ev.type !== "human_input")
3
5
  return false;
4
6
  const h = ev;
5
7
  return h.carrier === "objective" && h.delivery === "applied";
6
8
  }
9
+ function rewrittenOnDelivery(ev) {
10
+ return ev.rewrittenBy !== undefined;
11
+ }
7
12
  export function createRunAcceptance(deps, input) {
8
13
  let accepted = false;
14
+ let delivered;
9
15
  let terminalSeen = false;
10
16
  let stream;
17
+ const fills = [];
11
18
  const deliver = (frame) => {
12
19
  if (stream === undefined || terminalSeen || !stream.reachable())
13
20
  return;
14
21
  stream.write(frame);
15
22
  };
16
- const accept = () => {
23
+ const runFill = async (face, fill, objective) => {
24
+ try {
25
+ await fill(objective);
26
+ }
27
+ catch (err) {
28
+ recordFailOpen("server.objective-delivery.face-fill-failed", `face=${face} err=${seatText("operator-log-text", err instanceof Error ? err.message : String(err))}`);
29
+ }
30
+ };
31
+ const accept = (objective) => {
17
32
  if (accepted)
18
- return;
33
+ return Promise.resolve();
19
34
  accepted = true;
35
+ delivered = objective;
36
+ if (objective === undefined)
37
+ return Promise.resolve();
20
38
  if (input.sessionId)
21
- deps.sessionTitler?.maybeTitle(input.sessionId, input.objective, input.model, deliver);
39
+ deps.sessionTitler?.maybeTitle(input.sessionId, objective, input.model, deliver);
40
+ if (!input.screened)
41
+ return Promise.resolve();
42
+ return Promise.all(fills.map(({ face, fill }) => runFill(face, fill, objective))).then(() => undefined);
22
43
  };
23
44
  return {
45
+ objectiveAtSubmit: input.screened ? undefined : input.objective,
24
46
  observe(ev) {
25
47
  if (ev.type === "done")
26
48
  terminalSeen = true;
27
49
  if (!accepted && isObjectiveApplied(ev))
28
- accept();
50
+ return accept(rewrittenOnDelivery(ev) ? undefined : input.objective);
51
+ return Promise.resolve();
29
52
  },
30
53
  settle(result) {
31
54
  if (result.stats.turns > 0)
32
- accept();
55
+ void accept(input.screened ? undefined : input.objective);
56
+ },
57
+ onDelivered(face, fill) {
58
+ if (!input.screened)
59
+ return;
60
+ if (accepted) {
61
+ if (delivered !== undefined)
62
+ void runFill(face, fill, delivered);
63
+ return;
64
+ }
65
+ fills.push({ face, fill });
33
66
  },
34
67
  attachStream(sink) {
35
68
  stream = sink;
@@ -136,6 +136,9 @@ export declare function buildCancelledLedgerRow(c: PersistedTaskResult): {
136
136
  * `signal.reason`:那会改掉引擎与 provider 层收到的拒绝值,是在下游改上游接缝的输入(立宪禁区)。
137
137
  */
138
138
  export interface RunAbort {
139
+ /** 这条腿的**属主**(run 行的 `owner` 列;无主体 = `null`)—— 在飞集(`inflightRuns`)里「谁占着几条」的唯一读处:并发帽按主体数在飞腿时读它
140
+ * (`http/run-concurrency-cap.ts`),不另起一张按 taskId 键控的旁表。句柄与腿同寿,换腿就是换句柄。 */
141
+ readonly owner: string | null;
139
142
  /** 交给引擎的那只信号(`spec.signal` / 审批 / 提问 ALS 的 `abortSignal` / 子代 stoppedBy 归因都用它)。 */
140
143
  readonly signal: AbortSignal;
141
144
  /** 以 `cause` 中止本腿。已中止 ⇒ no-op(成因不改写)。 */
@@ -143,6 +146,6 @@ export interface RunAbort {
143
146
  /** 让本腿停下来的那次中止的成因;未中止 ⇒ `undefined`。 */
144
147
  cause(): AbortCause | undefined;
145
148
  }
146
- export declare function createRunAbort(): RunAbort;
149
+ export declare function createRunAbort(owner: string | null): RunAbort;
147
150
  export {};
148
151
  //# sourceMappingURL=run-cancel-context.d.ts.map
@@ -51,10 +51,11 @@ export function buildCancelledLedgerRow(c) {
51
51
  ...(c.abortCause !== undefined ? { abortCause: c.abortCause } : {}),
52
52
  };
53
53
  }
54
- export function createRunAbort() {
54
+ export function createRunAbort(owner) {
55
55
  const ctrl = new AbortController();
56
56
  let landed;
57
57
  return {
58
+ owner,
58
59
  signal: ctrl.signal,
59
60
  abort(cause) {
60
61
  if (ctrl.signal.aborted)