@sema-agent/server 7.136.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 (214) hide show
  1. package/MIGRATION.md +173 -0
  2. package/USAGE.md +84 -16
  3. package/deploy/sema-up/README.md +14 -0
  4. package/deploy/sema-up/chart/templates/sandbox-rbac.yaml +7 -1
  5. package/deploy/sema-up/stack/sema-stack-cluster.yaml +7 -1
  6. package/dist/approval-content-kind.d.ts +6 -0
  7. package/dist/approval-content-kind.js +7 -0
  8. package/dist/approval-gate-currency.d.ts +48 -6
  9. package/dist/approval-gate-currency.js +16 -9
  10. package/dist/approval-hmac.d.ts +13 -1
  11. package/dist/approval-hmac.js +1 -1
  12. package/dist/approval.js +3 -3
  13. package/dist/auth-bridge.js +2 -2
  14. package/dist/boot/execution-env.d.ts +24 -1
  15. package/dist/boot/execution-env.js +69 -6
  16. package/dist/boot/reapers.d.ts +3 -0
  17. package/dist/boot/reapers.js +15 -1
  18. package/dist/boot/resolve-spec.d.ts +4 -0
  19. package/dist/boot/resolve-spec.js +16 -17
  20. package/dist/boot/stage-03-store-backend.d.ts +1 -1
  21. package/dist/boot/stage-04-stores.d.ts +1 -1
  22. package/dist/boot/stage-05-execution-env.d.ts +3 -1
  23. package/dist/boot/stage-05-execution-env.js +11 -2
  24. package/dist/boot/stage-06-runners.d.ts +3 -1
  25. package/dist/boot/stage-06-runners.js +5 -1
  26. package/dist/boot/stage-07-capability-layer.d.ts +4 -1
  27. package/dist/boot/stage-07-capability-layer.js +1 -1
  28. package/dist/boot/stage-08-reapers.d.ts +4 -1
  29. package/dist/boot/stage-08-reapers.js +2 -1
  30. package/dist/boot/stage-09-leader.d.ts +4 -1
  31. package/dist/boot/stage-10-http-server.d.ts +4 -1
  32. package/dist/boot/stage-10-http-server.js +3 -2
  33. package/dist/boot/stores.d.ts +1 -1
  34. package/dist/broker/broker.d.ts +89 -0
  35. package/dist/broker/broker.js +287 -0
  36. package/dist/broker/constants.d.ts +39 -0
  37. package/dist/broker/constants.js +15 -0
  38. package/dist/broker/http1.d.ts +111 -0
  39. package/dist/broker/http1.js +256 -0
  40. package/dist/broker/lease-book.d.ts +87 -0
  41. package/dist/broker/lease-book.js +142 -0
  42. package/dist/broker/lease-file.d.ts +60 -0
  43. package/dist/broker/lease-file.js +127 -0
  44. package/dist/broker/main.d.ts +2 -0
  45. package/dist/broker/main.js +20 -0
  46. package/dist/capabilities/composite-members.d.ts +2 -1
  47. package/dist/capabilities/composite-members.js +2 -1
  48. package/dist/capabilities/engine-capabilities.js +1 -0
  49. package/dist/capabilities/scenarios.d.ts +23 -8
  50. package/dist/capabilities/scenarios.js +13 -8
  51. package/dist/capabilities/skills.d.ts +28 -5
  52. package/dist/capabilities/skills.js +8 -0
  53. package/dist/center-credential-renewer.js +5 -1
  54. package/dist/checkpoint-ctx-seal-m28.d.ts +3 -10
  55. package/dist/checkpoint-ctx-seal-m28.js +1 -1
  56. package/dist/checkpoint-identity.d.ts +3 -3
  57. package/dist/config-catalog.js +9 -6
  58. package/dist/config-types.d.ts +9 -0
  59. package/dist/config.d.ts +1 -1
  60. package/dist/config.js +34 -8
  61. package/dist/deployment-governance.d.ts +2 -2
  62. package/dist/deployment-governance.js +7 -9
  63. package/dist/device-ws-protocol.d.ts +1 -0
  64. package/dist/device-ws-protocol.js +2 -1
  65. package/dist/file-history-disclosure.d.ts +4 -2
  66. package/dist/file-history-disclosure.js +2 -0
  67. package/dist/hooks/classic-stations.d.ts +11 -54
  68. package/dist/http/admission.js +10 -7
  69. package/dist/http/approval-sweeps.js +4 -3
  70. package/dist/http/caller-trust.d.ts +2 -1
  71. package/dist/http/caller-trust.js +1 -3
  72. package/dist/http/checkpoint-ctx.d.ts +48 -7
  73. package/dist/http/checkpoint-ctx.js +40 -0
  74. package/dist/http/dispatch.js +15 -10
  75. package/dist/http/durable-run-leg.d.ts +5 -1
  76. package/dist/http/durable-run-leg.js +1 -1
  77. package/dist/http/health-self-report.d.ts +2 -2
  78. package/dist/http/mcp-remote-text.d.ts +5 -0
  79. package/dist/http/mcp-remote-text.js +7 -0
  80. package/dist/http/principal-gate.d.ts +15 -10
  81. package/dist/http/principal-gate.js +48 -9
  82. package/dist/http/resume-legs.d.ts +2 -20
  83. package/dist/http/resume-legs.js +52 -38
  84. package/dist/http/route-ctx.d.ts +8 -2
  85. package/dist/http/routes/approvals-assistant.js +18 -4
  86. package/dist/http/routes/capabilities.js +11 -1
  87. package/dist/http/routes/mcp-reconnect.d.ts +1 -1
  88. package/dist/http/routes/mcp-reconnect.js +5 -7
  89. package/dist/http/routes/runs.js +2 -1
  90. package/dist/http/routes/session-sync.js +42 -6
  91. package/dist/http/routes/sessions.js +2 -1
  92. package/dist/http/routes/tasks.js +8 -6
  93. package/dist/http/server.d.ts +4 -0
  94. package/dist/http/stored-body-replay.d.ts +3 -3
  95. package/dist/http/stored-body-replay.js +6 -1
  96. package/dist/http/wire-types.d.ts +52 -27
  97. package/dist/identity-source.d.ts +65 -0
  98. package/dist/identity-source.js +45 -0
  99. package/dist/memory-store-refusal.d.ts +3 -3
  100. package/dist/model-entry-refusal.d.ts +2 -0
  101. package/dist/model-entry-refusal.js +11 -0
  102. package/dist/model-select.d.ts +6 -17
  103. package/dist/model-select.js +0 -3
  104. package/dist/namespaced-rule-name.d.ts +1 -1
  105. package/dist/observability/fail-open.d.ts +13 -1
  106. package/dist/observability/fail-open.js +13 -1
  107. package/dist/observability/metrics.js +1 -1
  108. package/dist/observability/run-terminal.js +1 -0
  109. package/dist/operator-ask.d.ts +2 -2
  110. package/dist/org-delivery-gate.js +1 -1
  111. package/dist/org-memory-admission.d.ts +2 -2
  112. package/dist/org-memory-admission.js +39 -2
  113. package/dist/parked-decide.js +3 -2
  114. package/dist/peer-directory.js +2 -0
  115. package/dist/permission-rule-list.d.ts +2 -2
  116. package/dist/plugins/checkpoint-store-sql.d.ts +19 -9
  117. package/dist/plugins/checkpoint-store-sql.js +6 -6
  118. package/dist/plugins/file-history-store-sql.js +7 -14
  119. package/dist/plugins/file-run-store.js +8 -2
  120. package/dist/plugins/k8s-exec-protocol.d.ts +69 -13
  121. package/dist/plugins/k8s-exec-protocol.js +116 -28
  122. package/dist/plugins/k8s-pod-delete-failure.d.ts +25 -0
  123. package/dist/plugins/k8s-pod-delete-failure.js +17 -0
  124. package/dist/plugins/k8s-pod-reaper.d.ts +112 -0
  125. package/dist/plugins/k8s-pod-reaper.js +122 -0
  126. package/dist/plugins/local-checkpoint-store.d.ts +3 -6
  127. package/dist/plugins/local-checkpoint-store.js +3 -3
  128. package/dist/plugins/memory-run-store.js +2 -0
  129. package/dist/plugins/one-time-migrations.d.ts +28 -9
  130. package/dist/plugins/one-time-migrations.js +48 -5
  131. package/dist/plugins/permission-rule-store-file.js +2 -2
  132. package/dist/plugins/permission-rule-store-sql.d.ts +17 -17
  133. package/dist/plugins/permission-rule-store-sql.js +16 -17
  134. package/dist/plugins/pg-pool.js +4 -0
  135. package/dist/plugins/pg-session-storage.js +2 -2
  136. package/dist/plugins/remote-env-adb.d.ts +3 -0
  137. package/dist/plugins/remote-env-adb.js +1 -0
  138. package/dist/plugins/remote-env-device.d.ts +3 -0
  139. package/dist/plugins/remote-env-device.js +4 -0
  140. package/dist/plugins/remote-env-e2b.d.ts +3 -0
  141. package/dist/plugins/remote-env-e2b.js +1 -0
  142. package/dist/plugins/remote-env-file-error.d.ts +8 -1
  143. package/dist/plugins/remote-env-file-error.js +7 -1
  144. package/dist/plugins/remote-env-host.d.ts +7 -1
  145. package/dist/plugins/remote-env-host.js +7 -1
  146. package/dist/plugins/remote-env-k8s.d.ts +137 -10
  147. package/dist/plugins/remote-env-k8s.js +276 -14
  148. package/dist/plugins/remote-env-local-docker.d.ts +3 -0
  149. package/dist/plugins/remote-env-local-docker.js +1 -0
  150. package/dist/plugins/remote-env-ssh.d.ts +3 -0
  151. package/dist/plugins/remote-env-ssh.js +1 -0
  152. package/dist/plugins/run-store-sql.js +10 -4
  153. package/dist/plugins/session-ownership.d.ts +17 -3
  154. package/dist/plugins/session-ownership.js +19 -5
  155. package/dist/plugins/sql-errors.d.ts +23 -2
  156. package/dist/plugins/sql-errors.js +15 -0
  157. package/dist/plugins/store-contracts.d.ts +10 -1
  158. package/dist/plugins/store-fault.d.ts +2 -2
  159. package/dist/plugins/store-fault.js +4 -2
  160. package/dist/plugins/tidb-pool.js +7 -0
  161. package/dist/plugins/tidb-session-store.js +2 -2
  162. package/dist/request-key-closure.js +1 -1
  163. package/dist/request-value-closure.d.ts +31 -2
  164. package/dist/request-value-closure.js +7 -1
  165. package/dist/rule-entry-list.d.ts +3 -2
  166. package/dist/rule-entry-list.js +2 -2
  167. package/dist/rules-consent.js +7 -4
  168. package/dist/run-local.d.ts +2 -2
  169. package/dist/runs.js +2 -1
  170. package/dist/runtime-governance.d.ts +5 -5
  171. package/dist/sandbox-git-lease.d.ts +49 -0
  172. package/dist/sandbox-git-lease.js +60 -0
  173. package/dist/sandbox-git-plane.d.ts +84 -0
  174. package/dist/sandbox-git-plane.js +105 -0
  175. package/dist/security.d.ts +16 -4
  176. package/dist/security.js +8 -3
  177. package/dist/server-secret-env.d.ts +6 -0
  178. package/dist/server-secret-env.js +14 -0
  179. package/dist/session-error-wire.d.ts +14 -6
  180. package/dist/session-error-wire.js +2 -1
  181. package/dist/session-policy-wire.d.ts +1 -1
  182. package/dist/session-policy-wire.js +2 -2
  183. package/dist/session-sync.d.ts +1 -1
  184. package/dist/spec-fields.d.ts +43 -13
  185. package/dist/spec-fields.js +33 -12
  186. package/dist/stored-request-prompt-profile-m30.d.ts +49 -0
  187. package/dist/stored-request-prompt-profile-m30.js +25 -0
  188. package/dist/task-mcp.d.ts +1 -1
  189. package/dist/task-settings.d.ts +2 -2
  190. package/dist/tool-approval.d.ts +12 -2
  191. package/dist/tool-approval.js +8 -3
  192. package/dist/tool-rule-names.d.ts +5 -0
  193. package/dist/trace/core-keyset-guard.d.ts +4 -4
  194. package/dist/trace/engine-notice-wire.d.ts +1 -1
  195. package/dist/trace/engine-notice-wire.js +2 -0
  196. package/dist/trace/exit-seat.js +2 -2
  197. package/dist/trace/ledger-sink.d.ts +4 -5
  198. package/dist/trace/ledger-sink.js +3 -7
  199. package/dist/trace/project.d.ts +37 -3
  200. package/dist/trace/project.js +20 -6
  201. package/dist/trace/redact.d.ts +18 -6
  202. package/dist/trace/redact.js +118 -53
  203. package/dist/trace/self-minted-wire.d.ts +377 -3
  204. package/dist/trace/self-minted-wire.js +122 -0
  205. package/dist/trace/wire-projection-faces.d.ts +5 -3
  206. package/dist/trace/wire-projection-faces.js +7 -3
  207. package/dist/web-admin-client/apply-effective.js +0 -1
  208. package/dist/web-admin-client/center-request.d.ts +20 -2
  209. package/dist/web-admin-client/center-request.js +40 -5
  210. package/dist/web-admin-client/http-client.js +6 -6
  211. package/dist/web-admin-client/types.d.ts +5 -5
  212. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +11 -5
  213. package/package.json +2 -2
  214. package/wire-changes/server@7.137.0.json +825 -0
