@sema-agent/server 7.69.0 → 7.70.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 (51) hide show
  1. package/README.md +3 -0
  2. package/README.zh-CN.md +3 -0
  3. package/USAGE.md +103 -7
  4. package/dist/approval-ask-machine.d.ts +1 -1
  5. package/dist/approval-card.d.ts +3 -2
  6. package/dist/approval-reconciler.d.ts +0 -54
  7. package/dist/approval-reconciler.js +2 -2
  8. package/dist/boot/parked-revive-gate.js +10 -9
  9. package/dist/boot/runner-deps.d.ts +35 -4
  10. package/dist/boot/runner-deps.js +2 -6
  11. package/dist/budget.js +3 -2
  12. package/dist/config-catalog.d.ts +31 -2
  13. package/dist/config-catalog.js +41 -23
  14. package/dist/http/routes/approvals-assistant.js +3 -12
  15. package/dist/http/routes/diagnostics.js +4 -0
  16. package/dist/http/routes/runs.js +2 -3
  17. package/dist/http/routes/tasks.js +22 -18
  18. package/dist/http/server.js +4 -2
  19. package/dist/main.js +2 -2
  20. package/dist/observability/metrics.d.ts +0 -6
  21. package/dist/observability/metrics.js +3 -1
  22. package/dist/observability/permission-rule-events.d.ts +20 -0
  23. package/dist/observability/permission-rule-events.js +28 -0
  24. package/dist/observability/run-terminal-log.d.ts +9 -19
  25. package/dist/observability/run-terminal-log.js +183 -32
  26. package/dist/observability/tool-trace.d.ts +12 -0
  27. package/dist/observability/tool-trace.js +5 -1
  28. package/dist/plugins/store-backend.d.ts +2 -1
  29. package/dist/plugins/workflow-journal-store-sql.d.ts +1 -1
  30. package/dist/plugins/workflow-journal-store-sql.js +3 -10
  31. package/dist/posture-source.d.ts +23 -3
  32. package/dist/posture-source.js +1 -1
  33. package/dist/read-face-posture.d.ts +8 -1
  34. package/dist/read-face-posture.js +1 -0
  35. package/dist/run-cancel-context.d.ts +50 -1
  36. package/dist/run-cancel-context.js +22 -0
  37. package/dist/run-local.d.ts +14 -6
  38. package/dist/run-local.js +1 -1
  39. package/dist/runs.d.ts +0 -51
  40. package/dist/runs.js +40 -25
  41. package/dist/tool-approval.d.ts +14 -8
  42. package/dist/tool-approval.js +3 -3
  43. package/dist/trace/core-keyset-guard.d.ts +7 -3
  44. package/dist/trace/ledger-events.d.ts +17 -1
  45. package/dist/trace/ledger-events.js +6 -1
  46. package/dist/trace/ledger-sink.d.ts +10 -8
  47. package/dist/trace/ledger-sink.js +2 -1
  48. package/dist/trace/project.d.ts +3 -2
  49. package/dist/trace/redact.d.ts +28 -0
  50. package/dist/trace/redact.js +50 -27
  51. package/package.json +3 -3
@@ -1,5 +1,6 @@
1
1
  import { CROSS_SESSION_INBOUND_SETTINGS, CROSS_SESSION_DIALOG_EXPIRY_VALUES } from "@sema-agent/core";
2
2
  import { THINKING_FORMATS } from "./model-compat.js";
3
+ import { webSearchConfigFromEnv } from "./plugins/web-search.js";
3
4
  import { CENTER_READ_FACE_LIVE_KEYS } from "./config-center/read-face.js";
4
5
  export const CATALOG_VALUE_TYPES = ["boolean", "number", "string", "enum", "csv", "json", "path", "url", "secret", "opaque"];
5
6
  export const CATALOG_TIMINGS = ["hot", "restart", "deploy", "inert"];
