@sema-agent/server 7.148.0 → 7.149.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 (247) hide show
  1. package/MIGRATION.md +429 -0
  2. package/README.md +16 -1
  3. package/README.zh-CN.md +4 -0
  4. package/USAGE.md +89 -55
  5. package/dist/adoption/wire.d.ts +3 -3
  6. package/dist/approval-card.d.ts +32 -32
  7. package/dist/approval-leg-origin.d.ts +33 -0
  8. package/dist/approval-leg-origin.js +4 -0
  9. package/dist/auth-bridge.d.ts +9 -1
  10. package/dist/auth-bridge.js +4 -4
  11. package/dist/auth-keys.d.ts +10 -1
  12. package/dist/auth-keys.js +8 -0
  13. package/dist/boot/execution-env.js +10 -11
  14. package/dist/boot/execution-lane.d.ts +6 -4
  15. package/dist/boot/execution-lane.js +3 -1
  16. package/dist/boot/org-delivery.d.ts +3 -2
  17. package/dist/boot/resolve-spec.js +6 -4
  18. package/dist/boot/runner-deps.js +3 -1
  19. package/dist/boot/stage-01-config.d.ts +1 -0
  20. package/dist/boot/stage-01-config.js +4 -3
  21. package/dist/boot/stage-02-web-admin-pull.d.ts +1 -0
  22. package/dist/boot/stage-02-web-admin-pull.js +10 -2
  23. package/dist/boot/stage-03-store-backend.d.ts +1 -0
  24. package/dist/boot/stage-03-store-backend.js +2 -2
  25. package/dist/boot/stage-04-stores.d.ts +1 -0
  26. package/dist/boot/stage-05-execution-env.d.ts +1 -0
  27. package/dist/boot/stage-06-runners.d.ts +2 -1
  28. package/dist/boot/stage-07-capability-layer.d.ts +2 -1
  29. package/dist/boot/stage-08-reapers.d.ts +2 -1
  30. package/dist/boot/stage-09-leader.d.ts +2 -1
  31. package/dist/boot/stage-10-http-server.d.ts +2 -1
  32. package/dist/boot/stores.d.ts +3 -0
  33. package/dist/boot/stores.js +22 -19
  34. package/dist/capabilities/tool-defer.d.ts +1 -1
  35. package/dist/capability-facts.d.ts +5 -4
  36. package/dist/capability-facts.js +3 -14
  37. package/dist/card-settle-stored-scope.d.ts +56 -0
  38. package/dist/card-settle-stored-scope.js +27 -0
  39. package/dist/card-void.d.ts +7 -12
  40. package/dist/card-void.js +2 -7
  41. package/dist/config-catalog.d.ts +12 -0
  42. package/dist/config-catalog.js +36 -32
  43. package/dist/config-invariants.d.ts +5 -4
  44. package/dist/config-invariants.js +4 -4
  45. package/dist/config-types.d.ts +37 -5
  46. package/dist/config.d.ts +6 -5
  47. package/dist/config.js +35 -24
  48. package/dist/context-window-notice.d.ts +31 -0
  49. package/dist/context-window-notice.js +28 -0
  50. package/dist/deployment-shape.d.ts +20 -11
  51. package/dist/deployment-shape.js +1 -0
  52. package/dist/env-name-allowlist-knobs.d.ts +6 -5
  53. package/dist/env-name-allowlist-knobs.js +5 -0
  54. package/dist/execution-lane-caps.d.ts +20 -5
  55. package/dist/execution-lane-caps.js +18 -4
  56. package/dist/hooks/command-spawn.d.ts +7 -2
  57. package/dist/hooks/command-spawn.js +13 -6
  58. package/dist/hooks/hook-runner.d.ts +5 -0
  59. package/dist/hooks/hook-runner.js +11 -7
  60. package/dist/http/admission.js +3 -3
  61. package/dist/http/approval-sweeps.js +3 -2
  62. package/dist/http/claim-heartbeat.d.ts +3 -3
  63. package/dist/http/claim-heartbeat.js +3 -3
  64. package/dist/http/claimed-run.d.ts +16 -9
  65. package/dist/http/claimed-run.js +8 -0
  66. package/dist/http/dispatch.js +5 -3
  67. package/dist/http/durable-run-leg.d.ts +8 -1
  68. package/dist/http/durable-run-leg.js +30 -11
  69. package/dist/http/execution-context.d.ts +115 -14
  70. package/dist/http/execution-context.js +130 -40
  71. package/dist/http/owner-access.d.ts +3 -2
  72. package/dist/http/owner-access.js +2 -3
  73. package/dist/http/principal-gate.d.ts +18 -11
  74. package/dist/http/principal-gate.js +3 -1
  75. package/dist/http/resume-ctx-read.d.ts +14 -3
  76. package/dist/http/resume-ctx-read.js +8 -2
  77. package/dist/http/resume-legs.d.ts +35 -3
  78. package/dist/http/resume-legs.js +244 -92
  79. package/dist/http/route-ctx.d.ts +6 -0
  80. package/dist/http/routes/a2a-serve.js +2 -0
  81. package/dist/http/routes/approvals-assistant.js +3 -2
  82. package/dist/http/routes/capabilities.js +1 -1
  83. package/dist/http/routes/diagnostics.d.ts +3 -1
  84. package/dist/http/routes/diagnostics.js +30 -5
  85. package/dist/http/routes/principal-inbox.d.ts +3 -1
  86. package/dist/http/routes/principal-inbox.js +3 -2
  87. package/dist/http/routes/runs.js +10 -7
  88. package/dist/http/routes/session-sync.d.ts +24 -5
  89. package/dist/http/routes/session-sync.js +46 -34
  90. package/dist/http/routes/sessions-list.d.ts +1 -1
  91. package/dist/http/routes/tasks.js +127 -93
  92. package/dist/http/server.d.ts +4 -5
  93. package/dist/http/server.js +30 -5
  94. package/dist/http/submit-ctx.d.ts +1 -0
  95. package/dist/http/submit-ctx.js +2 -3
  96. package/dist/index.d.ts +3 -1
  97. package/dist/index.js +1 -0
  98. package/dist/leg-facts.d.ts +7 -2
  99. package/dist/leg-facts.js +1 -0
  100. package/dist/local-store-declaration.d.ts +54 -0
  101. package/dist/local-store-declaration.js +59 -0
  102. package/dist/mcp-assertion-header.d.ts +1 -1
  103. package/dist/mcp-assertion-header.js +1 -1
  104. package/dist/memory-project-layer.js +1 -1
  105. package/dist/memory-scope.d.ts +4 -4
  106. package/dist/memory-scope.js +4 -4
  107. package/dist/memory-sync-client.js +2 -2
  108. package/dist/model-context-window.d.ts +50 -11
  109. package/dist/model-context-window.js +20 -1
  110. package/dist/model-entry-refusal.d.ts +5 -4
  111. package/dist/model-entry-refusal.js +17 -3
  112. package/dist/model-reach-scope.d.ts +2 -1
  113. package/dist/observability/fail-open.d.ts +14 -2
  114. package/dist/observability/fail-open.js +14 -2
  115. package/dist/observability/metrics.js +1 -1
  116. package/dist/observability/run-terminal.js +1 -0
  117. package/dist/observability/secret-env-scrub.d.ts +1 -1
  118. package/dist/observability/secret-env-scrub.js +1 -1
  119. package/dist/org-delivery-gate.d.ts +31 -6
  120. package/dist/org-delivery-gate.js +7 -4
  121. package/dist/owner-faces.d.ts +3 -2
  122. package/dist/park-row-commit.d.ts +67 -0
  123. package/dist/park-row-commit.js +76 -0
  124. package/dist/parked-decide.d.ts +9 -7
  125. package/dist/parked-decide.js +25 -6
  126. package/dist/peer-directory.d.ts +1 -1
  127. package/dist/plugins/adoption-log-sql.d.ts +3 -3
  128. package/dist/plugins/adoption-log-sql.js +19 -19
  129. package/dist/plugins/approval-ask-store-sql.d.ts +1 -1
  130. package/dist/plugins/approval-ask-store-sql.js +3 -5
  131. package/dist/plugins/approval-exemption-skill-cutoff.d.ts +64 -0
  132. package/dist/plugins/approval-exemption-skill-cutoff.js +122 -0
  133. package/dist/plugins/approval-exemption-skill-m46.d.ts +0 -6
  134. package/dist/plugins/approval-exemption-skill-m46.js +6 -10
  135. package/dist/plugins/approval-exemption-store.d.ts +26 -1
  136. package/dist/plugins/approval-exemption-store.js +28 -19
  137. package/dist/plugins/ensure-schema-lock-policy.d.ts +36 -3
  138. package/dist/plugins/ensure-schema-lock-policy.js +75 -9
  139. package/dist/plugins/file-run-store.d.ts +22 -9
  140. package/dist/plugins/file-run-store.js +50 -22
  141. package/dist/plugins/local-checkpoint-store.d.ts +4 -0
  142. package/dist/plugins/local-checkpoint-store.js +2 -1
  143. package/dist/plugins/local-data-root-admission.d.ts +52 -0
  144. package/dist/plugins/local-data-root-admission.js +100 -0
  145. package/dist/plugins/local-engine-high-water.d.ts +28 -7
  146. package/dist/plugins/local-engine-high-water.js +27 -22
  147. package/dist/plugins/local-session-store.d.ts +134 -49
  148. package/dist/plugins/local-session-store.js +149 -108
  149. package/dist/plugins/memory-run-store.d.ts +14 -9
  150. package/dist/plugins/memory-run-store.js +47 -21
  151. package/dist/plugins/one-time-migration-marker.d.ts +19 -3
  152. package/dist/plugins/one-time-migration-marker.js +7 -1
  153. package/dist/plugins/one-time-migrations.d.ts +46 -20
  154. package/dist/plugins/one-time-migrations.js +102 -79
  155. package/dist/plugins/org-delivery-quota-store.d.ts +34 -11
  156. package/dist/plugins/org-delivery-quota-store.js +29 -14
  157. package/dist/plugins/permission-rule-store-sql.d.ts +1 -1
  158. package/dist/plugins/permission-rule-store-sql.js +5 -9
  159. package/dist/plugins/pg-pool.d.ts +1 -1
  160. package/dist/plugins/pg-pool.js +10 -12
  161. package/dist/plugins/remote-env-device.d.ts +3 -2
  162. package/dist/plugins/remote-env-e2b.d.ts +1 -7
  163. package/dist/plugins/remote-env-e2b.js +10 -7
  164. package/dist/plugins/remote-env-host.d.ts +50 -9
  165. package/dist/plugins/remote-env-host.js +77 -20
  166. package/dist/plugins/remote-env-k8s.js +4 -2
  167. package/dist/plugins/remote-env-local-docker.d.ts +6 -3
  168. package/dist/plugins/remote-env-local-docker.js +8 -23
  169. package/dist/plugins/run-store-sql.d.ts +26 -7
  170. package/dist/plugins/run-store-sql.js +44 -16
  171. package/dist/plugins/scenario-prefix-switch-m42.d.ts +3 -1
  172. package/dist/plugins/scenario-prefix-switch-m42.js +4 -3
  173. package/dist/plugins/schema-consistency.d.ts +89 -5
  174. package/dist/plugins/schema-consistency.js +176 -7
  175. package/dist/plugins/schema-drift-sentence.d.ts +6 -5
  176. package/dist/plugins/schema-drift-sentence.js +13 -5
  177. package/dist/plugins/session-placement.d.ts +5 -4
  178. package/dist/plugins/shell-sentinel-owner-m48.d.ts +22 -0
  179. package/dist/plugins/shell-sentinel-owner-m48.js +6 -0
  180. package/dist/plugins/stop-then-start-sentence.d.ts +16 -0
  181. package/dist/plugins/stop-then-start-sentence.js +7 -0
  182. package/dist/plugins/store-backend.d.ts +22 -8
  183. package/dist/plugins/store-backend.js +58 -24
  184. package/dist/plugins/store-contracts.d.ts +33 -3
  185. package/dist/plugins/store-contracts.js +16 -0
  186. package/dist/plugins/tidb-pool.d.ts +11 -6
  187. package/dist/plugins/tidb-pool.js +18 -12
  188. package/dist/plugins/workspace-custody.d.ts +44 -0
  189. package/dist/plugins/workspace-custody.js +47 -0
  190. package/dist/project-identity.d.ts +8 -7
  191. package/dist/project-identity.js +40 -17
  192. package/dist/project-layer-key.d.ts +13 -2
  193. package/dist/project-layer-key.js +9 -1
  194. package/dist/read-deny-floor-notice.d.ts +36 -0
  195. package/dist/read-deny-floor-notice.js +40 -0
  196. package/dist/{plain-string-record.d.ts → request-declaration-disposition.d.ts} +9 -23
  197. package/dist/request-declaration-disposition.js +39 -0
  198. package/dist/run-cancel-card.d.ts +28 -0
  199. package/dist/run-cancel-card.js +19 -0
  200. package/dist/run-cancel-context.d.ts +10 -0
  201. package/dist/run-cancel-context.js +1 -0
  202. package/dist/run-local.js +28 -13
  203. package/dist/runs.d.ts +31 -7
  204. package/dist/runs.js +34 -16
  205. package/dist/security.d.ts +2 -2
  206. package/dist/server-secret-env.d.ts +34 -20
  207. package/dist/server-secret-env.js +61 -18
  208. package/dist/session-agent.d.ts +3 -2
  209. package/dist/session-children-retire.d.ts +6 -3
  210. package/dist/session-children-retire.js +2 -5
  211. package/dist/session-workspace.d.ts +3 -3
  212. package/dist/store-admission.d.ts +33 -0
  213. package/dist/store-admission.js +49 -0
  214. package/dist/store-posture.d.ts +5 -0
  215. package/dist/store-posture.js +18 -1
  216. package/dist/stores-sql/checkpoint-store-sql.d.ts +1 -1
  217. package/dist/stores-sql/checkpoint-store-sql.js +3 -5
  218. package/dist/stores-sql/file-history-store-sql.d.ts +1 -1
  219. package/dist/stores-sql/file-history-store-sql.js +3 -5
  220. package/dist/stores-sql/tool-result-store-sql.d.ts +1 -1
  221. package/dist/stores-sql/tool-result-store-sql.js +4 -6
  222. package/dist/task-a2a.d.ts +3 -8
  223. package/dist/task-a2a.js +25 -27
  224. package/dist/task-cwd.d.ts +23 -7
  225. package/dist/task-cwd.js +3 -0
  226. package/dist/task-mcp.d.ts +3 -3
  227. package/dist/task-mcp.js +1 -1
  228. package/dist/task-settings.js +1 -0
  229. package/dist/terminal.d.ts +2 -0
  230. package/dist/terminal.js +21 -6
  231. package/dist/tool-approval.d.ts +44 -36
  232. package/dist/tool-approval.js +66 -47
  233. package/dist/trace/engine-notice-wire.d.ts +24 -21
  234. package/dist/trace/engine-notice-wire.js +7 -3
  235. package/dist/trace/ledger-sink.d.ts +3 -1
  236. package/dist/trace/project.d.ts +4 -2
  237. package/dist/trace/project.js +8 -7
  238. package/dist/trace/redact.d.ts +12 -0
  239. package/dist/trace/redact.js +5 -1
  240. package/dist/trace/self-minted-wire.d.ts +131 -52
  241. package/dist/trace/self-minted-wire.js +71 -39
  242. package/dist/web-admin-client/apply-effective.js +22 -9
  243. package/docs/error-text-lexicon.json +317 -0
  244. package/package.json +3 -1
  245. package/scripts/embed-minimal.mjs +183 -0
  246. package/wire-changes/server@7.149.0.json +372 -0
  247. package/dist/plain-string-record.js +0 -70
