@sema-agent/server 7.114.0 → 7.116.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 (159) hide show
  1. package/MIGRATION.md +237 -2
  2. package/USAGE.md +39 -3
  3. package/deploy/sema-up/cluster-up.sh +13 -1
  4. package/deploy/sema-up/kube-up.sh +13 -1
  5. package/deploy/sema-up/preflight.sh +13 -1
  6. package/deploy/sema-up/sema-up.sh +13 -1
  7. package/deploy/sema-up/smoke.sh +13 -1
  8. package/dist/adoption/wire.d.ts +0 -3
  9. package/dist/approval-ask-audit-store.d.ts +25 -6
  10. package/dist/approval-ask-audit-store.js +6 -1
  11. package/dist/approval-card.d.ts +25 -24
  12. package/dist/approval-card.js +14 -0
  13. package/dist/approval-crash-convergence.d.ts +60 -0
  14. package/dist/approval-crash-convergence.js +75 -0
  15. package/dist/approval-deny-reasons.d.ts +13 -0
  16. package/dist/approval-deny-reasons.js +2 -0
  17. package/dist/approval.js +7 -3
  18. package/dist/boot/coordinators.js +0 -12
  19. package/dist/boot/execution-env.js +2 -1
  20. package/dist/boot/exit-boundary.d.ts +3 -2
  21. package/dist/boot/exit-boundary.js +3 -0
  22. package/dist/boot/resolve-spec.js +5 -1
  23. package/dist/boot/shutdown.d.ts +19 -0
  24. package/dist/boot/shutdown.js +12 -1
  25. package/dist/boot/stage-07-capability-layer.js +21 -15
  26. package/dist/boot/stores.js +4 -4
  27. package/dist/capabilities/composite-members.d.ts +1 -0
  28. package/dist/capabilities/composite-members.js +1 -0
  29. package/dist/capabilities/hands-lane.d.ts +0 -23
  30. package/dist/capabilities/hands-lane.js +3 -2
  31. package/dist/capabilities/send-user-file-tool.d.ts +10 -22
  32. package/dist/capabilities/send-user-file-tool.js +1 -8
  33. package/dist/capability-facts.d.ts +296 -0
  34. package/dist/capability-facts.js +354 -0
  35. package/dist/config-catalog.js +2 -1
  36. package/dist/config-center/apply-effective.js +11 -11
  37. package/dist/config-provider.d.ts +16 -0
  38. package/dist/config-provider.js +11 -2
  39. package/dist/config-types.d.ts +20 -1
  40. package/dist/config.d.ts +55 -29
  41. package/dist/config.js +48 -25
  42. package/dist/deployment-governance.d.ts +15 -4
  43. package/dist/deployment-governance.js +20 -2
  44. package/dist/fleet/fleet-bus.d.ts +1 -22
  45. package/dist/hooks/hook-runner.d.ts +29 -3
  46. package/dist/hooks/hook-runner.js +30 -17
  47. package/dist/http/admission.d.ts +6 -0
  48. package/dist/http/admission.js +29 -7
  49. package/dist/http/approval-sweeps.js +20 -0
  50. package/dist/http/checkpoint-ctx.d.ts +68 -0
  51. package/dist/http/checkpoint-ctx.js +36 -0
  52. package/dist/http/durable-run-leg.js +2 -1
  53. package/dist/http/principal-gate.d.ts +3 -0
  54. package/dist/http/principal-gate.js +3 -0
  55. package/dist/http/resume-legs.js +19 -1
  56. package/dist/http/retired-skill-manifest.d.ts +23 -0
  57. package/dist/http/retired-skill-manifest.js +22 -0
  58. package/dist/http/route-ctx.d.ts +7 -0
  59. package/dist/http/route-table.d.ts +3 -2
  60. package/dist/http/route-table.js +5 -5
  61. package/dist/http/routes/a2a-serve.d.ts +1 -3
  62. package/dist/http/routes/a2a-serve.js +14 -23
  63. package/dist/http/routes/admin-config-refresh.js +7 -2
  64. package/dist/http/routes/admin-drain.js +7 -2
  65. package/dist/http/routes/admin-memory-optout.d.ts +0 -2
  66. package/dist/http/routes/admin-memory-optout.js +20 -7
  67. package/dist/http/routes/adoption.js +19 -5
  68. package/dist/http/routes/agents-roster.js +4 -2
  69. package/dist/http/routes/approvals-assistant.js +41 -18
  70. package/dist/http/routes/background.d.ts +0 -2
  71. package/dist/http/routes/background.js +4 -5
  72. package/dist/http/routes/capabilities.d.ts +2 -1
  73. package/dist/http/routes/capabilities.js +63 -65
  74. package/dist/http/routes/config-catalog.d.ts +4 -5
  75. package/dist/http/routes/config-catalog.js +16 -2
  76. package/dist/http/routes/devices.js +17 -11
  77. package/dist/http/routes/diagnostics.d.ts +5 -0
  78. package/dist/http/routes/diagnostics.js +8 -1
  79. package/dist/http/routes/fleet.js +4 -2
  80. package/dist/http/routes/images.js +30 -3
  81. package/dist/http/routes/leader.js +7 -1
  82. package/dist/http/routes/mcp-reconnect.js +17 -10
  83. package/dist/http/routes/memory-bundle.js +20 -6
  84. package/dist/http/routes/memory-compliance.js +19 -5
  85. package/dist/http/routes/memory-consolidation.d.ts +9 -12
  86. package/dist/http/routes/memory-consolidation.js +17 -3
  87. package/dist/http/routes/memory-origin.js +19 -5
  88. package/dist/http/routes/notify-wake.js +5 -3
  89. package/dist/http/routes/observability.js +19 -5
  90. package/dist/http/routes/retention-ops.js +6 -1
  91. package/dist/http/routes/rules.js +49 -21
  92. package/dist/http/routes/run-memory-capture.js +5 -2
  93. package/dist/http/routes/runs.js +90 -60
  94. package/dist/http/routes/session-sync.js +64 -53
  95. package/dist/http/routes/sessions-list.js +8 -5
  96. package/dist/http/routes/sessions.js +40 -25
  97. package/dist/http/routes/shared-memory.js +5 -3
  98. package/dist/http/routes/tasks.js +5 -3
  99. package/dist/http/routes/trace-usage.js +18 -11
  100. package/dist/http/routes/workflows.js +36 -13
  101. package/dist/http/server.js +4 -1
  102. package/dist/http/wire-types.d.ts +6 -0
  103. package/dist/index.d.ts +1 -1
  104. package/dist/leader/wire.js +2 -1
  105. package/dist/local-data-root.d.ts +1 -1
  106. package/dist/mcp-content-origin.d.ts +0 -8
  107. package/dist/model-entry-refusal.d.ts +10 -2
  108. package/dist/model-entry-refusal.js +19 -3
  109. package/dist/namespaced-rule-name.d.ts +54 -0
  110. package/dist/namespaced-rule-name.js +33 -0
  111. package/dist/observability/corrupt-read-seat.d.ts +4 -3
  112. package/dist/observability/corrupt-read-seat.js +4 -3
  113. package/dist/observability/fail-open.d.ts +10 -9
  114. package/dist/observability/fail-open.js +5 -1
  115. package/dist/observability/permission-rule-events.d.ts +3 -0
  116. package/dist/observability/permission-rule-events.js +4 -0
  117. package/dist/permission-rule-list.d.ts +81 -0
  118. package/dist/permission-rule-list.js +17 -0
  119. package/dist/plugins/file-run-store.d.ts +7 -1
  120. package/dist/plugins/file-run-store.js +20 -7
  121. package/dist/plugins/memory-run-store.js +2 -1
  122. package/dist/plugins/one-time-migrations.d.ts +4 -2
  123. package/dist/plugins/one-time-migrations.js +26 -3
  124. package/dist/plugins/pg-session-storage.js +1 -1
  125. package/dist/plugins/run-store-sql.js +4 -4
  126. package/dist/plugins/session-policy-store-sql.js +4 -2
  127. package/dist/plugins/store-backend.d.ts +6 -1
  128. package/dist/plugins/store-backend.js +2 -1
  129. package/dist/plugins/store-contracts.d.ts +27 -8
  130. package/dist/plugins/store-contracts.js +12 -3
  131. package/dist/plugins/tidb-session-store.js +1 -1
  132. package/dist/request-key-closure.js +1 -1
  133. package/dist/run-local.d.ts +37 -3
  134. package/dist/run-local.js +52 -6
  135. package/dist/runs.d.ts +0 -58
  136. package/dist/runs.js +15 -20
  137. package/dist/runtime-governance.d.ts +21 -1
  138. package/dist/runtime-governance.js +6 -1
  139. package/dist/security.js +2 -2
  140. package/dist/session-policy-wire.d.ts +40 -59
  141. package/dist/session-policy-wire.js +60 -32
  142. package/dist/spec-fields.d.ts +32 -0
  143. package/dist/spec-fields.js +31 -16
  144. package/dist/standing-answer-grant.d.ts +8 -0
  145. package/dist/standing-answer-grant.js +5 -0
  146. package/dist/task-settings.d.ts +17 -6
  147. package/dist/task-settings.js +16 -30
  148. package/dist/tool-approval.d.ts +32 -4
  149. package/dist/tool-approval.js +31 -8
  150. package/dist/trace/core-keyset-guard.d.ts +13 -8
  151. package/dist/trace/project.js +9 -0
  152. package/dist/trace/self-minted-wire.d.ts +42 -0
  153. package/dist/trace/self-minted-wire.js +13 -0
  154. package/dist/user-input-ledger.d.ts +8 -5
  155. package/package.json +6 -4
  156. package/scripts/drain-check-local.mjs +528 -0
  157. package/scripts/drain-check.mysql.sql +67 -0
  158. package/scripts/drain-check.pg.sql +71 -0
  159. package/scripts/retired-mode-seed-drain-local.mjs +0 -317
