@sema-agent/server 7.130.0 → 7.132.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 (294) hide show
  1. package/MIGRATION.md +396 -0
  2. package/README.md +13 -6
  3. package/README.zh-CN.md +9 -5
  4. package/USAGE.md +187 -50
  5. package/dist/approval-card.d.ts +3 -1
  6. package/dist/approval-card.js +2 -3
  7. package/dist/audit.d.ts +7 -10
  8. package/dist/audit.js +4 -1
  9. package/dist/auth-bridge.d.ts +4 -0
  10. package/dist/auth-bridge.js +10 -11
  11. package/dist/bench/s1/live-deps.js +2 -0
  12. package/dist/boot/deployment-mods.d.ts +31 -0
  13. package/dist/boot/deployment-mods.js +26 -0
  14. package/dist/boot/execution-env.d.ts +1 -1
  15. package/dist/boot/execution-env.js +19 -6
  16. package/dist/boot/memory-boundary.d.ts +2 -2
  17. package/dist/boot/read-deny-anchor-audit.d.ts +17 -0
  18. package/dist/boot/read-deny-anchor-audit.js +36 -0
  19. package/dist/boot/resolve-spec.d.ts +1 -5
  20. package/dist/boot/resolve-spec.js +56 -61
  21. package/dist/boot/runner-deps.d.ts +19 -8
  22. package/dist/boot/runner-deps.js +14 -2
  23. package/dist/boot/session-faces.d.ts +1 -7
  24. package/dist/boot/stage-02-web-admin-pull.js +9 -1
  25. package/dist/boot/stage-03-store-backend.d.ts +1 -1
  26. package/dist/boot/stage-03-store-backend.js +2 -2
  27. package/dist/boot/stage-04-stores.d.ts +1 -1
  28. package/dist/boot/stage-05-execution-env.d.ts +4 -4
  29. package/dist/boot/stage-05-execution-env.js +7 -10
  30. package/dist/boot/stage-06-runners.d.ts +2 -2
  31. package/dist/boot/stage-06-runners.js +5 -4
  32. package/dist/boot/stage-07-capability-layer.d.ts +2 -2
  33. package/dist/boot/stage-07-capability-layer.js +3 -3
  34. package/dist/boot/stage-08-reapers.d.ts +2 -2
  35. package/dist/boot/stage-09-leader.d.ts +3 -9
  36. package/dist/boot/stage-10-http-server.d.ts +3 -9
  37. package/dist/boot/stage-10-http-server.js +12 -10
  38. package/dist/boot/stores.d.ts +1 -1
  39. package/dist/boot/stores.js +5 -6
  40. package/dist/boot/web-admin.d.ts +0 -3
  41. package/dist/boot/web-admin.js +35 -35
  42. package/dist/brain.js +17 -22
  43. package/dist/budget.js +5 -4
  44. package/dist/caller-text-echo.d.ts +20 -0
  45. package/dist/caller-text-echo.js +24 -0
  46. package/dist/capabilities/brand-identity.d.ts +26 -0
  47. package/dist/capabilities/brand-identity.js +27 -0
  48. package/dist/capabilities/builtin-tools.js +2 -1
  49. package/dist/capabilities/center-plugins.js +1 -1
  50. package/dist/capabilities/center-prompts.d.ts +32 -59
  51. package/dist/capabilities/center-prompts.js +18 -32
  52. package/dist/capabilities/composite-members.d.ts +1 -0
  53. package/dist/capabilities/composite-members.js +1 -0
  54. package/dist/capabilities/engine-capabilities.js +2 -1
  55. package/dist/capabilities/hands-lane.d.ts +9 -6
  56. package/dist/capabilities/hands-lane.js +3 -3
  57. package/dist/capabilities/host-tool-names.d.ts +17 -0
  58. package/dist/capabilities/host-tool-names.js +15 -0
  59. package/dist/capabilities/mcp-probe.d.ts +14 -1
  60. package/dist/capabilities/mcp-probe.js +22 -133
  61. package/dist/capabilities/repo-tools.js +4 -3
  62. package/dist/capabilities/scenarios.d.ts +42 -47
  63. package/dist/capabilities/scenarios.js +19 -35
  64. package/dist/capabilities/select-environment-tool.js +2 -1
  65. package/dist/capabilities/send-user-file-tool.d.ts +0 -1
  66. package/dist/capabilities/send-user-file-tool.js +2 -2
  67. package/dist/capabilities/skills.d.ts +19 -4
  68. package/dist/capabilities/skills.js +30 -10
  69. package/dist/capabilities/tool-defer.js +2 -1
  70. package/dist/config-catalog.js +22 -11
  71. package/dist/config-invariants.js +2 -2
  72. package/dist/config-lkg.d.ts +9 -9
  73. package/dist/config-lkg.js +20 -4
  74. package/dist/config-provider.d.ts +3 -1
  75. package/dist/config-provider.js +14 -6
  76. package/dist/config-types.d.ts +13 -9
  77. package/dist/config.d.ts +2 -2
  78. package/dist/config.js +34 -20
  79. package/dist/device-ws-protocol.d.ts +1 -1
  80. package/dist/device-ws-protocol.js +1 -0
  81. package/dist/elicitation.d.ts +7 -0
  82. package/dist/elicitation.js +20 -5
  83. package/dist/env-name-allowlist-knobs.d.ts +12 -2
  84. package/dist/env-name-allowlist-knobs.js +5 -4
  85. package/dist/exec-bounds.d.ts +15 -3
  86. package/dist/exec-bounds.js +4 -0
  87. package/dist/execution-lane-caps.d.ts +9 -2
  88. package/dist/execution-lane-caps.js +11 -0
  89. package/dist/hooks/classic-stations.d.ts +86 -0
  90. package/dist/hooks/classic-stations.js +27 -0
  91. package/dist/hooks/hook-llm.d.ts +8 -5
  92. package/dist/hooks/hook-llm.js +80 -37
  93. package/dist/hooks/hook-runner.d.ts +273 -75
  94. package/dist/hooks/hook-runner.js +293 -317
  95. package/dist/http/admission.d.ts +8 -2
  96. package/dist/http/admission.js +103 -59
  97. package/dist/http/approval-sweeps.js +59 -18
  98. package/dist/http/checkpoint-ctx.d.ts +36 -18
  99. package/dist/http/checkpoint-ctx.js +14 -10
  100. package/dist/http/claimed-run.d.ts +13 -0
  101. package/dist/http/claimed-run.js +15 -0
  102. package/dist/http/device-placement.js +5 -10
  103. package/dist/http/dispatch.d.ts +0 -4
  104. package/dist/http/dispatch.js +6 -10
  105. package/dist/http/durable-run-leg.d.ts +2 -1
  106. package/dist/http/durable-run-leg.js +5 -3
  107. package/dist/http/health-self-report.d.ts +7 -5
  108. package/dist/http/health-self-report.js +4 -18
  109. package/dist/http/principal-gate.d.ts +23 -6
  110. package/dist/http/principal-gate.js +24 -5
  111. package/dist/http/refusal-body.d.ts +21 -0
  112. package/dist/http/refusal-body.js +4 -0
  113. package/dist/http/request-guards.d.ts +27 -0
  114. package/dist/http/request-guards.js +24 -3
  115. package/dist/http/resume-legs.d.ts +25 -0
  116. package/dist/http/resume-legs.js +48 -26
  117. package/dist/http/routes/approvals-assistant.js +1 -1
  118. package/dist/http/routes/capabilities.js +7 -17
  119. package/dist/http/routes/diagnostics.js +1 -1
  120. package/dist/http/routes/rules.js +2 -2
  121. package/dist/http/routes/runs.js +13 -17
  122. package/dist/http/routes/session-sync.js +4 -2
  123. package/dist/http/routes/sessions.js +23 -11
  124. package/dist/http/routes/shared-memory.js +2 -13
  125. package/dist/http/routes/side-query.js +2 -3
  126. package/dist/http/routes/tasks.js +34 -19
  127. package/dist/http/scenario-read-face.d.ts +14 -0
  128. package/dist/http/scenario-read-face.js +5 -0
  129. package/dist/http/send.d.ts +13 -1
  130. package/dist/http/send.js +12 -1
  131. package/dist/http/server.d.ts +37 -23
  132. package/dist/http/server.js +30 -4
  133. package/dist/http/stored-body-replay.d.ts +10 -3
  134. package/dist/http/stored-body-replay.js +27 -6
  135. package/dist/http/submit-ctx.d.ts +25 -0
  136. package/dist/http/submit-ctx.js +39 -0
  137. package/dist/http/wire-types.d.ts +18 -5
  138. package/dist/index.d.ts +3 -2
  139. package/dist/index.js +3 -2
  140. package/dist/lane-home-probe.d.ts +43 -0
  141. package/dist/lane-home-probe.js +35 -0
  142. package/dist/limit-refusal.d.ts +47 -0
  143. package/dist/limit-refusal.js +12 -0
  144. package/dist/main.js +1 -1
  145. package/dist/mcp-assertion-header.d.ts +3 -11
  146. package/dist/mcp-assertion-header.js +3 -9
  147. package/dist/mcp-tool-faces.d.ts +5 -0
  148. package/dist/mcp-tool-faces.js +5 -1
  149. package/dist/memory-scope.d.ts +1 -1
  150. package/dist/model-entry-refusal.d.ts +1 -7
  151. package/dist/model-provider.d.ts +39 -12
  152. package/dist/model-provider.js +19 -2
  153. package/dist/model-route-endpoint.d.ts +52 -12
  154. package/dist/model-route-endpoint.js +48 -26
  155. package/dist/model-route-gap.d.ts +5 -4
  156. package/dist/model-route-gap.js +1 -1
  157. package/dist/model-select.d.ts +58 -0
  158. package/dist/model-select.js +31 -0
  159. package/dist/namespaced-rule-name.d.ts +7 -4
  160. package/dist/namespaced-rule-name.js +4 -3
  161. package/dist/observability/cost-taxonomy.d.ts +7 -7
  162. package/dist/observability/fail-open.d.ts +53 -12
  163. package/dist/observability/fail-open.js +47 -7
  164. package/dist/observability/metrics.js +3 -1
  165. package/dist/observability/prompt-manifest.d.ts +47 -82
  166. package/dist/observability/prompt-manifest.js +119 -17
  167. package/dist/observability/run-terminal.js +14 -6
  168. package/dist/observability/secret-env-scrub.d.ts +13 -1
  169. package/dist/observability/secret-env-scrub.js +13 -1
  170. package/dist/observability/tool-trace.d.ts +16 -21
  171. package/dist/observability/tool-trace.js +51 -42
  172. package/dist/per-task-image.d.ts +7 -1
  173. package/dist/per-task-image.js +3 -3
  174. package/dist/plain-string-record.d.ts +25 -0
  175. package/dist/plain-string-record.js +32 -0
  176. package/dist/plugins/blob-backend.d.ts +68 -13
  177. package/dist/plugins/blob-backend.js +53 -10
  178. package/dist/plugins/checkpoint-store-sql.d.ts +14 -4
  179. package/dist/plugins/checkpoint-store-sql.js +39 -26
  180. package/dist/plugins/k8s-bg-scripts.d.ts +11 -0
  181. package/dist/plugins/k8s-bg-scripts.js +22 -0
  182. package/dist/plugins/local-checkpoint-store.js +5 -2
  183. package/dist/plugins/local-session-store.js +3 -3
  184. package/dist/plugins/one-time-migrations.d.ts +147 -91
  185. package/dist/plugins/one-time-migrations.js +372 -208
  186. package/dist/plugins/permission-rule-store-sql.d.ts +13 -7
  187. package/dist/plugins/permission-rule-store-sql.js +4 -18
  188. package/dist/plugins/pg-session-storage.d.ts +4 -5
  189. package/dist/plugins/pg-session-storage.js +7 -17
  190. package/dist/plugins/remote-env-adb.d.ts +6 -0
  191. package/dist/plugins/remote-env-adb.js +9 -4
  192. package/dist/plugins/remote-env-device.js +3 -0
  193. package/dist/plugins/remote-env-e2b.d.ts +1 -1
  194. package/dist/plugins/remote-env-e2b.js +21 -9
  195. package/dist/plugins/remote-env-host.d.ts +9 -9
  196. package/dist/plugins/remote-env-host.js +10 -32
  197. package/dist/plugins/remote-env-k8s.d.ts +95 -26
  198. package/dist/plugins/remote-env-k8s.js +265 -90
  199. package/dist/plugins/remote-env-local-docker.d.ts +20 -1
  200. package/dist/plugins/remote-env-local-docker.js +77 -9
  201. package/dist/plugins/remote-env-ssh.d.ts +15 -0
  202. package/dist/plugins/remote-env-ssh.js +109 -3
  203. package/dist/plugins/remote-shell.d.ts +0 -1
  204. package/dist/plugins/remote-shell.js +1 -2
  205. package/dist/plugins/retention-store-sql.js +6 -2
  206. package/dist/plugins/schema-consistency.d.ts +54 -0
  207. package/dist/plugins/schema-consistency.js +52 -0
  208. package/dist/plugins/session-epoch-anchor.d.ts +13 -0
  209. package/dist/plugins/session-epoch-anchor.js +23 -0
  210. package/dist/plugins/shared-memory-store-sql.d.ts +9 -3
  211. package/dist/plugins/shared-memory-store-sql.js +5 -16
  212. package/dist/plugins/sql-driver.d.ts +141 -0
  213. package/dist/plugins/sql-driver.js +115 -1
  214. package/dist/plugins/sql-errors.d.ts +41 -0
  215. package/dist/plugins/sql-errors.js +36 -0
  216. package/dist/plugins/store-backend.js +2 -1
  217. package/dist/plugins/tidb-session-storage.d.ts +7 -11
  218. package/dist/plugins/tidb-session-storage.js +6 -16
  219. package/dist/plugins/tidb-session-store.js +1 -1
  220. package/dist/prompts-domain-validate.d.ts +17 -61
  221. package/dist/prompts-domain-validate.js +9 -119
  222. package/dist/question.d.ts +18 -9
  223. package/dist/question.js +21 -13
  224. package/dist/request-key-closure.d.ts +4 -4
  225. package/dist/request-key-closure.js +18 -13
  226. package/dist/rule-entry-list.d.ts +134 -24
  227. package/dist/rule-entry-list.js +162 -30
  228. package/dist/run-local.d.ts +3 -3
  229. package/dist/run-local.js +26 -25
  230. package/dist/runs.d.ts +48 -2
  231. package/dist/runs.js +61 -2
  232. package/dist/server-secret-env.d.ts +44 -18
  233. package/dist/server-secret-env.js +49 -8
  234. package/dist/session-incarnation.d.ts +15 -5
  235. package/dist/session-incarnation.js +16 -9
  236. package/dist/session-titler.js +12 -2
  237. package/dist/spec-fields.d.ts +34 -18
  238. package/dist/spec-fields.js +46 -53
  239. package/dist/sql-engine-posture.d.ts +29 -4
  240. package/dist/sql-engine-posture.js +36 -1
  241. package/dist/stored-request-hooks-m20.d.ts +23 -0
  242. package/dist/stored-request-hooks-m20.js +124 -0
  243. package/dist/stored-request-tool-axes-m19.d.ts +20 -5
  244. package/dist/stored-request-tool-axes-m19.js +30 -2
  245. package/dist/task-a2a.d.ts +9 -1
  246. package/dist/task-a2a.js +73 -47
  247. package/dist/task-mcp.d.ts +54 -102
  248. package/dist/task-mcp.js +194 -92
  249. package/dist/task-settings.d.ts +47 -25
  250. package/dist/task-settings.js +46 -61
  251. package/dist/text-bidi.d.ts +3 -46
  252. package/dist/text-bidi.js +3 -3
  253. package/dist/tool-approval.d.ts +1 -1
  254. package/dist/trace/core-keyset-guard.d.ts +24 -4
  255. package/dist/trace/engine-notice-wire.d.ts +11 -3
  256. package/dist/trace/engine-notice-wire.js +8 -1
  257. package/dist/trace/ledger-events.d.ts +3 -1
  258. package/dist/trace/ledger-sink.d.ts +29 -0
  259. package/dist/trace/ledger-sink.js +15 -1
  260. package/dist/trace/model-segment.js +1 -1
  261. package/dist/trace/project.d.ts +25 -6
  262. package/dist/trace/project.js +68 -22
  263. package/dist/trace/wire-projection-faces.d.ts +80 -2
  264. package/dist/trace/wire-projection-faces.js +23 -1
  265. package/dist/user-authored-text.d.ts +7 -0
  266. package/dist/user-authored-text.js +3 -0
  267. package/dist/web-admin-client/apply-effective.js +10 -5
  268. package/dist/web-admin-client/candidate-gate.d.ts +6 -6
  269. package/dist/web-admin-client/candidate-gate.js +3 -3
  270. package/dist/web-admin-client/http-client.js +0 -6
  271. package/dist/web-admin-client/read-warnings.d.ts +13 -0
  272. package/dist/web-admin-client/read-warnings.js +10 -0
  273. package/dist/web-admin-client/skills-mcp.d.ts +3 -0
  274. package/dist/web-admin-client/skills-mcp.js +9 -6
  275. package/dist/web-admin-client/types.d.ts +3 -4
  276. package/dist/workspace-cache.d.ts +35 -0
  277. package/dist/workspace-cache.js +36 -0
  278. package/dist/workspace-space.d.ts +3 -2
  279. package/dist/workspace-space.js +2 -0
  280. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +8 -1
  281. package/package.json +4 -4
  282. package/scripts/drain-check-local.mjs +37 -3
  283. package/scripts/drain-check.mysql.sql +30 -2
  284. package/scripts/drain-check.pg.sql +29 -2
  285. package/dist/capabilities/memory-notice.d.ts +0 -138
  286. package/dist/capabilities/memory-notice.js +0 -64
  287. package/dist/capabilities/prompt.d.ts +0 -17
  288. package/dist/capabilities/prompt.js +0 -6
  289. package/dist/mcp-content-origin.d.ts +0 -34
  290. package/dist/mcp-content-origin.js +0 -10
  291. package/dist/tool-axis-words.d.ts +0 -27
  292. package/dist/tool-axis-words.js +0 -9
  293. package/dist/wire-name-bound.d.ts +0 -14
  294. package/dist/wire-name-bound.js +0 -13