@@ -176,10 +176,27 @@ export function buildStorePosture(a) {
176
176
  ...(a.degradedReason !== undefined ? { degradedReason: a.degradedReason } : {}),
177
177
  durability: weakestDurability(roots.map((r) => r.verdict)),
178
178
  roots,
179
+ ...localDeclarationOf(backend, a.localDeclaration),
179
180
  };
180
181
  }
182
+ function localDeclarationOf(backend, declaration) {
183
+ switch (backend) {
184
+ case "local":
185
+ if (declaration === undefined)
186
+ throw new Error("store posture: a local backend was assembled without the declaration it handed its stores — wiring defect (read it back from the backend)");
187
+ return { localDeclaration: declaration };
188
+ case "memory":
189
+ case "mysql":
190
+ case "pg":
191
+ return {};
192
+ default: {
193
+ const unhandled = backend;
194
+ throw new Error(`store posture: unknown backend kind ${String(unhandled)}`);
195
+ }
196
+ }
197
+ }
181
198
  export function unwiredStorePosture(declared) {
182
- return buildStorePosture({ declared, assembled: undefined, degradedReason: undefined, evidence: undefined, memoryRootWired: false });
199
+ return buildStorePosture({ declared, assembled: undefined, degradedReason: undefined, evidence: undefined, memoryRootWired: false, localDeclaration: undefined });
183
200
  }