@@ -145,7 +145,7 @@ export declare const MYSQL_CONNECTION_UNUSABLE_ERRORS: ReadonlyArray<{
145
145
  export declare function isConnectionUnusableError(err: unknown): boolean;
146
146
  /**
147
147
  * **连接类故障**的识别表(闭集;逐行登记「为什么」)—— 「到库的那条连接坏了 / 库此刻不接」:与这一条语句的内容无关、什么都不改稍后再试可能成。
148
- * 读者(整仓一只判官 `plugins/store-fault.ts` 的 `storeFaultClassOf`,三处同读):审批队列读口、文件历史店的折点(`permanence: "transient"`)、会话同步拉取口(`503`)。
148
+ * 读者(整仓一只判官 `plugins/store-fault.ts` 的 `storeFaultClassOf`,三处同读):审批队列读口、文件历史店的折点(`permanence: "transient"`)、会话同步拉取口(`503` + `permanence: "transient"`)。
149
149
  *
150
150
  * 识别集 = 三张表的并集,**没有第四处**:
151
151
  * · 本表(下):套接字层与协议层的断连 / 拒连 / 连接数满 / 库的关停与只读切换,按 `code` 登记(MySQL 协议的数字 errno 在场的并登 —— 同本文件其余谓词的姿势:`errno` 与 `code` 是同一件事的两种写法);
@@ -153,7 +153,7 @@ export declare function isConnectionUnusableError(err: unknown): boolean;
153
153
  * · {@link CONNECTION_FAULT_DRIVER_SENTENCES}:驱动自己抛的**无 `code` 的裸 `Error`**(node-pg 的断连在这一形),按整句相等认。
154
154
  *
155
155
  * **不在识别集里**(认不出一律不是 —— 错判成连接类的代价是调用方被劝「再试」去撞一个注定失败的语句):
156
- * · 语句级的错:唯一键冲突、CHECK 被拒、缺表缺列、语法错、权限错、行 / 包超墙(1153 / 8025 / `54000`);
156
+ * · 语句级的错:唯一键冲突、CHECK 被拒、缺列、语法错、行 / 包超墙(1153 / 8025 / `54000`);缺表与权限拒也不是连接类 —— 它们是**部署错配类**(判官的另一个词,识别表 {@link DEPLOYMENT_FAULT_ERRORS},与本表互斥);
157
157
  * · 死锁(引擎的仲裁产物,重试的前提在调用方的整事务重来,见 {@link isDeadlockError})与锁等待 / 语句超时(延迟信号,重试只把长等待乘上次数);
158
158
  * · 认证类拒连(MySQL 1045、PG `28xxx`、`08004`:凭据 / 白名单要人去改)与协议违例(PG `08P01`、mysql2 `PROTOCOL_ERROR`:我方的码病);
159
159
  * · TiDB 自己的忙 / 区域不可用(9001 / 9005 一族)—— 候真库逐码读数再登,今天不在表里(登记在案的未收项);
@@ -180,6 +180,27 @@ export declare const CONNECTION_FAULT_DRIVER_SENTENCES: ReadonlyArray<{
180
180
  * 归因键三种:`code`(字符串)、`errno`(数字)、无 `code` 的驱动裸 `Error` 的整句 —— 只读被抛出的这一只对象身上的结构化字段 / 整句,**不读散文里的在册词**。
181
181
  */
182
182
  export declare function isConnectionFaultError(err: unknown): boolean;
183
+ /**
184
+ * **部署错配类故障**的识别表(闭集;逐行登记「为什么」)—— 到库的连接是好的、语句也是对的,是这套库的形与本部署的要求不符:**重试注定失败**,修表 / 授权之前什么都不会变
185
+ * (与连接类互为反面:那一类是「什么都不改稍后再试可能成」)。读者:整仓一只判官 `plugins/store-fault.ts` 的 `storeFaultClassOf` 的 `deployment` 一词 ——
186
+ * 文件历史店的折点与会话同步拉取口(`503 state.file_history_unreadable`)据它判 `permanence: "terminal"`;审批队列读口并入「此刻读不出」的 503;各读者各自表态,**不在自己那里写码**。
187
+ *
188
+ * 识别集 = 本表一张,按归因键登记:`code`(字符串)与 MySQL 协议的数字 `errno`(两键是同一件事的两种写法,代理 / 序列化往返可能只留一个;同本文件其余谓词的姿势)。
189
+ * **与连接类互斥**:本表的任何一码都不在 {@link CONNECTION_FAULT_ERRORS} / {@link MYSQL_CONNECTION_UNUSABLE_ERRORS} 的识别集里,反之亦然(`test/store-fault-judge.test.ts` 钉两个方向)—— 同一只错只落一个词。
190
+ *
191
+ * **不在表里**(认不出一律不是;本批只收缺表与权限拒两形,其余是登记在案的未收项,不是「已裁定不判」):缺列(1054 / `42703`:升级前置断言 `assert*Schema` 族有自己的判据与出路)、
192
+ * 认证类拒连(MySQL 1045、PG `28xxx`:凭据 / 白名单)、库级与列级的权限拒(MySQL 1044 / 1143)、TiDB 自己的忙 / 区域不可用一族(同连接类表的登记)。
193
+ */
194
+ export declare const DEPLOYMENT_FAULT_ERRORS: ReadonlyArray<{
195
+ readonly code: string;
196
+ readonly errno?: number;
197
+ readonly why: string;
198
+ }>;
199
+ /**
200
+ * 「这个错误**是**部署错配类故障吗」—— {@link DEPLOYMENT_FAULT_ERRORS} 的唯一判据(识别集与排除项见该表头注)。纯谓词、不抛;认不出的一律不是。
201
+ * 只读被抛出的**这一只**对象身上的结构化字段(`code` 字符串 / `errno` 数字),不沿 `cause` 链找、不读散文里的在册词。
202
+ */
203
+ export declare function isDeploymentFaultError(err: unknown): boolean;
183
204
  /**
184
205
  * 检查点店的两笔写(续跑输入 `putCtx` / 停驻行 `put`)被引擎以「一行装不下」拒 ⇒ **这一只**带码错误(码 `store.row_too_large`),不以裸驱动错上抛
185
206
  * (其余店写今天仍是各自的驱动错 —— 未收,登记在案)。
@@ -130,6 +130,21 @@ export function isConnectionFaultError(err) {
130
130
  return true;
131
131
  return err instanceof Error && code === undefined && CONNECTION_FAULT_SENTENCES.has(err.message);
132
132
  }
133
+ export const DEPLOYMENT_FAULT_ERRORS = [
134
+ { code: "42P01", why: "PG undefined_table(缺表:库没按本版的表结构建好 / 迁移没跑 —— 重试不会让表长出来)" },
135
+ { code: "42501", why: "PG insufficient_privilege(权限拒:库账号缺这张表 / 这类语句的授权 —— 授权之前重试注定失败)" },
136
+ { code: "ER_NO_SUCH_TABLE", errno: 1146, why: "MySQL 协议(MySQL / MariaDB / TiDB)缺表:同 PG `42P01`" },
137
+ { code: "ER_TABLEACCESS_DENIED_ERROR", errno: 1142, why: "MySQL 协议表级权限拒:库账号对这张表缺这类语句的授权,同 PG `42501`" },
138
+ ];
139
+ const DEPLOYMENT_FAULT_CODES = new Set(DEPLOYMENT_FAULT_ERRORS.map((r) => r.code));
140
+ const DEPLOYMENT_FAULT_ERRNOS = new Set(DEPLOYMENT_FAULT_ERRORS.flatMap((r) => (r.errno === undefined ? [] : [r.errno])));
141
+ export function isDeploymentFaultError(err) {
142
+ if (err === null || typeof err !== "object")
143
+ return false;
144
+ const code = keyOf(err, "code");
145
+ const errno = keyOf(err, "errno");
146
+ return (typeof code === "string" && DEPLOYMENT_FAULT_CODES.has(code)) || (typeof errno === "number" && DEPLOYMENT_FAULT_ERRNOS.has(errno));
147
+ }
133
148
  export class StoreRowTooLargeError extends Error {
134
149
  row;
135
150
  wall;
@@ -19,6 +19,7 @@
19
19
  * code should import from this file.
20
20
  */
21
21
  import type { PeerSessionTempo, TaskResult, TaskStatus } from "@sema-agent/core";
22
+ import type { RunIdentity } from "../identity-source.js";
22
23
  /**
23
24
  * **File 店族的 boot 期拒启基类**([ref];把 `RunStoreStrictHydrateError` 的那条论证升成一条族规)。
24
25
  *
@@ -212,8 +213,13 @@ export interface RunRecord {
212
213
  * (MCP façade / portal). null when the caller didn't supply one. */
213
214
  jobId: string | null;
214
215
  /** Which SYSTEM's credential submitted the run (oa/cc-mcp/portal…), derived at the auth gate — never
215
- * self-declared. null = legacy single-token deployment (authenticated, no system identity). */
216
+ * self-declared. null = legacy single-token deployment (authenticated, no system identity).
217
+ * **归因用,不是认证来源**:任何凭据判都不读它(认证来源 = {@link RunRecord.identity});系统名是运维写的任意串,`"sso"` 也只是归因值。 */
216
218
  source: string | null;
219
+ /** 🔴 **run 行身份元组**(三列 `identity_source` / `identity_scope` / `identity_principal_inc`):受理时在凭据门上铸、建行时写入、之后只读 ——
220
+ * 续跑认领 / 挂起 / 终局 / 重启都不改它,没有按当前状态重算的读者。`null` = 这一行没有「属主在受理时验过的凭据」的记录(三列之前的行 /
221
+ * 无鉴权写面的受理 / 别人替属主按下的那一跑),一律不合格。律与形的唯一属主 = `identity-source.ts`。不上 wire(run 读面是显式投影,不带它)。 */
222
+ identity: RunIdentity | null;
217
223
  /** The replica currently HOLDING this run — written at createRun and TAKEN OVER by markResuming ([ref]: a
218
224
  * cross-replica resume moves the seat, so a reader comparing it with its own instanceId is asking "is the
219
225
  * live leg here NOW", not "who minted the row"). Lets a preempt/cancel/steer handler tell "this run is on
@@ -263,6 +269,9 @@ export interface RunIdemBinding {
263
269
  export interface NewRunMeta {
264
270
  jobId?: string | null;
265
271
  source?: string | null;
272
+ /** 这一行的身份元组(`RunRecord.identity` 的写口)。缺席 = `null`(没有记录;不合格)。`src/http/**` 的每个建行点都显式写它(机器门
273
+ * `test/run-identity-tuple.test.ts`),取值只经凭据门铸下的那一份(`principal-gate.ts` 的 `runIdentityOf`)。 */
274
+ identity?: RunIdentity | null;
266
275
  objectivePreview?: string | null;
267
276
  idem?: RunIdemBinding | null;
268
277
  }