@@ -4,6 +4,7 @@
4
4
  * body WITHOUT the HTTP-layer 400 validation, so a malformed value must fall back to a safe default, never throw.
5
5
  */
6
6
  import { type AgentDefinition, type TaskSpec } from "@sema-agent/core";
7
+ import { type UnreachableNamespacedRuleName } from "./namespaced-rule-name.js";
7
8
  /**
8
9
  * E12 — normalize `body.suggestNextPrompts` → `TaskSpec.suggestNextPrompts`. `true` = on with core defaults
9
10
  * (count 3, cheap role); an object tunes `count` (clamped 1..10) / `role` (a free string — core validates it against
@@ -42,6 +43,36 @@ export declare function normalizeResumeAtMode(v: unknown, resumeAtResolved: bool
42
43
  * (每个定义的 whenToUse/systemPrompt 都是无条件进系统提示的 prompt 载荷),与 [ref] 退役的
43
44
  * **技能**条数帽不是同一轴:技能清单由引擎按预算渲染(超了裁描述),agent 定义没有那个预算面。 */
44
45
  export declare const MAX_TASK_AGENTS = 32;
46
+ /**
47
+ * core 7.41.0 #1303 —— 技能的两只清单 `allowedTools` / `disallowedTools`(`dist/core/agent-definition.d.ts`:本次 run 的 allow 规则来源 /
48
+ * deny 规则来源,不是围栏)的**形**谓词。两道技能门(顶层 `body.skills[]` 的 `validateUserSkills`、`agents[].skills[]` 的
49
+ * {@link SKILL_FIELD_SHAPES})**同一只**:一边判一边不判 = 同一个键两种话(S-377 baseDir 吃过的亏)。
50
+ *
51
+ * 本谓词只判「字符串数组」:引擎 `compileSkillGrant` 直接 `for (const entry of list)` —— 一个裸字符串 `"Bash"` 会被逐字符迭代成
52
+ * `B` / `a` / `s` / `h` 四条(deny 侧 = 意图静默落空,安全轴上的放宽方向)。条目的**文法**不在受理面判:引擎在开跑时对读不出的条目
53
+ * (`*` / 写路径 / 域名)发 `skills.allowed_tool_not_granted`,文法的属主是引擎(directory loader 同样原样抄、不判)。
54
+ * 形对之后两道门再问一问 {@link unreachableSkillToolListEntryOf}(协议名的服务器段铸不出 / 缺段 ⇒ 400)—— 那一问不是文法,是「写得出却永远匹配不到」,
55
+ * 与配置表、会话规则写口同一只判官。
56
+ * 无条数 / 长度帽([ref]:技能面的受理帽整族退役,体积由 8 MiB 请求体上限兜)。
57
+ */
58
+ export declare const skillToolListOk: (v: unknown) => boolean;
59
+ /** 技能的两只清单(`allowedTools` = allow 规则来源,`disallowedTools` = deny 规则来源;条目的工具名那一段两只同读)。 */
60
+ declare const SKILL_TOOL_LISTS: readonly ["allowedTools", "disallowedTools"];
61
+ /** 技能清单里一条够不着任何挂载工具的协议名条目。 */
62
+ export interface SkillToolListUnreachable {
63
+ readonly key: (typeof SKILL_TOOL_LISTS)[number];
64
+ readonly entry: string;
65
+ readonly verdict: UnreachableNamespacedRuleName;
66
+ }
67
+ /**
68
+ * 一条技能(形已过两道门的字符串数组判)的两只清单里,第一条「协议名写得出、却永远匹配不到」的条目 —— 判官是 `namespaced-rule-name.ts` 那一只
69
+ * (配置表 / 请求三桶 / 会话规则写口同调),条目的工具名那一段经同一只 `ruleEntryToolName` 取(与配置表的规则编译器消费者同读)。都够得着 ⇒ `undefined`。
70
+ * 两处受理口同调:顶层 `skills[]`(`http/admission.ts` 的 `validateUserSkills`)、子代 `agents[].skills[]`({@link validateTaskAgents})。
71
+ * 续跑重放**不问**这一问(两层都是:重放腿不过受理口,存量体照旧原样交给引擎 —— 引擎把它编成一条永不命中的规则,与修前同一种命运,不另开读法)。
72
+ */
73
+ export declare function unreachableSkillToolListEntryOf(skill: Record<string, unknown>): SkillToolListUnreachable | undefined;
74
+ /** 两处受理口同一句(句子的后半与配置表 / 会话规则写口同一只 `describeUnreachableNamespacedRuleName`)。 */
75
+ export declare function describeUnreachableSkillToolListEntry(u: SkillToolListUnreachable): string;
45
76
  /** [ref] 单条 agent 定义的形状裁决:null=合法;否则一句可 400 的英文错误(不带索引,调用方自己加)。
46
77
  * 白名单外键 fail-loud —— 与 resilience/limits 的「容忍未知键」相反:agents 是能力定义,静默丢字段 =
47
78
  * 定义被悄悄削(一个丢掉的 isolation 就是放宽),比 400 更危险,故整条拒。⚠️ 已知残账(core 半场):
@@ -157,4 +188,5 @@ export declare function normalizeApproachNotice(raw: unknown): false | {
157
188
  * 全空 ⇒ undefined(spec 不挂 limits 键,byte-compat)。
158
189
  */
159
190
  export declare function resolveTaskLimits(bodyLimits: unknown, caps: TaskLimitCaps, envTimeoutSec: number, requirePrincipal: boolean | undefined, big: boolean, warn?: (event: string, fields: Record<string, unknown>) => void): TaskSpec["limits"];
191
+ export {};
160
192
  //# sourceMappingURL=spec-fields.d.ts.map
@@ -1,6 +1,7 @@
1
1
  import { isThinkingLevel } from "@sema-agent/core";
2
2
  import { taskWallClockSec } from "./task-workflow.js";
3
3
  import { isValidCwd } from "./task-cwd.js";
