@sema-agent/server 7.135.0 → 7.137.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 (342) hide show
  1. package/MIGRATION.md +237 -2
  2. package/USAGE.md +146 -41
  3. package/deploy/sema-up/README.md +15 -1
  4. package/deploy/sema-up/chart/templates/NOTES.txt +1 -1
  5. package/deploy/sema-up/chart/templates/sandbox-rbac.yaml +7 -1
  6. package/deploy/sema-up/chart/values.yaml +1 -1
  7. package/deploy/sema-up/stack/sema-stack-cluster.yaml +7 -1
  8. package/dist/approval-card.d.ts +60 -4
  9. package/dist/approval-card.js +18 -1
  10. package/dist/approval-content-kind.d.ts +7 -0
  11. package/dist/approval-content-kind.js +8 -1
  12. package/dist/approval-gate-currency.d.ts +48 -6
  13. package/dist/approval-gate-currency.js +16 -9
  14. package/dist/approval-hmac.d.ts +13 -1
  15. package/dist/approval-hmac.js +1 -1
  16. package/dist/approval.js +3 -3
  17. package/dist/auth-bridge.d.ts +34 -5
  18. package/dist/auth-bridge.js +22 -5
  19. package/dist/boot/budget-tracing.d.ts +1 -2
  20. package/dist/boot/budget-tracing.js +2 -2
  21. package/dist/boot/execution-env.d.ts +24 -1
  22. package/dist/boot/execution-env.js +69 -6
  23. package/dist/boot/org-memory.js +1 -1
  24. package/dist/boot/reapers.d.ts +3 -0
  25. package/dist/boot/reapers.js +15 -1
  26. package/dist/boot/resolve-spec.d.ts +4 -0
  27. package/dist/boot/resolve-spec.js +24 -22
  28. package/dist/boot/runner-deps.js +4 -2
  29. package/dist/boot/stage-03-store-backend.d.ts +1 -1
  30. package/dist/boot/stage-04-stores.d.ts +2 -2
  31. package/dist/boot/stage-05-execution-env.d.ts +4 -2
  32. package/dist/boot/stage-05-execution-env.js +11 -2
  33. package/dist/boot/stage-06-runners.d.ts +4 -2
  34. package/dist/boot/stage-06-runners.js +5 -1
  35. package/dist/boot/stage-07-capability-layer.d.ts +5 -2
  36. package/dist/boot/stage-07-capability-layer.js +1 -1
  37. package/dist/boot/stage-08-reapers.d.ts +5 -2
  38. package/dist/boot/stage-08-reapers.js +2 -1
  39. package/dist/boot/stage-09-leader.d.ts +5 -2
  40. package/dist/boot/stage-10-http-server.d.ts +5 -2
  41. package/dist/boot/stage-10-http-server.js +3 -2
  42. package/dist/boot/stores.d.ts +1 -1
  43. package/dist/boot/stores.js +2 -3
  44. package/dist/boot/web-admin.js +10 -17
  45. package/dist/brain.js +12 -3
  46. package/dist/broker/broker.d.ts +89 -0
  47. package/dist/broker/broker.js +287 -0
  48. package/dist/broker/constants.d.ts +39 -0
  49. package/dist/broker/constants.js +15 -0
  50. package/dist/broker/http1.d.ts +111 -0
  51. package/dist/broker/http1.js +256 -0
  52. package/dist/broker/lease-book.d.ts +87 -0
  53. package/dist/broker/lease-book.js +142 -0
  54. package/dist/broker/lease-file.d.ts +60 -0
  55. package/dist/broker/lease-file.js +127 -0
  56. package/dist/broker/main.d.ts +2 -0
  57. package/dist/broker/main.js +20 -0
  58. package/dist/budget.d.ts +3 -3
  59. package/dist/budget.js +4 -0
  60. package/dist/capabilities/composite-members.d.ts +2 -1
  61. package/dist/capabilities/composite-members.js +2 -1
  62. package/dist/capabilities/engine-capabilities.js +1 -0
  63. package/dist/capabilities/mcp-probe.d.ts +5 -4
  64. package/dist/capabilities/scenarios.d.ts +23 -8
  65. package/dist/capabilities/scenarios.js +13 -8
  66. package/dist/capabilities/skills.d.ts +28 -5
  67. package/dist/capabilities/skills.js +8 -0
  68. package/dist/capability-facts.d.ts +3 -7
  69. package/dist/capability-facts.js +3 -5
  70. package/dist/center-credential-renewer.js +5 -1
  71. package/dist/center-credential.d.ts +18 -4
  72. package/dist/center-credential.js +18 -3
  73. package/dist/checkpoint-ctx-seal-m28.d.ts +3 -10
  74. package/dist/checkpoint-ctx-seal-m28.js +1 -1
  75. package/dist/checkpoint-identity.d.ts +3 -3
  76. package/dist/config-catalog.js +21 -16
  77. package/dist/config-d-migration.js +1 -1
  78. package/dist/config-invariants.d.ts +1 -1
  79. package/dist/config-invariants.js +8 -0
  80. package/dist/config-lkg.d.ts +0 -38
  81. package/dist/config-lkg.js +1 -83
  82. package/dist/config-types.d.ts +21 -4
  83. package/dist/config.d.ts +1 -1
  84. package/dist/config.js +60 -16
  85. package/dist/deployment-governance.d.ts +2 -2
  86. package/dist/deployment-governance.js +7 -9
  87. package/dist/deployment-shape.d.ts +1 -1
  88. package/dist/device-ws-protocol.d.ts +1 -0
  89. package/dist/device-ws-protocol.js +2 -1
  90. package/dist/elicitation.d.ts +8 -7
  91. package/dist/elicitation.js +9 -8
  92. package/dist/file-history-disclosure.d.ts +8 -4
  93. package/dist/file-history-disclosure.js +24 -6
  94. package/dist/fleet-client.d.ts +32 -1
  95. package/dist/fleet-client.js +24 -4
  96. package/dist/fleet-lease.d.ts +7 -3
  97. package/dist/fleet-lease.js +31 -23
  98. package/dist/hooks/classic-stations.d.ts +11 -54
  99. package/dist/hooks/hook-llm.js +14 -8
  100. package/dist/hooks/hook-runner.d.ts +4 -4
  101. package/dist/hooks/hook-runner.js +2 -2
  102. package/dist/http/admission.js +10 -7
  103. package/dist/http/approval-sweeps.js +4 -3
  104. package/dist/http/caller-trust.d.ts +2 -1
  105. package/dist/http/caller-trust.js +1 -3
  106. package/dist/http/checkpoint-ctx.d.ts +76 -16
  107. package/dist/http/checkpoint-ctx.js +45 -1
  108. package/dist/http/dispatch.js +24 -19
  109. package/dist/http/durable-run-leg.d.ts +5 -1
  110. package/dist/http/durable-run-leg.js +1 -1
  111. package/dist/http/health-self-report.d.ts +2 -2
  112. package/dist/http/mcp-remote-text.d.ts +5 -0
  113. package/dist/http/mcp-remote-text.js +7 -0
  114. package/dist/http/principal-gate.d.ts +15 -10
  115. package/dist/http/principal-gate.js +48 -9
  116. package/dist/http/resume-ctx-read.d.ts +2 -16
  117. package/dist/http/resume-ctx-read.js +2 -3
  118. package/dist/http/resume-legs.d.ts +2 -20
  119. package/dist/http/resume-legs.js +52 -38
  120. package/dist/http/route-ctx.d.ts +8 -2
  121. package/dist/http/routes/approvals-assistant.d.ts +0 -2
  122. package/dist/http/routes/approvals-assistant.js +65 -16
  123. package/dist/http/routes/capabilities.js +11 -1
  124. package/dist/http/routes/mcp-reconnect.d.ts +1 -1
  125. package/dist/http/routes/mcp-reconnect.js +5 -7
  126. package/dist/http/routes/runs.js +4 -3
  127. package/dist/http/routes/session-sync.js +70 -12
  128. package/dist/http/routes/sessions.js +20 -15
  129. package/dist/http/routes/tasks.js +8 -6
  130. package/dist/http/server.d.ts +4 -0
  131. package/dist/http/stored-body-replay.d.ts +3 -3
  132. package/dist/http/stored-body-replay.js +6 -1
  133. package/dist/http/submit-ctx.js +1 -1
  134. package/dist/http/wire-types.d.ts +57 -30
  135. package/dist/identity-source.d.ts +65 -0
  136. package/dist/identity-source.js +45 -0
  137. package/dist/index.d.ts +1 -0
  138. package/dist/index.js +1 -0
  139. package/dist/memory-store-refusal.d.ts +3 -3
  140. package/dist/model-entry-refusal.d.ts +2 -0
  141. package/dist/model-entry-refusal.js +11 -0
  142. package/dist/model-route-gap.d.ts +49 -4
  143. package/dist/model-route-gap.js +34 -1
  144. package/dist/model-select.d.ts +6 -17
  145. package/dist/model-select.js +0 -3
  146. package/dist/namespaced-rule-name.d.ts +1 -1
  147. package/dist/object-store-buckets.d.ts +13 -0
  148. package/dist/object-store-buckets.js +3 -0
  149. package/dist/observability/fail-open.d.ts +19 -7
  150. package/dist/observability/fail-open.js +19 -7
  151. package/dist/observability/metrics.js +2 -2
  152. package/dist/observability/principal-context.d.ts +4 -1
  153. package/dist/observability/principal-context.js +6 -1
  154. package/dist/observability/run-terminal.js +1 -0
  155. package/dist/operator-ask.d.ts +2 -2
  156. package/dist/org-delivery-gate.js +1 -1
  157. package/dist/org-memory-admission.d.ts +2 -2
  158. package/dist/org-memory-admission.js +39 -2
  159. package/dist/parked-decide.d.ts +4 -3
  160. package/dist/parked-decide.js +29 -9
  161. package/dist/peer-directory.d.ts +2 -1
  162. package/dist/peer-directory.js +7 -5
  163. package/dist/permission-rule-list.d.ts +2 -2
  164. package/dist/plugins/approval-ask-store-sql.d.ts +8 -0
  165. package/dist/plugins/approval-ask-store-sql.js +3 -0
  166. package/dist/plugins/blob-backend.js +2 -1
  167. package/dist/plugins/checkpoint-index-columns.d.ts +50 -1
  168. package/dist/plugins/checkpoint-index-columns.js +28 -0
  169. package/dist/plugins/checkpoint-store-sql.d.ts +46 -12
  170. package/dist/plugins/checkpoint-store-sql.js +64 -35
  171. package/dist/plugins/file-history-store-sql.d.ts +6 -0
  172. package/dist/plugins/file-history-store-sql.js +37 -37
  173. package/dist/plugins/file-run-store.js +8 -2
  174. package/dist/plugins/k8s-exec-protocol.d.ts +69 -13
  175. package/dist/plugins/k8s-exec-protocol.js +116 -28
  176. package/dist/plugins/k8s-pod-delete-failure.d.ts +25 -0
  177. package/dist/plugins/k8s-pod-delete-failure.js +17 -0
  178. package/dist/plugins/k8s-pod-reaper.d.ts +112 -0
  179. package/dist/plugins/k8s-pod-reaper.js +122 -0
  180. package/dist/plugins/local-checkpoint-store.d.ts +3 -6
  181. package/dist/plugins/local-checkpoint-store.js +7 -6
  182. package/dist/plugins/memory-run-store.js +2 -0
  183. package/dist/plugins/one-time-migrations.d.ts +49 -37
  184. package/dist/plugins/one-time-migrations.js +87 -128
  185. package/dist/plugins/permission-rule-store-file.js +2 -2
  186. package/dist/plugins/permission-rule-store-sql.d.ts +17 -17
  187. package/dist/plugins/permission-rule-store-sql.js +16 -17
  188. package/dist/plugins/pg-pool.js +4 -0
  189. package/dist/plugins/pg-session-storage.js +2 -2
  190. package/dist/plugins/remote-env-adb.d.ts +3 -0
  191. package/dist/plugins/remote-env-adb.js +1 -0
  192. package/dist/plugins/remote-env-device.d.ts +3 -0
  193. package/dist/plugins/remote-env-device.js +4 -0
  194. package/dist/plugins/remote-env-e2b.d.ts +3 -0
  195. package/dist/plugins/remote-env-e2b.js +1 -0
  196. package/dist/plugins/remote-env-file-error.d.ts +8 -1
  197. package/dist/plugins/remote-env-file-error.js +7 -1
  198. package/dist/plugins/remote-env-host.d.ts +7 -1
  199. package/dist/plugins/remote-env-host.js +7 -1
  200. package/dist/plugins/remote-env-k8s.d.ts +154 -14
  201. package/dist/plugins/remote-env-k8s.js +300 -21
  202. package/dist/plugins/remote-env-local-docker.d.ts +3 -0
  203. package/dist/plugins/remote-env-local-docker.js +1 -0
  204. package/dist/plugins/remote-env-ssh.d.ts +3 -0
  205. package/dist/plugins/remote-env-ssh.js +1 -0
  206. package/dist/plugins/run-store-sql.js +10 -4
  207. package/dist/plugins/s3-answer.d.ts +15 -0
  208. package/dist/plugins/s3-answer.js +5 -0
  209. package/dist/plugins/session-ownership.d.ts +17 -3
  210. package/dist/plugins/session-ownership.js +19 -5
  211. package/dist/plugins/snapshot-bucket-presence.d.ts +39 -4
  212. package/dist/plugins/snapshot-bucket-presence.js +35 -7
  213. package/dist/plugins/sql-errors.d.ts +60 -0
  214. package/dist/plugins/sql-errors.js +64 -1
  215. package/dist/plugins/store-contracts.d.ts +10 -1
  216. package/dist/plugins/store-fault.d.ts +4 -0
  217. package/dist/plugins/store-fault.js +10 -0
  218. package/dist/plugins/tidb-pool.js +8 -1
  219. package/dist/plugins/tidb-session-store.js +2 -2
  220. package/dist/plugins/web-search.d.ts +1 -1
  221. package/dist/plugins/web-search.js +2 -2
  222. package/dist/request-declaration-refusal.d.ts +94 -0
  223. package/dist/request-declaration-refusal.js +78 -0
  224. package/dist/request-image-urls.d.ts +6 -0
  225. package/dist/request-image-urls.js +27 -0
  226. package/dist/request-key-closure.d.ts +5 -0
  227. package/dist/request-key-closure.js +5 -2
  228. package/dist/request-value-closure.d.ts +31 -2
  229. package/dist/request-value-closure.js +7 -1
  230. package/dist/rule-entry-list.d.ts +3 -2
  231. package/dist/rule-entry-list.js +2 -2
  232. package/dist/rules-consent.js +7 -4
  233. package/dist/run-local.d.ts +2 -2
  234. package/dist/runs.js +2 -1
  235. package/dist/runtime-caps-resolver.js +1 -1
  236. package/dist/runtime-governance.d.ts +5 -5
  237. package/dist/sandbox-git-lease.d.ts +49 -0
  238. package/dist/sandbox-git-lease.js +60 -0
  239. package/dist/sandbox-git-plane.d.ts +84 -0
  240. package/dist/sandbox-git-plane.js +105 -0
  241. package/dist/sandbox-pkg-source.d.ts +28 -1
  242. package/dist/sandbox-pkg-source.js +1 -2
  243. package/dist/sealed-key.d.ts +19 -0
  244. package/dist/sealed-key.js +47 -4
  245. package/dist/security.d.ts +16 -4
  246. package/dist/security.js +8 -3
  247. package/dist/server-secret-env.d.ts +6 -0
  248. package/dist/server-secret-env.js +14 -0
  249. package/dist/session-error-wire.d.ts +14 -6
  250. package/dist/session-error-wire.js +2 -1
  251. package/dist/session-incarnation.d.ts +16 -5
  252. package/dist/session-incarnation.js +7 -4
  253. package/dist/session-policy-wire.d.ts +1 -1
  254. package/dist/session-policy-wire.js +2 -2
  255. package/dist/session-row.d.ts +3 -17
  256. package/dist/session-row.js +4 -0
  257. package/dist/session-sync.d.ts +15 -3
  258. package/dist/session-sync.js +5 -2
  259. package/dist/spec-fields.d.ts +43 -13
  260. package/dist/spec-fields.js +33 -12
  261. package/dist/stored-request-prompt-profile-m30.d.ts +49 -0
  262. package/dist/stored-request-prompt-profile-m30.js +25 -0
  263. package/dist/task-a2a.d.ts +52 -81
  264. package/dist/task-a2a.js +219 -89
  265. package/dist/task-mcp.d.ts +7 -15
  266. package/dist/task-mcp.js +16 -51
  267. package/dist/task-settings.d.ts +2 -2
  268. package/dist/tool-approval.d.ts +35 -12
  269. package/dist/tool-approval.js +59 -39
  270. package/dist/tool-rule-names.d.ts +5 -0
  271. package/dist/trace/core-keyset-guard.d.ts +5 -5
  272. package/dist/trace/engine-notice-wire.d.ts +1 -1
  273. package/dist/trace/engine-notice-wire.js +2 -0
  274. package/dist/trace/exit-seat.js +37 -13
  275. package/dist/trace/ledger-sink.d.ts +4 -5
  276. package/dist/trace/ledger-sink.js +3 -7
  277. package/dist/trace/project.d.ts +37 -3
  278. package/dist/trace/project.js +20 -6
  279. package/dist/trace/redact.d.ts +40 -12
  280. package/dist/trace/redact.js +332 -128
  281. package/dist/trace/self-minted-wire.d.ts +641 -0
  282. package/dist/trace/self-minted-wire.js +201 -0
  283. package/dist/trace/wire-projection-faces.d.ts +5 -3
  284. package/dist/trace/wire-projection-faces.js +7 -3
  285. package/dist/url-credential-refusal.d.ts +52 -0
  286. package/dist/url-credential-refusal.js +14 -0
  287. package/dist/url-refusal.d.ts +13 -4
  288. package/dist/url-refusal.js +8 -2
  289. package/dist/web-admin-client/apply-effective.js +0 -1
  290. package/dist/web-admin-client/center-request.d.ts +20 -2
  291. package/dist/web-admin-client/center-request.js +40 -5
  292. package/dist/web-admin-client/http-client.js +6 -6
  293. package/dist/web-admin-client/types.d.ts +5 -5
  294. package/dist/workspace-space.d.ts +27 -0
  295. package/dist/workspace-space.js +26 -0
  296. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +11 -5
  297. package/package.json +4 -3
  298. package/scripts/drain-check-local.mjs +2 -1
  299. package/scripts/drain-check.mysql.sql +3 -1
  300. package/scripts/drain-check.pg.sql +3 -1
  301. package/skills/schedule.md +1 -1
  302. package/wire-changes/server@7.104.0.json +134 -0
  303. package/wire-changes/server@7.105.0.json +53 -0
  304. package/wire-changes/server@7.106.0.json +475 -0
  305. package/wire-changes/server@7.107.0.json +64 -0
  306. package/wire-changes/server@7.108.0.json +153 -0
  307. package/wire-changes/server@7.109.0.json +46 -0
  308. package/wire-changes/server@7.110.0.json +90 -0
  309. package/wire-changes/server@7.111.0.json +86 -0
  310. package/wire-changes/server@7.112.0.json +60 -0
  311. package/wire-changes/server@7.113.0.json +26 -0
  312. package/wire-changes/server@7.114.0.json +26 -0
  313. package/wire-changes/server@7.115.0.json +97 -0
  314. package/wire-changes/server@7.116.0.json +218 -0
  315. package/wire-changes/server@7.116.1.json +26 -0
  316. package/wire-changes/server@7.117.0.json +65 -0
  317. package/wire-changes/server@7.118.0.json +129 -0
  318. package/wire-changes/server@7.119.0.json +65 -0
  319. package/wire-changes/server@7.120.0.json +89 -0
  320. package/wire-changes/server@7.121.0.json +219 -0
  321. package/wire-changes/server@7.122.0.json +54 -0
  322. package/wire-changes/server@7.123.0.json +26 -0
  323. package/wire-changes/server@7.124.0.json +336 -0
  324. package/wire-changes/server@7.124.1.json +26 -0
  325. package/wire-changes/server@7.125.0.json +111 -0
  326. package/wire-changes/server@7.126.0.json +285 -0
  327. package/wire-changes/server@7.127.0.json +77 -0
  328. package/wire-changes/server@7.127.1.json +26 -0
  329. package/wire-changes/server@7.127.2.json +53 -0
  330. package/wire-changes/server@7.128.0.json +26 -0
  331. package/wire-changes/server@7.129.0.json +106 -0
  332. package/wire-changes/server@7.129.1.json +26 -0
  333. package/wire-changes/server@7.130.0.json +26 -0
  334. package/wire-changes/server@7.131.0.json +118 -0
  335. package/wire-changes/server@7.132.0.json +226 -0
  336. package/wire-changes/server@7.133.0.json +1155 -0
  337. package/wire-changes/server@7.134.0.json +107 -0
  338. package/wire-changes/server@7.135.0.json +174 -0
  339. package/wire-changes/server@7.136.0.json +448 -0
  340. package/wire-changes/server@7.137.0.json +825 -0
  341. package/dist/url-credential.d.ts +0 -52
  342. package/dist/url-credential.js +0 -49