@@ -83,7 +84,7 @@ export const CONFIG_CATALOG = [
83
84
  r("BG_AGENT_STALE_RUNNING_MS", "orchestration", "number", "running 后台 agent 行写者心跳断更超此值翻 failed(ms;下限 10min 硬钳)", { staticDefault: String(15 * 60_000), configKey: "backgroundAgentStaleRunningMs" }),
84
85
  r("BIND_HOST", "auth", "string", "监听绑址(显式值恒生效)", { danger: SEC, derivedDefaultNote: "缺席:无鉴权写面(allowUnauthedWrites ∧ 零凭证)自动收窄 127.0.0.1,其余全接口", resolve: (c) => c.bindHost ?? null }),
85
86
  r("COMPLIANCE_ADDITIONAL_DENIES", "auth", "csv", "合规档位之上额外拒绝的能力名(只能收紧;词表=core 闭集,拼错拒启)", { danger: SEC }),
86
- r("COMPLIANCE_PROFILE", "auth", "enum", "本部署合规档位(core compliancePostureResolver 供数;拼错拒启)", { danger: SEC, enumValues: ["hipaa", "zdr"] }),
87
+ r("COMPLIANCE_PROFILE", "auth", "enum", "本部署合规档位(core compliancePostureResolver 供数;拼错拒启)", { danger: SEC, enumValues: ["hipaa", "zdr"], resolve: (c) => c.compliancePosture?.profile ?? null }),
87
88
  r("CONFIG_BOOT_FETCH_BUDGET_MS", "integrations", "number", "boot 首拉配置的抢答窗(ms;过窗以 env/兜底起服,拉取转后台热应用)", { staticDefault: "1500", configKey: "configBootFetchBudgetMs" }),
88
89
  r("CONFIG_CATALOG_ENABLED", "auth", "boolean", "本目录端点 GET /v1/config/catalog 的旋钮(operator-only 面;合规部署可关 ⇒ 路径 404 + 能力位翻假)", { staticDefault: "true", danger: SEC, configKey: "configCatalogEnabled" }),
89
90
  r("CONFIG_LKG_DURABLE", "integrations", "boolean", "LKG 跨重启 handoff(严格 'true' 才开:重启先用 LKG 起服,tier 候选借此落地)", { staticDefault: "false" }),
@@ -200,7 +201,7 @@ export const CONFIG_CATALOG = [
200
201
  r("LOG_LEVEL", "observability", "string", "日志档位", { staticDefault: "info", configKey: "logLevel" }),
201
202
  r("LSP_ENABLED", "orchestration", "boolean", "LSP 沙箱腿 opt-in(需烤好的 LSP 模板;host 腿另有 LSP_HOST_ENABLED)", { staticDefault: "false", configKey: "lspEnabled" }),
202
203
  r("LSP_HOST_ENABLED", "orchestration", "boolean", "LSP host 腿开关(本机 language server,优雅降级)", { staticDefault: "true", configKey: "lspHostEnabled" }),
203
- r("MANUAL_MODE_SHELL_GATE", "approval", "enum", "手动模式 shell 门收紧腿(off=no-op 非放松 —— 本键只有收紧半场;拼错拒启)", { danger: SEC, enumValues: ["always", "classify", "off"] }),
204
+ r("MANUAL_MODE_SHELL_GATE", "approval", "enum", "手动模式 shell 门收紧腿(off=no-op 非放松 —— 本键只有收紧半场;拼错拒启)", { danger: SEC, enumValues: ["always", "classify", "off"], resolve: (c) => c.manualModeShellGate ?? null, derivedDefaultNote: "off/未设 ⇒ 本键不贡献任何东西(归一成键缺席)⇒ 回显 null;真姿态只有 always/classify 两个" }),
204
205
  r("MAX_PRINCIPAL_COST_USD", "limitsHttp", "number", "per-principal 窗内成本天花板(0=关)", { staticDefault: "0", danger: BILL, configKey: "maxPrincipalCostUsd", center: cLimits("maxPrincipalCostUsd") }),
205
206
  r("MAX_TASK_COST_USD", "limitsHttp", "number", "单任务成本天花板(0=关)", { staticDefault: "0", danger: BILL, configKey: "maxTaskCostUsd", center: cLimits("maxTaskCostUsd") }),
206
207
  r("MAX_TASK_TOKENS", "limitsHttp", "number", "单任务 token 天花板(per-slice 窗;0=关)", { staticDefault: "0", danger: BILL, configKey: "maxTaskTokens", center: cLimits("maxTaskTokens") }),
@@ -210,7 +211,7 @@ export const CONFIG_CATALOG = [
210
211
  r("MCP_ELICITATION_MIN_INTERVAL_MS", "approval", "number", "elicitation per-server 最小间隔(ms)", { derivedDefaultNote: "缺席 ⇒ core 出厂窗" }),
211
212
  r("MCP_ELICITATION_TTL_MS", "approval", "number", "elicitation 无人应答释放窗(ms)", { derivedDefaultNote: "缺席 ⇒ core 出厂窗" }),
212
213
  r("MEMORY_CAPTURE_POLICY", "memory", "enum", "记忆 capture 姿态(open=声明按面值;governed=按 per-principal verdict;capture-required=恒采)", { enumValues: ["open", "governed", "capture-required"], staticDefault: "open", danger: SEC, configKey: "memoryCapturePolicy" }),
213
- r("MEMORY_CONSOLIDATION_DRIVER", "memory", "enum", "记忆折叠阀门(off=零装配零探针,两 admin 口 404 族)", { enumValues: ["on", "off"], staticDefault: "off", danger: BILL }),
214
+ r("MEMORY_CONSOLIDATION_DRIVER", "memory", "enum", "记忆折叠阀门(off=零装配零探针,两 admin 口 404 族)", { enumValues: ["on", "off"], staticDefault: "off", danger: BILL, resolve: (c) => c.memoryConsolidationDriver.enabled, booleanEnum: { true: "on", false: "off" } }),
214
215
  r("MEMORY_CONSOLIDATION_INTERVAL_SEC", "memory", "number", "折叠周期腿节律(秒;本 build 任何正值拒启 —— 周期腿未落地,恒 0)", { staticDefault: "0" }),
215
216
  r("MEMORY_CONSOLIDATION_SCOPES", "memory", "csv", "折叠阀门 scope 表(恰一条时=admin 口缺省;不是授权边界)"),
216
217
  r("MEMORY_DELEGATION_EVIDENCE", "memory", "enum", "委派臂污染标记的证据标准", { enumValues: ["static-face", "attested-only"], staticDefault: "static-face", danger: SEC, configKey: "memoryDelegationEvidence" }),
@@ -219,7 +220,7 @@ export const CONFIG_CATALOG = [
219
220
  r("MEMORY_EMBEDDER_ENDPOINT", "memory", "url", "OpenAI 兼容 /embeddings 端点(pg 记忆后端的向量面)"),
220
221
  r("MEMORY_EMBEDDER_MODEL", "memory", "string", "embeddings 模型名"),
221
222
  r("MEMORY_EMBEDDER_TIMEOUT_MS", "memory", "number", "单次 embed 活性上限(ms)", { staticDefault: "30000" }),
222
- r("MEMORY_ENGINE", "memory", "enum", "记忆引擎总开关(注入式文件记忆面;双族词表,未知词拒启)", { enumValues: ["on", "off"], staticDefault: "on" }),
223
+ r("MEMORY_ENGINE", "memory", "enum", "记忆引擎总开关(注入式文件记忆面;双族词表,未知词拒启)", { enumValues: ["on", "off"], staticDefault: "on", configKey: "memoryEngineEnabled", booleanEnum: { true: "on", false: "off" } }),
223
224
  r("MEMORY_ENGINE_BACKEND", "memory", "enum", "记忆持久面方言(file=现状;pg/tidb=DB 真身,多租户点亮的唯一门)", { enumValues: ["file", "pg", "tidb"], staticDefault: "file", danger: DATA, configKey: "memoryEngineBackend" }),
224
225
  r("MEMORY_ENGINE_DIR", "memory", "path", "记忆引擎根覆写", { derivedDefaultNote: "缺席 ⇒ core 默认链(AGENT_DATA_DIR → ~/.ai-agent)", configKey: "memoryEngineDir" }),
225
226
  r("MEMORY_ENGINE_REMOTE_LANE", "memory", "enum", "远端执行车道上放行 file 记忆引擎的 plane-split 覆写", { enumValues: ["allow"] }),
@@ -227,7 +228,7 @@ export const CONFIG_CATALOG = [
227
228
  r("MEMORY_ORG_DIRECTORY_JSON", "memory", "opaque", "单机形 org 目录原文(principal→org:scope 授权表;解析期整段校验)", { danger: SEC }),
228
229
  r("MEMORY_ORG_GRANT_TTL_MS", "memory", "number", "org 目录判决缓存 TTL(ms;准入判决滞后上限)", { staticDefault: "60000", configKey: "memoryOrgGrantTtlMs" }),
229
230
  r("MEMORY_ORG_UNAVAILABLE_BACKOFF_MS", "memory", "number", "org 目录取数失败退避窗(ms)", { staticDefault: "10000", configKey: "memoryOrgUnavailableBackoffMs" }),
230
- r("MEMORY_PERSISTENCE_CAPABLE", "memory", "enum", "本部署能否把「记住 X」落到持久处的自我声明(三态;未知词拒启)", { enumValues: ["on", "off"], derivedDefaultNote: "缺席 ⇒ 键不上 TaskSpec,core 按 roster 推断" }),
231
+ r("MEMORY_PERSISTENCE_CAPABLE", "memory", "enum", "本部署能否把「记住 X」落到持久处的自我声明(三态;未知词拒启)", { enumValues: ["on", "off"], derivedDefaultNote: "缺席 ⇒ 键不上 TaskSpec,core 按 roster 推断", configKey: "memoryPersistenceCapable", booleanEnum: { true: "on", false: "off" } }),
231
232
  r("MEMORY_PROVENANCE", "memory", "enum", "已暴露会话的记忆写处置(carry=带 origin 标记照写;off=关)", { enumValues: ["off", "carry"], staticDefault: "carry", danger: SEC, configKey: "memoryProvenance" }),
232
233
  r("MEMORY_SCOPE", "memory", "string", "单用户部署的记忆 scope 名", { derivedDefaultNote: "缺席:引擎开 ∧ 单用户 ⇒ local;其余 ⇒ 缺席(记忆暗)", configKey: "memoryScope" }),
233
234
  r("MEMORY_SYNC_MAX_PULL_ENTRIES", "memory", "number", "TOC 同步单轮 pull 条数帽(设了必须 ≥1 整数)", { derivedDefaultNote: "缺席 ⇒ 键缺席,core 默认" }),
@@ -351,12 +352,12 @@ export const CONFIG_CATALOG = [
351
352
  r("RESOURCE_SUSPEND_TOTAL_TOKENS", "approval", "number", "跨片 token 总额(0=不设;与 MAX_TASK_TOKENS 的 per-slice 窗是两根轴)", { staticDefault: "0", danger: BILL, configKey: "resourceSuspendTotalTokens" }),
352
353
  r("RESOURCE_SUSPEND_TTL_SEC", "approval", "number", "resource/preempt checkpoint 的弃置 GC TTL(秒;0 ⇒ core 默认 30d)", { staticDefault: "0", danger: DATA, configKey: "resourceSuspendTtlSec" }),
353
354
  r("RETENTION_MAX_AGE_DAYS", "auth", "number", "托管留存期声明(core 启动期能力校验;真删除归 sweep lane)", { danger: DATA }),
354
- r("RETENTION_MODE", "auth", "enum", "留存 sweep 灰度档(拼错拒启 —— audit-only 谎成 enforce 两个方向都致命)", { enumValues: ["audit-only", "enforce"], staticDefault: "audit-only", danger: DATA }),
355
+ r("RETENTION_MODE", "auth", "enum", "留存 sweep 灰度档(拼错拒启 —— audit-only 谎成 enforce 两个方向都致命)", { enumValues: ["audit-only", "enforce"], staticDefault: "audit-only", danger: DATA, resolve: (c) => c.retentionSweep.mode }),
355
356
  r("RETENTION_SWEEP_INTERVAL_SEC", "auth", "number", "留存 sweep 节律(秒;0=lane 关)", { staticDefault: "0", danger: DATA }),
356
357
  r("REWIND_SNAPSHOT_MAX_MB", "store", "number", "已退役(S-15 整树快照纪元退役):设了只 boot warn 一次,零行为", { envTiming: "inert" }),
357
358
  r("ROSTER_RETENTION_MS", "orchestration", "number", "agent_roster 行 TTL(ms;SQL twins 扩展面)", { staticDefault: String(30 * 24 * 60 * 60 * 1000), danger: DATA, configKey: "rosterRetentionMs" }),
358
359
  r("ROUTER_ENABLED", "orchestration", "boolean", "A 价值路由(per-task 编排自动决策;ship-dark)", { staticDefault: "false", configKey: "routerEnabled" }),
359
- r("RUN_STORE_STRICT_HYDRATE", "store", "enum", "file run-store 列不出 runs/ 时拒启的 fail-closed 门(缺省带病起动+响亮披露)", { enumValues: ["on", "off"], staticDefault: "off", danger: DATA, configKey: "runStoreStrictHydrate" }),
360
+ r("RUN_STORE_STRICT_HYDRATE", "store", "enum", "file run-store 列不出 runs/ 时拒启的 fail-closed 门(缺省带病起动+响亮披露)", { enumValues: ["on", "off"], staticDefault: "off", danger: DATA, configKey: "runStoreStrictHydrate", booleanEnum: { true: "on", false: "off" } }),
360
361
  r("S3_ENDPOINT", "store", "url", "外接 S3 兼容位(单独在场即开公链签发,兼作公网面)", { danger: DATA }),
361
362
  r("S3_PUBLIC_BUCKET", "store", "string", "SendUserFile 公链桶", { derivedDefaultNote: "缺席 ⇒ sema-public" }),
362
363
  r("S3_PUBLIC_ENDPOINT", "store", "url", "SendUserFile 公网面 endpoint(跨仓契约名,禁改)"),
@@ -441,7 +442,7 @@ export const CONFIG_CATALOG = [
441
442
  r("WEB_SEARCH_ENDPOINT", "orchestration", "url", "web search 后端 endpoint 覆写(searxng 自建面)"),
442
443
  r("WEB_SEARCH_MAX_RESULTS", "orchestration", "number", "web search 单次结果数上限", { derivedDefaultNote: "缺席/非正 ⇒ backend 默认" }),
443
444
  r("WEB_SEARCH_PROBE_ON_BOOT", "orchestration", "boolean", "boot 期 web search 连通性探测(只认 true/1;含糊值当没开——猜错方向是凭空出网)", { staticDefault: "false" }),
444
- r("WEB_SEARCH_PROVIDER", "orchestration", "enum", "web search 工具后端(设合法词=装配 WebSearch;其它/缺席=不装配)", { enumValues: ["brave", "tavily", "searxng"], danger: SEC }),
445
+ r("WEB_SEARCH_PROVIDER", "orchestration", "enum", "web search 工具后端(设合法词=装配 WebSearch;其它/缺席=不装配)", { enumValues: ["brave", "tavily", "searxng"], danger: SEC, resolve: (_c, env) => webSearchConfigFromEnv(env)?.provider ?? null }),
445
446
  r("WEB_SEARCH_SEARXNG_PARAMS", "orchestration", "string", "searxng 额外查询参数(k=v 对;解析不出任何一对 ⇒ 键不铸)"),
446
447
  r("WEB_SEARCH_TIMEOUT_MS", "orchestration", "number", "web search 单次超时(ms)", { derivedDefaultNote: "缺席/非正 ⇒ backend 默认" }),
447
448
  r("WORKFLOW_AGENTS_READONLY", "orchestration", "boolean", "workflow 子 agent 只读钳(TOB 保守旋钮;默认同权 CC parity)", { staticDefault: "false", danger: SEC, configKey: "workflowAgentsReadOnly" }),
@@ -483,17 +484,36 @@ function stripUrlUserinfo(v) {
483
484
  u.hash = "";
484
485
  return u.href;
485
486
  }
486
- function sanitizeValue(type, v) {
487
+ function sanitizeEnumValue(spec, v) {
488
+ const words = spec.enumValues;
489
+ if (v === null)
490
+ return { value: null };
491
+ if (words === undefined)
492
+ return { value: null, invalid: true };
493
+ if (typeof v === "boolean") {
494
+ const word = spec.booleanEnum?.[v ? "true" : "false"];
495
+ if (word === null)
496
+ return { value: null };
497
+ return word !== undefined && words.includes(word) ? { value: word } : { value: null, invalid: true };
498
+ }
499
+ if (typeof v === "string")
500
+ return words.includes(v) ? { value: v } : { value: null, invalid: true };
501
+ return { value: null, invalid: true };
502
+ }
503
+ function sanitizeValue(spec, v) {
504
+ const type = spec.type;
487
505
  if (type === "url") {
488
506
  if (typeof v === "string")
489
- return v.includes(",") ? v.split(",").map((s) => stripUrlUserinfo(s.trim())).filter((s) => s.length > 0) : stripUrlUserinfo(v);
507
+ return { value: v.includes(",") ? v.split(",").map((s) => stripUrlUserinfo(s.trim())).filter((s) => s.length > 0) : stripUrlUserinfo(v) };
490
508
  if (Array.isArray(v))
491
- return v.map((s) => stripUrlUserinfo(s));
492
- return v;
509
+ return { value: v.map((s) => stripUrlUserinfo(s)) };
510
+ return { value: v };
493
511
  }
494
512
  if (type === "csv" && typeof v === "string")
495
- return v.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
496
- return v;
513
+ return { value: v.split(",").map((s) => s.trim()).filter((s) => s.length > 0) };
514
+ if (type === "enum")
515
+ return sanitizeEnumValue(spec, v);
516
+ return { value: v };
497
517
  }
498
518
  function valueClassOf(type) {
499
519
  if (type === "secret")
@@ -520,6 +540,7 @@ export function buildConfigCatalog(inputs) {
520
540
  const centerEffective = seat !== undefined && managedNow && centerTiming === "hot" && (seat.precedence === "center-wins" || !envHeld);
521
541
  const effectiveLane = centerEffective ? "center" : envHeld ? "env" : "default";
522
542
  let effectiveValue;
543
+ let effectiveValueInvalid;
523
544
  if (valueClass === "secret") {
524
545
  effectiveValue = { present: envHeld };
525
546
  }
@@ -527,15 +548,11 @@ export function buildConfigCatalog(inputs) {
527
548
  effectiveValue = envHeld ? { present: true, length: raw.length } : { present: false };
528
549
  }
529
550
  else {
530
- const resolved = spec.resolve !== undefined ? spec.resolve(config) : spec.configKey !== undefined ? primitiveConfigValue(config, spec.configKey) : undefined;
531
- effectiveValue =
532
- resolved !== undefined
533
- ? sanitizeValue(spec.type, resolved)
534
- : knob !== undefined
535
- ? knob.value
536
- : envHeld
537
- ? sanitizeValue(spec.type, raw)
538
- : spec.staticDefault ?? null;
551
+ const resolved = spec.resolve !== undefined ? spec.resolve(config, env) : spec.configKey !== undefined ? primitiveConfigValue(config, spec.configKey) : undefined;
552
+ const picked = resolved !== undefined ? resolved : knob !== undefined ? knob.value : envHeld ? raw : spec.staticDefault ?? null;
553
+ const sanitized = sanitizeValue(spec, picked);
554
+ effectiveValue = sanitized.value;
555
+ effectiveValueInvalid = sanitized.invalid;
539
556
  }
540
557
  return {
541
558
  name: spec.name,
@@ -549,6 +566,7 @@ export function buildConfigCatalog(inputs) {
549
566
  ...(spec.staticDefault !== undefined ? { staticDefault: spec.staticDefault } : {}),
550
567
  ...(spec.derivedDefaultNote !== undefined ? { derivedDefaultNote: spec.derivedDefaultNote } : {}),
551
568
  effectiveValue,
569
+ ...(effectiveValueInvalid === true ? { effectiveValueInvalid } : {}),
552
570
  valueClass,
553
571
  danger: spec.danger ?? [],
554
572
  ...(seat !== undefined && centerTiming !== undefined
@@ -8,6 +8,7 @@ import { principalFrom, decodeCheckpointScope, PRINCIPAL_TOKEN_HEADER, APPROVAL_
8
8
  import { verifyDirectDoorProof } from "../../principal-jwt.js";
9
9
  import { MAX_APPROVAL_REASON_CHARS } from "../../approval-hmac.js";
10
10
  import { redactedPreview, redactSecrets } from "../../trace/redact.js";
11
+ import { redactRiskDescriptor } from "../../observability/run-terminal-log.js";
11
12
  import { fleetRunLabels } from "../../fleet/fleet-bus.js";
12
13
  import { sleep } from "../sse-log.js";
13
14
  import { bindSseLifecycle } from "../sse-lifecycle.js";
@@ -546,18 +547,8 @@ function redactPendingDisclosures(row) {
546
547
  const rd = row.riskDescriptor;
547
548
  if (rd === null || rd === undefined)
548
549
  return row;
549
- const shadowedRule = typeof rd.shadowedRule === "string" ? redactSecrets(rd.shadowedRule) : undefined;
550
- const summary = typeof rd.summary === "string" ? redactSecrets(rd.summary) : undefined;
551
- if (shadowedRule === undefined && summary === undefined)
552
- return row;
553
- return {
554
- ...row,
555
- riskDescriptor: {
556
- ...rd,
557
- ...(shadowedRule !== undefined ? { shadowedRule } : {}),
558
- ...(summary !== undefined ? { summary } : {}),
559
- },
560
- };
550
+ const next = redactRiskDescriptor(rd);
551
+ return next === rd ? row : { ...row, riskDescriptor: next };
561
552
  }
562
553
  function projectPendingForWire(row, governance) {
563
554
  const redacted = redactPendingDisclosures(row);
@@ -10,21 +10,25 @@ export function buildServerWiringGates(input) {
10
10
  backend: input.backend,
11
11
  parkFacility: input.checkpointStore !== undefined,
12
12
  });
13
+ const noCenterArm = (knob) => `this knob has no config-center arm on this deployment — it is set by ${knob} on this machine, or derived from the deployment posture`;
13
14
  return {
14
15
  streamApproval: gate.active ? "active" : gate.reason,
15
16
  durableApproval: buildPostureKnobReading(input.durableApproval, {
16
17
  knob: "DURABLE_APPROVAL",
17
18
  source: input.durableApprovalSource,
19
+ centerMeaning: noCenterArm("DURABLE_APPROVAL"),
18
20
  postureMeaning: "derived from the single-user turnkey posture (REQUIRE_PRINCIPAL is not set) composed with a durable store being present: park/resume and the /v1/approvals routes are on by default — set DURABLE_APPROVAL=false to pin them off",
19
21
  }),
20
22
  streamAskWindowMs: buildPostureKnobReading(input.streamAskWindowMs, {
21
23
  knob: "STREAM_ASK_WINDOW_MS",
22
24
  source: input.streamAskWindowMsSource,
25
+ centerMeaning: noCenterArm("STREAM_ASK_WINDOW_MS"),
23
26
  postureMeaning: "derived from the single-user turnkey posture (REQUIRE_PRINCIPAL is not set): an approval card waits 24h instead of 5 minutes, so stepping away does not settle the run unattended — set STREAM_ASK_WINDOW_MS to pin your own window",
24
27
  }),
25
28
  sessionAutoTitle: buildPostureKnobReading(input.sessionAutoTitle, {
26
29
  knob: "SESSION_AUTO_TITLE",
27
30
  source: input.sessionAutoTitleSource,
31
+ centerMeaning: noCenterArm("SESSION_AUTO_TITLE"),
28
32
  postureMeaning: "this knob has no posture arm — its default is the same on every deployment shape",
29
33
  }),
30
34
  checkpointStore: input.checkpointStore !== undefined,
@@ -24,7 +24,7 @@ import { normalizeRunEventType } from "../../trace/project.js";
24
24
  import { sendJson, sendError, httpErrorCode, sseHeaders } from "../send.js";
25
25
  import { buildActiveRunConflict } from "../active-run-conflict.js";
26
26
  import { clearTurnActivity, readTurnActivityMs } from "../../turn-activity.js";
27
- import { buildCancelContext } from "../../run-cancel-context.js";
27
+ import { mintCancelledResult } from "../../run-cancel-context.js";
28
28
  import { buildCrossSliceUsage, readResourceWindow, recordResourceWindow } from "../../resource-window.js";
29
29
  import { headerStr, gatedPrincipal, explicitOperatorOk } from "../principal-gate.js";
30
30
  import { ActorAssertionWire } from "@sema-agent/settings-schema";
@@ -331,8 +331,7 @@ async function handleRunsBody(req, res, url, ctx, miss) {
331
331
  }
332
332
  const err = note;
333
333
  try {
334
- const cancelContext = buildCancelContext(taskId, Date.parse(run.createdAt));
335
- const result = { taskId, sessionId: run.sessionId, terminal: { kind: "failed", code: "cancelled", message: err }, stats: { turns: 0, tokens: 0 }, ...(cancelContext ? { cancelContext } : {}) };
334
+ const result = mintCancelledResult({ taskId, sessionId: run.sessionId, startedAtMs: Date.parse(run.createdAt), message: err });
336
335
  let lastErr;
337
336
  for (let attempt = 0;; attempt++) {
338
337
  try {
@@ -9,7 +9,7 @@ import { emitPendingWorkflowCompletions, taskNotificationInboxEntry, taskNotific
9
9
  import { createLedgerSink } from "../../trace/ledger-sink.js";
10
10
  import { registerEngineNoticeLeg } from "../../trace/engine-notice-wire.js";
11
11
  import { clearTurnActivity, readTurnActivityMs, recordTurnActivity } from "../../turn-activity.js";
12
- import { buildCancelContext } from "../../run-cancel-context.js";
12
+ import { mintCancelledResult } from "../../run-cancel-context.js";
13
13
  import { recordResourceWindow } from "../../resource-window.js";
14
14
  import { redactSecrets } from "../../trace/redact.js";
15
15
  import { contextUsageEventData, toolStartEventData, toolEndEventData, taskProgressEventData, taskNotificationEventData, compactedEventData, diagnosticsEventData, brainStatusEventData, steeringInjectedEventData, compactionOutcomeEventData, workspaceChangedEventData, wiringManifestEventData, wiringManifestOperatorEventData, toolRosterDeltaEventData, humanInputEventData, appendModelUsageDelta, attachModelUsage } from "../../trace/project.js";
@@ -190,17 +190,13 @@ async function handleTasksBody(req, res, url, ctx, miss) {
190
190
  let runRowSettled = false;
191
191
  let durableTaskId;
192
192
  let durableHeartbeat;
193
- const mintCancelledResult = (errorMessage) => {
194
- const ctx = durableTaskId === undefined ? undefined : buildCancelContext(durableTaskId, streamLegStartedAt);
195
- return {
196
- taskId: durableTaskId ?? "",
197
- sessionId: prepared.spec.sessionId ?? "",
198
- terminal: { kind: "failed", code: "cancelled", message: errorMessage },
199
- result: "",
200
- stats: { turns: 0, tokens: 0 },
201
- ...(ctx ? { cancelContext: ctx } : {}),
202
- };
203
- };
193
+ const cancelledResult = (errorMessage, engineResult) => mintCancelledResult({
194
+ taskId: durableTaskId ?? "",
195
+ sessionId: prepared.spec.sessionId ?? "",
196
+ startedAtMs: streamLegStartedAt,
197
+ message: errorMessage,
198
+ ...(engineResult !== undefined ? { engineResult } : {}),
199
+ });
204
200
  let userMsgEntryId;
205
201
  let anchorPut = false;
206
202
  const putRewindAnchor = () => {
@@ -398,12 +394,13 @@ async function handleTasksBody(req, res, url, ctx, miss) {
398
394
  break;
399
395
  if (ev.type === "message_committed" && ev.role === "user")
400
396
  userMsgEntryId = ev.entryId;
397
+ const cancelVerbAtDone = ev.type === "done" && durableTaskId !== undefined && cancelledViaVerb.has(durableTaskId);
401
398
  if (ev.type === "done") {
402
399
  finalResult = redactTerminalResult(stripResumeToken(await graftParkToolCallId(ev.result, deps.checkpointStore)));
403
400
  if (durableTaskId && finalResult)
404
401
  finalResult = { ...finalResult, taskId: durableTaskId };
405
- if (durableTaskId && cancelledViaVerb.has(durableTaskId) && finalResult?.terminal.kind === "failed" && !finalResult.terminal.code) {
406
- finalResult = { ...finalResult, terminal: { ...finalResult.terminal, code: "cancelled" } };
402
+ if (cancelVerbAtDone && finalResult !== undefined) {
403
+ finalResult = cancelledResult("cancelled by user", finalResult);
407
404
  }
408
405
  finalizeTaskResult(ev.result, principal, prepared.spec.objective, prepared.spec.sessionId);
409
406
  noteRunOutcome(finalResult);
@@ -644,7 +641,7 @@ async function handleTasksBody(req, res, url, ctx, miss) {
644
641
  }
645
642
  else {
646
643
  const dcErr = "stream client disconnected before completion (run aborted)";
647
- const dc = mintCancelledResult(dcErr);
644
+ const dc = cancelledResult(dcErr);
648
645
  if (ledgerSink && !ledgerTerminal) {
649
646
  ledgerTerminal = true;
650
647
  await ledgerSink.flush().catch(() => undefined);
@@ -696,7 +693,7 @@ async function handleTasksBody(req, res, url, ctx, miss) {
696
693
  }
697
694
  else if (ac.signal.aborted) {
698
695
  const err = "stream aborted before completion (run cancelled)";
699
- const c = mintCancelledResult(err);
696
+ const c = cancelledResult(err);
700
697
  if (ledgerSink && !ledgerTerminal) {
701
698
  ledgerTerminal = true;
702
699
  await ledgerSink.flush().catch(() => undefined);
@@ -745,6 +742,7 @@ async function handleTasksBody(req, res, url, ctx, miss) {
745
742
  else {
746
743
  const withDiscoveryNotice = (body) => prepared.verify || prepared.cascade ? { ...body, notice: MULTI_ATTEMPT_NO_EVENT_CHANNEL_NOTICE } : body;
747
744
  const runSyncSubmitLeg = async () => {
745
+ const syncLegStartedAt = Date.now();
748
746
  let durableTaskId;
749
747
  let durableHeartbeat;
750
748
  if (deps.runStore && prepared.spec.sessionId) {
@@ -808,8 +806,14 @@ async function handleTasksBody(req, res, url, ctx, miss) {
808
806
  cancelledViaVerb.delete(durableTaskId);
809
807
  }
810
808
  }
811
- if (cancelVerbLabel && result.terminal.kind === "failed" && !result.terminal.code) {
812
- result = { ...result, terminal: { ...result.terminal, code: "cancelled" } };
809
+ if (cancelVerbLabel) {
810
+ result = mintCancelledResult({
811
+ taskId: durableTaskId ?? "",
812
+ sessionId: prepared.spec.sessionId ?? "",
813
+ startedAtMs: syncLegStartedAt,
814
+ message: "cancelled by user",
815
+ engineResult: result,
816
+ });
813
817
  }
814
818
  const resumeAtStatus = resumeAtHttpStatus(result);
815
819
  if (resumeAtStatus) {
@@ -17,6 +17,7 @@ import { normalizeApproachNotice, unknownTaskAgentModelRef, validateTaskAgents }
17
17
  import { isValidCwd, MAX_ADDITIONAL_DIRS } from "../task-cwd.js";
18
18
  import { runInBackground, evictIfConflict, parkToolCallId, TurnAnchorCapture, HEARTBEAT_MS, markChildrenStoppedByUserOnAbort } from "../runs.js";
19
19
  import { failureCodeOf, nestedPauseOf, stripResumeToken } from "../terminal.js";
20
+ import { mintCancelledResult } from "../run-cancel-context.js";
20
21
  import { personDecision } from "../host-decision.js";
21
22
  import { readTurnActivityMs, recordTurnActivity } from "../turn-activity.js";
22
23
  import { recordResumeResourceWindow } from "../resource-window.js";
@@ -1306,6 +1307,7 @@ export function createHttpServer(rawDeps) {
1306
1307
  return await Promise.race([accepted, leg]);
1307
1308
  }
1308
1309
  async function driveResumeLeg(args, onAccepted) {
1310
+ const resumeLegStartedAt = Date.now();
1309
1311
  const { runner: legRunner, token, sessionId, principal, fleetScope, taskConfig, resumeObjective, outcome, verifyRounds } = args;
1310
1312
  const legKey = createHash("sha256").update(String(token)).digest("hex");
1311
1313
  const streamApprovalOn = resolveStreamApprovalGate({
@@ -1797,7 +1799,7 @@ export function createHttpServer(rawDeps) {
1797
1799
  }
1798
1800
  }
1799
1801
  if (claimedRow && taskId && deps.runStore) {
1800
- const c = { taskId, sessionId, terminal: { kind: "failed", code: "cancelled", message: e.message }, stats: { turns: 0, tokens: 0 } };
1802
+ const c = mintCancelledResult({ taskId, sessionId, startedAtMs: resumeLegStartedAt, message: e.message });
1801
1803
  await deps.runStore.setTerminal(taskId, "failed", c, e.message).catch(() => undefined);
1802
1804
  }
1803
1805
  settleFleet("failed");
@@ -1852,7 +1854,7 @@ export function createHttpServer(rawDeps) {
1852
1854
  }
1853
1855
  if (cancelCtrl.signal.aborted) {
1854
1856
  if (taskId && deps.runStore) {
1855
- const c = { taskId, sessionId, terminal: { kind: "failed", code: "cancelled", message: "cancelled by user" }, stats: { turns: 0, tokens: 0 } };
1857
+ const c = mintCancelledResult({ taskId, sessionId, startedAtMs: resumeLegStartedAt, message: "cancelled by user" });
1856
1858
  await deps.runStore.setTerminal(taskId, "failed", c, "cancelled by user").catch(() => undefined);
1857
1859
  }
1858
1860
  settleFleet("failed");
package/dist/main.js CHANGED
@@ -272,7 +272,7 @@ async function main() {
272
272
  config, logger, metrics, localRoot, promptSource: configCenter.promptSource, rosterStore, backgroundAgentStore, mailboxStore, usageWindowStore, brain,
273
273
  pricing, tracer, outcomeSink, elicitation, question, toolApproval, sessionStore, memoryEngine,
274
274
  sessionCaptureRecordStore,
275
- memorySyncRunner, toolResultStore: runnerOffloadStore, sessionPolicyStore, runtimeCapsResolver, fileHistoryStore,
275
+ memorySyncRunner, toolResultStore: runnerOffloadStore, checkpointStore, sessionPolicyStore, runtimeCapsResolver, fileHistoryStore,
276
276
  legacyRewindBoundaryProbe: backend?.legacyRewindBoundaryProbe ? backend.legacyRewindBoundaryProbe.bind(backend) : undefined,
277
277
  permissionRuleStore: permissionRuleProvider,
278
278
  executionEnvFactory, lspManager, fleetBus, deploymentHooks, workflowRunStore, workflowJournalStore,
@@ -345,6 +345,7 @@ async function main() {
345
345
  rosterStore,
346
346
  deploymentHooks,
347
347
  toolResultStore: runnerOffloadStore,
348
+ checkpointStore,
348
349
  sessionPolicyStore,
349
350
  usageWindowStore,
350
351
  orgMemoryAdmission,
@@ -356,7 +357,6 @@ async function main() {
356
357
  loadProjectMemory: runnerDeps.loadProjectMemory ? runnerDeps.loadProjectMemory : undefined,
357
358
  probeInstructionSources: runnerDeps.probeInstructionSources ? runnerDeps.probeInstructionSources : undefined,
358
359
  onError: runnerDeps.onError ? runnerDeps.onError : undefined,
359
- checkpointStore: checkpointStore ? checkpointStore : undefined,
360
360
  };
361
361
  const subRunner = new Runner(subRunnerDeps);
362
362
  const handslessSubRunner = new Runner(withoutExecutionEnv(subRunnerDeps));
@@ -1,9 +1,3 @@
1
- /**
2
- * Tiny in-process metrics registry with Prometheus text exposition — no dependency.
3
- *
4
- * Per-instance (each container exposes its own counters); aggregate across instances at the scraper.
5
- * Supports labeled counters, gauges (static + dynamic), and duration histograms.
6
- */
7
1
  export type Labels = Record<string, string>;
8
2
  export declare class Metrics {
9
3
  private counters;
@@ -1,3 +1,4 @@
1
+ import { buildPermissionRuleEventsHelp } from "./permission-rule-events.js";
1
2
  const DEFAULT_BUCKETS = [0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, 30, 60];
2
3
  function labelKey(labels) {
3
4
  return Object.keys(labels)
@@ -260,8 +261,9 @@ export function createMetrics() {
260
261
  m.counter("stale_cache_conflict_total", "Warm session-cache entries evicted by a cross-instance write while cached (S25)");
261
262
  m.counter("breaker_writethrough_failed_total", "Circuit-breaker cross-replica write-through failures (LOW), by backend");
262
263
  m.counter("web_search_bad_payload_total", "Web-search provider responses whose results field was not an array (LOW), by provider");
264
+ m.counter("auto_mode_classifier_unavailable_total", "Auto-mode gate denials caused by a classifier OUTAGE, by cause (core AUTO_MODE_UNAVAILABLE_CAUSES: error/timeout; 'other' = this build cannot name the word). Distinct from permission_denied_total{deniedBy=\"classifier\"}, which also counts genuine safety blocks — a spike here means the classifier itself is failing (gateway/credential fault, or too slow), not that the model kept asking for dangerous things");
263
265
  m.counter("permission_denied_total", "Tool-gate denials by the LAYER whose verdict is the deny (core's DeniedBy: policy/hook/org/classifier/plan_mode/compliance/write_protection/ask_resolution/persisted_rule; 'other' = this build cannot name the layer; label is `deniedBy` since server 7.64.0 — it was `source` over core's retired PermissionDeniedSource; goal B4 always-on deny meter)");
264
- m.counter("permission_rule_events_total", "Persisted allow-rule seam events by event (persisted_rule_allowed = a rule authorized a call with nobody asked; rule_store_unreadable = the store failed to read so the call was adjudicated with ZERO rules)");
266
+ m.counter("permission_rule_events_total", buildPermissionRuleEventsHelp());
265
267
  m.counter("compaction_events_total", "Compaction lifecycle events by outcome (started/completed/failed/skipped) and trigger");
266
268
  m.counter("hook_llm_calls_total", "prompt/agent hook entries that completed a model call (hooks 3b), by type");
267
269
  m.counter("auth_bridge_verify_total", "Registry SSO JWT verifications (auth-bridge), by outcome");
@@ -0,0 +1,20 @@
1
+ /** 计量面的 `event` 词表(顺序 = HELP 行里的出现顺序)。 */
2
+ export declare const PERMISSION_RULE_EVENT_TABLE: readonly [{
3
+ readonly event: "persisted_rule_allowed";
4
+ readonly meaning: "a persisted allow rule authorized the call and nobody was asked";
5
+ }, {
6
+ readonly event: "rule_store_unreadable";
7
+ readonly meaning: "the rule store failed to read, so the call was adjudicated with ZERO rules (expect an approval storm whose cause is the store, not the model)";
8
+ }, {
9
+ readonly event: "read_only_allowed";
10
+ readonly meaning: "the engine's read-only shell verdict cleared an ask that was NOT an operator-written rule (core decisionReason \"read_only\"; an operator-declared ask carries matchedAskRule and is never cleared this way)";
11
+ }];
12
+ /** 词表里的一个词(闭集)。 */
13
+ export type PermissionRuleEventWord = (typeof PERMISSION_RULE_EVENT_TABLE)[number]["event"];
14
+ /** 这一帧是否进 `permission_rule_events_total`(判别式与词表同源,不再手抄第二串 `===`)。 */
15
+ export declare function isPermissionRuleEventKind(kind: string): boolean;
16
+ /** 帧名 → `event` 标签值。调用方须先过 {@link isPermissionRuleEventKind}。 */
17
+ export declare function permissionRuleEventWordOf(kind: string): string;
18
+ /** `/metrics` 的 HELP 行文案(从词表派生 —— 加词自动出现,不存在「第二份会漂的散文」)。 */
19
+ export declare function buildPermissionRuleEventsHelp(): string;
20
+ //# sourceMappingURL=permission-rule-events.d.ts.map
@@ -0,0 +1,28 @@
1
+ const PERMISSION_TRACE_PREFIX = "permission.";
2
+ export const PERMISSION_RULE_EVENT_TABLE = [
3
+ {
4
+ event: "persisted_rule_allowed",
5
+ meaning: "a persisted allow rule authorized the call and nobody was asked",
6
+ },
7
+ {
8
+ event: "rule_store_unreadable",
9
+ meaning: "the rule store failed to read, so the call was adjudicated with ZERO rules (expect an approval storm whose cause is the store, not the model)",
10
+ },
11
+ {
12
+ event: "read_only_allowed",
13
+ meaning: "the engine's read-only shell verdict cleared an ask that was NOT an operator-written rule (core decisionReason \"read_only\"; an operator-declared ask carries matchedAskRule and is never cleared this way)",
14
+ },
15
+ ];
16
+ const PERMISSION_RULE_EVENT_KINDS = new Set(PERMISSION_RULE_EVENT_TABLE.map((r) => `${PERMISSION_TRACE_PREFIX}${r.event}`));
17
+ export function isPermissionRuleEventKind(kind) {
18
+ return PERMISSION_RULE_EVENT_KINDS.has(kind);
19
+ }
20
+ export function permissionRuleEventWordOf(kind) {
21
+ return kind.slice(PERMISSION_TRACE_PREFIX.length);
22
+ }
23
+ export function buildPermissionRuleEventsHelp() {
24
+ return ("Permission allow-layer attribution events by event — the ONE answer to \"why was I not asked this time?\" (" +
25
+ PERMISSION_RULE_EVENT_TABLE.map((r) => `${r.event} = ${r.meaning}`).join("; ") +
26
+ ")");
27
+ }
28
+ //# sourceMappingURL=permission-rule-events.js.map
@@ -52,26 +52,15 @@ export declare const REDACT_INPUT_CAP = 65536;
52
52
  * 脱敏**之后**按字节封顶(占位已替换完,切在占位中间不是泄漏)并带标记。 */
53
53
  export declare const REDACT_OUTPUT_MAX_BYTES = 65000;
54
54
  export declare function redactErrorMessage(raw: string | undefined): string | undefined;
55
- /**
56
- * 同口:终态 `result` 整块也持久化(`GET /v1/runs/:id` 直出),写入前把里面的**自由文本座**脱敏,其余键
57
- * 逐字不动;`undefined`/`null` 透传。
58
- *
59
- * 🔴 S-136(core 7.6.0 [ref] D-8):自由文本座**搬家了** —— 失败文案与阻塞理由从顶层平面键
60
- * (`errorMessage` / `blockedReason`)搬进因由 `terminal` 的两条臂(`failed.message` / `blocked.reason`)。
61
- * 本函数**两种形都认**,而这不是「兼容读臂」(codex 对抗复审 r1 [high],亲读验真后**改判**;首版删掉了旧键
62
- * 臂,那是错的):本函数是**读盘面**,而 `task_run.result` / `task_event(done).result` 里躺着 7.63.0 及以前
63
- * 写下的**真字节**,它们本来就是平面形。写口只剩因由一条(没有任何东西会再写出旧形),所以这里不存在
64
- * 「两个写者」;而不认旧形的代价是**可复现的凭据回放**:上线前落盘的原文经 `GET /v1/runs/:id/events` 重新
65
- * 明文外发(读面这一层纵深正是为那批行存在的)。⇒ 规则仍只有一条:**读盘认盘上真有的形,写盘只写一种形**。
66
- *
67
- * 读面也走本函数,所以 `terminal` 形上取 `unknown`:旧持久 blob 与非 core 的 Runner 实现给的形不可信,
68
- * 运行期逐臂判形、不抛(与 `remoteEnvFailures` / `editedFiles` 两座同款不信任)。
69
- */
55
+ /** {@link redactTerminalResult} 会碰的全部顶层键 = 现行形的 `"redacted"` 行 ∪ 旧形平面键;
56
+ * `memoRedactTerminalResult` 的记忆键由它派生(两处同源,见 `RESULT_KEY_TREATMENT` 顶注)。 */
57
+ export declare const REDACTABLE_RESULT_LEAVES: readonly string[];
70
58
  export declare function redactTerminalResult<T extends {
71
59
  terminal?: unknown;
72
- /** 旧形(≤7.63.0)的两个顶层自由文本座;形上取 `unknown` —— 读盘面对盘上字节不作假设。 */
60
+ /** 旧形(≤7.63.0)的三个顶层座;形上取 `unknown` —— 读盘面对盘上字节不作假设。 */
73
61
  errorMessage?: unknown;
74
62
  blockedReason?: unknown;
63
+ checkpointGate?: unknown;
75
64
  remoteEnvFailures?: readonly {
76
65
  message?: string | undefined;
77
66
  }[] | undefined;
@@ -94,9 +83,10 @@ export declare function redactWorkerReportError<T extends {
94
83
  error?: string | undefined;
95
84
  checkpointGate?: unknown;
96
85
  }>(report: T): T;
97
- /** codex r5 [high]:`WorkerReport.checkpointGate`(core `CheckpointGate`)整块随 LeaderResult 落库/直出——`reason` 与
98
- * `riskDescriptor.summary`/`shadowedRule` 是自由文本([ref] 实测 summary 带 PEM 明文;[ref] R3 登记),按类型表脱这三叶;
99
- * `kind`/`toolName`/`axes`/`severity`/`touchedPaths` 闭集或路径,逐字。非对象形原样返回。 */
86
+ export declare function redactRiskDescriptor(rd: unknown): unknown;
87
+ /** `CheckpointGate`(core 类型)整块上 wire —— `reason` 与 `riskDescriptor` 的叶族是自由文本
88
+ * ([ref] 实测 summary 带 PEM 明文;[ref] R3 登记;[ref] 把它扩到结果/账本两面)。逐键**按表**处置,
89
+ * 叶表属主见 {@link redactRiskDescriptor}。非对象形原样返回(同一引用);字节没变也回同一引用。 */
100
90
  export declare function redactCheckpointGate(gate: unknown): unknown;
101
91
  /** 工件字节(patch / *.rej 拒绝块):codex r6 [high]——拒绝块能带 `git clone https://u:TOKEN@host` 或 `_authToken=` 行,
102
92
  * 「工件保真」不能让原凭据上元数据读面。做**原位密钥形替换**(与预览同一张规则表,字节只在密钥处变)、**不截断**;