4
+ import { describeUnreachableNamespacedRuleName, ruleEntryToolName, unreachableNamespacedRuleNameOf } from "./namespaced-rule-name.js";
4
5
  export function normalizeSuggestNextPrompts(v) {
5
6
  if (v === true)
6
7
  return true;
@@ -65,28 +66,34 @@ const toolList = (v) => Array.isArray(v) &&
65
66
  v.length <= MAX_AGENT_TOOLS &&
66
67
  v.every((x) => typeof x === "string" && x.length > 0 && x.length <= MAX_AGENT_TOOL_NAME_CHARS);
67
68
  const plainObject = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
68
- const skillManifestOk = (m) => {
69
- if (!plainObject(m))
70
- return false;
71
- for (const k of Object.keys(m)) {
72
- if (k !== "allowTools" && k !== "allowPaths" && k !== "egress" && k !== "lineageId")
73
- return false;
69
+ export const skillToolListOk = (v) => Array.isArray(v) && v.every((x) => typeof x === "string");
70
+ const SKILL_TOOL_LISTS = ["allowedTools", "disallowedTools"];
71
+ export function unreachableSkillToolListEntryOf(skill) {
72
+ for (const key of SKILL_TOOL_LISTS) {
73
+ const list = skill[key];
74
+ if (!Array.isArray(list))
75
+ continue;
76
+ for (const entry of list) {
77
+ if (typeof entry !== "string")
78
+ continue;
79
+ const verdict = unreachableNamespacedRuleNameOf(ruleEntryToolName(entry));
80
+ if (verdict !== undefined)
81
+ return { key, entry, verdict };
82
+ }
74
83
  }
75
- if (!(Array.isArray(m.allowTools) && m.allowTools.every((x) => typeof x === "string")))
76
- return false;
77
- if (m.allowPaths !== undefined && !(Array.isArray(m.allowPaths) && m.allowPaths.every((x) => typeof x === "string")))
78
- return false;
79
- if (m.egress !== undefined && typeof m.egress !== "boolean")
80
- return false;
81
- return typeof m.lineageId === "string" && m.lineageId.length > 0;
82
- };
84
+ return undefined;
85
+ }
86
+ export function describeUnreachableSkillToolListEntry(u) {
87
+ return `skill.${u.key} names "${u.entry}", which can never match a tool (${describeUnreachableNamespacedRuleName(u.verdict)})`;
88
+ }
83
89
  const skillFilesOk = (v) => Array.isArray(v) &&
84
90
  v.every((f) => plainObject(f) && typeof f.path === "string" && f.path.length > 0 && typeof f.content === "string");
85
91
  const SKILL_FIELD_SHAPES = {
86
92
  name: { required: true, ok: (v) => typeof v === "string" && v.length > 0 },
87
93
  description: { required: true, ok: (v) => typeof v === "string" },
88
94
  content: { required: true, ok: (v) => typeof v === "string" && v.length > 0 && v.length <= MAX_AGENT_SKILL_CONTENT_CHARS },
89
- manifest: { required: false, ok: skillManifestOk },
95
+ allowedTools: { required: false, ok: skillToolListOk },
96
+ disallowedTools: { required: false, ok: skillToolListOk },
90
97
  files: { required: false, ok: skillFilesOk },
91
98
  baseDir: { required: false, ok: isValidCwd },
92
99
  };
@@ -136,7 +143,7 @@ const TASK_AGENT_FIELD_SHAPES = {
136
143
  allowTools: { desc: `an array of at most ${MAX_AGENT_TOOLS} non-empty strings of at most ${MAX_AGENT_TOOL_NAME_CHARS} characters each`, ok: toolList },
137
144
  denyTools: { desc: `an array of at most ${MAX_AGENT_TOOLS} non-empty strings of at most ${MAX_AGENT_TOOL_NAME_CHARS} characters each`, ok: toolList },
138
145
  skills: {
139
- desc: "an array of { name, description, content } skill objects (optional manifest/files/baseDir; files entries need { path, content }; baseDir must be an absolute path with no \"..\" segment) — no count cap; the engine renders the listing within its own byte budget",
146
+ desc: "an array of { name, description, content } skill objects (optional allowedTools/disallowedTools/files/baseDir; allowedTools/disallowedTools are arrays of strings; files entries need { path, content }; baseDir must be an absolute path with no \"..\" segment) — no count cap; the engine renders the listing within its own byte budget",
140
147
  ok: (v) => Array.isArray(v) && v.every(skillOk),
141
148
  },
142
149
  background: { desc: "a boolean", ok: (v) => typeof v === "boolean" },
@@ -187,6 +194,14 @@ export function validateTaskAgents(v) {
187
194
  const issue = taskAgentIssue(v[i]);
188
195
  if (issue)
189
196
  return `agents[${i}]: ${issue}`;
197
+ const skills = v[i].skills;
198
+ if (Array.isArray(skills)) {
199
+ for (let j = 0; j < skills.length; j++) {
200
+ const unreachable = unreachableSkillToolListEntryOf(skills[j]);
201
+ if (unreachable !== undefined)
202
+ return `agents[${i}]: skills[${j}]: ${describeUnreachableSkillToolListEntry(unreachable)}`;
203
+ }
204
+ }
190
205
  const name = v[i].name;
191
206
  if (names.has(name))
192
207
  return `agents[${i}]: duplicate agent name "${name}"`;
@@ -0,0 +1,8 @@
1
+ /** 读口的入参:任何一种载着「这一问」的形(活的 `AskRequest`、`ToolCallRequest`、待决条目、停驻行的 `tool_approval` 臂)都结构上满足。 */
2
+ export interface StandingAnswerGrantCarrier {
3
+ readonly toolName?: unknown;
4
+ readonly boundGrantDigest?: unknown;
5
+ }
6
+ /** 这一问的应答是否授予这一次调用之外的东西(读法见文件头注)。 */
7
+ export declare function answerGrantsBeyondCall(carrier: StandingAnswerGrantCarrier): boolean;
8
+ //# sourceMappingURL=standing-answer-grant.d.ts.map
@@ -0,0 +1,5 @@
1
+ import { SKILL_TOOL_NAME } from "@sema-agent/core";
2
+ export function answerGrantsBeyondCall(carrier) {
3
+ return Object.hasOwn(carrier, "boundGrantDigest") || carrier.toolName === SKILL_TOOL_NAME;
4
+ }
5
+ //# sourceMappingURL=standing-answer-grant.js.map
@@ -11,10 +11,23 @@
11
11
  * operator/principal axis, [ref] §E6, a different trust axis + lifecycle). Re-applying on a resume leg is
12
12
  * idempotent (the same tighten), so no special resume handling is needed (mirrors runtime-governance).
13
13
  *
14
- * 🔒 SECURITY INVARIANTS (SDK settings.ts §3.3):
15
- * - permissions are TIGHTEN-ONLY — folded via `tightenTaskSpec` (deny-wins `combinePolicies`); it THROWS on any
16
- * override that would LOOSEN a stricter base safety field. The client can only ADD denies/asks/handsReadOnly on
17
- * top of the deployment + operator baseline, never widen it.
14
+ * 🔒 SECURITY INVARIANTS (SDK settings.ts §3.3) —— **scope: the `settings.permissions` channel this module projects, and only it**:
15
+ * - permissions are folded via `tightenTaskSpec` (deny-wins `combinePolicies`); it THROWS on any override that would
16
+ * LOOSEN a stricter base safety field. Through THIS channel the client can ADD denies/asks/handsReadOnly on top of the
17
+ * deployment + operator baseline, never widen it. Its `allow` rules (S-654 (c), 7.115.0) STATE an allow for the tools they name —
18
+ * the answer core 7.40.0 wants before it runs an MCP / A2A call nobody stated — and never override a deny or an ask
19
+ * from any layer (deny > ask > allow); a miss has no opinion. They release no ask a layer raises itself: on built-in
20
+ * tools the engine's own write gate and shell checks still ask (unlike the reference implementation's `permissions.allow`).
21
+ * - ⚠️ This is NOT a claim about the request as a whole. Since server 7.116.0 (core 7.41.0) a request-carried skill's
22
+ * `allowedTools` — activated by the model's approved `Skill` card, or named in `userInvokedSkills` — IS an accepted
23
+ * loosening on a different channel (`skills[]` → `TaskSpec.skills`, never through this module): for the rest of that run
24
+ * it releases the `unstated` / `ordinary` asks the engine itself raises (including the built-in write gate and the shell's
25
+ * ordinary asks; same predicate as a persisted allow rule), on hosted / multi-tenant deployments too — no deployment
26
+ * switch, by ruling. It still clears no deny / ask rule (request or operator), no governance / shell-floor /
27
+ * write-protection / plan-mode / probe / mandated ask, and opens no write containment. An operator who needs a person
28
+ * asked for a given tool regardless uses the per-tool knobs: `APPROVAL_REQUIRE` (an ask rule), `APPROVAL_NEVER_AUTO`
29
+ * (a person, mandated), `MANUAL_MODE_SHELL_GATE=always` (shell). Pinned on the real assembly:
30
+ * `test/skill-grant-operator-knobs.test.ts`.
18
31
  * - permissionMode(轴B #2 注释保鲜,1.254——[ref]/[ref] 后的真实现):`defaultMode` 收**五**模式
19
32
  * (default | acceptEdits | plan | bypassPermissions | auto),bypass/auto 被 HONOR 为 gate-shape 选择
20
33
  * ——但深层不变量未变:bypass=「不加 ask 门」,恒不越部署基线(tightenTaskSpec deny-wins,基线 deny/
@@ -224,8 +237,6 @@ export interface ParseTaskSettingsResult {
224
237
  * posture (an uncapped prompt body is a per-turn token-cost hole; cross review). Exported so the HTTP layer
225
238
  * (prepareSpec) can 400 fail-loud on submit with the SAME bound this defensive parse enforces on every path. */
226
239
  export declare const MAX_SETTINGS_OUTPUT_STYLE_CHARS = 16384;
227
- /** Cap on the element count of each permission list (allow/deny/ask) — bounds a pathological rule blast. */
228
- export declare const MAX_SETTINGS_PERMISSION_RULES = 1000;
229
240
  /** Caps on `settings.env` — bound a pathological env blast (count + per-value length; values land in the shell env). */
230
241
  export declare const MAX_SETTINGS_ENV_VARS = 256;
231
242
  export declare const MAX_SETTINGS_ENV_VALUE_CHARS = 32768;
@@ -1,6 +1,6 @@
1
- import { combinePolicies, createAllowDenyPolicy, createFsWriteGatePolicy, createPermissionRulePolicy, parseRuleText, tightenTaskSpec, resolveReasoningProfile, rankOf, isThinkingLevel, CYBER_RISK, URL_SAFETY, HARNESS_SECTION_ANCHOR, RUN_WORKFLOW_TOOL_NAME, PERMISSION_MODES, ruleToolGrammarOf, } from "@sema-agent/core";
2
- import { findUnmatchableToolNames, formatUnmatchableToolNames } from "./config.js";
3
- import { CONTROL_AND_BIDI_STRIP_RE } from "./text-bidi.js";
1
+ import { combinePolicies, createFsWriteGatePolicy, parseRuleText, tightenTaskSpec, resolveReasoningProfile, rankOf, isThinkingLevel, CYBER_RISK, URL_SAFETY, HARNESS_SECTION_ANCHOR, RUN_WORKFLOW_TOOL_NAME, PERMISSION_MODES, ruleToolGrammarOf, } from "@sema-agent/core";
2
+ import { unmatchableToolNamesRefusal } from "./config.js";
3
+ import { MAX_PERMISSION_RULES_PER_LIST, createRuleListPolicy } from "./permission-rule-list.js";
4
4
  import { MAX_KEY_ECHO, keyClosureClause, scanKeyClosure } from "./request-key-closure.js";
5
5
  import { parseHooksConfig } from "./hooks/hook-runner.js";
6
6
  import { heldRuleName } from "./tool-rule-names.js";
@@ -61,7 +61,6 @@ export function skillListingMaxDescOk(v) {
61
61
  return typeof v === "number" && Number.isSafeInteger(v) && v > 0;
62
62
  }
63
63
  export const MAX_SETTINGS_OUTPUT_STYLE_CHARS = 16_384;
64
- export const MAX_SETTINGS_PERMISSION_RULES = 1_000;
65
64
  export const MAX_SETTINGS_ENV_VARS = 256;
66
65
  export const MAX_SETTINGS_ENV_VALUE_CHARS = 32_768;
67
66
  export const MAX_SETTINGS_ENV_KEY_CHARS = 256;
@@ -106,8 +105,8 @@ export function settingsPermissionBucketIssue(perms) {
106
105
  let fields;
107
106
  if (!Array.isArray(v))
108
107
  fields = { field, reason: "bad_entry" };
109
- else if (v.length > MAX_SETTINGS_PERMISSION_RULES)
110
- fields = { field, reason: "over_cap", count: v.length, cap: MAX_SETTINGS_PERMISSION_RULES };
108
+ else if (v.length > MAX_PERMISSION_RULES_PER_LIST)
109
+ fields = { field, reason: "over_cap", count: v.length, cap: MAX_PERMISSION_RULES_PER_LIST };
111
110
  else {
112
111
  const index = v.findIndex((x) => typeof x !== "string" || x.length === 0);
113
112
  if (index >= 0)
@@ -266,20 +265,7 @@ export function acceptAppendSystemPrompt(v, warn, packDropsAppend) {
266
265
  }
267
266
  return v;
268
267
  }
269
- const RULE_SOURCE_BY_BEHAVIOR = { ask: "settings.permissions.ask", deny: "settings.permissions.deny" };
270
- function createRuleListPolicy(names, behavior, pathBases) {
271
- try {
272
- return createPermissionRulePolicy(names.map((name) => ({ rule: name, behavior, source: RULE_SOURCE_BY_BEHAVIOR[behavior] })), {
273
- defaultAction: "allow",
274
- caps: { maxRules: MAX_SETTINGS_PERMISSION_RULES },
275
- ...(pathBases !== undefined ? { pathBases } : {}),
276
- });
277
- }
278
- catch (e) {
279
- const coreMsg = (e instanceof Error ? e.message : String(e)).replace(CONTROL_AND_BIDI_STRIP_RE, "");
280
- throw new Error(`${RULE_SOURCE_BY_BEHAVIOR[behavior].replace("settings.", "")} has rule(s) core cannot compile — ${coreMsg}`, { cause: e });
281
- }
282
- }
268
+ const RULE_SOURCE_BY_BEHAVIOR = { allow: "settings.permissions.allow", ask: "settings.permissions.ask", deny: "settings.permissions.deny" };
283
269
  const WORKFLOW_ASK_NAMES = [RUN_WORKFLOW_TOOL_NAME];
284
270
  const isWorkflowName = (name) => name === RUN_WORKFLOW_TOOL_NAME;
285
271
  function createWorkflowAskPolicy(isExempt) {
@@ -327,16 +313,16 @@ function rejectUnmatchableSettingsNames(perms, pathBases) {
327
313
  }
328
314
  }
329
315
  }
330
- for (const [field, names, behavior, consumer] of [
331
- ["permissions.ask", perms.ask, "ask", "rule-policy"],
332
- ["permissions.deny", perms.deny, "deny", "rule-policy"],
333
- ["permissions.allow", perms.allow, "allow", "tool-name-table"],
316
+ for (const [field, names, behavior] of [
317
+ ["permissions.ask", perms.ask, "ask"],
318
+ ["permissions.deny", perms.deny, "deny"],
319
+ ["permissions.allow", perms.allow, "allow"],
334
320
  ]) {
335
321
  if (!names)
336
322
  continue;
337
- const bad = findUnmatchableToolNames(names, { consumer, behavior, ...(pathBases !== undefined ? { pathBases } : {}) });
338
- if (bad.length > 0)
339
- throw new Error(formatUnmatchableToolNames(field, bad));
323
+ const unmatchable = unmatchableToolNamesRefusal(field, names, { consumer: "rule-policy", behavior, ...(pathBases !== undefined ? { pathBases } : {}) });
324
+ if (unmatchable !== undefined)
325
+ throw new Error(unmatchable.message);
340
326
  }
341
327
  }
342
328
  export function deriveSettingsPolicy(settings, gate, workflowGate) {
@@ -350,11 +336,11 @@ export function deriveSettingsPolicy(settings, gate, workflowGate) {
350
336
  rejectUnmatchableSettingsNames(perms, pathBases);
351
337
  const policies = [];
352
338
  if (allow)
353
- policies.push(createAllowDenyPolicy({ allow }));
339
+ policies.push(createRuleListPolicy(allow, "allow", RULE_SOURCE_BY_BEHAVIOR.allow, pathBases));
354
340
  if (deny)
355
- policies.push(createRuleListPolicy(deny, "deny", pathBases));
341
+ policies.push(createRuleListPolicy(deny, "deny", RULE_SOURCE_BY_BEHAVIOR.deny, pathBases));
356
342
  if (ask)
357
- policies.push(createRuleListPolicy(ask, "ask", pathBases));
343
+ policies.push(createRuleListPolicy(ask, "ask", RULE_SOURCE_BY_BEHAVIOR.ask, pathBases));
358
344
  const fsWriteGate = (acceptCwd) => {
359
345
  if (!gate)
360
346
  return undefined;
@@ -95,6 +95,10 @@ export interface StandingAnswerFacts {
95
95
  readonly requiresRealApproval: boolean;
96
96
  /** core 闸站盖的强制位([ref]):规则清不掉、站着的答案退不掉,只有对**这一次调用**的应答能答。 */
97
97
  readonly mandated: boolean;
98
+ /** 7.116.0(审计修复批 S4,core 7.41.0 `Skill` 卡):这一问的应答**还授予**这一次调用之外的东西(批准 `Skill` = 激活技能 + 本 run 内
99
+ * 清单免问)。站着的答案要代答它必须读得出授予绑定、且绑定相同;core 7.41.0 在活的问上不给绑定 ⇒ 两侧都读不出 ⇒ 不可记、不被代答。
100
+ * 读者一律经 `standing-answer-grant.ts` 的 `answerGrantsBeyondCall`(唯一读口)折这一位。 */
101
+ readonly grantsBeyondCall: boolean;
98
102
  }
99
103
  /**
100
104
  * 🔴 **S-660 —— 会话级放行的唯一判据:站着的答案能不能退掉这一问。** 纯谓词,三个读者:
@@ -245,6 +249,15 @@ export interface ToolApprovalFrame {
245
249
  * 席照常答得了」说的是**这一次调用的应答者**,本仓不改它。
246
250
  */
247
251
  mandated?: true;
252
+ /**
253
+ * **7.116.0(审计修复批 S4;ADDITIVE,`"tool_approval"` only)—— 这张卡上的「本会话放行」只放行这一次、不会被记住。**
254
+ * 值 = 同一只判据 {@link standingAnswerMayRetire} 的否定(四位:治理强制 / 必须真人 / 强制 / 应答还授予别的东西),回决侧写授权
255
+ * (`finishRespond`)读的是同一份事实 ⇒ 帧说不可记 ⇔ 点了 `allow_session` 回执 `rememberApplied:false`。壳读这一位决定渲不渲
256
+ * 「本会话放行」,不再从 `requiresRealApproval` / `mandated` / `governanceForced` 三位自己推断(那一推断漏掉第四个理由:core 7.41.0
257
+ * 的 `Skill` 卡批准即授予清单,三位全无)。`card_json` 卡面、`livePending` 行、运维队列行同名同义。
258
+ * 🔴 **真才带,缺席绝不编 `false`**:缺席 = 可记(有会话时)或老服务端。没有会话的腿上点了照样只放行一次(回执说明),本位不管那一形。
259
+ */
260
+ rememberRefused?: true;
248
261
  /**
249
262
  * **S-721(core 7.32.0 #1092;ADDITIVE,`"tool_approval"` only)** —— 强制位**站在哪一个词上**:core `PERSISTED_RULE_MANDATES`
250
263
  * 六词之一(`operator_always` / `tool_marks` / `probe_mandate` / `probe_unanswered` / `write_protection` /
@@ -452,10 +465,7 @@ export interface ToolApprovalFrame {
452
465
  * 耐久路的对偶 = `gate.riskDescriptor.probeCause`(同值,core `buildRiskDescriptor` 铸),走 checkpoint
453
466
  * 行的**整体透传**到 `GET /v1/approvals` —— 键集零施工,钉在同一个文件的 §4。
454
467
  *
455
- * 姊妹键 `AskRequest.probeReason`(同一件事的**散文**兄弟)**今天不投影**:它与本键是同一个事实的两种
456
- * 拼法,而 core 的边界哲学(交 code + 数组、不交句子)与「一张卡上不放两份同义解释」都指向只投结构化
457
- * 那一份;散文那份还无法被消费端本地化。照 `preview` / `requiresRealApproval` 先例 = 候消费端(cli)
458
- * 提渲染需求再翻(登记在 `trace/core-keyset-guard.ts` 的 EXCLUDED 栏)。
468
+ * 姊妹键 `probeReason`(同一件事的**散文**兄弟)见下一键 —— 7.115.0 起投影(S-733),与本键**并列不替代**。
459
469
  */
460
470
  probeCause?: {
461
471
  code: string;
@@ -468,6 +478,21 @@ export interface ToolApprovalFrame {
468
478
  total: number;
469
479
  };
470
480
  };
481
+ /**
482
+ * S-733(7.115.0;**ADDITIVE**,`"tool_approval"` only)—— 这次 ask 被收紧时探针给出的那一句**散文**因由
483
+ * (`AskRequest.probeReason`:core 在 maybe 档收紧点 `inlineUntrusted(…, 200)` 过的一句话)。与
484
+ * {@link probeCause}(结构化因由)**并列、不替代**:引擎自己铸的因由走结构位,**部署自写的探针**只会写这一句
485
+ * —— 一只只给 reason 不给 cause 的探针,此前其审批人在活卡上读不到任何因由(耐久面
486
+ * `gate.riskDescriptor.probeReason` 早就带着这同一句,两张脸只有一张有话说)。
487
+ *
488
+ * 🔴 **在场才带**(缺席绝不编空串;缺席 ≠「没有原因」);窄读单点 = `approval-card.ts` 的 `readProbeReason`
489
+ * (非串 / 空白 ⇒ 缺席;超信封 ⇒ **整只丢不截**;内容族 ⇒ 脱敏,与耐久面同一规则 ⇒ 对同一句话两面同值)。
490
+ * 🔴 **只在本帧上**:呈卡帧 / `card_json` 不携它(`ApprovalCardSchema` 是 `.strict()` 的耐久信封,加键要另行裁定)。
491
+ * 🔴 **展示 / 分诊元数据,永不是裁决输入**;永不进 `message`(那是分类器 / 模型的输入面)。
492
+ * `\p{Cf}` 展示重排类字符(方向 / 零宽标记):core 在这个座上**不剥**(同值契约下 core 自己披露的残余,[ref]);本仓脱敏器
493
+ * (`redactSecrets`)入口剥全部 `\p{Cf}`(7.70.0 起),活卡帧与耐久面**都过它** ⇒ 两面同值、且都是剥后字节(`test/approval-probe-reason-wire.test.ts` 钉)。
494
+ */
495
+ probeReason?: string;
471
496
  /**
472
497
  * [ref] 件 G2(core 5.35.0 [ref] G-2;**ADDITIVE**,`"tool_approval"` only)—— 这只 ask 背后的
473
498
  * **规则出处证据**(`AskRequest.ruleEvidence`,引擎在 gate 内盖章):org 快照 `orgRevision` / 命中的
@@ -539,6 +564,9 @@ export interface LivePendingRow {
539
564
  * 值 = 条目上那一位(`askBroadcast` 铸条目时取自这一次过闸的 `req.mandated`,与 {@link frame} 上的同名键同源)。此前只在
540
565
  * `frame` 里:同一个 `GET /v1/approvals` 响应的 durable `pending[]` 行顶层带它(S-663 二段),live 行却要进帧里读。 */
541
566
  mandated?: true;
567
+ /** 7.116.0(审计修复批 S4):这张卡上的「本会话放行」不会被记住 —— 与 {@link ToolApprovalFrame.rememberRefused} 同名同义同源
568
+ * (同一只判据,读条目上的四位事实);真才带。 */
569
+ rememberRefused?: true;
542
570
  /** S-721(core 7.32.0 #1092):强制位站在哪一个词上 —— 与 `mandated` 同层同形(在场逐字,缺席不编词),值 = 条目上那一个词
543
571
  * (与 {@link frame} 上同名键同源;帧型该键顶注是全文)。 */
544
572
  mandate?: PersistedRuleMandate;
@@ -8,11 +8,12 @@ import { approvalPayloadFingerprint } from "./plugins/approval-payload-fingerpri
8
8
  import { createLogger } from "./observability/logger.js";
9
9
  import { recordFailOpen } from "./observability/fail-open.js";
10
10
  import { deriveAskId, deriveAskScopedBatchId, deriveBatchId, MAX_DECISION_NOTE_CHARS } from "./approval-ask-machine.js";
11
- import { ApprovalCardEnvelopeSchema, MAX_RULE_OFFERS, buildApprovalCard, buildApprovalCardEnvelope, buildApprovalRequestFrame, buildRevokeFrame, readDenialLimitFallback, readProbeCause, readReadRootCandidate, readRuleEvidence, readRuleOffersAbsence, readRuleOffersAbsenceSeat, readParkRowTrueSeat, readAskClassPair, readClosedWordSeat, RuleOfferRawSchema, backfillLegacyBatchMemberKind, screenUncoveredDetailSeat, } from "./approval-card.js";
11
+ import { ApprovalCardEnvelopeSchema, MAX_RULE_OFFERS, buildApprovalCard, buildApprovalCardEnvelope, buildApprovalRequestFrame, buildRevokeFrame, readDenialLimitFallback, readProbeCause, readProbeReason, readReadRootCandidate, readRuleEvidence, readRuleOffersAbsence, readRuleOffersAbsenceSeat, readParkRowTrueSeat, readAskClassPair, readClosedWordSeat, RuleOfferRawSchema, backfillLegacyBatchMemberKind, screenUncoveredDetailSeat, } from "./approval-card.js";
12
12
  import { MAX_SETTLED_BY_CHARS, POLICY_REFUSAL_DECISION, projectAskTerminalClaimTruth, settledByFitsColumn } from "./plugins/approval-ask-store-sql.js";
13
13
  import { settleByPolicy } from "./approval-policy-settlement.js";
14
14
  import { governanceAskMarksFor, runWithGovernanceAskScope } from "./governance-ask-marks.js";
15
15
  import { governanceOriginOf } from "./runtime-governance.js";
16
+ import { answerGrantsBeyondCall } from "./standing-answer-grant.js";
16
17
  const defaultLogger = createLogger();
17
18
  export const APPROVAL_GATE_KINDS = ["human", "irreversible_ask"];
18
19
  export function isApprovalGateKind(k) {
@@ -84,7 +85,7 @@ function sessionAllowKey(owner, sessionId, category) {
84
85
  return JSON.stringify([owner, sessionId, category]);
85
86
  }
86
87
  export function standingAnswerMayRetire(facts) {
87
- return !facts.governanceForced && !facts.requiresRealApproval && !facts.mandated;
88
+ return !facts.governanceForced && !facts.requiresRealApproval && !facts.mandated && !facts.grantsBeyondCall;
88
89
  }
89
90
  export function parkRowStandingAnswerFacts(pending, gate, governance) {
90
91
  const riskDescriptor = "riskDescriptor" in gate ? gate.riskDescriptor : undefined;
@@ -92,6 +93,15 @@ export function parkRowStandingAnswerFacts(pending, gate, governance) {
92
93
  governanceForced: governanceOriginOf({ riskDescriptor }, governance) === true,
93
94
  requiresRealApproval: readParkRowTrueSeat(pending, "requiresRealApproval"),
94
95
  mandated: readParkRowTrueSeat(pending, "mandated") || mandatedFromSeat(readRuleOffersAbsenceSeat(pending)),
96
+ grantsBeyondCall: answerGrantsBeyondCall(pending),
97
+ };
98
+ }
99
+ function pendingEntryStandingAnswerFacts(entry) {
100
+ return {
101
+ governanceForced: entry.governanceForced === true,
102
+ requiresRealApproval: entry.requiresRealApproval === true,
103
+ mandated: entry.mandated === true,
104
+ grantsBeyondCall: answerGrantsBeyondCall(entry),
95
105
  };
96
106
  }
97
107
  function mandatedFromSeat(seat) {
@@ -741,7 +751,7 @@ export class ToolApprovalCoordinator {
741
751
  return (this.governanceAskMarks ?? governanceAskMarksFor(this.streamKey(origin.owner, origin.sessionId, origin.taskId))).isMarked(req.toolCallId);
742
752
  }
743
753
  async standingGrantSettles(origin, req, governanceForced) {
744
- if (!standingAnswerMayRetire({ governanceForced, requiresRealApproval: req.requiresRealApproval === true, mandated: req.mandated === true }))
754
+ if (!standingAnswerMayRetire({ governanceForced, requiresRealApproval: req.requiresRealApproval === true, mandated: req.mandated === true, grantsBeyondCall: answerGrantsBeyondCall(req) }))
745
755
  return false;
746
756
  const sessionId = origin.sessionId;
747
757
  if (!sessionId)
@@ -1088,6 +1098,7 @@ export class ToolApprovalCoordinator {
1088
1098
  ...(governanceForced ? { governanceForced: true } : {}),
1089
1099
  ...(req.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
1090
1100
  ...(req.mandated === true ? { mandated: true } : {}),
1101
+ ...(standingAnswerMayRetire({ governanceForced, requiresRealApproval: req.requiresRealApproval === true, mandated: req.mandated === true, grantsBeyondCall: answerGrantsBeyondCall(req) }) ? {} : { rememberRefused: true }),
1091
1102
  ...(mandateWord !== undefined ? { mandate: mandateWord } : {}),
1092
1103
  ...(originWord !== undefined ? { origin: originWord } : {}),
1093
1104
  ...(askClassPair ?? {}),
@@ -1099,6 +1110,7 @@ export class ToolApprovalCoordinator {
1099
1110
  ...((f) => (f !== undefined ? { denialLimitFallback: f } : {}))(readDenialLimitFallback(req)),
1100
1111
  ...((g) => (g !== undefined ? { readRootCandidate: g } : {}))(readReadRootCandidate(req)),
1101
1112
  ...((c) => (c !== undefined ? { probeCause: c } : {}))(readProbeCause(req)),
1113
+ ...((r) => (r !== undefined ? { probeReason: r } : {}))(readProbeReason(req)),
1102
1114
  ...((c) => (c !== undefined ? { ruleEvidence: c } : {}))(readRuleEvidence(req)),
1103
1115
  ...(bounded.omitted ? { argsOmitted: true } : { args: bounded.args }),
1104
1116
  ...(bounded.hasBidi || req.hasBidiControls === true ? { inputHasBidi: true } : {}),
@@ -1613,6 +1625,20 @@ export class ToolApprovalCoordinator {
1613
1625
  return shapeOutcome(await allowedP);
1614
1626
  }
1615
1627
  }
1628
+ const rememberRefused = !standingAnswerMayRetire(pendingEntryStandingAnswerFacts({
1629
+ ...(effectiveGovernanceForced ? { governanceForced: true } : {}),
1630
+ ...(req.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
1631
+ ...(req.mandated === true ? { mandated: true } : {}),
1632
+ toolName: req.toolName,
1633
+ }));
1634
+ if (rememberRefused)
1635
+ frame.rememberRefused = true;
1636
+ else
1637
+ delete frame.rememberRefused;
1638
+ if (cardForFrame !== undefined && (cardForFrame.rememberRefused === true) !== rememberRefused) {
1639
+ const { rememberRefused: _stale, ...cardRest } = cardForFrame;
1640
+ cardForFrame = rememberRefused ? { ...cardRest, rememberRefused: true } : cardRest;
1641
+ }
1616
1642
  const pendingEntry = {
1617
1643
  settle,
1618
1644
  forceParkNow,
@@ -2211,11 +2237,7 @@ export class ToolApprovalCoordinator {
2211
2237
  }
2212
2238
  finishRespond(id, entry, parsed, opts) {
2213
2239
  let rememberApplied;
2214
- const grantable = standingAnswerMayRetire({
2215
- governanceForced: entry.governanceForced === true,
2216
- requiresRealApproval: entry.requiresRealApproval === true,
2217
- mandated: entry.mandated === true,
2218
- });
2240
+ const grantable = standingAnswerMayRetire(pendingEntryStandingAnswerFacts(entry));
2219
2241
  if (parsed.value === "allow_session")
2220
2242
  rememberApplied = Boolean(entry.sessionId) && grantable;
2221
2243
  if (parsed.value === "allow_session" && entry.sessionId && grantable) {
@@ -2259,6 +2281,7 @@ export class ToolApprovalCoordinator {
2259
2281
  ...(e.requiresRealApproval ? { requiresRealApproval: true } : {}),
2260
2282
  ...(e.governanceForced ? { governanceForced: true } : {}),
2261
2283
  ...(e.mandated ? { mandated: true } : {}),
2284
+ ...(standingAnswerMayRetire(pendingEntryStandingAnswerFacts(e)) ? {} : { rememberRefused: true }),
2262
2285
  ...(e.mandate !== undefined ? { mandate: e.mandate } : {}),
2263
2286
  ...(e.origin !== undefined ? { origin: e.origin } : {}),
2264
2287
  ...(e.originTaskId !== undefined ? { fromSubagent: true, originTaskId: e.originTaskId } : {}),
@@ -43,11 +43,11 @@ type _GuardBgNotifReverse = AssertAllKeysHandled<Exclude<BgNotifProjected | BgNo
43
43
  type RosterProjected = "name" | "agentId" | "sessionId" | "toolUseId" | "owner" | "scope" | "sessionScoped" | "rootSessionId" | "model" | "modelFallback" | "createdAt";
44
44
  type _GuardRoster = AssertAllKeysHandled<Exclude<keyof RosterEntry, RosterProjected>>;
45
45
  type _GuardRosterReverse = AssertAllKeysHandled<Exclude<RosterProjected, keyof RosterEntry>>;
46
- type AskProjected = "toolName" | "toolCallId" | "args" | "message" | "sourceTaskId" | "fromSubagent" | "sourceAgentName" | "delegation" | "ruleOffers" | "ruleOffersAbsence" | "persistedRuleShadowed" | "probeCause" | "ruleEvidence" | "requiresRealApproval" | "denialLimitFallback" | "origin" | "readRootCandidate" | "mandated" | "mandate" | "askClass" | "classes";
47
- type AskExcluded = "preview" | "principal" | "riskAxes" | "boundInputHash" | "isDelegatedChild" | "probeReason" | "hasBidiControls" | "previewWithheld" | "execCwd" | "authoredBy";
46
+ type AskProjected = "toolName" | "toolCallId" | "args" | "message" | "sourceTaskId" | "fromSubagent" | "sourceAgentName" | "delegation" | "ruleOffers" | "ruleOffersAbsence" | "persistedRuleShadowed" | "probeCause" | "probeReason" | "ruleEvidence" | "requiresRealApproval" | "denialLimitFallback" | "origin" | "readRootCandidate" | "mandated" | "mandate" | "askClass" | "classes";
47
+ type AskExcluded = "preview" | "principal" | "riskAxes" | "boundInputHash" | "isDelegatedChild" | "hasBidiControls" | "previewWithheld" | "execCwd" | "authoredBy";
48
48
  type _GuardAsk = AssertAllKeysHandled<Exclude<keyof AskRequest, AskProjected | AskExcluded>>;
49
49
  type _GuardAskReverse = AssertAllKeysHandled<Exclude<AskProjected | AskExcluded, keyof AskRequest>>;
50
- type AskFrameServerMinted = "type" | "approvalId" | "argsOmitted" | "outcome" | "governanceForced" | "expiresAtMs" | "expiresInMs" | "serverNowMs" | "parked" | "inputHasBidi";
50
+ type AskFrameServerMinted = "type" | "approvalId" | "argsOmitted" | "outcome" | "governanceForced" | "expiresAtMs" | "expiresInMs" | "serverNowMs" | "parked" | "inputHasBidi" | "rememberRefused";
51
51
  type _GuardAskFrame = AssertAllKeysHandled<Exclude<keyof ToolApprovalFrame, AskProjected | AskFrameServerMinted>>;
52
52
  type _GuardAskFrameReverse = AssertAllKeysHandled<Exclude<AskProjected | AskFrameServerMinted, keyof ToolApprovalFrame>>;
53
53
  type TaskEventHandled = "text_delta" | "reasoning_delta" | "tool_start" | "tool_end" | "turn_end" | "compacted" | "diagnostics" | "message_committed" | "status" | "task_notification" | "task_progress" | "steering_injected" | "workspace_changed" | "done" | "context_usage" | "compaction_outcome" | "human_input" | "wiring_manifest" | "text_end" | "tool_roster_delta" | "tool_progress" | "tool_disclosure";
@@ -88,10 +88,13 @@ type _GuardWiringSessionReverse = AssertAllKeysHandled<Exclude<WiringSessionHand
88
88
  type WiringToolsHandled = "schemaVersion" | "count" | "entries" | "digest";
89
89
  type _GuardWiringTools = AssertAllKeysHandled<Exclude<keyof WiringSection<"tools">, WiringToolsHandled>>;
90
90
  type _GuardWiringToolsReverse = AssertAllKeysHandled<Exclude<WiringToolsHandled, keyof WiringSection<"tools">>>;
91
- type FleetExcluded = "subagentTranscripts";
92
- type WiringFleetHandled = "backgroundAgentStore" | "hostChildEventSink" | FleetExcluded;
91
+ type WiringFleetHandled = "backgroundAgentStore" | "hostChildEventSink" | "subagentTranscripts";
93
92
  type _GuardWiringFleet = AssertAllKeysHandled<Exclude<keyof WiringSection<"fleet">, WiringFleetHandled>>;
94
93
  type _GuardWiringFleetReverse = AssertAllKeysHandled<Exclude<WiringFleetHandled, keyof WiringSection<"fleet">>>;
94
+ type SubagentTranscriptTier = WiringSection<"fleet">["subagentTranscripts"];
95
+ type SubagentTranscriptTierWords = "none" | "rows" | "full";
96
+ type _GuardSubagentTranscriptTierWords = AssertAllKeysHandled<Exclude<SubagentTranscriptTier, SubagentTranscriptTierWords>>;
97
+ type _GuardSubagentTranscriptTierWordsReverse = AssertAllKeysHandled<Exclude<SubagentTranscriptTierWords, SubagentTranscriptTier>>;
95
98
  type WiringMcpEntry = WiringSection<"mcp">[number];
96
99
  type WiringMcpExcluded = "error";
97
100
  type WiringMcpHandled = "name" | "source" | "status" | "errorCode" | "delivered" | "httpStatus" | "toolCount" | "liveness" | WiringMcpExcluded;
@@ -126,7 +129,7 @@ type WiringGovernanceHandled = "audience" | "lockedConfig" | "compliance" | "mem
126
129
  type _GuardWiringGovernance = AssertAllKeysHandled<Exclude<keyof WiringSection<"governance">, WiringGovernanceHandled>>;
127
130
  type _GuardWiringGovernanceReverse = AssertAllKeysHandled<Exclude<WiringGovernanceHandled, keyof WiringSection<"governance">>>;
128
131
  type PermissionTraceKind = Extract<TraceEvent["kind"], `permission.${string}`>;
129
- export type PermissionTraceProjected = "permission.persisted_rule_allowed" | "permission.rule_store_unreadable" | "permission.read_only_allowed";
132
+ export type PermissionTraceProjected = "permission.persisted_rule_allowed" | "permission.rule_store_unreadable" | "permission.read_only_allowed" | "permission.skill_allowed";
130
133
  type PermissionTraceDropped = "permission.sandbox_admitted" | "permission.rule_sync_resurrected" | "permission.rule_sync_dropped" | "permission.org_snapshot_unavailable";
131
134
  type _GuardPermissionTrace = AssertAllKeysHandled<Exclude<PermissionTraceKind, PermissionTraceProjected | PermissionTraceDropped>>;
132
135
  type _GuardPermissionTraceReverse = AssertAllKeysHandled<Exclude<PermissionTraceProjected | PermissionTraceDropped, PermissionTraceKind>>;
@@ -141,11 +144,11 @@ type ParkArmKeys = keyof Extract<PendingAction, {
141
144
  kind: "tool_approval";
142
145
  }>;
143
146
  type ParkArmQueueProjected = "toolName" | "toolCallId" | "boundInputHash" | "ruleOffers" | "hasBidiControls" | keyof import("../approval-card.js").ParkRowFacts;
144
- type ParkArmQueueExcluded = "kind" | "args" | "batchToolCallIds" | "completedCallIds" | "capabilityId" | "contractId" | "shapeDigest" | "implementationRevision" | "rosterDigest" | "family" | "pathTarget" | "names" | "preview" | "previewWithheld" | "execCwd" | "denialLimitFallback" | "authoredBy";
147
+ type ParkArmQueueExcluded = "kind" | "args" | "batchToolCallIds" | "completedCallIds" | "capabilityId" | "contractId" | "shapeDigest" | "implementationRevision" | "rosterDigest" | "family" | "pathTarget" | "names" | "preview" | "previewWithheld" | "execCwd" | "denialLimitFallback" | "authoredBy" | "boundGrantDigest";
145
148
  type _GuardParkArmQueue = AssertAllKeysHandled<Exclude<ParkArmKeys, ParkArmQueueProjected | ParkArmQueueExcluded>>;
146
149
  type _GuardParkArmQueueReverse = AssertAllKeysHandled<Exclude<ParkArmQueueProjected | ParkArmQueueExcluded, ParkArmKeys>>;
147
150
  type _NoDoubleBookParkArmQueue = AssertAllKeysHandled<Extract<ParkArmQueueProjected, ParkArmQueueExcluded>>;
148
- type TaskSpecWired = "objective" | "principal" | "images" | "sessionId" | "requireExistingSession" | "oneShot" | "clientContext" | "resumeAt" | "resumeAtMode" | "model" | "compactionModel" | "thinking" | "systemPrompt" | "appendSystemPrompt" | "promptProvider" | "memory" | "suggestNextPrompts" | "restoreFiles" | "acceptPartialRestore" | "rewindFilesTo" | "tools" | "excludeTools" | "deferTools" | "toolMaterializeStrategy" | "promptProfile" | "toolPolicy" | "onAsk" | "onQuestion" | "hooks" | "durableApproval" | "resourceSuspend" | "resilience" | "finalVerification" | "checkpointStore" | "handsReadOnly" | "memoryPersistenceCapable" | "additionalDirectories" | "additionalReadDirectories" | "enablePlanMode" | "interactiveTools" | "enableFork" | "shellGate" | "writeFace" | "mcp" | "a2a" | "skills" | "backgroundScope" | "envFacts" | "limits" | "configOverrides" | "forwardSubagentEvents" | "retainSubagentSessions" | "retainBackgroundProcesses" | "outputSchema" | "outputRetries" | "compaction" | "attachments" | "selfOrchestration" | "getApiKeyAndHeaders" | "interactionPosture" | "agents" | "permissionMode" | "taskId" | "signal" | "preemptSignal" | "modelRole" | "enableBlockedReport" | "maxSuspends" | "skillListingBudgetFraction" | "skillListingMaxDescChars" | "excludeAllTools" | "fileHistory";
151
+ type TaskSpecWired = "objective" | "principal" | "images" | "sessionId" | "requireExistingSession" | "oneShot" | "clientContext" | "resumeAt" | "resumeAtMode" | "model" | "compactionModel" | "thinking" | "systemPrompt" | "appendSystemPrompt" | "promptProvider" | "memory" | "suggestNextPrompts" | "restoreFiles" | "acceptPartialRestore" | "rewindFilesTo" | "tools" | "excludeTools" | "deferTools" | "toolMaterializeStrategy" | "promptProfile" | "toolPolicy" | "onAsk" | "onQuestion" | "hooks" | "durableApproval" | "resourceSuspend" | "resilience" | "finalVerification" | "checkpointStore" | "handsReadOnly" | "memoryPersistenceCapable" | "additionalDirectories" | "additionalReadDirectories" | "enablePlanMode" | "interactiveTools" | "enableFork" | "shellGate" | "writeFace" | "mcp" | "a2a" | "skills" | "backgroundScope" | "envFacts" | "limits" | "configOverrides" | "forwardSubagentEvents" | "retainSubagentSessions" | "retainBackgroundProcesses" | "outputSchema" | "outputRetries" | "compaction" | "attachments" | "selfOrchestration" | "getApiKeyAndHeaders" | "interactionPosture" | "agents" | "permissionMode" | "taskId" | "signal" | "preemptSignal" | "modelRole" | "enableBlockedReport" | "maxSuspends" | "skillListingBudgetFraction" | "skillListingMaxDescChars" | "excludeAllTools" | "fileHistory" | "userInvokedSkills";
149
152
  type TaskSpecUnwired = "actor" | "roles" | "lspManager" | "tracer" | "readFace" | "readDenyPatterns" | "interactiveQuestionFallback" | "alwaysLoadTools" | "restoreGatedTools" | "deferSelfResolve" | "basePolicyForResumeEdit" | "lspDiagnostics" | "streamingToolExecution" | "toolResultThresholdChars" | "peerPresence" | "outputStyle";
150
153
  type _GuardTaskSpec = AssertAllKeysHandled<Exclude<keyof TaskSpec, TaskSpecWired | TaskSpecUnwired>>;
151
154
  type _GuardTaskSpecReverse = AssertAllKeysHandled<Exclude<TaskSpecWired | TaskSpecUnwired, keyof TaskSpec>>;
@@ -288,6 +291,8 @@ export type CoreKeysetGuards = [
288
291
  _GuardWiringToolsReverse,
289
292
  _GuardWiringFleet,
290
293
  _GuardWiringFleetReverse,
294
+ _GuardSubagentTranscriptTierWords,
295
+ _GuardSubagentTranscriptTierWordsReverse,
291
296
  _GuardWiringMcp,
292
297
  _GuardWiringMcpReverse,
293
298
  _GuardWiringRules,
@@ -499,9 +499,18 @@ export function wiringManifestEventData(ev, instructions) {
499
499
  ...(Array.isArray(parkIn.reasons) ? { reasons: parkIn.reasons.filter((r) => typeof r === "string") } : {}),
500
500
  ...(str(parkIn.checkpointDurability) !== undefined ? { checkpointDurability: parkIn.checkpointDurability } : {}),
501
501
  });
502
+ const subagentTranscripts = (() => {
503
+ if (fleetIn.subagentTranscripts === undefined)
504
+ return undefined;
505
+ const word = str(fleetIn.subagentTranscripts);
506
+ if (word === undefined)
507
+ return dropProjectedFace("wiring_manifest.fleet.subagentTranscripts is not a non-empty string");
508
+ return word;
509
+ })();
502
510
  const fleet = nonEmpty({
503
511
  ...(bool(fleetIn.backgroundAgentStore) !== undefined ? { backgroundAgentStore: fleetIn.backgroundAgentStore } : {}),
504
512
  ...(bool(fleetIn.hostChildEventSink) !== undefined ? { hostChildEventSink: fleetIn.hostChildEventSink } : {}),
513
+ ...(subagentTranscripts !== undefined ? { subagentTranscripts } : {}),
505
514
  });
506
515
  const permissionRules = nonEmpty({
507
516
  ...(bool(rulesIn.storeWired) !== undefined ? { storeWired: rulesIn.storeWired } : {}),
@@ -113,6 +113,48 @@ export declare const SELF_MINTED_WIRE: {
113
113
  };
114
114
  };
115
115
  };
116
+ /**
117
+ * 审批问的**无人决终态归因**:`POST /v1/tasks/:taskId/asks/:askId/decision` 410 `gone.ask` 体的 `deniedReason`(问账行 `denied_reason` 列原值)。
118
+ * 词表属主 = `approval-deny-reasons.ts` 的 `DENY_REASONS` ∪ `VOID_REASONS`(`satisfies Record<DenyReason | VoidReason, …>`:表加词而本行没跟 ⇒ tsc 红)。
119
+ * 7.116.0 +2:启动收敛(承载它的进程没了)的两个词 —— 与 `crashConvergedCause` 同词(同一问在两个读面上一个成因)。
120
+ */
121
+ readonly askTerminalReason: {
122
+ readonly at: "POST /v1/tasks/:taskId/asks/:askId/decision 410 gone.ask deniedReason";
123
+ readonly members: {
124
+ routing_failure_fail_closed: {
125
+ attr: string;
126
+ since: string;
127
+ };
128
+ run_cancelled: {
129
+ attr: string;
130
+ since: string;
131
+ };
132
+ batch_aborted: {
133
+ attr: string;
134
+ since: string;
135
+ };
136
+ batch_bound_elsewhere: {
137
+ attr: string;
138
+ since: string;
139
+ };
140
+ adhoc_leg_no_durable_domain: {
141
+ attr: string;
142
+ since: string;
143
+ };
144
+ orphan_ttl_exceeded: {
145
+ attr: string;
146
+ since: string;
147
+ };
148
+ crashed_before_park: {
149
+ attr: string;
150
+ since: string;
151
+ };
152
+ shutdown_before_park: {
153
+ attr: string;
154
+ since: string;
155
+ };
156
+ };
157
+ };
116
158
  /** 本仓在出口对 core 值做的变换(值变、键不变)。 */
117
159
  readonly wireValueRule: {
118
160
  readonly at: "出口值变换(<端点>#<键> → 变换)";
@@ -40,6 +40,19 @@ export const SELF_MINTED_WIRE = {
40
40
  "internal.memory_control_plane_corrupt": { attr: 500, since: "7.108.0" },
41
41
  },
42
42
  },
43
+ askTerminalReason: {
44
+ at: "POST /v1/tasks/:taskId/asks/:askId/decision 410 gone.ask deniedReason",
45
+ members: {
46
+ routing_failure_fail_closed: { attr: "-", since: "7.3.0" },
47
+ run_cancelled: { attr: "-", since: "7.3.0" },
48
+ batch_aborted: { attr: "-", since: "7.3.0" },
49
+ batch_bound_elsewhere: { attr: "-", since: "7.3.0" },
50
+ adhoc_leg_no_durable_domain: { attr: "-", since: "7.3.0" },
51
+ orphan_ttl_exceeded: { attr: "-", since: "7.3.0" },
52
+ crashed_before_park: { attr: "-", since: "7.116.0" },
53
+ shutdown_before_park: { attr: "-", since: "7.116.0" },
54
+ },
55
+ },
43
56
  wireValueRule: {
44
57
  at: "出口值变换(<端点>#<键> → 变换)",
45
58
  members: {