@@ -0,0 +1,20 @@
1
+ import { BROKER_HOST, BROKER_LEASE_PATH, BROKER_LEASE_POLL_MS, BROKER_PORT, BROKER_PROBE_ARG } from "./constants.js";
2
+ import { probeBroker, runBroker } from "./broker.js";
3
+ const PROBE_TIMEOUT_MS = 2_000;
4
+ async function main() {
5
+ if (process.argv.includes(BROKER_PROBE_ARG)) {
6
+ process.exit((await probeBroker(BROKER_HOST, BROKER_PORT, PROBE_TIMEOUT_MS)) ? 0 : 1);
7
+ }
8
+ await runBroker({
9
+ leasePath: BROKER_LEASE_PATH,
10
+ host: BROKER_HOST,
11
+ port: BROKER_PORT,
12
+ pollMs: BROKER_LEASE_POLL_MS,
13
+ log: (e) => void process.stdout.write(`${JSON.stringify(e)}\n`),
14
+ });
15
+ }
16
+ main().catch((e) => {
17
+ process.stderr.write(`${JSON.stringify({ event: "broker_failed", error: e instanceof Error ? e.message : String(e) })}\n`);
18
+ process.exit(1);
19
+ });
20
+ //# sourceMappingURL=main.js.map
package/dist/budget.d.ts CHANGED
@@ -8,7 +8,7 @@
8
8
  import { type Model, type ModelPricing, type TracerHook } from "@sema-agent/core";