@@ -1,122 +1,12 @@
1
- import { createHash } from "node:crypto";
2
- import { createRequire } from "node:module";
3
- import { readFileSync } from "node:fs";
4
- import { dirname, join } from "node:path";
5
1
  import { verifyPromptArtifact } from "@sema-agent/core";
6
- import { PROMPT_SLOTS } from "@sema-agent/settings-schema";
7
- import { SHA256_DIGEST_RE } from "./digest-form.js";
8
- const ID_RE = /^[a-z0-9][a-z0-9/._-]*$/;
9
- const SLOTS = PROMPT_SLOTS;
10
- function declarationIssue(d, where) {
11
- if (d === null || typeof d !== "object")
12
- return `${where}: declaration is not an object`;
13
- const o = d;
14
- if (typeof o.id !== "string" || o.id.length === 0 || o.id.length > 128 || !ID_RE.test(o.id))
15
- return `${where}: bad id`;
16
- if (o.id.startsWith("core/"))
17
- return `${where}: id in the reserved core/ namespace`;
18
- if (typeof o.slot !== "string" || !SLOTS.includes(o.slot))
19
- return `${where}: bad slot`;
20
- if (typeof o.text !== "string" || o.text.length === 0)
21
- return `${where}: text must be a non-empty string`;
22
- if (typeof o.contentHash !== "string" || !SHA256_DIGEST_RE.test(o.contentHash))
23
- return `${where}: contentHash is required (sha256:<64hex> — the epoch declaration axis needs it for byte-level change detection)`;
24
- const expected = `sha256:${createHash("sha256").update(o.text).digest("hex")}`;
25
- if (o.contentHash !== expected)
26
- return `${where}: contentHash does not match sha256(text) — stale or altered declaration`;
27
- return undefined;
28
- }
29
- export function validateCenterPrompts(raw) {
30
- if (raw === null || typeof raw !== "object")
31
- return { ok: false, error: "prompts is not an object" };
32
- const p = raw;
33
- if (typeof p.packId !== "string" || !/^center:[0-9a-f]{12}$/.test(p.packId))
34
- return { ok: false, error: "bad packId form (center:<digest12>)" };
35
- if (typeof p.contentDigest !== "string" || !SHA256_DIGEST_RE.test(p.contentDigest))
36
- return { ok: false, error: "bad contentDigest form" };
37
- const arrayIssue = (decls, where) => {
38
- if (!Array.isArray(decls))
39
- return `${where} is not an array`;
40
- if (decls.length === 0)
41
- return `${where} is empty (an empty replacement set is not a v1 semantic — omit the key instead)`;
42
- const seen = new Set();
43
- for (let i = 0; i < decls.length; i++) {
44
- const issue = declarationIssue(decls[i], `${where}[${i}]`);
45
- if (issue)
46
- return issue;
47
- const id = decls[i].id;
48
- if (seen.has(id))
49
- return `${where}[${i}]: duplicate id "${id}" (core composer throws on duplicates)`;
50
- seen.add(id);
51
- }
52
- return undefined;
53
- };
54
- const sectionsIssue = arrayIssue(p.sections, "sections");
55
- if (sectionsIssue)
56
- return { ok: false, error: sectionsIssue };
57
- if (p.scenarioOverrides !== undefined) {
58
- if (p.scenarioOverrides === null || typeof p.scenarioOverrides !== "object" || Array.isArray(p.scenarioOverrides)) {
59
- return { ok: false, error: "scenarioOverrides is not an object" };
60
- }
61
- for (const [name, decls] of Object.entries(p.scenarioOverrides)) {
62
- const issue = arrayIssue(decls, `scenarioOverrides[${name}]`);
63
- if (issue)
64
- return { ok: false, error: issue };
65
- }
66
- }
67
- return { ok: true, value: raw };
68
- }
69
- export const CORE_ENGINE_VERSION = (() => {
70
- try {
71
- const entry = createRequire(import.meta.url).resolve("@sema-agent/core");
72
- return JSON.parse(readFileSync(join(dirname(entry), "..", "package.json"), "utf8")).version ?? "0.0.0";
73
- }
74
- catch {
75
- return "0.0.0";
76
- }
77
- })();
78
- export function validatePromptsDomain(raw) {
79
- if (raw === null || typeof raw !== "object")
80
- return { ok: false, error: "prompts is not an object" };
81
- const w = raw;
82
- if (w.schemaVersion === undefined) {
83
- return { ok: false, error: "prompts payload has no schemaVersion — the legacy flat shape was retired in 5.0.0; wrap as {schemaVersion:1, declarations:{...}} (registry-core EffectivePromptsWire)" };
84
- }
85
- if (w.schemaVersion !== 1)
86
- return { ok: false, error: `unsupported prompts schemaVersion ${String(w.schemaVersion)}` };
87
- if (w.declarations === undefined && w.catalog === undefined)
88
- return { ok: false, error: "dual-axis prompts carries neither axis (at least one of declarations|catalog required)" };
89
- const value = { identity: "", axes: [] };
90
- if (w.declarations !== undefined) {
91
- const d = validateCenterPrompts(w.declarations);
92
- if (!d.ok)
93
- return { ok: false, error: `declarations axis: ${d.error}` };
94
- value.declarations = d.value;
95
- value.axes.push("declarations");
96
- }
97
- if (w.catalog !== undefined) {
98
- const c = verifyPromptArtifact(w.catalog, { engineVersion: CORE_ENGINE_VERSION });
99
- if (!c.ok)
100
- return { ok: false, error: `catalog axis: ${c.code}: ${c.reason}` };
101
- value.catalog = w.catalog;
102
- value.axes.push("catalog");
103
- }
104
- value.identity = `${value.declarations?.contentDigest ?? "-"}+${value.catalog?.artifact.artifactDigest ?? "-"}`;
105
- return { ok: true, value };
106
- }
107
- export function centerPromptsFromEffective(eff) {
108
- const prompts = eff?.prompts;
109
- if (prompts === undefined || prompts === null)
110
- return undefined;
111
- if (typeof prompts !== "object" || Array.isArray(prompts))
112
- return prompts;
113
- const o = prompts;
114
- if ("schemaVersion" in o)
115
- return prompts;
116
- if ("packId" in o && "contentDigest" in o && "sections" in o)
117
- return prompts;
118
- if (o.catalog !== undefined)
119
- return { schemaVersion: 1, catalog: o.catalog };
120
- return undefined;
2
+ import { EffectivePromptsWire, formatConfigPath, sanitizeZodError } from "@sema-agent/settings-schema";
3
+ export function judgePromptsWire(raw) {
4
+ const wire = EffectivePromptsWire.safeParse(raw);
5
+ if (!wire.success) {
6
+ const issues = sanitizeZodError(wire.error).issues.map((i) => `${i.path.length === 0 ? "prompts" : `prompts.${formatConfigPath(i.path.map((s) => (typeof s === "number" ? s : String(s))))}`}: ${i.message}`);
7
+ return { ok: false, error: issues.join("; ") };
8
+ }
9
+ const verified = verifyPromptArtifact(wire.data.artifact);
10
+ return verified.ok ? { ok: true, artifact: verified.artifact } : { ok: false, error: `prompts.artifact refused by the engine (${verified.code}): ${verified.reason}` };
121
11
  }
122
12
  //# sourceMappingURL=prompts-domain-validate.js.map
@@ -23,20 +23,29 @@ export interface QuestionFrame {
23
23
  /** "question" only: `serverNowMs + expiresInMs` — absolute deadline on the server's clock. */
24
24
  expiresAtMs?: number;
25
25
  }
26
- /** The per-run context `onQuestion` recovers via ALS. `emit` delivers a frame to the run's live stream; `abortSignal`
27
- * (the run's own cancel signal) releases a question parked awaiting a human when the run aborts. */
26
+ /** The per-run context `onQuestion` recovers via ALS. `emit` delivers a frame to the run's stream; `abortSignal`
27
+ * (the run's own cancel signal) releases a question parked awaiting a human when the run aborts.
28
+ *
29
+ * 🔴 ONE LAW for every leg: a question frame that enters a run's stream has a LEDGER TWIN (the row
30
+ * `GET /v1/runs/:id/events` replays — a page reload recovers the open card from it); the leg decides only whether it
31
+ * ALSO writes a live socket. The background leg has no socket, so its `emit` IS the durable append; the synchronous
32
+ * stream leg lands the same frame in the ledger first and then writes the SSE connection (`createLedgerTwinEmit`).
33
+ * The ledger carries the CARD, not the answer door: `POST /v1/questions/:id/respond` stays live-only (this replica's
34
+ * memory, `ttlMs`), so a replayed card is answerable only until its own `expiresAtMs` and only on this replica. */
28
35
  export interface QuestionRunContext {
29
36
  taskId: string;
30
37
  owner: string | null;
31
38
  emit: (frame: QuestionFrame) => void | Promise<void>;
32
39
  abortSignal?: AbortSignal;
33
- /** OPTIONAL reachability predicate for this leg's delivery face, consulted by {@link QuestionCoordinator.hasLiveContext}
34
- * (the park-vs-live adjudication probe), NOT by `question` itself. CONSTRAINT: a leg whose face can go away WHILE the
35
- * leg keeps running MUST supply it — `/v1/tasks/stream` under `x-detach-on-disconnect: true` keeps executing inside
36
- * this ALS scope after the client left, and `emit` there writes to a dead socket and is silently dropped (question
37
- * frames have no durable ledger twin, unlike SendUserFile's ledgerSink). Without the predicate the adjudication would
38
- * read "live" and hand the leg to a face nobody can reach. ABSENT ⇒ always reachable, which is exactly right for the
39
- * background leg (emit appends to the durable events tail, readable whenever a client attaches). */
40
+ /** OPTIONAL reachability predicate for this leg's LIVE delivery face — one reading, two consumers:
41
+ * {@link QuestionCoordinator.hasLiveContext} (the park-vs-live adjudication probe) and the OPEN frame itself (a face
42
+ * that cannot show the card right now refuses the ask on the spot, the same failed-delivery arm, instead of parking it
43
+ * to TTL on a ledger row nobody is watching). CONSTRAINT: a leg whose face can go away WHILE the leg keeps running MUST
44
+ * supply it — `/v1/tasks/stream` under `x-detach-on-disconnect: true` keeps executing inside this ALS scope after the
45
+ * client left. Without the predicate the adjudication would read "live" and hand the leg to a face nobody can reach.
46
+ * ABSENT ⇒ always reachable, which is exactly right for the background leg (its face IS the durable events tail,
47
+ * readable whenever a client attaches). The CLOSE frame never asks it: it is a record, and it lands in the ledger twin
48
+ * whether or not the socket is still there. */
40
49
  deliverable?: () => boolean;
41
50
  }
42
51
  /** The per-run question window the deployment owns (core caps nothing). Env: `QUESTION_MAX_CONCURRENT_PER_RUN` /
package/dist/question.js CHANGED
@@ -3,6 +3,16 @@ import { uuidv7 } from "@sema-agent/core";
3
3
  import { redactDeep } from "./trace/redact.js";
4
4
  import { recordFailOpen } from "./observability/fail-open.js";
5
5
  const MAX_QUESTIONS_BYTES = 16384;
6
+ function isFaceReachable(ctx) {
7
+ if (ctx.deliverable === undefined)
8
+ return true;
9
+ try {
10
+ return ctx.deliverable();
11
+ }
12
+ catch {
13
+ return false;
14
+ }
15
+ }
6
16
  export const DEFAULT_QUESTION_THROTTLE = {
7
17
  maxConcurrentPerRun: 2,
8
18
  maxTotalPerRun: 20,
@@ -103,13 +113,20 @@ export class QuestionCoordinator {
103
113
  ctx.abortSignal?.addEventListener("abort", onAbort, { once: true });
104
114
  this.pending.set(id, { settle, owner: ctx.owner, taskId: ctx.taskId });
105
115
  const emitted = (async () => {
116
+ if (!isFaceReachable(ctx))
117
+ throw new Error("question delivery face unreachable — the open frame has nobody to show it to");
106
118
  const now = Date.now();
107
- return ctx.emit({ type: "question", questionId: id, questions, serverNowMs: now, expiresInMs: this.throttle.ttlMs, expiresAtMs: now + this.throttle.ttlMs });
119
+ await ctx.emit({ type: "question", questionId: id, questions, serverNowMs: now, expiresInMs: this.throttle.ttlMs, expiresAtMs: now + this.throttle.ttlMs });
120
+ return isFaceReachable(ctx) ? "ok" : "landed-face-gone";
108
121
  })()
109
- .then(() => "ok", () => "failed");
122
+ .then((r) => r, () => "failed");
110
123
  const delivery = await Promise.race([emitted, answerP.then(() => "stalled")]);
111
- if (delivery === "ok") {
124
+ if (delivery === "ok" || delivery === "landed-face-gone") {
112
125
  rc.total += 1;
126
+ if (delivery === "landed-face-gone") {
127
+ recordFailOpen("server.question.open-frame-undelivered", `question=${id} face-gone-while-landing`);
128
+ settle(UNAVAILABLE, "unanswered");
129
+ }
113
130
  }
114
131
  else if (delivery === "failed") {
115
132
  recordFailOpen("server.question.open-frame-undelivered", `question=${id}`);
@@ -146,16 +163,7 @@ export class QuestionCoordinator {
146
163
  }
147
164
  hasLiveContext() {
148
165
  const ctx = this.als.getStore();
149
- if (ctx === undefined)
150
- return false;
151
- if (ctx.deliverable === undefined)
152
- return true;
153
- try {
154
- return ctx.deliverable();
155
- }
156
- catch {
157
- return false;
158
- }
166
+ return ctx !== undefined && isFaceReachable(ctx);
159
167
  }
160
168
  countersFor(taskId) {
161
169
  let rc = this.counters.get(taskId);
@@ -1,8 +1,8 @@
1
1
  /** 拒体里逐字回显的键名条数上限。键名原样回显(不猜语义),但**条数有界** —— 一个塞满键的体不许把 400 响应
2
2
  * 放大成流量面。超出部分只报数。 */
3
3
  export declare const MAX_KEY_ECHO = 20;
4
- /** 键名回显的清洗:剥控制/双向字符(键名进的是 JSON 错误体与运维日志,一个 RLO 能把两行字读反),
5
- * 再按 {@link MAX_KEY_ECHO_CHARS} 截断。清洗之外**逐字**,不做大小写/近似猜测。 */
4
+ /** 键名回显 = 前缀 + 调用方串的唯一回显口径(`caller-text-echo.ts`:剥控制 / 双向字符、截到 64 字 + …;键名进的是 JSON 错误体与运维日志,
5
+ * 一个 RLO 能把两行字读反)。清洗之外**逐字**,不做大小写/近似猜测。 */
6
6
  export declare function echoKeyName(prefix: string, key: string): string;
7
7
  /** 一次键闭集裁决的产物:一句 wire 文案 + 两张分类键名表(消费端可机读,不必解析文案)。 */
8
8
  export interface KeyClosureIssue {
@@ -54,9 +54,9 @@ export type ContentSteerBody = {
54
54
  export type InterruptSteerBody = {
55
55
  readonly [K in (typeof INTERRUPT_STEER_BODY_KEYS)[number]]?: unknown;
56
56
  };
57
- /** 送话口的三种体形(闭集;词外是编译错)。 */
57
+ /** 送话口的体形(闭集;词外是编译错)。`interrupt` = `POST /v1/runs/:id/interrupt` 整扇门(bare 与带 `text` 两形同一张受理集)。 */
58
58
  export type SteerBodyForm = "run" | "content" | "interrupt";
59
- /** 送话口的顶层键裁决(四扇门同一只;`form` 选受理集)。 */
59
+ /** 送话口的顶层键裁决(各形同一只;`form` 选受理集)。 */
60
60
  export declare function steerBodyKeyIssue(raw: unknown, form: SteerBodyForm): KeyClosureIssue | null;
61
61
  export {};
62
62
  //# sourceMappingURL=request-key-closure.d.ts.map
@@ -1,9 +1,7 @@
1
- import { CONTROL_AND_BIDI_STRIP_RE } from "./text-bidi.js";
1
+ import { echoCallerText } from "./caller-text-echo.js";
2
2
  export const MAX_KEY_ECHO = 20;
3
- const MAX_KEY_ECHO_CHARS = 64;
4
3
  export function echoKeyName(prefix, key) {
5
- const clean = key.replace(CONTROL_AND_BIDI_STRIP_RE, "");
6
- return `${prefix}${clean.length > MAX_KEY_ECHO_CHARS ? `${clean.slice(0, MAX_KEY_ECHO_CHARS)}…` : clean}`;
4
+ return `${prefix}${echoCallerText(key)}`;
7
5
  }
8
6
  export function scanKeyClosure(bag, prefix, accepted, tolerated, into) {
9
7
  for (const k of Object.keys(bag)) {
@@ -28,25 +26,28 @@ const TASK_BODY_DECLARED_KEYS = [
28
26
  "resilience", "restoreFiles", "resumeAt", "resumeAtMode", "retainBackgroundProcesses", "retainSubagentSessions",
29
27
  "rewindFiles", "rewindFilesTo", "sandboxImageProfile", "scenario", "scratchpadDir", "selfOrchestration",
30
28
  "sessionId", "settings", "skills", "suggestNextPrompts", "systemPrompt", "taskId", "toolMaterializeStrategy",
31
- "userInvokedSkills", "verify", "verifyRounds",
29
+ "userAttachments", "userInvokedSkills", "verify", "verifyRounds",
32
30
  ];
33
31
  const TASK_BODY_SCENARIO_KEYS = ["repo"];
34
32
  const ACCEPTED_BODY = new Set([...TASK_BODY_DECLARED_KEYS, ...TASK_BODY_SCENARIO_KEYS]);
35
- const UNSUPPORTED_BODY_KEYS = ["handsReadOnly", "readDenyPatterns", "readFace", "shellGate", "userAttachments", "writeFace"];
36
- const UNSUPPORTED_BODY = new Set(UNSUPPORTED_BODY_KEYS);
37
- const UNSUPPORTED_LABEL = "unsupported (an engine seat this service does not read from the request body — set by the deployment, or not offered in this version)";
38
- function closedBodyKeyIssue(raw, accepted, door) {
33
+ const UNSUPPORTED_BODY_KEYS = ["handsReadOnly", "readDenyPatterns", "readFace", "shellGate", "writeFace"];
34
+ const UNSUPPORTED_BODY = {
35
+ keys: new Set(UNSUPPORTED_BODY_KEYS),
36
+ label: "unsupported (an engine seat this door does not read from the request body — set by the deployment)",
37
+ };
38
+ const OBJECTIVE_ONLY_BODY_KEYS = ["userAttachments"];
39
+ function closedBodyKeyIssue(raw, accepted, unsupported, door) {
39
40
  if (raw === null || raw === undefined || typeof raw !== "object" || Array.isArray(raw))
40
41
  return null;
41
42
  const buckets = { unknown: [], unsupported: [] };
42
- scanKeyClosure(raw, "", accepted, UNSUPPORTED_BODY, buckets);
43
+ scanKeyClosure(raw, "", accepted, unsupported.keys, buckets);
43
44
  if (buckets.unknown.length === 0 && buckets.unsupported.length === 0)
44
45
  return null;
45
46
  buckets.unknown.sort();
46
47
  buckets.unsupported.sort();
47
48
  const parts = [
48
49
  ...(buckets.unknown.length > 0 ? [keyClosureClause(buckets.unknown, `unknown (not ${door} key — check the spelling)`)] : []),
49
- ...(buckets.unsupported.length > 0 ? [keyClosureClause(buckets.unsupported, UNSUPPORTED_LABEL)] : []),
50
+ ...(buckets.unsupported.length > 0 ? [keyClosureClause(buckets.unsupported, unsupported.label)] : []),
50
51
  ];
51
52
  return {
52
53
  message: `body: unrecognized top-level key(s) — refused rather than dropped, because a dropped key reads as "in effect" to the caller. ` +
@@ -57,7 +58,7 @@ function closedBodyKeyIssue(raw, accepted, door) {
57
58
  };
58
59
  }
59
60
  export function taskBodyKeyIssue(raw) {
60
- return closedBodyKeyIssue(raw, ACCEPTED_BODY, "a task-request");
61
+ return closedBodyKeyIssue(raw, ACCEPTED_BODY, UNSUPPORTED_BODY, "a task-request");
61
62
  }
62
63
  const RUN_STEER_BODY_KEYS = ["mode", "priority", "text"];
63
64
  const CONTENT_STEER_BODY_KEYS = ["content"];
@@ -67,7 +68,11 @@ const STEER_DOORS = {
67
68
  content: { accepted: new Set(CONTENT_STEER_BODY_KEYS), door: "a steer-request" },
68
69
  interrupt: { accepted: new Set(INTERRUPT_STEER_BODY_KEYS), door: "an interrupt-request" },
69
70
  };
71
+ const STEER_UNSUPPORTED = {
72
+ keys: new Set([...UNSUPPORTED_BODY_KEYS, ...OBJECTIVE_ONLY_BODY_KEYS]),
73
+ label: "unsupported (an engine seat this door does not read from the request body — set by the deployment, or carried only by a new objective)",
74
+ };
70
75
  export function steerBodyKeyIssue(raw, form) {
71
- return closedBodyKeyIssue(raw, STEER_DOORS[form].accepted, STEER_DOORS[form].door);
76
+ return closedBodyKeyIssue(raw, STEER_DOORS[form].accepted, STEER_UNSUPPORTED, STEER_DOORS[form].door);
72
77
  }
73
78
  //# sourceMappingURL=request-key-closure.js.map
@@ -11,15 +11,25 @@
11
11
  * 请求 allow 桶、技能两只清单、运维表四张表同一只、同一句)。
12
12
  * ② (7.116.0–7.122.x 的「引擎读不到的一形」—— 工具段为空的协议名 `mcp__srv__` 四张表都拒 —— 随 core 7.48.0 #1400 退役:引擎判官给它判词
13
13
  * `unmintable_segment`,条目由下面 ③ 读成 `tool_name`,与对端段铸不出的名字同一条路。)
14
- * ③ **引擎读成空的条目**({@link inertRuleEntriesOf},判据 = 引擎包根 `compileAllowList(entries, behavior, 名册)`,名册 = 空集 `NO_DECLARED_TOOL_NAMES`
15
- * —— 条目的工具名也过引擎判官:退役名 / 无前缀短名 / 对端段或工具段铸不出、为空读成 `tool_name`,带判词与 1:1 现名;原因词是引擎的 `ALLOW_ENTRY_INERT_REASONS`):
16
- * 运维的 allow 表拒启(运维自己的配置,写错了当场改);技能的拒侧(`disallowedTools`)拒 —— 提交 400、存量体重放 409(下面的 {@link skillDenyInertEntryOf});
17
- * 请求的 allow 侧(`settings.permissions.allow`、技能 `allowedTools`)不拒:引擎在开跑时发 `permission.allowed_tool_not_granted`(受众 = 用户)。
14
+ * ③ **引擎读成空的条目**({@link inertRuleEntriesOf},判据 = 引擎包根 `compileAllowList(entries, behavior, 名册)`;原因词是引擎的 `ALLOW_ENTRY_INERT_REASONS`)。
15
+ * 条目的工具名也过引擎判官:退役名 / 无前缀短名 / 对端段或工具段铸不出、为空读成 `tool_name`,带判词与 1:1 现名;名册里有那只工具、条目只差大小写 ⇒ `near_miss`(`BASH` ⇒ 现名 `Bash`)。
16
+ * **名册只有一张**({@link TOOL_NAME_ROSTER}:引擎目录 ∪ 本仓自铸的工具名),每一处读条目名的地方都传它 —— 请求体的规则表(`settings.permissions.*`)、技能清单、选择器清单、
17
+ * 运维 / 中心的审批名单读同一份,没有「这一处空名册、那一处带名册」的两种读法。
18
+ * **读成空的哪些原因词是拒面,按方向定**:**限制侧**(读成空 = 静默放行:技能 `disallowedTools`、`excludeTools` / 子代 `denyTools`、`settings.permissions.deny` / `ask`、运维 / 中心的审批名单)四类全拒 ——
19
+ * 近似名 · 退役名 · 文法读不出 · 不带内容形的工具带内容;**放行侧**(读成空 = 想放的什么也没放:`settings.permissions.allow`、根 / 子代 `allowTools`、技能 `allowedTools`)**只拒近似名**
20
+ * ({@link allowSideReadsAsNothing}):近似名现名唯一、确定是笔误;退役名 / 内容形 / 文法读不出 / 协议名对端段通配照 7.130.0 受理、交引擎既有通告(其中选择器清单 `allowTools` 里**有后继**的退役名〔`Task` ⇒ `Agent`、`bash` ⇒ `Bash`〕core 7.52.0 起由引擎开跑前拒,不在本仓拒面;规则表上的退役名引擎照旧告运维 ——
21
+ * 壳转投的 CC 用户 settings 里这些极常见,尤其内容形里的 `$VAR`(`Bash(echo $HOME)`)读成 `grammar`,拒了 = 每次提交 400)。运维的 `APPROVAL_GRANT` 是运维自己的配置:任何读成空都拒启(写错了当场改)。
18
22
  * 技能拒侧那一拒与引擎 #1372(core 7.43.0:那只技能不激活)是同一件事的两处 —— 7.118.0 重估过、本版**不退**:本仓的续跑腿把引擎的
19
23
  * `checkpoint.config_refused`(`config.skill_deny_unreadable`)答得不一(资源停车续跑腿 500、`/decide` 先结了卡、`/wake` 先铸了 run 行),先统一那一层再退
20
- * (重估读数与退役条件在下面那只函数头注)。
24
+ * (重估读数与退役条件在下面 {@link skillDenyInertEntryOf} 头注)。
25
+ * ④ **工具名选择器清单**(请求顶层 `allowTools` / `excludeTools`、子代定义的 `allowTools` / `denyTools`;引擎的可用性选择器,**不是**规则条目表):读成空的条目在受理口**开跑前拒并点名现名**
26
+ * (拒面按 ③ 的方向分),拒句点名有界({@link MAX_NAMED_INERT_ENTRIES} 条 + 计数),清单先过条数帽({@link inertToolListEntryOf})。
27
+ * **真未知名**(拼写合法、没有近似也没有退役对应 —— 懒挂载的合法形,例如一台之后才连上的 MCP 服务器的工具)不是读成空:原样保留、运维行告知一次,不拒。
28
+ * 选择器读一条条目 = 引擎 `judgeToolNameList` 的读法({@link selectorEntryName}):先取工具名那一段再只判名字(限制侧 `Tool(content)` ⇒ 整只工具,内容里写什么都不读;放行侧内容形引擎告「什么也不授」,受理口不拒);
29
+ * `*` 在放行侧是「本层不设名单」、在限制侧读成空;限制侧 `mcp__*` / `a2a__*`(整个协议的拒)合法。
21
30
  */
22
31
  import { compileAllowList } from "@sema-agent/core";
32
+ import { type LimitRefusal } from "./limit-refusal.js";
23
33
  /** ① 形:字符串数组(空串也是字符串 —— 引擎把它读成 `grammar` 并通告 / 拒,见 ③)。 */
24
34
  export declare const ruleEntryListShapeOk: (v: unknown) => v is readonly string[];
25
35
  /**
@@ -33,45 +43,145 @@ export declare const ruleEntryListShapeOk: (v: unknown) => v is readonly string[
33
43
  * 没有真实用法需要上千条」。抬不抬这个数是产品判断,不随提货改。
34
44
  */
35
45
  export declare const MAX_ALLOW_LAYER_ENTRIES = 1000;
36
- /** 超帽的拒句(不截:截掉的尾项读起来像「在生效」)—— 句子的唯一铸点(请求 allow 桶的 wire 拒体按字段重铸也调它)。 */
46
+ /** 超帽的拒句({@link allowLayerOverCap} 的句子;请求 allow 桶的拒句按字段重铸时读它)。 */
37
47
  export declare function describeAllowLayerOverCap(field: string, count: number): string;
38
- /** 一张条目表超了 {@link MAX_ALLOW_LAYER_ENTRIES} ⇒ 拒句 + 机读两数;没超 ⇒ `undefined`。四张表同调这一只。 */
39
- export declare function allowLayerEntryCountIssue(field: string, list: readonly unknown[]): {
40
- readonly message: string;
41
- readonly count: number;
42
- readonly cap: number;
43
- } | undefined;
48
+ /** 一张条目表超了 {@link MAX_ALLOW_LAYER_ENTRIES} ⇒ 拒(句子 + 机读位 `field` / `reason: "over_cap"` / `count` / `cap`);没超 ⇒ `undefined`。四张表同调这一只。 */
49
+ export declare function allowLayerEntryCountIssue(field: string, list: readonly unknown[]): LimitRefusal | undefined;
44
50
  /** 一条引擎读成空的条目(引擎的形:原因词,名字层另带判词 / 1:1 现名;包根不导出这只具名型 ⇒ 从 `compileAllowList` 的返回型取,不手抄)。 */
45
51
  export type InertRuleEntry = ReturnType<typeof compileAllowList>["inert"][number];
46
- /** ③ 引擎按 `behavior` 读这张表时读成空的条目(引擎的整表读法:allow 表里的 `*` 让整表读成空;deny 表里的 `*` 只读空它自己);条目名过引擎判官(名册 = 空集)。 */
52
+ /**
53
+ * ③ 引擎读一张**规则条目表**(`behavior` 方向)时读成空的条目(引擎的整表读法:allow 表里的 `*` 让整表读成空;deny 表里的 `*` 只读空它自己)。条目名过引擎判官,
54
+ * 名册是本仓**唯一**的一张({@link TOOL_NAME_ROSTER},`namespaced-rule-name.ts`:引擎目录 ∪ 本仓自铸的工具名)—— 两个方向、每一处读条目名的地方(请求体的规则表、技能清单、选择器清单、
55
+ * 运维 / 中心的审批名单)传同一张,没有「这一处空名册、那一处带名册」的两种读法。工具名选择器清单另走 {@link inertToolListEntryOf}。
56
+ */
47
57
  export declare function inertRuleEntriesOf(list: readonly string[], behavior: "allow" | "deny"): readonly InertRuleEntry[];
48
58
  /**
49
59
  * 原因词 → 「怎么写」(闭集穷举:引擎加一个原因词 ⇒ 编译红)。句子只说这一条为什么什么也不做、换成哪种写法;原因词本身原样带在句子里。
50
60
  * 名字层两词带引擎给的判词 / 1:1 现名(有就点名)。
51
61
  */
52
62
  export declare function inertRuleEntryGuidance(i: InertRuleEntry): string;
53
- /** ③ 的句子(`<field> entry "<e>" reads as nothing (<reason>): <怎么写>`;多条用分号连)。 */
63
+ /**
64
+ * 拒句 / 通告句里点名条目的**条数上界**(点名有界,同引擎自己通告的「最多五条 + and N more」一个形:体再大,一句话的长度不随条目数长)。
65
+ * 每条点名至多约 600 字节(条目回显 ≤ 122 字符 + 原因词 + 引擎的怎么写),五条 + 尾 ≤ 3.5 KB。
66
+ */
67
+ export declare const MAX_NAMED_INERT_ENTRIES = 5;
68
+ /** ③ 的句子(`<field> entry "<e>" reads as nothing (<reason>): <怎么写>`;多条用分号连;只点名前 {@link MAX_NAMED_INERT_ENTRIES} 条,其余写成 `; and K more`)。全部读成空的句子的**唯一组句器**。 */
54
69
  export declare function describeInertRuleEntries(field: string, inert: readonly InertRuleEntry[]): string;
55
70
  /** 提交体里技能所在的层(命中句与排空读数点名用):顶层 `skills[]` / 子代 `agents[].skills[]`。 */
56
71
  export type SkillListLayer = "skills" | "agents.skills";
57
- /** 一份提交体里第一处「技能的拒侧清单里有引擎读成空的条目」:在哪一层、拒句是什么。 */
58
- export interface SkillDenyInertEntry {
59
- readonly layer: SkillListLayer;
72
+ /** 清单的方向:`allow` = 放行名单(读成空 = 想放的什么也没放 = 静默收窄),`deny` = 限制名单(读成空 = 放行)。续跑重放按方向分流(见 {@link inertToolListEntryOf})。 */
73
+ export type ToolListSide = "allow" | "deny";
74
+ /**
75
+ * 每一张清单(闭集;"层")的方向、读法与拒码。拒码沿用该清单此前的码:技能与请求规则表 ⇒ 请求字段非法;子代定义的两张选择器 ⇒ 引擎的子代名单拒码;顶层两张选择器 ⇒ 引擎的
76
+ * 根 run 名单拒码(同一张表的文法形 / `*` 引擎仍拒这一码,一张表一个码)。续跑腿把同一只码放进 409 的 `blockedBy`。加一张清单 ⇒ 这里加一行,{@link toolListsOf} 的扫描一起编译红。
77
+ */
78
+ declare const TOOL_LIST_FACES: {
79
+ readonly skills: {
80
+ readonly side: "deny";
81
+ readonly kind: "rule_table";
82
+ readonly code: "request.field_invalid";
83
+ };
84
+ readonly "agents.skills": {
85
+ readonly side: "deny";
86
+ readonly kind: "rule_table";
87
+ readonly code: "request.field_invalid";
88
+ };
89
+ readonly "skills.allowedTools": {
90
+ readonly side: "allow";
91
+ readonly kind: "rule_table";
92
+ readonly code: "request.field_invalid";
93
+ };
94
+ readonly "agents.skills.allowedTools": {
95
+ readonly side: "allow";
96
+ readonly kind: "rule_table";
97
+ readonly code: "request.field_invalid";
98
+ };
99
+ readonly "agents.denyTools": {
100
+ readonly side: "deny";
101
+ readonly kind: "selector";
102
+ readonly code: "config.agent.unknown_tool";
103
+ };
104
+ readonly "agents.allowTools": {
105
+ readonly side: "allow";
106
+ readonly kind: "selector";
107
+ readonly code: "config.agent.unknown_tool";
108
+ };
109
+ readonly excludeTools: {
110
+ readonly side: "deny";
111
+ readonly kind: "selector";
112
+ readonly code: "config.tool_face_control";
113
+ };
114
+ readonly allowTools: {
115
+ readonly side: "allow";
116
+ readonly kind: "selector";
117
+ readonly code: "config.tool_face_control";
118
+ };
119
+ readonly "settings.permissions.allow": {
120
+ readonly side: "allow";
121
+ readonly kind: "rule_table";
122
+ readonly code: "request.field_invalid";
123
+ };
124
+ };
125
+ /** 提交体里读成空即拒的工具名清单所在的层(闭集 = {@link TOOL_LIST_FACES} 的键)。 */
126
+ export type ToolListLayer = keyof typeof TOOL_LIST_FACES;
127
+ /**
128
+ * 请求体 `settings.permissions.deny` / `ask`(规则编译器读的收紧表)所在的层 —— **不在** {@link toolListsOf} 的扫描里:它们的判与句子的唯一铸点在 `config.ts` 的
129
+ * `unmatchableToolNamesRefusal`(经 `task-settings.ts` 的 `rulePolicyBucketsRefusal`,单表桶序;拒点是 `applyTaskSettings`,422),存体预判(`http/stored-body-replay.ts`)调同一只函数(home 同源于部署配置),把命中装成本型。
130
+ * 层名进这个联合只为让命中能带坐标进 409 体与运维日志。
131
+ */
132
+ export type RulePolicyLayer = "settings.permissions.deny" | "settings.permissions.ask";
133
+ /** 一份提交体里第一处「工具名清单里有引擎读成空的条目」:在哪一层、哪个方向、拒码、拒句(超帽时另带机读字段,同 `settings.permissions.*` 桶超帽的形)。 */
134
+ export interface InertToolListEntry {
135
+ readonly layer: ToolListLayer | RulePolicyLayer;
136
+ readonly side: ToolListSide;
137
+ readonly code: string;
60
138
  readonly message: string;
139
+ readonly extra?: Readonly<Record<string, unknown>>;
61
140
  }
62
141
  /**
63
- * ③ 的拒侧在**提交体**上的判(顶层 `skills[]` 与子代 `agents[].skills[]` 两层;第一处命中即答)。
142
+ * 存体预判里请求设置的命中装成本型(拒点自己抛:桶门在 `resolveSpec` 抛 400、deny / ask 的名字层与文法在 `applyTaskSettings` 抛 422,都不经本型)—— 两个来源一只装配函数:
143
+ * 桶门命中(三个桶任一,带机读字段 `extra`)与 deny / ask 的规则判命中(限制侧,无 `extra`)。
144
+ */
145
+ export declare function replayInertEntry(layer: ToolListLayer | RulePolicyLayer, side: ToolListSide, code: string, message: string, extra?: Readonly<Record<string, unknown>>): InertToolListEntry;
146
+ /** 技能层的命中(排空脚本只数这两层)。 */
147
+ export interface SkillDenyInertEntry extends InertToolListEntry {
148
+ readonly layer: SkillListLayer;
149
+ }
150
+ /**
151
+ * 一条技能(顶层 `skills[k]` / 子代 `agents[i].skills[j]`,`at` = 它的体内坐标)两只清单的**条数帽**:先 `allowedTools` 后 `disallowedTools`(与 7.131.0 的判序同)。
152
+ * 判与句子就是 {@link overCapOfList} 那一只(拒点 `resolveSpec` 两腿同经的 {@link inertToolListEntryOf} 先过的也是它):受理口在技能的两道形门处调它
153
+ * (`http/admission.ts` 的 `validateUserSkills` 与 `spec-fields.ts` 的 `validateTaskAgents`:两道门都逐条先形、后帽 —— 与 7.131.0 的拒点与判序同,码与先后不变),句子与机读位点体内坐标。
154
+ */
155
+ export declare function skillToolListsOverCap(skill: unknown, at: string, scope: "top" | "agent"): InertToolListEntry | undefined;
156
+ /**
157
+ * ③ 的拒侧在**提交体**上的判,技能那两层(顶层 `skills[]` 与子代 `agents[].skills[]`;第一处命中即答)。全部工具名清单的判是 {@link inertToolListEntryOf}(调用点两处都调它);
158
+ * 本函数单独导出是给随包排空脚本:那条判据(`skill-deny-inert`)只数技能这两层,SQL 车道的孪生也只看技能的清单。
64
159
  *
65
- * 🔴 **调用点两处**:`resolveSpec`(`boot/resolve-spec.ts`,**拒点** —— 新提交与五条续跑腿都经过它,所以「新提交会被拒的体,续跑重放时同样拒」
66
- * 是结构性的)与 `http/checkpoint-ctx.ts` `storedBodyReplayRefusal`(存量体重放前的**预判**,五个续跑问点同调 —— 含 SLA 拒扫的跳过臂与终态 steer
67
- * 铸唤醒卡之前,这两处不经 `resolveSpec`)。提交腿 ⇒ 400 `request.field_invalid`;续跑腿 ⇒ 409 `resume_blocked_by_policy`(`blockedBy: request.field_invalid`,
68
- * 句子同一句,铸点 `checkpoint-ctx.ts` `resumeBlockedByPolicyAnswer`),拒在认领之前,行 / 卡 / ctx 不动。随包排空判据 `skill-deny-inert`
69
- * (`scripts/drain-check-local.mjs` 调本函数)。判据就是引擎的读法(`compileAllowList(entries, "deny", 名册 = 空集)`,与 {@link inertRuleEntriesOf} 同一只;
70
- * 7.118.0 起条目名也过引擎判官,退役名 / 对端段铸不出读成 `tool_name` —— 技能清单的名字层在受理口不再另判)。
160
+ * 判据就是引擎的读法({@link inertRuleEntriesOf};7.118.0 起条目名也过引擎判官,退役名 / 对端段铸不出读成 `tool_name` —— 技能清单的名字层在受理口不再另判)。
71
161
  * 7.118.0 重估(core 7.43.0 #1372 到货:读成空的拒侧条目让那只技能不激活)—— **本版不退**。实测(本判退掉之后跑同一批夹具):引擎的拒到本仓各续跑腿的
72
162
  * 答法不一 —— 资源停车续跑腿把 `checkpoint.config_refused` 答成 500 `internal.error`;工具审批 `/decide` 在引擎拒之前已把卡结掉;`/wake` 在引擎拒之前已铸
73
163
  * run 行;子代 `agents[].skills[]` 不在引擎的认领前屏里(续跑放行、技能在调用时被拒)。退役条件:本仓续跑腿对引擎 `checkpoint.config_refused` 的答法
74
164
  * 收成一只(拒在认领前、行 / 卡不动、同一个 409)之后,本判与本仓的预判一起退,排空判据随之重估。
75
165
  */
76
166
  export declare function skillDenyInertEntryOf(body: unknown): SkillDenyInertEntry | undefined;
167
+ /**
168
+ * **凡工具名清单,读成空的条目 = 开跑前拒并点名现名;真未知名只告** —— 提交体上全部这类清单的判(见 {@link TOOL_LIST_FACES}:技能的 `disallowedTools` 两层、子代定义的 `denyTools` / `allowTools`、
169
+ * 顶层 `excludeTools` / `allowTools`、请求的 `settings.permissions.allow`;第一处命中即答,顺序见 {@link toolListsOf})。判官只有一只:引擎的条目读法(名册 = {@link TOOL_NAME_ROSTER}),
170
+ * 本函数只是「在体的哪几处取清单」;句子只有一个有界的组句器({@link describeInertRuleEntries}),清单先过条数帽({@link allowLayerEntryCountIssue})。
171
+ *
172
+ * 为什么限制侧四类全拒、放行侧只拒近似名:引擎 7.51.x 把「没有任何工具答应的名字」改成条目原样保留 + 运维行告知一次。限制侧读成空 = **静默放行**(`denyTools:["BASH"]` 以为禁了 `Bash`,子代照用);
173
+ * 放行侧读成空 = 想放的什么也没放 —— 其中**近似名**(现名唯一)是确定的笔误,拒并点名现名;**退役名**(选择器清单里有后继的由引擎开跑前拒)、**内容形**与**文法读不出**在放行侧照 7.130.0 受理、交引擎既有通告(见头注 ③)。
174
+ * **真未知名**(没有近似也没有退役对应)不在读成空之列,原样保留、告运维一次(之后挂上同名工具即生效)。请求的 `settings.permissions.deny` / `ask`
175
+ * 与运维 / 中心的审批名单读同一张名册,走另一只入口({@link unmatchableToolNamesRefusal},`config.ts`),不在本函数(存体预判调同一只判据、home 同源于部署配置,见 `http/stored-body-replay.ts`)。
176
+ *
177
+ * `sides` = 只看哪个方向的清单(缺省两个都看)。**条数帽是资源帽,不分方向**:超帽的清单(整只拒,不逐条读)在任一方向的过滤下都命中 —— 引擎每次准备都逐条播种这些表,一份超帽的清单不论条目读得出读不出都会卡住事件循环。
178
+ * **提交腿两个方向都拒**;**续跑重放按方向分流**:限制方向(`deny`)的存量条目读成空 ⇒ 拒(409:放宽方向不能默认,那条限制从没生效过);放行方向(`allow`)的存量条目读成空 ⇒ 照旧续跑、条目原样留着
179
+ * (升级前它本就这样跑着、没那只工具,继续是安全方向)+ 一行运维 warn。
180
+ *
181
+ * 🔴 **调用点两处**:`resolveSpec`(`boot/resolve-spec.ts`,**拒点** —— 新提交与五条续跑腿都经过它)与 `http/stored-body-replay.ts` `storedBodyReplayRefusal`
182
+ * (存量体重放前的**预判**,五个续跑问点同调,只问限制方向)。提交腿 ⇒ 400,码 = 该层的拒码;续跑腿 ⇒ 409 `resume_blocked_by_policy`(`blockedBy` = 同一只码,句子同一句,铸点
183
+ * `checkpoint-ctx.ts` `resumeBlockedByPolicyAnswer`),拒在认领之前,行 / 卡 / ctx 不动。
184
+ */
185
+ export declare function inertToolListEntryOf(body: unknown, sides?: readonly ToolListSide[]): InertToolListEntry | undefined;
186
+ export {};
77
187
  //# sourceMappingURL=rule-entry-list.d.ts.map