184
201
  export function buildStoreFreshStartLine(posture, evidence) {
185
202
  if (posture.backend !== "local" || evidence.dataRootHeldData)
@@ -40,7 +40,7 @@ export declare const CHECKPOINT_ADDED_COLUMNS: ReadonlyArray<{
40
40
  *
41
41
  * 判据 = **能力探测**(恒空的 `WHERE 1=0` 读)+ **方言的缺列错误码**({@link isMissingColumnError},
42
42
  * 判据属主 `sql-errors.ts`);其余错误**原样 rethrow**,一个字都不加工 —— 把一次瞬时故障诊断成「缺列」的拒因
43
- * 比它要挡的缺陷更贵([ref] 两轮收窄的结论逐字继承)。拒因的出路是**闭集三条**(与整库对账同一份,{@link describeProbedTableDrift}),
43
+ * 比它要挡的缺陷更贵([ref] 两轮收窄的结论逐字继承)。拒因的出路是**闭集三条**(与整库对账同一份,{@link probedMissingColumnRefusal}),
44
44
  * 没有销毁性的一条:这张表里是在飞的停驻,出路是原表上加列。
45
45
  *
46
46
  * 位置:与三条先例同款,不在 `ensureSchema` 里(那条通道的契约是「只发 CREATE」),放在 boot 的
@@ -8,7 +8,7 @@ import { recordFailOpen } from "../observability/fail-open.js";
8
8
  import { durableRowIdentity, withoutCheckpointToken } from "../checkpoint-identity.js";
9
9
  import { mysqlDriver, pgDriver, dialectProtocolJsonEncoder } from "../plugins/sql-driver.js";
10
10
  import { isDupKeyError, isMissingColumnError, rowTooLargeOrSelf, StoreRowTooLargeError } from "../plugins/sql-errors.js";
11
- import { describeProbedTableDrift } from "../plugins/schema-drift-sentence.js";
11
+ import { probedMissingColumnRefusal } from "../plugins/schema-drift-sentence.js";
12
12
  import { MANAGED_RETENTION, UNMANAGED_RETENTION } from "../plugins/retention-declaration.js";
13
13
  import { approvalPayloadFingerprint } from "../plugins/approval-payload-fingerprint.js";
14
14
  import { approvalDecisionOfWinner, boundedRuleOffers, boundedToolInput, candidateIdentityOf, DECIDED_REPLAY_SCAN_CAP, DENY_SWEEP_PAGE_ROWS, isUnreadableSummaryForm, parkedLegTaskId, queueRowGateKind, queueTriageOrder, readRiskDescriptorBody, TERMINAL_BACKSTOP_MS, TERMINAL_GRACE_MS, UnreadableCheckpointRowError, } from "../plugins/checkpoint-store-face.js";
@@ -77,12 +77,10 @@ export async function assertCheckpointColumnSchema(query, dialect) {
77
77
  catch (err) {
78
78
  if (!isMissingColumnError(err, dialect))
79
79
  throw err;
80
- throw new Error(`checkpoint is missing the column ${column} — refusing to start. ` +
80
+ throw await probedMissingColumnRefusal(query, dialect, ["checkpoint"], `checkpoint is missing the column ${column} — refusing to start. ` +
81
81
  `On this table ${breaksAt} — at runtime, after a healthy-looking boot. ` +
82
82
  "The table holds IN-FLIGHT durable parks (suspended approvals/reviews awaiting a decision), so the way forward is an in-place " +
83
- "ALTER that keeps them; `checkpoint_ctx`, run rows and every other persistent face are untouched.\n" +
84
- `Underlying probe error: ${err instanceof Error ? err.message : String(err)}\n\n` +
85
- (await describeProbedTableDrift(query, dialect, ["checkpoint"])));
83
+ "ALTER that keeps them; `checkpoint_ctx`, run rows and every other persistent face are untouched.", err);
86
84
  }
87
85
  }
88
86
  }
@@ -208,7 +208,7 @@ export declare class PgFileHistoryStore extends SqlFileHistoryStore {
208
208
  * ONLY a provable missing-column error gets the missing-column refusal — everything else
209
209
  * (timeout / reset / permission / missing table) rethrows untouched: diagnosing a connection blip as a
210
210
  * schema drift is more expensive than the defect this gate exists to stop. The refusal's ways out are the
211
- * closed set of three shared with the whole-database check (`describeProbedTableDrift`); none of them
211
+ * closed set of three shared with the whole-database check (`probedMissingColumnRefusal`); none of them
212
212
  * is destructive. Honest cost statement: the history lives in these tables (per-file REWIND history,
213
213
  * working-tree convenience state) — session transcripts and every other durable subsystem are untouched.
214
214
  */
@@ -3,7 +3,7 @@ import { captureFileBackupViaEnv, applyFileRestoreViaEnv, fileHistoryModeProvenD
3
3
  import { BlobTooLargeError, SqlBlobIndex } from "./blob-index-sql.js";
4
4
  import { mysqlDriver, pgDriver } from "../plugins/sql-driver.js";
5
5
  import { isMissingColumnError, retryOnDeadlock } from "../plugins/sql-errors.js";
6
- import { describeProbedTableDrift } from "../plugins/schema-drift-sentence.js";
6
+ import { probedMissingColumnRefusal } from "../plugins/schema-drift-sentence.js";
7
7
  import { recordFailOpen } from "../observability/fail-open.js";
8
8
  import { foldCaughtFailure } from "../file-history-disclosure.js";
9
9
  export const FILE_HISTORY_BLOB_TABLE = "file_history_blob";
@@ -844,12 +844,10 @@ export async function assertFileHistorySchema(query, dialect) {
844
844
  catch (err) {
845
845
  if (!isMissingColumnError(err, dialect))
846
846
  throw err;
847
- throw new Error(`${table} is missing required columns — refusing to start. This build's per-file rewind history (S-15) ` +
847
+ throw await probedMissingColumnRefusal(query, dialect, [table], `${table} is missing required columns — refusing to start. This build's per-file rewind history (S-15) ` +
848
848
  `references every probed column unconditionally; a partial table would fail mid-write and could publish a ` +
849
849
  `half-shaped history graph. The file_history_* tables hold the per-file REWIND history (working-tree convenience ` +
850
- `state); session transcripts and all other durable subsystems are untouched, and an in-place ALTER keeps the history.\n` +
851
- `Underlying probe error: ${err instanceof Error ? err.message : String(err)}\n\n` +
852
- (await describeProbedTableDrift(query, dialect, [table])));
850
+ `state); session transcripts and all other durable subsystems are untouched, and an in-place ALTER keeps the history.`, err);
853
851
  }
854
852
  }
855
853
  }
@@ -22,7 +22,7 @@ export declare function ensureSchema(pool: PgPool | PgPoolClient): Promise<void>
22
22
  * 一句看不出根因的话。这正是「响亮或 fail-closed,禁静默降级」那条要挡的形态。
23
23
  *
24
24
  * 判据用**能力探测**而不是版本号/information_schema:发一条恒空的 `WHERE 1=0` 读,列不在就报错。
25
- * 探针读抛且是**可证**的缺列 ⇒ 拒启(其余错误原样上抛,与兄弟探针同一条规则)。拒因的出路是**闭集三条**(与整库对账同一份,{@link describeProbedTableDrift} / {@link schemaWaysOut}),没有销毁性的一条:
25
+ * 探针读抛且是**可证**的缺列 ⇒ 拒启(其余错误原样上抛,与兄弟探针同一条规则)。拒因的出路是**闭集三条**(与整库对账同一份,{@link probedMissingColumnRefusal} / {@link schemaWaysOut}),没有销毁性的一条:
26
26
  * 缺的出处两列由对账同一套件逐条打印 `ALTER TABLE tool_result ADD COLUMN …`;键宽不够则第 (2) 条是部署者自己把 `ref` 加宽到本构建的定义。
27
27
  */
28
28
  export declare function assertToolResultProvenanceSchema(query: (sql: string) => Promise<{
@@ -3,7 +3,7 @@ import { pgSanitizeText } from "../plugins/pg-safe-json.js";
3
3
  import { mysqlDriver, pgDriver } from "../plugins/sql-driver.js";
4
4
  import { MANAGED_RETENTION, UNMANAGED_RETENTION } from "../plugins/retention-declaration.js";
5
5
  import { ensureIndexes } from "../plugins/ensure-index.js";
6
- import { describeProbedTableDrift, schemaWaysOut } from "../plugins/schema-drift-sentence.js";
6
+ import { probedMissingColumnRefusal, schemaMismatchRefusal, schemaWaysOut } from "../plugins/schema-drift-sentence.js";
7
7
  import { isMissingColumnError } from "../plugins/sql-errors.js";
8
8
  export const PG_TOOL_RESULT_SCHEMA = [
9
9
  `CREATE TABLE IF NOT EXISTS tool_result (
@@ -29,7 +29,7 @@ export async function assertToolResultProvenanceSchema(query, dialect) {
29
29
  : "SELECT character_maximum_length AS len FROM information_schema.columns WHERE table_schema = current_schema() AND table_name = 'tool_result' AND column_name = 'ref'").catch(() => ({ rows: [] }));
30
30
  const width = Number(widthRow.rows[0]?.len ?? NaN);
31
31
  if (Number.isFinite(width) && width < MAX_MINTED_TOOL_RESULT_REF_CHARS) {
32
- throw new Error(`tool_result.ref is VARCHAR(${width}) but core 5.26.0 mints refs up to ${MAX_MINTED_TOOL_RESULT_REF_CHARS} characters ` +
32
+ throw schemaMismatchRefusal(`tool_result.ref is VARCHAR(${width}) but core 5.26.0 mints refs up to ${MAX_MINTED_TOOL_RESULT_REF_CHARS} characters ` +
33
33
  `(#119 made the ref injective: four \`~\`-separated segments) — refusing to start. A truncating primary key aliases two distinct refs onto ` +
34
34
  `one row — one run's tool output reads back from another run's ref. ` +
35
35
  `The ${dialect === "pg" ? "PostgreSQL" : "MySQL-protocol"} database is behind this build on that key column; offloaded results are a recoverable-window cache, ` +
@@ -46,13 +46,11 @@ export async function assertToolResultProvenanceSchema(query, dialect) {
46
46
  catch (err) {
47
47
  if (!isMissingColumnError(err, dialect))
48
48
  throw err;
49
- throw new Error(`tool_result is missing the #119 provenance columns (owner_session_id / owner_task_id) — refusing to start. ` +
49
+ throw await probedMissingColumnRefusal(query, dialect, ["tool_result"], `tool_result is missing the #119 provenance columns (owner_session_id / owner_task_id) — refusing to start. ` +
50
50
  `This release picks up @sema-agent/core 5.26.0, which changed how an offloaded tool result is addressed ` +
51
51
  `(the ref grew past the old VARCHAR(190) key) and made the store record who wrote each row. Without the columns every offload write ` +
52
52
  `fails with unknown column and core's failure arm turns it into an inline placeholder — the service keeps running and the tool output is lost. ` +
53
- `Offloaded tool results are a recoverable-window cache (the inline previews in transcripts are unaffected).\n` +
54
- `Underlying probe error: ${err instanceof Error ? err.message : String(err)}\n\n` +
55
- (await describeProbedTableDrift(query, dialect, ["tool_result"])));
53
+ `Offloaded tool results are a recoverable-window cache (the inline previews in transcripts are unaffected).`, err);
56
54
  }
57
55
  }
58
56
  function toolResultAgeCutoff(now, opts) {
@@ -57,7 +57,7 @@ export declare const A2A_PEER_DISPOSITION: {
57
57
  readonly toolAxes: "engine";
58
58
  };
59
59
  /** 请求体 `a2aPeers[i]` 的键闭集 = 处置表的键(书写序即判序)。 */
60
- export declare const A2A_PEER_ENTRY_KEYS: readonly ("url" | "name" | "headers" | "cardUrl" | "principalHeader" | "allowSkills" | "toolAxes")[];
60
+ export declare const A2A_PEER_ENTRY_KEYS: readonly ("url" | "name" | "headers" | "toolAxes" | "principalHeader" | "cardUrl" | "allowSkills")[];
61
61
  /**
62
62
  * **请求入口的一次读**(`boot/resolve-spec.ts` 在腿首铸一份,受理门与读器——续跑重放腿只有读器——都吃这一份):`body.a2aPeers` 若是数组,返回同形数组
63
63
  * (每个对象条目换成它的 {@link PeerSnapshot};非对象条目原样;长度保真 —— 界外的条数仍是受理门撞帽拒与读器丢弃留声的依据,只快照界内的条目);不是数组(缺席 / `null` / 别的形)原样返回。
@@ -68,8 +68,6 @@ export declare const A2A_PEER_ENTRY_KEYS: readonly ("url" | "name" | "headers" |
68
68
  export declare function snapshotRequestA2a(raw: unknown, options: {
69
69
  readonly reread: boolean;
70
70
  }): unknown;
71
- /** 读这条对端的是哪条腿:`fresh` = 新提交;`resume` = 续跑重放升级前存下的请求体(`boot/resolve-spec.ts` 的 `opts.leg` 分流,同一个读法)。 */
72
- type A2aRequestLeg = "fresh" | "resume";
73
71
  /** 请求面对端 → 草稿:按 {@link A2A_PEER_DISPOSITION} 拆(`headers` 进件,其余进声明;拆法与 MCP 请求腿同一只 `splitDeclaration`)。件缺席或空 ⇒ 草稿不带件。纯函数,不登记。 */
74
72
  export declare function toA2aDraft(peer: RequestA2aPeer): A2aDraft;
75
73
  /**
@@ -81,8 +79,8 @@ export declare function validateRequestA2a(raw: unknown): {
81
79
  dropped: A2aInjectionDrop[];
82
80
  } | null;
83
81
  /**
84
- * 一份请求的 `a2aPeers` 里,读器挂得上、`headers` 却剔掉了成员的那些坐标(`a2aPeers[<下标>].headers.__proto__`:成员名过回显清洗,只会是读器的保留名,不带值、不带调用方写的对端名)——
85
- * 「请求成员读不出」那一只判据(`unread-request-members.ts`)的 A2A 一座。读器与挂载同一只({@link normalizeA2aPeer},续跑重放腿的读法;新提交腿上同一形由判官表 400)。
82
+ * 一份请求的 `a2aPeers` 里,读器挂得上、`headers` 却剔掉了成员的那些坐标(`a2aPeers[<下标>].headers.<保留名>`:成员名只会是契约包保留成员名表里的名字,不带值、不带调用方写的对端名)——
83
+ * 「请求成员读不出」那一只判据(`unread-request-members.ts`)的 A2A 一座。读器与挂载同一只({@link normalizeA2aPeer},只有一种读法;新提交腿上同一形先由判官表 400)。
86
84
  * 挂不上的对端不算(整条不进,有自己的留声)。`storedToolAxes` = 续跑重放腿递的 M19 重读座。不是数组 ⇒ 空。
87
85
  */
88
86
  export declare function requestA2aHeadersUnread(raw: unknown, storedToolAxes?: StoredToolAxesRereadM19): string[];
@@ -123,9 +121,6 @@ desk: Pick<PeerDesk, "declare">, lease: PeerLease, logger?: {
123
121
  info?(m: string, meta?: unknown): void;
124
122
  warn(m: string, meta?: unknown): void;
125
123
  },
126
- /** 哪条腿在读(`boot/resolve-spec.ts` 按 `opts.leg` 递;缺省 = 新提交,最严的读法):只决定 `headers` 里 `__proto__` 名成员的处置(见 {@link normalizeA2aPeer})。 */
127
- leg?: A2aRequestLeg,
128
124
  /** 一次性迁移 M19 的重读座(窗口见 `plugins/one-time-migrations.ts` 登记表):只在续跑重放腿、只在窗开着时由 `boot/resolve-spec.ts` 递进来 —— 与 MCP 请求腿同一份。 */
129
125
  storedToolAxes?: StoredToolAxesRereadM19): A2aServerSpec[] | undefined;
130
- export {};
131
126
  //# sourceMappingURL=task-a2a.d.ts.map
package/dist/task-a2a.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import { types } from "node:util";
2
2
  import { resolveComplianceDenies } from "@sema-agent/core";
3
- import { describeNotStringArray, describeValueShape, findUrlCredential, isStringArray } from "@sema-agent/settings-schema";
3
+ import { describeNotStringArray, describeValueShape, findUrlCredential, isStringArray, readPlainStringRecord } from "@sema-agent/settings-schema";
4
4
  import { errorKindOf } from "./observability/err-text.js";
5
- import { declarationKeys, readPlainStringRecord, splitDeclaration } from "./plain-string-record.js";
5
+ import { declarationKeys, splitDeclaration } from "./request-declaration-disposition.js";
6
6
  import { mintDeclaration, peerSentence } from "./peer-seat.js";
7
7
  import { readRequestToolAxes } from "./request-tool-axes.js";
8
- import { MAX_KEY_ECHO, echoKeyName, keyClosureClause, scanKeyNames } from "./request-key-closure.js";
8
+ import { MAX_KEY_ECHO, keyClosureClause, scanKeyNames } from "./request-key-closure.js";
9
9
  import { urlCredentialHeaderExit, urlCredentialRefusalReason } from "./url-credential-refusal.js";
10
10
  import { createMountSeat, declarationEntryNotObject, declarationIssueFacts, declarationNameIssue, declarationOverCapFacts, declaredName, judgeDeclarationKeys, requestDeclarationRefusal, throwDeclarationRefusal, } from "./request-declaration-refusal.js";
11
11
  import { sendHeaderOwnershipIssue } from "./task-mcp.js";
@@ -53,7 +53,12 @@ function peerUrlIssue(key) {
53
53
  };
54
54
  }
55
55
  const A2A_SEND_HEADER_PATHS = { headers: "headers", principalHeader: "principalHeader" };
56
- const HEADERS_RESERVED_MEMBER_PATH = "headers.__proto__";
56
+ function readPeerHeaders(raw) {
57
+ const read = readPlainStringRecord(raw);
58
+ if (!read.ok)
59
+ return { record: undefined, issues: [{ path: "headers", reason: `must be a plain object of string values — ${read.detail}` }] };
60
+ return { record: read.record, issues: read.reservedMembers.map((name) => ({ path: `headers.${name}`, reason: `no member of a declaration may be named "${name}"` })) };
61
+ }
57
62
  const PEER_SNAPSHOT = Symbol("a2aPeerSnapshot");
58
63
  function openCloneNode(v, ancestors) {
59
64
  if (v === null || v === undefined || typeof v === "string" || typeof v === "number" || typeof v === "boolean")
@@ -134,7 +139,7 @@ function mintPeerSnapshot(entry, reread) {
134
139
  cardUrl: source.cardUrl,
135
140
  principalHeader: source.principalHeader,
136
141
  allowSkills: Array.isArray(allowSkills) ? Array.from(allowSkills) : allowSkills,
137
- headers: headers === undefined ? undefined : readPlainStringRecord(headers),
142
+ headers: headers === undefined ? undefined : readPeerHeaders(headers),
138
143
  toolAxes: toolAxesRead,
139
144
  toolAxesDeclared: reread && toolAxesRead !== undefined && !toolAxesRead.ok ? cloneDeclaration(toolAxes) : undefined,
140
145
  unknownKeys: Object.keys(source).filter((name) => !A2A_PEER_ENTRY_KEY_SET.has(name)),
@@ -162,18 +167,10 @@ const A2A_PEER_KEY_JUDGES = {
162
167
  name: declarationNameIssue,
163
168
  url: peerUrlIssue("url"),
164
169
  cardUrl: peerUrlIssue("cardUrl"),
165
- headers: (o) => {
166
- const read = o.headers;
167
- if (read === undefined)
168
- return undefined;
169
- if (!read.ok)
170
- return { path: "headers", reason: `must be a plain object of string values — ${read.member === undefined ? read.fault : `its member "${echoKeyName("", read.member)}" ${read.fault}`}` };
171
- const reserved = read.stripped[0];
172
- return reserved === undefined ? undefined : { path: HEADERS_RESERVED_MEMBER_PATH, reason: reserved.fault };
173
- },
170
+ headers: (o) => o.headers?.issues[0],
174
171
  principalHeader: (o) => o.principalHeader !== undefined && typeof o.principalHeader !== "string"
175
172
  ? { path: "principalHeader", reason: `must be a string (a header name) — got ${describeValueShape(o.principalHeader)}` }
176
- : sendHeaderOwnershipIssue({ headers: o.headers?.ok === true ? o.headers.record : undefined, principalHeader: o.principalHeader }, "a2a", A2A_SEND_HEADER_PATHS),
173
+ : sendHeaderOwnershipIssue({ headers: o.headers?.record, principalHeader: o.principalHeader }, "a2a", A2A_SEND_HEADER_PATHS),
177
174
  allowSkills: (o) => o.allowSkills === undefined || isStringArray(o.allowSkills)
178
175
  ? undefined
179
176
  : { path: "allowSkills", reason: `must be an array of strings (omit the key to leave the peer's skills unrestricted) — got ${describeNotStringArray(o.allowSkills)}` },
@@ -182,19 +179,20 @@ const A2A_PEER_KEY_JUDGES = {
182
179
  return read === undefined || read.ok ? undefined : toolAxesIssue(read);
183
180
  },
184
181
  };
185
- function normalizeA2aPeer(e, leg, at, storedToolAxes) {
182
+ function normalizeA2aPeer(e, at, storedToolAxes) {
186
183
  const dropBecause = (issue) => ({ draft: null, because: declarationIssueFacts(at, issue) });
187
184
  const notObject = declarationEntryNotObject(A2A_SEAT, e, at);
188
185
  if (notObject !== undefined)
189
186
  return { draft: null, because: { field: at, reason: notObject } };
190
187
  const o = snapshotPeer(e, storedToolAxes !== undefined);
188
+ const lost = o.headers?.record === undefined ? [] : o.headers.issues;
191
189
  for (const key of A2A_PEER_ENTRY_KEYS) {
192
190
  const issue = A2A_PEER_KEY_JUDGES[key](o, at);
193
191
  if (issue === undefined)
194
192
  continue;
195
193
  if (issue.credential !== undefined)
196
194
  continue;
197
- if (leg === "resume" && issue.path === HEADERS_RESERVED_MEMBER_PATH)
195
+ if (lost.includes(issue))
198
196
  continue;
199
197
  if (key === "toolAxes" && storedToolAxes !== undefined)
200
198
  continue;
@@ -204,9 +202,9 @@ function normalizeA2aPeer(e, leg, at, storedToolAxes) {
204
202
  if (o.cardUrl !== undefined)
205
203
  spec.cardUrl = o.cardUrl;
206
204
  const { headers, toolAxes: axes } = o;
207
- if (headers?.ok === true)
205
+ if (headers?.record !== undefined)
208
206
  spec.headers = headers.record;
209
- const lost = headers?.ok === true && headers.stripped.length > 0 ? { headersLost: headers.stripped.map((m) => echoKeyName("headers.", m.name)) } : {};
207
+ const headersLost = lost.length > 0 ? { headersLost: lost.map((issue) => issue.path) } : {};
210
208
  if (o.principalHeader !== undefined)
211
209
  spec.principalHeader = o.principalHeader;
212
210
  if (o.allowSkills !== undefined)
@@ -227,22 +225,22 @@ function normalizeA2aPeer(e, leg, at, storedToolAxes) {
227
225
  return unread;
228
226
  if (again.toolAxes !== undefined)
229
227
  spec.toolAxes = again.toolAxes;
230
- return { draft: toA2aDraft(spec), toolAxesReread: peerSentence(axes.problem), ...lost };
228
+ return { draft: toA2aDraft(spec), toolAxesReread: peerSentence(axes.problem), ...headersLost };
231
229
  }
232
230
  if (axes.toolAxes !== undefined)
233
231
  spec.toolAxes = axes.toolAxes;
234
232
  }
235
- return { draft: toA2aDraft(spec), ...lost };
233
+ return { draft: toA2aDraft(spec), ...headersLost };
236
234
  }
237
235
  export function toA2aDraft(peer) {
238
236
  const split = splitDeclaration(peer, A2A_PEER_DISPOSITION);
239
237
  return { spec: split.engine, ...(split.pieces === undefined ? {} : { pieces: split.pieces }) };
240
238
  }
241
239
  export function validateRequestA2a(raw) {
242
- const v = validateRequestA2aEntries(raw, "fresh");
240
+ const v = validateRequestA2aEntries(raw);
243
241
  return v === null ? null : { ok: v.ok, dropped: v.dropped };
244
242
  }
245
- function validateRequestA2aEntries(raw, leg, storedToolAxes) {
243
+ function validateRequestA2aEntries(raw, storedToolAxes) {
246
244
  if (raw === undefined || raw === null)
247
245
  return { ok: [], dropped: [], toolAxesReread: [] };
248
246
  if (!Array.isArray(raw))
@@ -251,7 +249,7 @@ function validateRequestA2aEntries(raw, leg, storedToolAxes) {
251
249
  const dropped = [];
252
250
  const toolAxesReread = [];
253
251
  for (const [i, e] of raw.slice(0, MAX_REQUEST_A2A_PEERS).entries()) {
254
- const read = normalizeA2aPeer(e, leg, `${A2A_SEAT.list}[${i}]`, storedToolAxes);
252
+ const read = normalizeA2aPeer(e, `${A2A_SEAT.list}[${i}]`, storedToolAxes);
255
253
  if (read.draft !== null) {
256
254
  ok.push(read.draft);
257
255
  if (read.toolAxesReread !== undefined)
@@ -271,7 +269,7 @@ export function requestA2aHeadersUnread(raw, storedToolAxes) {
271
269
  const out = [];
272
270
  for (const [i, e] of raw.slice(0, MAX_REQUEST_A2A_PEERS).entries()) {
273
271
  const at = `${A2A_SEAT.list}[${i}]`;
274
- const read = normalizeA2aPeer(e, "resume", at, storedToolAxes);
272
+ const read = normalizeA2aPeer(e, at, storedToolAxes);
275
273
  if (read.draft !== null)
276
274
  for (const member of read.headersLost ?? [])
277
275
  out.push(`${at}.${member}`);
@@ -307,14 +305,14 @@ export function mergeRequestA2a(baseline, drafts, desk, lease) {
307
305
  const winners = drafts.filter((d) => !taken.has(d.spec.name));
308
306
  return winners.length === 0 ? baseline : [...(baseline ?? []), ...winners.map((d) => mintDeclaration(desk, d, lease))];
309
307
  }
310
- export function resolveRequestA2a(baseline, bodyA2a, config, desk, lease, logger, leg = "fresh", storedToolAxes) {
308
+ export function resolveRequestA2a(baseline, bodyA2a, config, desk, lease, logger, storedToolAxes) {
311
309
  if (bodyA2a === undefined || bodyA2a === null)
312
310
  return baseline;
313
311
  if (!a2aInjectionHonored(config)) {
314
312
  logger?.warn("a2a_injection_ignored", { reason: "gate_closed (per-request A2A peers honored only on a single-user, unlocked, compliance-permitted deployment)" });
315
313
  return baseline;
316
314
  }
317
- const v = validateRequestA2aEntries(bodyA2a, leg, storedToolAxes);
315
+ const v = validateRequestA2aEntries(bodyA2a, storedToolAxes);
318
316
  if (v === null) {
319
317
  logger?.warn("a2a_injection_ignored", { reason: "a2aPeers not an array" });
320
318
  return baseline;
@@ -1,4 +1,5 @@
1
1
  import { type DeploymentShapeInput } from "./deployment-shape.js";
2
+ import type { RunInternals } from "@sema-agent/core";
2
3
  /**
3
4
  * 词法归一:纯字符串运算,折 `.` / `..` / 重复分隔符,**不碰 fs**。
4
5
  * `posix.normalize` 把 `..` 在根部截断(`/../x` → `/x`),与「根之上没有东西」的语义一致;尾部分隔符
@@ -85,23 +86,38 @@ export declare function parseAdditionalDirectories(raw: unknown): string[] | und
85
86
  * sandbox — "当前工作目录为空"): resolve the HOST lane's effective workspace for a task. Precedence:
86
87
  * 1. the session's explicitly-registered `cwd` (the caller's own launch dir, the cwd seam) — an explicit
87
88
  * per-session choice always wins;
88
- * 2. the TRUSTED `ctx.parentCwd` (core threads the PARENT task's working root via `RunInternals` when this
89
- * task is a delegated child — never a TaskSpec field), UNLESS the child asked for `isolation:"worktree"`
90
- * (an isolated fan-out child must not share the parent's mutable dir) — then `undefined`;
91
- * 3. undefined → the factory's ephemeral per-task dir (top-level task with no registered cwd).
89
+ * 2. the TRUSTED `ctx.parentCwd` — the working root of the run that handed this leg its work, via `RunInternals`
90
+ * (never a TaskSpec field): a delegating parent (core threads it to a delegated child) or a composition host (the
91
+ * submission whose cascade ladder / verification gate this leg belongs to: those legs run in sessions the engine
92
+ * mints, which arm 1 cannot read, so the composition entries hand them the submission's registered cwd —
93
+ * {@link compositionLegRoot}) — UNLESS the leg asked for `isolation:"worktree"` (an isolated fan-out child must
94
+ * not share the parent's mutable dir) — then `undefined`;
95
+ * 3. undefined → the factory's ephemeral per-task dir (no registered cwd anywhere up the chain).
92
96
  * The `isolation:"worktree"` ⇒ `undefined` rule is this function's own and holds by itself (the child never inherits the
93
97
  * parent's dir). It is NOT what answers a worktree ask on the host lane: `boot/execution-env.ts` never reaches here with one —
94
98
  * the worktree wrapper (one layer out) mints the tree and does not call the base factory, and without the wrapper the base
95
99
  * factory REFUSES (`isolation.unavailable`) instead of minting the empty ephemeral dir rule 3 would give. That refusal arm is
96
100
  * revoked when engine 7.61.0 refuses by itself; this function's rule then goes on keeping the child off the parent's dir.
97
- * CC parity: a Task sub-agent inherits the main session's cwd. Trust: `parentCwd` comes from core's trusted
98
- * run-internals channel (same posture as `isolation`), so no tenancy gate is needed here — but the HOST lane
99
- * itself is single-user by deployment shape, and container lanes (k8s/e2b/local-docker) never consult this.
101
+ * CC parity: a Task sub-agent inherits the main session's cwd. Trust: `parentCwd` comes from the trusted
102
+ * run-internals channel (same posture as `isolation`) — written by core's delegation callers, or by this service's
103
+ * composition entries from a cwd the registration gate (`callerCwd`) already admitted — so no tenancy gate is needed
104
+ * here; the HOST lane itself is single-user by deployment shape, and container lanes (k8s/e2b/local-docker) never consult this.
100
105
  */
101
106
  export declare function effectiveHostWorkspace(sessionCwd: string | undefined, ctx: {
102
107
  isolation?: string;
103
108
  parentCwd?: string;
104
109
  }): string | undefined;
110
+ /**
111
+ * 组合入口(`runCascade` / `resumeCascade` / `runWithVerification` / `resumeWithVerification`)交给它驱动的**每一条腿**的那一格:这次提交登记的工作目录,
112
+ * 作交活方的工作目录(`RunInternals.parentCwd`)—— {@link effectiveHostWorkspace} 的第 2 臂,委派子代用的同一臂(device 车道的 `effectiveDeviceCwd` 同序)。
113
+ *
114
+ * 为什么组合腿要它:梯子的每一级是在引擎**现铸的会话**里冷跑(core `runCascade` 剥掉 spec 的 `sessionId`),验证门的验证器腿的 spec 不带会话 ——
115
+ * 工厂按腿的会话查登记表(第 1 臂)查不到提交登记在根会话上的那一枚,不交就落第 3 臂(临时目录):用户让梯子在项目里改文件,改到了别处;验证器验的是一只空目录。
116
+ * 跑在根会话上的腿(验证门的实现腿 / 修复腿)第 1 臂先命中,同一个值。
117
+ * 没登记(`registeredCwd` 缺席:非 host / device 车道、多租户、根会话的提交从没带过 `cwd`、嵌入方装配器没铸事实)⇒ 空对象 —— 每一条腿照旧落临时目录、收场由托管表删。
118
+ * 登记粘在会话上(`setSessionCwd` 只在请求带 `cwd` 时写):同一会话之前带过、这次不带 ⇒ 交的仍是那一枚 —— 与第 1 臂对同一会话上普通 run 的读法一致。
119
+ */
120
+ export declare function compositionLegRoot(registeredCwd: string | undefined): Pick<RunInternals, "parentCwd">;
105
121
  /** {@link launchWorkspaceOf} 读到的那一格出自哪里(闭集;起服那一行日志与随包文档的 `workspaceFrom` 词)。 */
106
122
  export type LaunchWorkspaceFrom = "workspace-flag" | "host-workspace-base" | "process-cwd";
107
123
  /** 起服时的「工作区」:目录 + 它读自哪一格。 */
package/dist/task-cwd.js CHANGED
@@ -46,6 +46,9 @@ export function parseAdditionalDirectories(raw) {
46
46
  export function effectiveHostWorkspace(sessionCwd, ctx) {
47
47
  return sessionCwd ?? (ctx.isolation !== "worktree" ? ctx.parentCwd : undefined);
48
48
  }
49
+ export function compositionLegRoot(registeredCwd) {
50
+ return registeredCwd !== undefined ? { parentCwd: registeredCwd } : {};
51
+ }
49
52
  export function launchWorkspaceOf(config, explicit, cwd = process.cwd) {
50
53
  if (explicit !== undefined)
51
54
  return { dir: explicit, from: "workspace-flag" };
@@ -69,9 +69,9 @@ export declare const MAX_REQUEST_MCP_DROP_NAMES = 32;
69
69
  */
70
70
  export declare const MCP_SERVER_ENTRY_KEYS: readonly ["allowTools", "contentOrigin", "elicitation", "name", "sessionMetaKey", "source", "startupTimeoutMs", "toolAxes", "toolFaces", "transport"];
71
71
  /** `transport.kind:"stdio"` 的键闭集 = stdio 臂处置表的键(`env` 是件,其余进声明)。 */
72
- export declare const STDIO_MCP_TRANSPORT_KEYS: readonly ("env" | "kind" | "args" | "command")[];
72
+ export declare const STDIO_MCP_TRANSPORT_KEYS: readonly ("kind" | "env" | "args" | "command")[];
73
73
  /** `transport.kind:"http"` 的键闭集 = http 臂处置表的键(`headers` 是件,`assertionHeader` 承载不发送,其余进声明)。 */
74
- export declare const HTTP_MCP_TRANSPORT_KEYS: readonly ("url" | "headers" | "kind" | "principalHeader" | "assertionHeader")[];
74
+ export declare const HTTP_MCP_TRANSPORT_KEYS: readonly ("url" | "kind" | "headers" | "principalHeader" | "assertionHeader")[];
75
75
  /**
76
76
  * Does THIS deployment honor caller-supplied per-request MCP servers? TRUE for a SINGLE-USER deployment
77
77
  * (deployment-shape row `multiTenant` does not hold) — the requester is the super-admin of their own worker, so their `.mcp.json` is
@@ -189,7 +189,7 @@ export declare function requestMcpDeclarationRefusal(list: unknown): Declaration
189
189
  * 重放腿走读器({@link normalizeServer} / {@link classifyRequestMcp}),处置:整只不是数组 ⇒ 整只忽略 + 运维 warn `mcp_injection_ignored`;条目不是对象 / 坏硬键
190
190
  * (`name` / `transport` 硬字段 / `allowTools` / `toolAxes` / `toolFaces`)⇒ 这一条 server 整条不进 + 运维 warn `mcp_injection_dropped` + 用户面通告 `mcp.injection_dropped` 点名 `field`
191
191
  * (`toolAxes` 在一次性迁移 M19 的窗开着时先按升级前读法重读一次,过了就挂,见读器的注);超上限的尾项 ⇒ `over_cap` 点名;坏软键(`contentOrigin` / `elicitation` / `source`)
192
- * 与词表外的键 ⇒ 丢键、server 照挂(`contentOrigin` 另有 `mcp_content_class_dropped` 留声);`transport` 软字段有几处读器不交(`env` / `headers` 不是纯数据的串记录、其中名叫 `__proto__` 的成员、
192
+ * 与词表外的键 ⇒ 丢键、server 照挂(`contentOrigin` 另有 `mcp_content_class_dropped` 留声);`transport` 软字段有几处读器不交(`env` / `headers` 不是纯数据的串记录、其中名叫保留名(`__proto__` / `constructor` / `prototype`)的成员、
193
193
  * `args` / `principalHeader` 坏形)⇒ 这份请求不重放(「请求成员读不出」那一只判据,`unread-request-members.ts`,经 {@link requestMcpTransportUnread} 读这一座;续跑腿折 409)。
194
194
  */
195
195
  export declare function assertRequestMcpEntries(bodyMcp: unknown): void;
package/dist/task-mcp.js CHANGED
@@ -5,7 +5,7 @@ import { GOVERNANCE_HTTP_STATUS } from "./governance-http-status.js";
5
5
  import { DEPLOYMENT_SHAPE_PREDICATES } from "./deployment-shape.js";
6
6
  import { MCP_SOURCE_LABEL_MAX, isMcpSourceLabel, readMcpSourceLabel } from "./mcp-source-label.js";
7
7
  import { MCP_HTTP_TRANSPORT_DISPOSITION, MCP_STDIO_TRANSPORT_DISPOSITION, describeMcpAssertionHeaderNotAString, describeMcpIdentityHeadersConflict, mcpIdentityHeadersIssue, noteMcpAssertionHeaderIgnored, noteMcpAssertionHeaderNotSent, specWithholdsMcpAssertionHeader, toMcpDraft, } from "./mcp-assertion-header.js";
8
- import { declarationKeys } from "./plain-string-record.js";
8
+ import { declarationKeys } from "./request-declaration-disposition.js";
9
9
  import { mintDeclaration, peerSentence } from "./peer-seat.js";
10
10
  import { MAX_KEY_ECHO, echoKeyName, keyClosureClause, scanKeyClosure } from "./request-key-closure.js";
11
11
  import { readRequestToolAxes } from "./request-tool-axes.js";
@@ -135,6 +135,7 @@ export function settingsEnvCredentialIssue(reaching, credentialNames) {
135
135
  message: `settings.env carries credentials: ${named.join(", ")}${more}. A credential reaches the engine only through the environment its process starts from, never through a request's settings.env — ` +
136
136
  "that block comes from a settings file the model can read, and the request hands it to the session's shell and hooks. Move these out of settings.env into the engine's environment " +
137
137
  "(a sema shell that keeps credentials in its credentials.env file does this; upgrade a shell that still writes them into settings.json). " +
138
+ "A settings hook reads a variable from the engine's environment only when the deployment names it in HOOK_ENV_ALLOW; this engine's own credential variables (the entries matched by name) reach no hook at all. " +
138
139
  "If an entry is not a credential: rename a variable that matched by name, or keep a value that matched by shape out of settings.env the same way (the shape scan is deliberately coarse)",
139
140
  fields: { field: "settings.env", entries },
140
141
  keys: hits.map((h) => h.key),
@@ -105,6 +105,8 @@ export declare function mapPauseSeats<T>(result: T, f: (seat: SeatObject, keys:
105
105
  * ①摘掉**耐久恢复能力凭据**(`CheckpointToken`)—— 座表 {@link PAUSE_SEATS} 上的每一座;②把**域外的失败码**折成本仓的
106
106
  * `internal.store_error`({@link outwardFailureCode},[ref])—— 码座两只:因由的 `terminal.code` 与 cascade 每一级的 `attempts[].errorCode`(同一类码,见 {@link foldOutwardFailureCodes});
107
107
  * 因由上折码时**失败句同时换成对外句**(类别句 + 原码尾注,库驱动原句不上 wire,{@link outwardStoreFailure})。
108
+ * 同一处还接上本仓对**读拒字节地板不守**的说法(属主 `read-deny-floor-notice.ts`):拒起的失败句是本仓那一句(码不变),照跑的结果按 `runId` 挂 `readDenyFloor`
109
+ * ({@link withReadDenyFloorSummary})—— 结果离开引擎的每个出口都经这里,说法一处接上。
108
110
  *
109
111
  * 🔴 为什么两件事合在一只函数里而不是并列两只(7.92.2 改名 `stripResumeToken` → `sealResultForExit`):
110
112
  * 它们是**同一个出口上的同一件事** —— 「这条结果可以见人了吗」。并列两只 = 六个结算点各自要记得调两次,
package/dist/terminal.js CHANGED
@@ -5,6 +5,7 @@ import { recordFailOpen } from "./observability/fail-open.js";
5
5
  import { redactSecrets } from "./trace/redact.js";
6
6
  import { seatText } from "./trace/exit-seat.js";
7
7
  import { storeFaultSentenceOfCode } from "./plugins/store-fault.js";
8
+ import { readDenyFloorRefusalMessage, readDenyFloorSummaryOf } from "./read-deny-floor-notice.js";
8
9
  export function failureCodeOf(terminal) {
9
10
  return terminal.kind === "failed" ? terminal.code : undefined;
10
11
  }
@@ -59,7 +60,14 @@ export function sealStoredResult(result, seat) {
59
60
  function seal(result, at) {
60
61
  if (result === null || typeof result !== "object")
61
62
  return result;
62
- return foldOutwardFailureCodes(mapPauseSeats(result, dropCapability), at);
63
+ const sealed = foldOutwardFailureCodes(mapPauseSeats(result, dropCapability), at);
64
+ return at === "exit" ? withReadDenyFloorSummary(sealed) : sealed;
65
+ }
66
+ function withReadDenyFloorSummary(result) {
67
+ if (result.readDenyFloor !== undefined)
68
+ return result;
69
+ const summary = readDenyFloorSummaryOf(result.runId);
70
+ return summary === undefined ? result : { ...result, readDenyFloor: summary };
63
71
  }
64
72
  function foldOutwardFailureCodes(result, at) {
65
73
  const terminal = foldCauseCode(result.terminal, at);
@@ -75,8 +83,10 @@ function foldCauseCode(terminal, at) {
75
83
  const t = failedCauseWithCode(terminal);
76
84
  if (t === undefined)
77
85
  return terminal;
78
- if (outwardFailureCode(t.code) === t.code)
79
- return terminal;
86
+ if (outwardFailureCode(t.code) === t.code) {
87
+ const presented = at === "exit" ? readDenyFloorRefusalMessage(t.code) : undefined;
88
+ return presented === undefined || presented === t.message ? terminal : { ...t, message: presented };
89
+ }
80
90
  const folded = outwardStoreFailure(t.code, t.message, at);
81
91
  return { ...t, code: folded.code, message: folded.message };
82
92
  }
@@ -139,12 +149,17 @@ function failedCauseWithCode(t) {
139
149
  return kind === "failed" && typeof code === "string" ? { ...t, kind, code } : undefined;
140
150
  }
141
151
  export function outwardPlane(terminal) {
152
+ return outwardProjection(terminal, "exit");
153
+ }
154
+ function outwardProjection(terminal, at) {
142
155
  const plane = terminalProjection(terminal);
143
156
  if (plane.errorCode === undefined)
144
157
  return plane;
145
158
  const outward = outwardFailureCode(plane.errorCode);
146
- if (outward === plane.errorCode)
147
- return plane;
159
+ if (outward === plane.errorCode) {
160
+ const presented = at === "exit" && plane.status === "failed" ? readDenyFloorRefusalMessage(plane.errorCode) : undefined;
161
+ return presented === undefined ? plane : { ...plane, errorMessage: presented };
162
+ }
148
163
  return plane.status === "failed" ? { ...plane, errorCode: outward, errorMessage: outwardStoreMessage(plane.errorCode) } : { ...plane, errorCode: outward };
149
164
  }
150
165
  export function nestedPauseOf(terminal) {
@@ -166,7 +181,7 @@ export function persistedPlaneOf(result) {
166
181
  if (r.terminal !== null && typeof r.terminal === "object") {
167
182
  let projected;
168
183
  try {
169
- projected = outwardPlane(r.terminal);
184
+ projected = outwardProjection(r.terminal, "stored");
170
185
  }
171
186
  catch (e) {
172
187
  recordFailOpen("server.terminal.persisted-plane-malformed", e instanceof Error ? e.message : String(e));