9
9
  import type { Metrics } from "./observability/metrics.js";
10
10
  import type { QuotaTracker } from "./observability/cost-quota.js";
11
- import type { FleetUsageAccumulator } from "./fleet-client.js";
11
+ import type { FleetUsageBins } from "./fleet-client.js";
12
12
  import type { FleetLeaseManager } from "./fleet-lease.js";
13
13
  import { type PromptManifestTracker } from "./observability/prompt-manifest.js";
14
14
  /** ③ Per-model pricing keyed by model id (core falls back to each Model.cost when a model is absent).
@@ -108,7 +108,7 @@ export { type PromptManifestRecord, promptManifestRecordOf, configAssembledRecor
108
108
  * fires for EVERY call (top-level + async + council sub-tasks) → `model_cost_micro_usd_total` is the authoritative
109
109
  * spend, and (when a {@link ModelUsageTracker} is supplied) the per-task `modelUsage` echo's source.
110
110
  */
111
- export declare function createTracer(metrics: Pick<Metrics, "inc" | "observe">, costQuota?: QuotaTracker, modelUsage?: ModelUsageTracker, fleetUsage?: Pick<FleetUsageAccumulator, "record">, fleetLease?: Pick<FleetLeaseManager, "recordSpend">, quotaWeightFor?: (model: string) => number, promptManifests?: PromptManifestTracker): TracerHook;
111
+ export declare function createTracer(metrics: Pick<Metrics, "inc" | "observe">, costQuota?: QuotaTracker, modelUsage?: ModelUsageTracker, fleetUsage?: Pick<FleetUsageBins, "record" | "pinExecution">, fleetLease?: Pick<FleetLeaseManager, "recordSpend" | "pinExecution">, quotaWeightFor?: (model: string) => number, promptManifests?: PromptManifestTracker): TracerHook;
112
112
  /**
113
113
  * [ref] side-query 记账 seam(codex R3 high:sideQuery 不走 core tracer——brain.stream 直调不发
114
114
  * brain.call——createTracer 的四路 sink(costQuota/model_cost 指标/fleetUsage 批报/fleetLease 本地扣减)
@@ -121,7 +121,7 @@ export declare function createTracer(metrics: Pick<Metrics, "inc" | "observe">,
121
121
  * (保守,绝不少收);根修=core 在 SideQueryResult 上回权威 costMicroUsd(已提货 core)。model 标签/
122
122
  * weight 键用**路由身份 r.model**(池配置有界)非 servedModel(provider 上报,无界)。
123
123
  */