@@ -1,4 +1,4 @@
1
- export type StoreFaultClass = "corrupt" | "connection" | "unclassified";
2
- /** 判官本体。纯函数、不抛;只看被抛出的这一只对象(不沿 `cause` 链找)。 */
1
+ export type StoreFaultClass = "corrupt" | "connection" | "deployment" | "unclassified";
2
+ /** 判官本体。纯函数、不抛;只看被抛出的这一只对象(不沿 `cause` 链找)。两张识别表互斥,判序不影响结果(`test/store-fault-judge.test.ts` 钉互斥)。 */
3
3
  export declare function storeFaultClassOf(err: unknown): StoreFaultClass;
4
4
  //# sourceMappingURL=store-fault.d.ts.map
@@ -1,8 +1,10 @@
1
- import { isConnectionFaultError } from "./sql-errors.js";
1
+ import { isConnectionFaultError, isDeploymentFaultError } from "./sql-errors.js";
2
2
  import { StoreCorruptError } from "./sql-json-column.js";
3
3
  export function storeFaultClassOf(err) {
4
4
  if (err instanceof StoreCorruptError)
5
5
  return "corrupt";
6
- return isConnectionFaultError(err) ? "connection" : "unclassified";
6
+ if (isConnectionFaultError(err))
7
+ return "connection";
8
+ return isDeploymentFaultError(err) ? "deployment" : "unclassified";
7
9
  }