124
- export declare function createSideQueryAccountant(metrics: Pick<Metrics, "inc">, costQuota?: QuotaTracker, fleetUsage?: Pick<FleetUsageAccumulator, "record">, fleetLease?: Pick<FleetLeaseManager, "recordSpend">, quotaWeightFor?: (model: string) => number): (principal: string | undefined, r: {
124
+ export declare function createSideQueryAccountant(metrics: Pick<Metrics, "inc">, costQuota?: QuotaTracker, fleetUsage?: Pick<FleetUsageBins, "record">, fleetLease?: Pick<FleetLeaseManager, "recordSpend">, quotaWeightFor?: (model: string) => number): (principal: string | undefined, r: {
125
125
  model: string;
126
126
  usage?: {
127
127
  input?: number;
package/dist/budget.js CHANGED
@@ -77,6 +77,10 @@ export function createTracer(metrics, costQuota, modelUsage, fleetUsage, fleetLe
77
77
  const modelId = cardinalityGuard(256, () => metrics.inc("metric_label_overflow_total", { label: "model" }));
78
78
  const providerName = cardinalityGuard(64, () => metrics.inc("metric_label_overflow_total", { label: "provider" }));
79
79
  return (e) => {
80
+ if (e.kind === "task.start") {
81
+ fleetUsage?.pinExecution();
82
+ fleetLease?.pinExecution();
83
+ }
80
84
  if (e.kind === "brain.call") {
81
85
  metrics.inc("brain_calls_total", { provider: providerName(e.provider ?? "unknown"), model: modelId(e.model) });
82
86
  if (e.costMicroUsd) {
@@ -38,7 +38,7 @@ import type { NamedKeysOf } from "../request-key-closure.js";
38
38
  * B.2b)与 CHANGELOG。present-iff 的位与成员照样登记(在场律写在附录的「在场律」列)。
39
39
  */
40
40
  export declare const COMPOSITE_CAPABILITY_MEMBERS: {
41
- readonly deviceExecutor: readonly ["enabled", "protocolVersion", "maxInflightPerDevice", "wsPath", "management"];
41
+ readonly deviceExecutor: readonly ["enabled", "protocolVersion", "maxInflightPerDevice", "wsPath", "management", "relayUrl"];
42
42
  readonly fleet: readonly ["stream", "sessionScope", "observe", "resume", "bgNotifyFailClosed", "steerPriority", "hookFailureNotice", "leaseClient", "generationEcho"];
43
43
  readonly memoryEngine: readonly ["backend", "vectorMode"];
44
44
  readonly store: readonly ["declared", "backend", "durable", "degradedReason", "sql"];
@@ -55,6 +55,7 @@ export declare const COMPOSITE_CAPABILITY_MEMBERS: {
55
55
  readonly sealedKey: readonly ["alg", "publicKeyId", "publicKey"];
56
56
  readonly workspaceSync: readonly ["persist", "keep", "timeoutMs", "cache"];
57
57
  readonly userAttachments: readonly ["entries", "kinds", "supplies", "limits"];
58
+ readonly credentialPlane: readonly ["sandboxGit"];
58
59
  };
59
60
  type CompositeTable = typeof COMPOSITE_CAPABILITY_MEMBERS;
60
61
  /** 值的**具名成员对象臂**(标量 / 字面词 / `false` / `null` / `undefined` / 数组 / 函数臂剥掉)。 */
@@ -1,5 +1,5 @@
1
1
  export const COMPOSITE_CAPABILITY_MEMBERS = {
2
- deviceExecutor: ["enabled", "protocolVersion", "maxInflightPerDevice", "wsPath", "management"],
2
+ deviceExecutor: ["enabled", "protocolVersion", "maxInflightPerDevice", "wsPath", "management", "relayUrl"],
3
3
  fleet: ["stream", "sessionScope", "observe", "resume", "bgNotifyFailClosed", "steerPriority", "hookFailureNotice", "leaseClient", "generationEcho"],
4
4
  memoryEngine: ["backend", "vectorMode"],
5
5
  store: ["declared", "backend", "durable", "degradedReason", "sql"],
@@ -16,6 +16,7 @@ export const COMPOSITE_CAPABILITY_MEMBERS = {
16
16
  sealedKey: ["alg", "publicKeyId", "publicKey"],
17
17
  workspaceSync: ["persist", "keep", "timeoutMs", "cache"],
18
18
  userAttachments: ["entries", "kinds", "supplies", "limits"],
19
+ credentialPlane: ["sandboxGit"],
19
20
  };
20
21
  export function gateCompositeMembers(body) {
21
22
  return body;
@@ -3,6 +3,7 @@ import { ownCredentialsWithheld, ownCredentialValuesWithheld } from "../server-s
3
3
  const ENGINE_CAPABILITY_FACTS = {
4
4
  "inherit_env.own_credentials_withheld": ownCredentialsWithheld,
5
5
  "inherit_env.own_credential_values_withheld": ownCredentialValuesWithheld,
6
+ "mods.skill_seat": () => false,
6
7
  };
7
8
  export function engineCapabilityBits() {
8
9
  const out = {};
@@ -33,7 +33,7 @@
33
33
  * 在 core 那边没有任何期限(core `docs/KNOWN-LIMITS.md`)—— 那一形上本面同样没有期限,补偿只有限速与缓存。
34
34
  */
35
35
  import { type McpServerSpec } from "@sema-agent/core";
36
- import type { UrlCredentialFacts } from "../url-credential.js";
36
+ import type { DeclarationRefusal } from "../request-declaration-refusal.js";
37
37
  /**
38
38
  * 本面的**拒码闭集**(设计稿 §2 的四枚)。闭集写成型,是为了让 {@link MCP_PROBE_HTTP_STATUS} 那张表
39
39
  * 「漏一枚 = 编译红」—— [ref] 的安全轴纪律:词表是闭集,未处置的成员必须是编译错误而不是运行期惊喜。
@@ -158,8 +158,9 @@ export interface McpProbeRefusal {
158
158
  readonly message: string;
159
159
  /** `request.body_shape` 专用:词表外 / 本面不接线的键,全路径形(已清洗+截断+排序+封顶)。 */
160
160
  readonly unknownKeys?: readonly string[];
161
- /** [ref]:申报的 `transport.url` 带凭据 ⇒ `field` + `reason`(与提交腿同一只回答者交回的同一组机读位,原样转述)。 */
162
- readonly facts?: UrlCredentialFacts;
161
+ /** 申报超上限 ⇒ 撞帽机读位一组键(`field` · `reason:"over_cap"` · `count` · `cap`);申报的 `transport.url` 带凭据 ⇒ `field` + `reason` ——
162
+ * 与提交腿同一只回答者交回的同一组机读位,原样转述。 */
163
+ readonly facts?: DeclarationRefusal["facts"];
163
164
  }
164
165
  /**
165
166
  * `POST …/probe` 的体读器:闭形、逐条点名、零静默丢。成功 ⇒ 交给 core 的 `specs`(**原样**转发被读出来的
@@ -167,7 +168,7 @@ export interface McpProbeRefusal {
167
168
  *
168
169
  * ## 申报「能不能原样用」:问提交腿 fresh 门**同一只**回答者,不另判
169
170
  * 本面只判自己那一层(体是对象、顶层只受理 `mcpServers`、这个键必填);`mcpServers` 本身 —— 列表的形、上限、每条的键名与键值 —— 全部交给
170
- * `task-mcp.ts` 的 `requestMcpDeclarationRefusal`,拒(码 + 句 + `unknownKeys` + url 带凭据那一形的 `field` / `reason`)原样转述:同一份申报在两扇门上只有一个答案,零拨号之前就给。
171
+ * `task-mcp.ts` 的 `requestMcpDeclarationRefusal`,拒(码 + 句 + `unknownKeys` + 撞帽 / url 带凭据两形的机读位)原样转述:同一份申报在两扇门上只有一个答案,零拨号之前就给。
171
172
  * 回答者受理的申报由**同一只**读器(`validateRequestMcp`)交回,读器的产物就是申报本身(只差判官自己的合法归一),经 `toCoreMcpServerSpec`
172
173
  * 得到的就是一条任务提交会交给引擎的那一份 —— 本面拨的正是它。读器只在回答者受理之后才跑,所以它对重放存量 body 才有的降级留痕
173
174
  * (`source` 写坏的 F 类丢弃)不会在一次被拒的探测上虚记。
@@ -2,7 +2,7 @@ import { type WebFetchConfig, type BackgroundAgentStore, type CheckpointStore, t
2
2
  import type { Logger } from "../observability/logger.js";
3
3
  import { type SubagentExtraToolSet } from "./builtin-tools.js";
4
4
  import { type RepoReadClient } from "./repo-tools.js";
5
- import { type LoadedSkill, type ScenarioSkillEntry } from "./skills.js";
5
+ import { type LoadedSkill, type RunSkillEntry, type ScenarioSkillEntry, type SkillSource } from "./skills.js";
6
6
  import { type ScenarioHands } from "./hands-lane.js";
7
7
  import type { DelegationRootWitness } from "../delegated-session-owner.js";
8
8
  /**
@@ -315,14 +315,29 @@ export declare function centerScenarios(deps: ScenarioDeps, specs: CenterScenari
315
315
  shadows: string[];
316
316
  };
317
317
  /**
318
- * Personalization seam: merge the caller's own skills AFTER the scenario's, with
319
- * SCENARIO-WINS on a name clash — the scenario / center skills own the name (they are the deployment's baseline
320
- * wording, not an enforcement face: permissions / write gates / hooks do not ride on skills, and a caller may
321
- * unload the whole set with `disableSkills`, [ref]); user content never shadows them. Duplicate names within the user list keep the first. Clashes are logged for audit
322
- * (silent drops would make "my skill didn't fire" undebuggable). Input is already shape/size-validated
323
- * by the HTTP layer (validateUserSkills); this stays pure merge policy.
318
+ * 一条 run 的技能(交给引擎的 `TaskSpec.skills`)与它的名册(`wiring_manifest.skills`)—— **同一次合并的两个投影**,铸点只有 {@link buildRunSkills}。
319
+ * · `specs` —— 交给引擎的那一份(`undefined` = 一只都没有且没被关;`disable` ⇒ 显式 `[]`,引擎对空表零 `<skills>` 块、不挂 `Skill`);
320
+ * · `roster` —— 同一组、同一个序,逐条带来处;零技能 ⇒ `[]`(零技能是一句陈述,不是缺席)。
324
321
  */
325
- export declare function mergeUserSkills(scenarioSkills: SkillSpec[] | undefined, userSkills: SkillSpec[] | undefined, logger?: Logger): SkillSpec[] | undefined;
322
+ export interface RunSkills {
323
+ readonly specs: SkillSpec[] | undefined;
324
+ readonly roster: RunSkillEntry[];
325
+ }
326
+ /**
327
+ * Personalization seam + run 名册的**唯一铸点**:调用方自带的技能接在场景的之后,**同名场景赢**(场景 / 中心的技能拥有那个名字 —— 它们是部署的基线措辞,
328
+ * 不是执法面:权限 / 写门 / hook 都不骑在技能上,调用方要整组卸掉用 `disableSkills`,[ref]);用户内容永不遮住它们。用户表内重名留第一个。
329
+ * 被丢的名字落审计行(静默丢会让「我的技能没生效」查不出来)。形已由 HTTP 门判过(`validateUserSkills`);本函数只做合并政策与来处。
330
+ *
331
+ * 来处(`roster[].source`):场景那一半按**部署装载簿**的来处标(`deploymentSources`:装载腿写下的 `LoadedSkill.source`,名字在装载簿里唯一 ——
332
+ * 装载器同名即抛、配置源按名覆盖);场景里有、装载簿里没有的技能是嵌入方场景代码自带的 ⇒ `factory`(本部署自带,不是配置源交来的、也不是这次请求带的)。
333
+ * 请求那一半恒 `request`。`disable` ⇒ 两个投影都空(`specs: []`、`roster: []`)。
334
+ */
335
+ export declare function buildRunSkills(args: {
336
+ readonly scenario: SkillSpec[] | undefined;
337
+ readonly request: readonly SkillSpec[] | undefined;
338
+ readonly disable: boolean;
339
+ readonly deploymentSources: ReadonlyMap<string, SkillSource>;
340
+ }, logger?: Logger): RunSkills;
326
341
  /** center caps view 的解析结果两键(形状锚定)。`scenario` null = 三层皆未配。 */
327
342
  export interface ScenarioRuling {
328
343
  scenario: string | null;
@@ -262,23 +262,28 @@ const REVIEWER_ROLE_TEXT = "You are a senior code reviewer. Call repo_tree to ma
262
262
  "the files in scope, then review them yourself. " +
263
263
  "Produce ONE prioritized review: a short summary, then findings grouped by severity " +
264
264
  "(blocker / major / minor), each with `path:line`, the problem, and a concrete fix. Cite real code — never invent files or symbols.";
265
- export function mergeUserSkills(scenarioSkills, userSkills, logger) {
266
- if (!userSkills || userSkills.length === 0)
267
- return scenarioSkills;
268
- const reserved = new Set((scenarioSkills ?? []).map((s) => s.name));
269
- const out = [...(scenarioSkills ?? [])];
265
+ export function buildRunSkills(args, logger) {
266
+ if (args.disable)
267
+ return { specs: [], roster: [] };
268
+ const scenario = args.scenario ?? [];
269
+ const roster = scenario.map((s) => ({ name: s.name, description: s.description, source: args.deploymentSources.get(s.name) ?? "factory" }));
270
+ if (!args.request || args.request.length === 0)
271
+ return { specs: args.scenario, roster };
272
+ const reserved = new Set(scenario.map((s) => s.name));
273
+ const specs = [...scenario];
270
274
  const dropped = [];
271
- for (const s of userSkills) {
275
+ for (const s of args.request) {
272
276
  if (reserved.has(s.name)) {
273
277
  dropped.push(s.name);
274
278
  continue;
275
279
  }
276
280
  reserved.add(s.name);
277
- out.push(s);
281
+ specs.push(s);
282
+ roster.push({ name: s.name, description: s.description, source: "request" });
278
283
  }
279
284
  if (dropped.length > 0)
280
285
  logger?.warn("user_skills_dropped", { reason: "name reserved by scenario (or duplicate)", names: dropped });
281
- return out;
286
+ return { specs, roster };
282
287
  }
283
288
  function registeredScenariosClause(known) {
284
289
  return `this deployment's scenarios are: ${known.join(", ")}`;
@@ -1,4 +1,4 @@
1
- import type { SkillSpec } from "@sema-agent/core";
1
+ import type { SkillSpec, TaskSpec } from "@sema-agent/core";
2
2
  import type { RemoteExecProvider } from "../execution-lane-caps.js";
3
3
  import { type Logger } from "../observability/logger.js";
4
4
  /**
@@ -90,14 +90,37 @@ export declare function loadDeploymentSkills(config: {
90
90
  /** 一个技能范围交进 `TaskSpec` 的技能(场景工厂的 `skills`)。范围从哪来见 `scenarios.ts` 的 `BUILTIN_SCENARIO_SKILL_SCOPE`(center 场景 = 它自己的名字)。 */
91
91
  export declare function skillsForScenario(loaded: readonly LoadedSkill[], scope: string): SkillSpec[];
92
92
  /**
93
- * [ref] —— 场景卡技能清单的一条(`GET /v1/capabilities/scenarios/:name` 的 `skills[]`):名字 / 描述 / 来处,**不带正文**,也不带技能上的两只工具清单
94
- * (`allowedTools` / `disallowedTools`)。名字就是提交体 `userInvokedSkills` 点名时用的那个串。
93
+ * 技能清单的一条 —— **一张形,两个面**:场景卡(`GET /v1/capabilities/scenarios/:name` 的 `skills[]`,[ref])与 run 级名册
94
+ * (`wiring_manifest.skills[]`)。名字 / 描述 / 来处,**不带正文**,也不带技能上的两只工具清单(`allowedTools` / `disallowedTools`)。
95
+ * 名字就是提交体 `userInvokedSkills` 点名时用的那个串。两个面只差来处的值域(`S`):场景卡上只有部署两处,run 名册上多一个 `request`。
95
96
  */
96
- export interface ScenarioSkillEntry {
97
+ export interface SkillListEntry<S extends string> {
97
98
  readonly name: string;
98
99
  readonly description: string;
99
- readonly source: SkillSource;
100
+ readonly source: S;
100
101
  }
102
+ /** 场景卡技能清单的一条(来处 = 部署两处)。 */
103
+ export type ScenarioSkillEntry = SkillListEntry<SkillSource>;
104
+ /** run 级技能来处的闭集 = 部署两处({@link SkillSource})∪ 本请求体 `skills`。 */
105
+ export type RunSkillSource = SkillSource | "request";
106
+ /** run 级名册的一条(`wiring_manifest.skills[]`)。铸点唯一:`capabilities/scenarios.ts` 的 `buildRunSkills`。 */
107
+ export type RunSkillEntry = SkillListEntry<RunSkillSource>;
108
+ /**
109
+ * run 级技能名册在 TaskSpec 上的**显式字段键** —— 与执行车道字段(`hands-lane.ts` 的 `HANDS_LANE_FIELD`,[ref])同一形:symbol 键随
110
+ * spread / rest 解构走(续跑腿的 `taskConfig` 就是 rest 解构出来的),对上游 core 的键面不可见(JSON / 结构化克隆跳过 symbol 键)。
111
+ * 铸点唯一 = `boot/resolve-spec.ts` 的 resolveSpec 出口({@link stampRunSkills});读点唯一 = {@link runSkillsOf}(每条投 `wiring_manifest` 的腿)。
112
+ */
113
+ export declare const RUN_SKILLS_FIELD: unique symbol;
114
+ /**
115
+ * 铸名册(**单一属主**:只许 resolveSpec 的出口调用)。字段 `enumerable`(复制得走)、`writable:false` + `configurable:false`(同一只 spec 被第二次铸 ⇒
116
+ * 当场 TypeError,两个写者是漂移的成因)。名册冻结一份拷贝(spec 与腿不共享可变引用)。
117
+ */
118
+ export declare function stampRunSkills<T extends TaskSpec>(spec: T, roster: readonly RunSkillEntry[]): T;
119
+ /**
120
+ * 读名册。`undefined` = 这只 spec 不是本仓装配出口产的(嵌入方自写 `resolveSpec` / 测试桩)⇒ 腿上没有关于技能的陈述(段缺席)。
121
+ * 不是保护型量(缺席只少一段披露,不放宽任何东西)⇒ 缺席照常跑,与车道字段的「缺席即拒」不同。入参收 `object`:续跑腿手里是 rest 解构出来的 taskConfig。
122
+ */
123
+ export declare function runSkillsOf(spec: object): readonly RunSkillEntry[] | undefined;
101
124
  /** 一个技能范围的卡上清单 —— 与 {@link skillsForScenario} 同一只过滤、同一个序,只是投影不同。 */
102
125
  export declare function scenarioSkillEntries(loaded: readonly LoadedSkill[], scope: string): ScenarioSkillEntry[];
103
126
  /** {@link parseFrontmatter} 的三种读数(闭集):`absent` = 第一行不是围栏,没有 frontmatter(正当的全局技能)· `block` = 围栏收好了的块 · `unclosed` = 第一行是围栏而块没收好(读不了 —— 调用方拒载)。 */
@@ -127,6 +127,14 @@ function skillsInScope(loaded, scope) {
127
127
  export function skillsForScenario(loaded, scope) {
128
128
  return skillsInScope(loaded, scope).map((k) => k.spec);
129
129
  }
130
+ export const RUN_SKILLS_FIELD = Symbol("sema-server.runSkills");
131
+ export function stampRunSkills(spec, roster) {
132
+ Object.defineProperty(spec, RUN_SKILLS_FIELD, { value: Object.freeze(roster.map((e) => Object.freeze({ ...e }))), enumerable: true, writable: false, configurable: false });
133
+ return spec;
134
+ }
135
+ export function runSkillsOf(spec) {
136
+ return spec[RUN_SKILLS_FIELD];
137
+ }
130
138
  export function scenarioSkillEntries(loaded, scope) {
131
139
  return skillsInScope(loaded, scope).map((k) => ({ name: k.spec.name, description: k.spec.description, source: k.source }));
132
140
  }
@@ -23,6 +23,7 @@ import type { ServiceConfig } from "./config.js";
23
23
  import type { OwnerAwareSessionStore } from "./security.js";
24
24
  import type { ServiceFileHistoryStore } from "./plugins/store-backend.js";
25
25
  import { resolveStreamApprovalGate, type ApprovalFrameFamily } from "./tool-approval.js";
26
+ import { type SyncBlobHistoryStore } from "./session-sync.js";
26
27
  import { type WorkspaceSyncCapability } from "./workspace-space.js";
27
28
  import type { WorkspaceCacheCapability } from "./workspace-cache.js";
28
29
  /** why 闭集:词 → 给运维看的一句义(诊断 / 测试读它;wire 上的拒句由各门自写)。 */
@@ -171,13 +172,8 @@ export declare function sessionSync(d: Pick<Deps, "backend" | "sessionStorage" |
171
172
  sessionStorage: SyncSessionStore;
172
173
  historyStore: ServiceFileHistoryStore;
173
174
  }>;
174
- /** blob 两面齐备的文件历史店(marker 门,不做名字探测:local 的引擎文件店带一只同名私有助手)。 */
175
- export type SyncBlobHistoryStore = ServiceFileHistoryStore & {
176
- readonly syncBlobFaces: true;
177
- } & {
178
- [P in "putBlob" | "getBlob"]-?: NonNullable<ServiceFileHistoryStore[P]>;
179
- };
180
- /** 会话同步的**文件历史半族**:对话半族 ∧ 文件历史店有 blob 搬运面(只有 SQL 两只店有)。 */
175
+ /** 会话同步的**文件历史半族**:对话半族 ∧ 文件历史店有 blob 搬运面(只有 SQL 两只店有)。「店有没有 blob 两面」只在 `syncBlobFacesOf` 判一次(marker 门,不做名字探测),
176
+ * blob 取值口 `blobGetterOf` 读的是同一只 —— 本事实为真 ⟺ 取值口通到 `historyStore.getBlob`。 */
181
177
  export declare function sessionSyncFileHistory(d: Pick<Deps, "backend" | "sessionStorage" | "fileHistoryStore" | "sessionPolicyStore">): Fact<SessionSyncWhy | "sync_blob_faces_absent", {
182
178
  historyStore: SyncBlobHistoryStore;
183
179
  }>;
@@ -4,7 +4,7 @@ import { DEPLOYMENT_SHAPE_PREDICATES } from "./deployment-shape.js";
4
4
  import { presentedApprovalFrameFamily, resolveStreamApprovalGate } from "./tool-approval.js";
5
5
  import { selfOrchestrationDenial } from "./task-workflow.js";
6
6
  import { centerEntitlementSourceWired } from "./runtime-caps-resolver.js";
7
- import { overwriteWipeIncapable } from "./session-sync.js";
7
+ import { overwriteWipeIncapable, syncBlobFacesOf } from "./session-sync.js";
8
8
  import { WORKSPACE_PERSIST_DEFAULT, WORKSPACE_SNAPSHOT_KEEP_DEFAULT, workspaceSnapshotTimeoutOf } from "./workspace-space.js";
9
9
  import { headReadOf } from "./session-incarnation.js";
10
10
  export const CAPABILITY_FACT_WHY = {
@@ -172,10 +172,8 @@ export function sessionSyncFileHistory(d) {
172
172
  const sync = sessionSync(d);
173
173
  if (!sync.available)
174
174
  return sync;
175
- const fs = sync.historyStore;
176
- return fs.syncBlobFaces === true && typeof fs.putBlob === "function" && typeof fs.getBlob === "function"
177
- ? yes({ historyStore: fs })
178
- : no("sync_blob_faces_absent");
175
+ const faces = syncBlobFacesOf(sync.historyStore);
176
+ return faces !== undefined ? yes({ historyStore: faces }) : no("sync_blob_faces_absent");
179
177
  }
180
178
  export function checkpointPark(d) {
181
179
  return d.checkpointStore !== undefined ? yes({ checkpointStore: d.checkpointStore }) : no("checkpoint_store_absent");
@@ -3,7 +3,7 @@ import { closeSync, fchmodSync, fsyncSync, openSync, readdirSync, readFileSync,
3
3
  import { basename, dirname, join } from "node:path";
4
4
  import { WEB_ADMIN_ENV, WebAdminCredentialEnvelope, parseWebAdminCredentialFile } from "@sema-agent/settings-schema";
5
5
  import { ACCESS_TOKEN_TTL_SECONDS, OAuthErrorResponse, TokenGrantResponse } from "@sema-agent/settings-schema/api/auth";
6
- import { CENTER_PULL_TIMEOUT_MS, centerTokenRefresh } from "./web-admin-client/center-request.js";
6
+ import { CENTER_PULL_TIMEOUT_MS, CenterRedirectRefusedError, centerTokenRefresh } from "./web-admin-client/center-request.js";
7
7
  export const CENTER_CREDENTIAL_RENEW_LEAD_KEY = "CENTER_CREDENTIAL_RENEW_LEAD_MS";
8
8
  export const CENTER_CREDENTIAL_RENEW_LEAD_DEFAULT_MS = 5 * 60_000;
9
9
  export const CENTER_CREDENTIAL_RENEW_LEAD_MIN_MS = CENTER_PULL_TIMEOUT_MS;
@@ -277,6 +277,10 @@ export function createCenterCredentialRenewer(deps) {
277
277
  res = await centerTokenRefresh(envelope.issuer, envelope.refreshToken, { timeoutMs: CENTER_PULL_TIMEOUT_MS, ...(deps.fetchImpl ? { fetchImpl: deps.fetchImpl } : {}) });
278
278
  }
279
279
  catch (err) {
280
+ if (err instanceof CenterRedirectRefusedError) {
281
+ judgeRefusal(usedDigest, "rejected", { status: err.status });
282
+ return;
283
+ }
280
284
  settleRetrying("center_unavailable", usedDigest, { error: err instanceof Error ? err.name : "fetch error" });
281
285
  return;
282
286
  }
@@ -97,9 +97,6 @@ export type CenterCredentialSourceSpec = Exclude<WebAdminCredentialDeclaration,
97
97
  * 路径(之后 cwd 变了也读同一个文件 —— 纯契约不碰 `node:path`,这一步归读方)。
98
98
  */
99
99
  export declare function readCenterCredentialDeclaration(env?: Readonly<Record<string, string | undefined>>): WebAdminCredentialDeclaration;
100
- /** 拉取令牌形的身份 = worker(拉取令牌代表 worker,不是人)。种类判别与 formatVersion 1 LKG 的一次性迁移
101
- * (`config-lkg.ts` `migrateV1Lkg`:v1 信封只记 worker,只有这一形的身份推得出来)共用这一只。 */
102
- export declare function pullTokenIdentity(worker: string | undefined): string;
103
100
  /**
104
101
  * 凭证来源工厂(活对象:文件形每次取值读盘并记健康态)。`worker` = `SEMA_WEB_ADMIN_WORKER`(拉取令牌形的身份)。
105
102
  * `readFile` / `now` 只为测试注入。
@@ -161,6 +158,23 @@ export declare function answerStillCurrent(source: CenterCredentialSource, usedI
161
158
  * 中心答复;缓存 / 在飞 / 条件请求的校验子全在箱里,换了人结构上够不着上一个人的。
162
159
  * · 请求发出之后才换人:答复到达时由 {@link answerStillCurrent} 判丢弃(请求装配侧,见 http-client.ts)。
163
160
  * 旧身份的箱不淘汰(量级 = 进程生命期内出现过的身份数):换回来的身份在 TTL 内照常命中它自己的缓存。
161
+ * 取箱口两只({@link IdentityBins}):`now`(此刻身份)与 `accounting`(记账身份:执行开始时钉下的那个,[ref])。
164
162
  */
165
- export declare function createIdentityBins<T>(source: CenterCredentialSource, make: (pinned: CenterCredentialSource) => T): () => T;
163
+ export declare function createIdentityBins<T>(source: CenterCredentialSource, make: (pinned: CenterCredentialSource) => T): IdentityBins<T>;
164
+ /**
165
+ * 同一只分箱规则的两个取箱口(箱表、钉住的凭证、惰建都只有一份):
166
+ * · `now` —— 此刻凭证身份的箱(缓存 / 在飞去重 / 用量环上报这一类「此刻是谁就替谁办」的读者);
167
+ * · `accounting` —— **记账身份**的箱([ref]:花费扣减、用量记账):这次执行开始时钉下的身份({@link pinAccountingIdentity});不在执行里 / 没钉 ⇒ 同 `now`。
168
+ * 在飞调用完成时凭证换了人或暂时读不到,都不改它记在谁的箱里。
169
+ */
170
+ export interface IdentityBins<T> {
171
+ now(): T;
172
+ accounting(): T;
173
+ }
174
+ /**
175
+ * [ref] —— 把这次执行的**记账身份**钉在此刻(执行的第一帧 `task.start` 由 tracer 调;每个凭证来源每次执行至多钉一次,先到为准 ——
176
+ * 同一执行里子任务的 `task.start` 不改它)。此刻身份未知(凭证读不到)⇒ **不钉**:不把这次执行记到「身份未知」的无主箱,它的记账按记账那一刻的身份。
177
+ * 不在执行作用域里(`withPrincipal` 之外)⇒ 无动作。
178
+ */
179
+ export declare function pinAccountingIdentity(source: CenterCredentialSource): void;
166
180
  //# sourceMappingURL=center-credential.d.ts.map
@@ -2,6 +2,7 @@ import { readFileSync } from "node:fs";
2
2
  import { createHash } from "node:crypto";
3
3
  import { resolve as resolvePath } from "node:path";
4
4
  import { looksLikeJwt } from "@sema-agent/settings-schema/api/auth-bridge";
5
+ import { currentExecutionPins } from "./observability/principal-context.js";
5
6
  import { WEB_ADMIN_ENV, judgeWebAdminCredentialDeclaration, parseWebAdminCredentialFile } from "@sema-agent/settings-schema";
6
7
  export class CenterCredentialUnavailableError extends Error {
7
8
  reason;
@@ -37,7 +38,7 @@ function classify(bearer, worker) {
37
38
  return { ok: true, kind: "user-jwt", bearer, identity: userTokenIdentity(bearer) };
38
39
  return { ok: true, kind: "pull-token", bearer, identity: pullTokenIdentity(worker), workerScoped: bearer.startsWith("wpt_") };
39
40
  }
40
- export function pullTokenIdentity(worker) {
41
+ function pullTokenIdentity(worker) {
41
42
  return `pull-token:${worker ?? "global"}`;
42
43
  }
43
44
  const NONE = { ok: true, kind: "none", identity: "none" };
@@ -178,14 +179,28 @@ export function answerStillCurrent(source, usedIdentity) {
178
179
  }
179
180
  export function createIdentityBins(source, make) {
180
181
  const bins = new Map();
181
- return () => {
182
- const identity = credentialIdentityNow(source);
182
+ const binOf = (identity) => {
183
183
  if (bins.has(identity))
184
184
  return bins.get(identity);
185
185
  const made = make(pinCredential(source, identity));
186
186
  bins.set(identity, made);
187
187
  return made;
188
188
  };
189
+ return {
190
+ now: () => binOf(credentialIdentityNow(source)),
191
+ accounting: () => binOf(accountingIdentityOf(source)),
192
+ };
193
+ }
194
+ export function pinAccountingIdentity(source) {
195
+ const pins = currentExecutionPins();
196
+ if (pins === undefined || pins.has(source))
197
+ return;
198
+ const identity = credentialIdentityNow(source);
199
+ if (identity !== undefined)
200
+ pins.set(source, identity);
201
+ }
202
+ function accountingIdentityOf(source) {
203
+ return currentExecutionPins()?.get(source) ?? credentialIdentityNow(source);
189
204
  }
190
205
  function pinCredential(source, identity) {
191
206
  return {
@@ -12,16 +12,9 @@
12
12
  * 本条在起服第 6 段 checkpoint 店建成、封签器的装配判(`CHECKPOINT_CTX_SEALING_REQUIRED`)过了之后调一次,早于任何路由装配(与 M10 / M19 / M25
13
13
  * 同为在迁移段之外跑的条目)。读数一行 info `checkpoint_ctx_credentials_sealed`,不进 `OneTimeMigrationReport`(登记行 `reportKeys` 空)。
14
14
  */
15
- import { type CheckpointCtxSealer, type SealedCheckpointCtx } from "./http/checkpoint-ctx.js";
15
+ import { type CheckpointCtxSealer } from "./http/checkpoint-ctx.js";
16
16
  import { type OneTimeMigrationWindow } from "./plugins/one-time-migrations.js";
17
- import type { CtxRowsRewriteM28 } from "./plugins/checkpoint-store-sql.js";
18
- /** 两孪生的店腿(`SqlCheckpointStore` / `LocalCheckpointStore` 的 `rewriteCtxRowsM28`)。 */
19
- export interface CheckpointCtxRowsRewriterM28 {
20
- rewriteCtxRowsM28(rewrite: (sessionId: string, ctx: object) => {
21
- readonly ctx: SealedCheckpointCtx;
22
- readonly values: number;
23
- } | undefined): Promise<CtxRowsRewriteM28>;
24
- }
17
+ import type { CheckpointCtxRowsRewriter } from "./plugins/checkpoint-store-sql.js";
25
18
  /** M28 一次起服的读数(起服 info 行逐键打出;测试按它断言)。`window: "closed"` ⇒ 一行都没读(其余全 0)。 */
26
19
  export interface CheckpointCtxSealM28Report {
27
20
  readonly window: "open" | "closed";
@@ -35,7 +28,7 @@ export interface CheckpointCtxSealM28Report {
35
28
  * 跑一次 M28(`today` = UTC 日历日 YYYY-MM-DD;`window` 缺省 = 登记表 M28 那一行 —— 只有测试递别的窗)。窗关 ⇒ 不扫。
36
29
  * 店腿的每一行:`sealStoredCheckpointCtxM28` 交回新体(凭据位上的明文封好)⇒ 回写;交回 `undefined`(没有明文)⇒ 不写。
37
30
  */
38
- export declare function sealCheckpointCtxCredentialsM28(store: CheckpointCtxRowsRewriterM28, sealer: CheckpointCtxSealer, opts: {
31
+ export declare function sealCheckpointCtxCredentialsM28(store: CheckpointCtxRowsRewriter, sealer: CheckpointCtxSealer, opts: {
39
32
  readonly today: string;
40
33
  readonly window?: Pick<OneTimeMigrationWindow, "removableAfter">;
41
34
  }): Promise<CheckpointCtxSealM28Report>;
@@ -10,7 +10,7 @@ export async function sealCheckpointCtxCredentialsM28(store, sealer, opts) {
10
10
  if (!migrationWindowOpen(opts.window ?? m28Window(), opts.today)) {
11
11
  return { window: "closed", rowsScanned: 0, rowsSealed: 0, valuesSealed: 0, rowsUnreadable: 0, rowsTooLarge: 0 };
12
12
  }
13
- const r = await store.rewriteCtxRowsM28((sessionId, ctx) => {
13
+ const r = await store.rewriteCtxRows((sessionId, ctx) => {
14
14
  const out = sealStoredCheckpointCtxM28(ctx, sealer, sessionId);
15
15
  return out === undefined ? undefined : { ctx: out.ctx, values: out.valuesSealed };
16
16
  });
@@ -7,13 +7,13 @@ export declare const MAX_CHECKPOINT_ID_CHARS = 128;
7
7
  export declare function withoutCheckpointToken(text: string, token: unknown): string;
8
8
  /** 持久层读出的 `checkpointId` 过信任边界:是串、非空、不超长、不含 token(大小写不敏感)⇒ 原值;否则 `undefined`。判据全文见本文件头注。 */
9
9
  export declare function trustedCheckpointId(raw: unknown, token: unknown): string | undefined;
10
- /** 带 `checkpointId` 当 join 键的两类 durable 读面(计数 detail 的 `face`)。 */
11
- export type DurableCheckpointFace = "approvals-queue" | "assistant-inbox";
10
+ /** 带 `checkpointId` 当 join 键的 durable 面(计数 detail 的 `face`):运维队列行、停驻收件行,与 7.137.0 起的 park 账本行(`GET /v1/runs/:id/events` 的 `suspended` / `needs_review`,写时判一次)。 */
11
+ export type DurableCheckpointFace = "approvals-queue" | "assistant-inbox" | "run-events-park";
12
12
  /**
13
13
  * durable 读面的一行 → 它的身份片段,**由调用方原样展开进行投影**(`...durableRowIdentity(face, raw, token)`):读得出可信值 ⇒ `{ checkpointId }`;
14
14
  * 读不出 ⇒ `{}`(这一行**照列**,键缺席)+ 计一次(见本文件头注「durable 面的 join 键」)。
15
15
  * `raw === undefined` = 行上没有这一格(`why=absent`,老行);在场却没过信任边界(非串 / 空串 / 超长 / 含 token / 读不出)= `why=untrusted`。
16
- * 三处读面(两只店的 `listPending` + 收件 handler)只经这一只 —— 读法、计数、缺席形各只有一份。
16
+ * 三处读面(两只店的 `listPending` + 收件 handler)与 park 账本行的唯一构造器(`trace/project.ts` 的 `parkEventData`)只经这一只 —— 读法、计数、缺席形各只有一份。
17
17
  */
18
18
  export declare function durableRowIdentity(face: DurableCheckpointFace, raw: unknown, token: unknown): {
19
19
  readonly checkpointId?: string;