8
10
  //# sourceMappingURL=store-fault.js.map
@@ -7,6 +7,7 @@ import { TIDB_ADOPTION_LOG_STATEMENTS } from "./adoption-log-sql.js";
7
7
  import { TIDB_PERMISSION_RULE_STATEMENTS } from "./permission-rule-store-sql.js";
8
8
  import { TIDB_RETENTION_STATEMENTS } from "./retention-store-sql.js";
9
9
  import { SESSION_META_INCARNATION_COLUMN } from "../session-incarnation.js";
10
+ import { RUN_IDENTITY_COLUMNS } from "../identity-source.js";
10
11
  import { SESSION_RULES_COLUMN } from "./session-rules-column.js";
11
12
  import { SESSION_TITLE_MAX_CHARS } from "../session-row.js";
12
13
  import { PRINCIPAL_MAX_UTF8_BYTES } from "../security.js";
@@ -155,6 +156,12 @@ export const SCHEMA_STATEMENTS = [
155
156
  -- source (credential-derived system identity, clay 2026-06-12): which door submitted the run
156
157
  -- (oa / cc-mcp / portal).
157
158
  source VARCHAR(64) NULL,
159
+ -- identity_source / identity_scope / identity_principal_inc: the run's identity tuple, minted ONCE at the credential gate when the
160
+ -- run is accepted and never rewritten (no UPDATE touches them). NULL = no record of a credential the owner presented at acceptance
161
+ -- (rows from before these columns, unauthenticated-write acceptance, a run someone else accepted on the owner's behalf) ⇒ not
162
+ -- qualified. Column definitions' single owner = src/identity-source.ts RUN_IDENTITY_COLUMNS (the boot column probe derives its
163
+ -- ALTER suggestion from this statement). \`source\` above is attribution only, never an authentication source.
164
+ ${RUN_IDENTITY_COLUMNS.tidb},
158
165
  -- objective_preview: the list's "what is this task" glance — redacted + truncated at write.
159
166
  objective_preview VARCHAR(160) NULL,
160
167
  -- cancel_requested: durable cross-replica CANCEL flag. The running instance's heartbeat tick honors it
@@ -11,7 +11,7 @@ import { CLAIM_OWNERLESS_PLACED_SQL, claimOwnerlessParams, createPlacedSessionRo
11
11
  import { createLogger } from "../observability/logger.js";
12
12
  import { placementCapabilityOf, LIST_PLACED_BATCH, PLACEMENT_SELECT_COLUMNS, placedRowFromRow, placementFromRow, placementInsertValues, SQL_SESSION_PLACEMENTS, } from "./session-placement.js";
13
13
  import { classifySyncRelationshipByIds, SyncConflictError, StagedCommitOwnerChangedError, stagingIdFor, STAGING_ID_MARKER, STAGING_GC_GRACE_MS, } from "../session-sync-kernel.js";
14
- import { assertFirstOwnerWritable, forkOwnerOf } from "./session-ownership.js";
14
+ import { assertFirstOwnerWritable, forkOwnerOf, isSessionOwnerValue } from "./session-ownership.js";
15
15
  import { forkCopyOf, forkPointOf } from "./session-fork.js";
16
16
  export class TiDBSessionStore {
17
17
  pool;
@@ -58,7 +58,7 @@ export class TiDBSessionStore {
58
58
  return { session: new StoredSession(storage), sessionId };
59
59
  }
60
60
  async register(sessionId, owner) {
61
- if (typeof owner !== "string" && owner !== null) {
61
+ if (!isSessionOwnerValue(owner)) {
62
62
  if ((await this.headOf(sessionId)) !== undefined)
63
63
  return;
64
64
  assertFirstOwnerWritable(sessionId, owner);
@@ -26,7 +26,7 @@ const TASK_BODY_DECLARED_KEYS = [
26
26
  "excludeAllTools", "excludeTools", "fileHistory",
27
27
  "finalVerification", "forwardSubagentEvents", "images", "inheritEnv", "interactiveTools", "jobId", "limits", "maxCostUsd",
28
28
  "maxTokens", "mcpServers", "memory", "memoryCapture", "memoryWrite", "model", "objective", "oneShot", "outputRetries", "outputStyle",
29
- "outputSchema", "permissionMode", "projectId", "projectMemory", "promptProfile", "reasoningEffort", "requireExistingSession",
29
+ "outputSchema", "permissionMode", "projectId", "projectMemory", "promptLean", "reasoningEffort", "requireExistingSession",
30
30
  "resilience", "restoreFiles", "resumeAt", "resumeAtMode", "retainBackgroundProcesses", "retainSubagentSessions",
31
31
  "rewindFiles", "rewindFilesTo", "sandboxImageProfile", "scenario", "scratchpadDir", "selfOrchestration",
32
32
  "sessionId", "settings", "skills", "suggestNextPrompts", "systemPrompt", "taskId", "toolMaterializeStrategy",
@@ -118,7 +118,36 @@ export declare const INHERIT_ENV_WORDS: readonly ["all", "scrub"];
118
118
  export type TaskInheritEnv = (typeof INHERIT_ENV_WORDS)[number];
119
119
  /** 词表外的拒句(wire 可见;点名两词与各自的意思 —— 拿错的人要在同一条回执里读到该发什么)。 */
120
120
  export declare const INHERIT_ENV_VALUE_MESSAGE: string;
121
- /** `inheritEnv` 在场且不在词表里 ⇒ 拒句;缺席或合法 ⇒ `null`。**两腿(fresh admission / resume 重放)共用这一只**。 */
122
- export declare function inheritEnvFieldIssue(raw: unknown): string | null;
121
+ /**
122
+ * `inheritEnv` 的 `request.field_invalid` 400 的机读 `reason`([ref];闭集,**一种拒因一个词**,读者穷举读)—— 拒体另带 `field: "inheritEnv"` 与它,
123
+ * 壳靠这两位认「这条 400 真是 `inheritEnv` 的」,不猜句子。同一个键的**三只** 400 是一类,词表一张:
124
+ * · `unknown_word` —— 值形:是字符串,但不在受理词表(`"ALL"` · `"everything"` · `""`);
125
+ * · `not_a_string` —— 值形:根本不是字符串(名单形 `string[]` · 布尔 · 数字 · `null` · 对象);
126
+ * · `lane_not_host` —— `"all"` 落在非 host 车道(只有 host 工厂消费这一位,别的车道没有消费点);
127
+ * · `no_authorizer_session` —— `"all"` 而部署不接 authorizer(绑定按会话键控,没有会话就没有地方兑现)。
128
+ * 前两词的判官是 {@link inheritEnvFieldIssue}(只看体);后两词的判官在 `boot/resolve-spec.ts`(要读部署事实,两腿同过)。
129
+ * 不在这组的:`422 request.host_posture_refused`(托管部署不兑现 `"all"`)—— 那不是这个键的值写错,而是部署姿态拒,码与写容纳面那条腿共用,按 `errorCode` 认(契约 §18)。
130
+ * 加一种拒因 ⇒ 这里加一个词(并在判官处给它一个分支);词与因一一对应,没有一因两词、也没有一词两因。
131
+ * 词的铸点只有 {@link inheritEnvFacts} 一处:受理门与 resolveSpec 只经它出机读位,不手拼 `field` / `reason`。
132
+ */
133
+ export declare const INHERIT_ENV_REFUSAL_REASONS: readonly ["unknown_word", "not_a_string", "lane_not_host", "no_authorizer_session"];
134
+ export type InheritEnvRefusalReason = (typeof INHERIT_ENV_REFUSAL_REASONS)[number];
135
+ /** 拒体上的机读位(加性键:直接当 `request.field_invalid` 的附加键用;型别名而不是接口 —— 要能直接当 `Record<string, unknown>` 用)。 */
136
+ export type InheritEnvFacts = {
137
+ readonly field: "inheritEnv";
138
+ readonly reason: InheritEnvRefusalReason;
139
+ };
140
+ /** `inheritEnv` 机读位的**唯一铸点**:`field` 恒为键名,`reason` 是 {@link INHERIT_ENV_REFUSAL_REASONS} 里的词。 */
141
+ export declare function inheritEnvFacts(reason: InheritEnvRefusalReason): InheritEnvFacts;
142
+ /** 一次值形裁决的产物:wire 句 + 机读位。 */
143
+ export interface InheritEnvFieldIssue {
144
+ readonly message: string;
145
+ readonly facts: InheritEnvFacts;
146
+ }
147
+ /**
148
+ * `inheritEnv` 在场且不在词表里 ⇒ 拒(句 + 机读位);缺席或合法 ⇒ `null`。**两腿(fresh admission / resume 重放)共用这一只**,判据与句子在这一处,机读位经 {@link inheritEnvFacts}。
149
+ * 句子对两种坏值形是同一句(点名两词与「名单不是请求值」);`reason` 区分它们。
150
+ */
151
+ export declare function inheritEnvFieldIssue(raw: unknown): InheritEnvFieldIssue | null;
123
152
  export {};
124
153
  //# sourceMappingURL=request-value-closure.d.ts.map
@@ -37,12 +37,18 @@ export const INHERIT_ENV_WORDS = ["all", "scrub"];
37
37
  export const INHERIT_ENV_VALUE_MESSAGE = `inheritEnv must be one of ${INHERIT_ENV_WORDS.map((w) => JSON.stringify(w)).join(" | ")} when present ` +
38
38
  `("all" = the model-driven host shell inherits this machine's whole environment except this deployment's own credentials (its configuration directory's secret-typed names), honored only on a single-user host-lane deployment; ` +
39
39
  `"scrub" = the default scrub of credential-shaped variables, accepted on every lane; absent = "scrub") — a name list is not a request value`;
40
+ export const INHERIT_ENV_REFUSAL_REASONS = ["unknown_word", "not_a_string", "lane_not_host", "no_authorizer_session"];
41
+ export function inheritEnvFacts(reason) {
42
+ return { field: "inheritEnv", reason };
43
+ }
40
44
  export function inheritEnvFieldIssue(raw) {
41
45
  if (raw === null || raw === undefined || typeof raw !== "object" || Array.isArray(raw))
42
46
  return null;
43
47
  const value = raw.inheritEnv;
44
48
  if (value === undefined)
45
49
  return null;
46
- return INHERIT_ENV_WORDS.includes(value) ? null : INHERIT_ENV_VALUE_MESSAGE;
50
+ if (INHERIT_ENV_WORDS.includes(value))
51
+ return null;
52
+ return { message: INHERIT_ENV_VALUE_MESSAGE, facts: inheritEnvFacts(typeof value === "string" ? "unknown_word" : "not_a_string") };
47
53
  }
48
54
  //# sourceMappingURL=request-value-closure.js.map
@@ -26,7 +26,7 @@
26
26
  * (拒面按 ③ 的方向分),拒句点名有界({@link MAX_NAMED_INERT_ENTRIES} 条 + 计数),清单先过条数帽({@link inertToolListEntryOf})。
27
27
  * **真未知名**(拼写合法、没有近似也没有退役对应 —— 懒挂载的合法形,例如一台之后才连上的 MCP 服务器的工具)不是读成空:原样保留、运维行告知一次,不拒。
28
28
  * 选择器读一条条目 = 引擎 `judgeToolNameList` 的读法({@link selectorEntryName}):先取工具名那一段再只判名字(限制侧 `Tool(content)` ⇒ 整只工具,内容里写什么都不读;放行侧内容形引擎告「什么也不授」,受理口不拒);
29
- * `*` 在放行侧是「本层不设名单」、在限制侧读成空;限制侧整个协议的名字 `mcp__*` / `mcp__*__*` / `a2a__*` 合法 —— 那是引擎条目读法的读数(core 7.55.0 [ref]:
29
+ * `*` 在放行侧是「本层不设名单」、在限制侧是整名 glob(引擎文法 5,7.137.0 起;此前读成空、受理口拒);限制侧整个协议的名字 `mcp__*` / `mcp__*__*` / `a2a__*` 合法 —— 那是引擎条目读法的读数(core 7.55.0 [ref]:
30
30
  * `compileAllowList(…, "deny", 名册)` 把它们读成这一协议的每一只工具),本仓不另写豁免。
31
31
  * ⑤ **机读位**:拒体的码与句子是第一处命中的清单的(句子冻结),体另带这一遍判下来的**全部**读成空的条目(`entry-problems.ts` 的 `detail.problems`,{@link inertToolListEntryOf} 铸);
32
32
  * 放行侧选择器清单里受理口没拒、却什么也不放的条目随受理应答的 `warnings` 交回({@link allowSelectorWarningsOf})—— 两处读同一次条目读法({@link selectorReadings}),不另判。
@@ -55,7 +55,8 @@ export declare function allowLayerEntryCountIssue(field: string, list: readonly
55
55
  /** 一条引擎读成空的条目(引擎的形:原因词,名字层另带判词 / 1:1 现名;包根不导出这只具名型 ⇒ 从 `compileAllowList` 的返回型取,不手抄)。 */
56
56
  export type InertRuleEntry = ReturnType<typeof compileAllowList>["inert"][number];
57
57
  /**
58
- * ③ 引擎读一张**规则条目表**(`behavior` 方向)时读成空的条目(引擎的整表读法:allow 表里的 `*` 让整表读成空;deny 表里的 `*` 只读空它自己)。条目名过引擎判官,
58
+ * ③ 引擎读一张**规则条目表**(`behavior` 方向)时读成空的条目(引擎文法 5,core 7.56.0:allow 表里名内的 `*` 只在协议名的工具段里读,别处的 `*` 只作废它自己、旁边的条目照读;
59
+ * deny / ask 表里带 `*` 的名是整名 glob,不读空)。条目名过引擎判官,
59
60
  * 名册是本仓**唯一**的一张({@link TOOL_NAME_ROSTER},`namespaced-rule-name.ts`:引擎目录 ∪ 本仓自铸的工具名)—— 两个方向、每一处读条目名的地方(请求体的规则表、技能清单、选择器清单、
60
61
  * 运维 / 中心的审批名单)传同一张,没有「这一处空名册、那一处带名册」的两种读法。工具名选择器清单另走 {@link inertToolListEntryOf}。
61
62
  */
@@ -27,9 +27,9 @@ export function inertRuleEntryGuidance(i) {
27
27
  case "near_miss":
28
28
  return `the tool name differs from a mounted tool only by case${write}`;
29
29
  case "wildcard_all":
30
- return "`*` names no tool (on an allow list it voids the whole list) — name the tools instead";
30
+ return "`*` grants nothing on an allow list (the entries beside it still apply) — name the tools instead; on a deny or ask list `*` is every tool";
31
31
  case "namespace_wildcard":
32
- return "a server segment takes no partial wildcard (`mcp__git*`) — name the server: mcp__<server> or mcp__<server>__*; a deny or ask list may write mcp__* for every MCP tool";
32
+ return "an allow list reads a `*` only in a protocol name's tool segment — name the server: mcp__<server> or mcp__<server>__*; on a deny or ask list a `*` in a name is a glob over the whole name (mcp__* is every MCP tool, mcp__git* every tool whose name starts with it)";
33
33
  case "content_unsupported":
34
34
  return "this tool takes no content form here — name the tool itself, or use Bash(<command>) / Bash(<prefix>:*) for a shell command";
35
35
  case "grammar":
@@ -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, ruleToolGrammarOf, precheckEditedRuleText, prepareCardApproval, prepareCcImport, redeemRuleBatch, redeemRuleTicket, addPersistedRule, removePersistedRule, isValidDurableScope, effectiveOrThrow, readRowBehavior, } from "@sema-agent/core";
8
+ import { confirmRuleApproval, RULE_BEHAVIORS, parseRuleText, ruleReachesProgramRun, ruleToolGrammarOf, 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;
@@ -70,16 +70,19 @@ export function sameWireRuleIdentity(row, identity) {
70
70
  function storeForBucket(provider, ref) {
71
71
  if (typeof ref === "string")
72
72
  return provider.forPrincipal(ref);
73
- switch (ref.kind) {
73
+ const owner = readRuleOwner(ref);
74
+ if (owner === undefined)
75
+ throw new Error("unrecognised permission-rule bucket reference (not a plain owner record, an unknown kind, or a principal that is no principal name) — refusing to guess which bucket it names");
76
+ switch (owner.kind) {
74
77
  case "principal":
75
- return provider.forPrincipal(ref.principal);
78
+ return provider.forPrincipal(owner.principal);
76
79
  case "local-owner":
77
80
  if (provider.forLocalOwner === undefined) {
78
81
  throw new Error("this provider has no local-owner bucket (forLocalOwner is not implemented) — a local-owner rule cannot be listed or removed through it");
79
82
  }
80
83
  return provider.forLocalOwner();
81
84
  default: {
82
- const unknown = ref;
85
+ const unknown = owner;
83
86
  throw new Error(`unrecognised permission-rule bucket reference (${JSON.stringify(unknown)}) — refusing to guess which bucket it names`);
84
87
  }
85
88
  }
@@ -174,8 +174,8 @@ type NoTtyRemedy = NoTtySpokenRemedy | "terminal_only";
174
174
  * · `shell_floor`:shell 地板被抬到 always —— `AUTONOMY=ask`(`autonomyOverrides`)与 `MANUAL_MODE_SHELL_GATE=always`,真 Runner 读数两者的类集都是 `[shell_floor]`(**不是** `operator_forced`):
175
175
  * 单独一句,不与 `operator_forced` 的来源混在一起 —— 混说会把 `APPROVAL_NEVER_AUTO` 的问指到一定没设的 `AUTONOMY`;
176
176
  * · 其余:引擎自己的问(写门 / 形状层 / 写保护 / 探针 / 钩子 / 组织规则 / 引擎的守卫 / 工具标记 …)没有一只本部署旋钮能放行 —— 只说去终端答。
177
- * 不借 core 的 `headlessApprovalRemedy`:那一句是给**库的调用方**的出路(「wire an approver (`onAsk`)」「run with `permissionMode: "bypassPermissions"`」),
178
- * 而本腿的席**已经接好**(缺的是终端),也没有任何命令行旗给 `permissionMode` —— 照搬会把运维指向一个不存在的旋钮。
177
+ * 不借 core 的 `headlessApprovalRemedy`:那一句是给**库的调用方**的出路(「wire an approver (`onAsk`)」;core 7.56.0 [ref] 起它不再教模式放行,
178
+ * 那一条出路只作数据骑在通告的 `detail.remedies` 上),而本腿的席**已经接好**(缺的是终端),也没有任何命令行旗给 `permissionMode` —— 照搬会把运维指向一个不存在的旋钮。
179
179
  *
180
180
  * `autoBudgetReleases`:`APPROVAL_AUTO_BUDGET` 能放行这类问吗(读 `createDurableAskPolicy` 的预算臂:只放行**没进 `APPROVAL_NEVER_AUTO`、耐久资源没耗尽**的
181
181
  * `APPROVAL_REQUIRE` 命中,换成一句带理由的 allow)。按 `classes` **整体**读:集里每一类都放得行才建议预算 —— 折叠了别的类(`operator_forced` 等)说明另一层的问仍在,
package/dist/runs.js CHANGED
@@ -7,6 +7,7 @@ import { isSessionErrorCode, sessionErrorHttp } from "./session-error-wire.js";
7
7
  import { redactErrorMessage } from "./observability/run-terminal.js";
8
8
  import { withPrincipal } from "./observability/principal-context.js";
9
9
  import { withModelReachScope } from "./model-reach-scope.js";
10
+ import { runSkillsOf } from "./capabilities/skills.js";
10
11
  import { createInstructionSelectionLeg } from "./instruction-selection.js";
11
12
  import { redactSecrets } from "./trace/redact.js";
12
13
  import { taskNotificationEventData, appendModelUsageDelta, appendPromptManifest, attachModelUsage } from "./trace/project.js";
@@ -330,7 +331,7 @@ export async function runInBackground(runner, spec, runStore, taskId, metrics, p
330
331
  metrics?.inc("tasks_total", { status: "needs_review" });
331
332
  };
332
333
  const instructionLeg = createInstructionSelectionLeg();
333
- const sink = createLedgerSink({ appendEvent: (seq, type, data) => runStore.appendEvent(taskId, seq, type, data), persistThinking, notifiedKeys, onActivity: (kind, statusData) => recordTurnActivity(taskId, kind, statusData), instructionsSegment: instructionLeg.segment });
334
+ const sink = createLedgerSink({ appendEvent: (seq, type, data) => runStore.appendEvent(taskId, seq, type, data), persistThinking, notifiedKeys, onActivity: (kind, statusData) => recordTurnActivity(taskId, kind, statusData), legSections: () => ({ instructions: instructionLeg.segment(), skills: runSkillsOf(spec) }) });
334
335
  const append = (type, data) => sink.append(type, data);
335
336
  const flush = () => sink.flush();
336
337
  const flushModelUsage = () => appendModelUsageDelta(append, modelUsage, taskId);
@@ -65,9 +65,9 @@ export declare function validateCommandRules(rules: CommandRule[]): string[];
65
65
  * (deny-wins) by {@link applyRuntimeGovernance}, so it is tighten-only.
66
66
  *
67
67
  * 🔴 **[ref]:返回叶,不在这里折**(改名自 `compileCommandPolicy`,修前返回 blocklist 形里 `combinePolicies(coarseDeny, askList)` 的
68
- * 一只折叠)。复合命令(`a && b`、管道、`;`)上两只叶都按「解析不了 ⇒ 问」问 ⇒ 那只折叠带出 `foldedAskClasses`,而治理层随后要给每只
69
- * ask 盖部署出身 —— 盖在折叠的输出上就是展开一只 core 担保过身份的对象(core [ref] 7.39.0 按名拒)。叶交出去,由
70
- * {@link applyRuntimeGovernance} 逐叶盖位、折**一次**:本函数不知道出身这件事,治理层只有一个折叠点。 (Assumes commands are pre-validated by
68
+ * 一只折叠)。复合命令(`a && b`、管道、`;`)上两只叶都按「解析不了 ⇒ 问」问 ⇒ 那只折叠把落选者的类记进引擎的认证记录(core 7.56.0:
69
+ * `attestation.foldedClasses`;7.39.0–7.48.x 是对象身份担保的 `foldedAskClasses` 成员,副本被按名拒;7.49.0 [ref] 起带引擎记录的副本照收),而治理层要给每只 ask 盖部署出身。
70
+ * 叶交出去,由 {@link applyRuntimeGovernance} 逐叶盖位、折**一次**:本函数不知道出身这件事,治理层只有一个折叠点。 (Assumes commands are pre-validated by
71
71
  * {@link validateCommandRules}; an un-matchable command here is simply inert, never a parse error.)
72
72
  *
73
73
  * 🔴 **[ref]:词表外的 `decision` 折 `deny`,不折 `ask`。** {@link validateCommandRules} 的头注写下的那条
@@ -271,8 +271,8 @@ export declare function governanceOriginOf(gate: {
271
271
  *
272
272
  * 🔴 **[ref](core [ref] 7.39.0)—— 治理层只有一个折叠点**:两种输入都交**叶**(`compileCommandPolicyLayers` 的那几只、
273
273
  * `sensitivePathLayers` 的那几只),本函数对每一片叶各盖一次部署出身(`createDeploymentAuthoredPolicy`),再交 core
274
- * `combinePolicies` 折一次;折叠是最外层,输出经标记观察器原物交回。修前是「先折、再在折叠输出上展开盖位」——折叠带出
275
- * `foldedAskClasses` 时交给 core 的是副本,7.39.0 起按名拒(工具不跑、无卡)。
274
+ * `combinePolicies` 折一次;折叠是最外层,输出经标记观察器原物交回。修前是「先折、再在折叠输出上展开盖位」——7.39.0–7.48.x 上折叠带出
275
+ * `foldedAskClasses` 时交给 core 的是副本,被按名拒(工具不跑、无卡);core 7.56.0 起那一成员退役、落选者的类骑在 `attestation` 记录上。
276
276
  */
277
277
  export declare function applyRuntimeGovernance(base: TaskSpec, governance: {
278
278
  autonomy?: Autonomy;
@@ -0,0 +1,49 @@
1
+ /** Why an entry is withdrawn — the server's own words (closed set; the vault's per-origin and whole-answer words join it with the plane's renewal). */
2
+ export type SandboxLeaseWithdrawReason = "center_unavailable" | "insecure_transport";
3
+ /** The sentence kinds an entry carries: a withdrawal reason, or the `ready` entry's "the git host refused the credential". */
4
+ export type SandboxLeaseSentenceKind = SandboxLeaseWithdrawReason | "credential_rejected";
5
+ /** What git shows for an entry (single line, no credential, names the origin and the way out). */
6
+ export declare function sandboxLeaseSentence(kind: SandboxLeaseSentenceKind, origin: string): string;
7
+ /** One origin's lease as the server decides it (the writer supplies `i`, `origin`, `leaseId` and the sentences). */
8
+ export type SandboxLeaseEntryInput = {
9
+ readonly state: "ready";
10
+ readonly username: string;
11
+ readonly secret: string;
12
+ readonly credentialId: string;
13
+ readonly version: number;
14
+ readonly ttlMs: number;
15
+ } | {
16
+ readonly state: "withdrawn";
17
+ readonly reason: SandboxLeaseWithdrawReason;
18
+ };
19
+ /**
20
+ * The lease file text for a pod whose origin table is `origins` (one input per origin, same order). Refused (never sent) when the broker
21
+ * would refuse it: an input count that is not the table's, a `ready` input for an http origin, `ttlMs` outside (0, 300 000], a malformed
22
+ * value. The refusal names the entry and the rule, never a value.
23
+ */
24
+ export declare function buildSandboxLeaseFile(origins: readonly string[], entries: readonly SandboxLeaseEntryInput[], mintLeaseId?: () => string): {
25
+ ok: true;
26
+ text: string;
27
+ } | {
28
+ ok: false;
29
+ problem: string;
30
+ };
31
+ /** The lease a pod starts with, before the first answer from the vault: every entry withdrawn (https ⇒ `center_unavailable`, http ⇒ `insecure_transport`). */
32
+ export declare function placeholderLeaseEntries(origins: readonly string[]): SandboxLeaseEntryInput[];
33
+ /**
34
+ * The lease time of one vault answer: `min(cacheUntil, expiresAt ?? cacheUntil) − answeredAt − rttMs`, capped at the broker's ceiling,
35
+ * at least 1; `undefined` when that is ≤ 0 or any reading is not a finite number (the entry is then written `withdrawn`). The three times are
36
+ * the vault's own clock; `rttMs` is this server's monotonic measure of the request it sent and the answer it got.
37
+ */
38
+ export declare function sandboxLeaseTtlMs(answer: {
39
+ readonly answeredAt: number;
40
+ readonly cacheUntil: number;
41
+ readonly expiresAt: number | null;
42
+ }, rttMs: number): number | undefined;
43
+ /**
44
+ * The agent container's git routes (non-secret env): one `url.<broker route>.insteadOf = <origin>/` triplet per origin, in the pod's
45
+ * origin-table order — git then sends `<origin>/<owner>/<repo>/…` to the broker's loopback route `/o/<i>/…` (http origins too: they are
46
+ * forwarded without a credential and answered with their sentence).
47
+ */
48
+ export declare function sandboxGitRouteEnv(origins: readonly string[]): Record<string, string>;
49
+ //# sourceMappingURL=sandbox-git-lease.d.ts.map
@@ -0,0 +1,60 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { BROKER_HOST, BROKER_PORT } from "./broker/constants.js";
3
+ import { LEASE_TTL_MAX_MS, parseLeaseFile, serializeLeaseFile } from "./broker/lease-file.js";
4
+ export function sandboxLeaseSentence(kind, origin) {
5
+ switch (kind) {
6
+ case "center_unavailable":
7
+ return `your git credential for ${origin} is not available right now (the console could not be reached); retry shortly, and do not paste a token into the conversation`;
8
+ case "insecure_transport":
9
+ return `${origin} is served over plain http, so member credentials are never sent to it; ask the operator to serve it over https`;
10
+ case "credential_rejected":
11
+ return `${origin} rejected your git credential; reconnect it in the console, then retry`;
12
+ }
13
+ }
14
+ export function buildSandboxLeaseFile(origins, entries, mintLeaseId = () => `l_${randomUUID()}`) {
15
+ if (entries.length !== origins.length)
16
+ return { ok: false, problem: `the lease has ${entries.length} entries for a pod routed to ${origins.length} origins` };
17
+ const file = {
18
+ v: 1,
19
+ origins: entries.map((e, i) => {
20
+ const origin = origins[i];
21
+ return e.state === "ready"
22
+ ? {
23
+ i,
24
+ origin,
25
+ state: "ready",
26
+ leaseId: mintLeaseId(),
27
+ ttlMs: e.ttlMs,
28
+ username: e.username,
29
+ secret: e.secret,
30
+ credentialId: e.credentialId,
31
+ version: e.version,
32
+ unusableMessage: sandboxLeaseSentence("center_unavailable", origin),
33
+ rejectedMessage: sandboxLeaseSentence("credential_rejected", origin),
34
+ }
35
+ : { i, origin, state: "withdrawn", reason: e.reason, unusableMessage: sandboxLeaseSentence(e.reason, origin) };
36
+ }),
37
+ };
38
+ const text = serializeLeaseFile(file);
39
+ const judged = parseLeaseFile(text);
40
+ return judged.ok ? { ok: true, text } : { ok: false, problem: judged.problem };
41
+ }
42
+ export function placeholderLeaseEntries(origins) {
43
+ return origins.map((o) => ({ state: "withdrawn", reason: o.startsWith("https://") ? "center_unavailable" : "insecure_transport" }));
44
+ }
45
+ export function sandboxLeaseTtlMs(answer, rttMs) {
46
+ const until = answer.expiresAt === null ? answer.cacheUntil : Math.min(answer.cacheUntil, answer.expiresAt);
47
+ const ms = until - answer.answeredAt - rttMs;
48
+ if (!Number.isFinite(ms) || ms <= 0)
49
+ return undefined;
50
+ return Math.max(1, Math.min(LEASE_TTL_MAX_MS, Math.floor(ms)));
51
+ }
52
+ export function sandboxGitRouteEnv(origins) {
53
+ const env = { GIT_CONFIG_COUNT: String(origins.length) };
54
+ origins.forEach((origin, i) => {
55
+ env[`GIT_CONFIG_KEY_${i}`] = `url.http://${BROKER_HOST}:${BROKER_PORT}/o/${i}/.insteadOf`;
56
+ env[`GIT_CONFIG_VALUE_${i}`] = `${origin}/`;
57
+ });
58
+ return env;
59
+ }
60
+ //# sourceMappingURL=sandbox-git-lease.js.map
@@ -0,0 +1,84 @@
1
+ import type { ServiceConfig } from "./config.js";
2
+ import type { SandboxSecretSeat } from "./plugins/remote-env-k8s.js";
3
+ import type { IdentitySource, RunIdentity } from "./identity-source.js";
4
+ import type { Logger } from "./observability/logger.js";
5
+ /** The off reasons, in judge order (closed set; an added premise is a word here, a row in {@link SANDBOX_GIT_PLANE_OFF_TEXT}, a step in the judge). */
6
+ export declare const SANDBOX_GIT_PLANE_OFF_REASONS: readonly ["single_user", "lane_without_boundary", "no_caller_credential", "center_not_https", "secret_seat_unverified", "secret_seat_denied", "pod_patch_reaches_broker", "broker_image_unset", "no_origins"];
7
+ export type SandboxGitPlaneOffReason = (typeof SANDBOX_GIT_PLANE_OFF_REASONS)[number];
8
+ /** One origin of an on plane. */
9
+ export type SandboxGitPlaneOrigin = {
10
+ readonly origin: string;
11
+ readonly delivers: true;
12
+ } | {
13
+ readonly origin: string;
14
+ readonly delivers: false;
15
+ readonly reason: "insecure_transport";
16
+ };
17
+ /** The plane's reading (the wire value of `credentialPlane.sandboxGit`). */
18
+ export type SandboxGitPlane = {
19
+ readonly state: "on";
20
+ readonly origins: readonly SandboxGitPlaneOrigin[];
21
+ } | {
22
+ readonly state: "off";
23
+ readonly reason: SandboxGitPlaneOffReason;
24
+ };
25
+ /** Operator sentence per off reason (the boot line and the docs read it; the record type makes a missing word a compile error). */
26
+ export declare const SANDBOX_GIT_PLANE_OFF_TEXT: {
27
+ readonly [R in SandboxGitPlaneOffReason]: string;
28
+ };
29
+ /** The premises, each as the judge reads it. */
30
+ export interface SandboxGitPlanePremises {
31
+ readonly multiTenant: boolean;
32
+ readonly k8sLane: boolean;
33
+ readonly callerCredential: boolean;
34
+ /** The console base address (`SEMA_WEB_ADMIN_URL`); absent ⇒ not https. */
35
+ readonly centerUrl: string | undefined;
36
+ /** The boot's SelfSubjectAccessReview answer for the sandbox Secret seat; `undefined` = not asked (counts as unverified). */
37
+ readonly secretSeat: SandboxSecretSeat | undefined;
38
+ /** The patch judge's refusal for a pod that carries the broker (`podSpecPatchBrokerRefusal`); `undefined` = the patch may land. */
39
+ readonly patchRefusal: string | undefined;
40
+ readonly brokerImage: string | undefined;
41
+ /** The routed origins (`readSandboxGitConfig`); `null` = no `sandboxGit` block. */
42
+ readonly origins: readonly string[] | null;
43
+ }
44
+ /** THE judge: the first premise that fails, in the written order; all hold ⇒ on with per-origin rows. Pure. */
45
+ export declare function judgeSandboxGitPlane(p: SandboxGitPlanePremises): SandboxGitPlane;
46
+ /** The secret seat is asked for the plane only when every premise before it holds (a deployment the plane is off for asks nothing for it). */
47
+ export declare function sandboxGitPlaneAsksSecretSeat(p: Pick<SandboxGitPlanePremises, "multiTenant" | "k8sLane" | "callerCredential" | "centerUrl">): boolean;
48
+ /** The premises read from config, plus the two boot readings (seat answer, patch refusal) when the boot has them. */
49
+ export declare function sandboxGitPlanePremisesOf(config: ServiceConfig, readings: {
50
+ readonly secretSeat?: SandboxSecretSeat;
51
+ readonly patchRefusal?: string;
52
+ }): SandboxGitPlanePremises;
53
+ /**
54
+ * Wiring point — the effective `sandboxGit` block (the origins this deployment routes through the broker, canonical form, ≤ 16). Its shape
55
+ * and its canonical-form judge belong to the settings-schema contract package (14.7.0); until that release is picked up there is no block to
56
+ * read ⇒ `null` (the plane stays off, `no_origins`). Replaced by the package reader on pickup — no local copy of the shape.
57
+ */
58
+ export declare function readSandboxGitConfig(_config: ServiceConfig): {
59
+ readonly origins: readonly string[];
60
+ } | null;
61
+ /**
62
+ * Wiring point — does this worker hold the credential the console's vault admits on its resolve endpoint (the worker-assertion token)?
63
+ * The server has no reader for it yet ⇒ `false` (the plane stays off, `no_caller_credential`). Replaced by the plane's vault client.
64
+ */
65
+ export declare function readSandboxGitCallerCredential(_config: ServiceConfig): boolean;
66
+ /** The run store's by-id read, as far as the plane needs it: the row's identity tuple (`RunRecord.identity`). Every run store's `getRun` is one. */
67
+ export interface SandboxGitRunRows {
68
+ getRun(taskId: string): Promise<{
69
+ readonly identity: RunIdentity | null;
70
+ } | null | undefined>;
71
+ }
72
+ /**
73
+ * The identity source recorded on the pod's run row (`RunRecord.identity.source`, minted once at admission, never recomputed). `runId` is the
74
+ * ONE reading of "the run that owns this pod" (the per-run binding's: the top-level run's id; a delegated child / a service run / no task id ⇒
75
+ * none). No run id / no run store / no row / `identity: null` ⇒ `undefined`; a read that throws ⇒ `undefined` and one warn line
76
+ * `sandbox_broker_identity_unread` —— every miss reads as "not sso" ⇒ no broker (fail-closed), none of them throws into the pod's connect.
77
+ */
78
+ export declare function runIdentitySourceOf(runId: string | undefined, runs: SandboxGitRunRows | undefined, logger?: Pick<Logger, "warn">): Promise<IdentitySource | undefined>;
79
+ /** The broker seat for one run's pod: the plane on ∧ the image set ∧ the run's recorded identity source `sso` ⇒ the image + every origin of the reading (http too); otherwise none. */
80
+ export declare function sandboxBrokerSeatOf(plane: SandboxGitPlane, brokerImage: string | undefined, identitySource: IdentitySource | undefined): {
81
+ readonly image: string;
82
+ readonly origins: readonly string[];
83
+ } | undefined;
84
+ //# sourceMappingURL=sandbox-git-plane.d.ts.map