@sema-agent/server 7.56.0 → 7.57.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 (110) hide show
  1. package/README.md +3 -2
  2. package/README.zh-CN.md +2 -2
  3. package/USAGE.md +101 -9
  4. package/dist/approval-ask-audit-store.d.ts +100 -1
  5. package/dist/approval-ask-audit-store.js +103 -2
  6. package/dist/approval-card.d.ts +148 -13
  7. package/dist/approval-card.js +82 -13
  8. package/dist/approval.d.ts +31 -0
  9. package/dist/approval.js +18 -0
  10. package/dist/auto-mode-face.d.ts +111 -0
  11. package/dist/auto-mode-face.js +99 -0
  12. package/dist/bench/s1/arms.js +5 -5
  13. package/dist/bench/s1/live-deps.js +11 -11
  14. package/dist/bench/s1/run-firm.js +3 -0
  15. package/dist/bench/s1/runner-ctx.d.ts +4 -0
  16. package/dist/bench/s1/runner-ctx.js +7 -0
  17. package/dist/boot/config-center.js +23 -1
  18. package/dist/boot/coordinators.js +13 -1
  19. package/dist/boot/parked-revive-gate.d.ts +7 -2
  20. package/dist/boot/parked-revive-gate.js +12 -1
  21. package/dist/boot/resolve-spec.d.ts +3 -0
  22. package/dist/boot/resolve-spec.js +3 -1
  23. package/dist/boot/runner-deps.d.ts +12 -0
  24. package/dist/boot/runner-deps.js +28 -3
  25. package/dist/boot/runtime-caps.d.ts +19 -9
  26. package/dist/boot/runtime-caps.js +49 -21
  27. package/dist/boot/session-shell-gate-registry.d.ts +39 -0
  28. package/dist/boot/session-shell-gate-registry.js +21 -0
  29. package/dist/config-catalog.js +14 -5
  30. package/dist/config-types.d.ts +41 -10
  31. package/dist/config.d.ts +21 -0
  32. package/dist/config.js +40 -5
  33. package/dist/http/route-ctx.d.ts +75 -0
  34. package/dist/http/routes/a2a-serve.js +5 -3
  35. package/dist/http/routes/approvals-assistant.js +19 -6
  36. package/dist/http/routes/capabilities.js +21 -4
  37. package/dist/http/routes/memory-origin.d.ts +2 -2
  38. package/dist/http/routes/rules.js +3 -2
  39. package/dist/http/routes/runs.d.ts +0 -14
  40. package/dist/http/routes/runs.js +20 -8
  41. package/dist/http/routes/tasks.js +34 -8
  42. package/dist/http/routes/workflows.js +19 -4
  43. package/dist/http/server.d.ts +11 -1
  44. package/dist/http/server.js +215 -43
  45. package/dist/http/wire-types.d.ts +9 -4
  46. package/dist/leader/diffout.js +2 -1
  47. package/dist/leader/endpoint.js +45 -12
  48. package/dist/leader/fanout.js +6 -5
  49. package/dist/leader/leader.js +17 -14
  50. package/dist/leader/merge.js +17 -12
  51. package/dist/leader/planner.js +3 -2
  52. package/dist/leader/repair-oracle.js +6 -4
  53. package/dist/leader/repair-wire.js +5 -3
  54. package/dist/leader/wire.d.ts +30 -1
  55. package/dist/leader/wire.js +36 -19
  56. package/dist/main.js +43 -7
  57. package/dist/observability/err-text.d.ts +6 -0
  58. package/dist/observability/err-text.js +10 -0
  59. package/dist/observability/fail-open.d.ts +40 -0
  60. package/dist/observability/fail-open.js +19 -0
  61. package/dist/observability/metrics.js +1 -1
  62. package/dist/observability/run-terminal-log.d.ts +120 -0
  63. package/dist/observability/run-terminal-log.js +360 -0
  64. package/dist/orchestration/workflow-completion-inbox.d.ts +9 -0
  65. package/dist/orchestration/workflow-completion-inbox.js +8 -0
  66. package/dist/permission-rule-vocab.d.ts +40 -0
  67. package/dist/permission-rule-vocab.js +17 -0
  68. package/dist/plugins/checkpoint-store-sql.d.ts +14 -0
  69. package/dist/plugins/checkpoint-store-sql.js +19 -3
  70. package/dist/plugins/file-run-store.d.ts +3 -34
  71. package/dist/plugins/file-run-store.js +19 -0
  72. package/dist/plugins/local-checkpoint-store.d.ts +10 -0
  73. package/dist/plugins/local-checkpoint-store.js +3 -0
  74. package/dist/plugins/memory-run-store.d.ts +3 -15
  75. package/dist/plugins/memory-run-store.js +19 -0
  76. package/dist/plugins/permission-rule-store-file.js +8 -3
  77. package/dist/plugins/permission-rule-store-sql.js +16 -10
  78. package/dist/plugins/remote-scratchpad.js +3 -2
  79. package/dist/plugins/run-store-sql.d.ts +3 -42
  80. package/dist/plugins/run-store-sql.js +30 -1
  81. package/dist/plugins/store-backend.d.ts +1 -1
  82. package/dist/plugins/store-contracts.d.ts +66 -1
  83. package/dist/plugins/workflow-run-store-sql.d.ts +5 -0
  84. package/dist/plugins/workflow-run-store-sql.js +10 -2
  85. package/dist/rules-consent.js +20 -13
  86. package/dist/run-cancel-context.d.ts +13 -0
  87. package/dist/run-cancel-context.js +15 -0
  88. package/dist/run-local.js +2 -2
  89. package/dist/runs.d.ts +4 -1
  90. package/dist/runs.js +41 -10
  91. package/dist/runtime-caps-resolver.d.ts +133 -17
  92. package/dist/runtime-caps-resolver.js +41 -3
  93. package/dist/task-settings.d.ts +57 -3
  94. package/dist/task-settings.js +78 -5
  95. package/dist/task-workflow.d.ts +32 -2
  96. package/dist/task-workflow.js +8 -3
  97. package/dist/tool-approval.d.ts +26 -3
  98. package/dist/tool-approval.js +96 -13
  99. package/dist/trace/core-keyset-guard.d.ts +2 -2
  100. package/dist/trace/ledger-sink.d.ts +13 -4
  101. package/dist/trace/ledger-sink.js +14 -6
  102. package/dist/trace/project.d.ts +13 -0
  103. package/dist/trace/project.js +10 -1
  104. package/dist/trace/redact.d.ts +22 -11
  105. package/dist/trace/redact.js +655 -20
  106. package/dist/trace/sema-provenance.d.ts +26 -0
  107. package/dist/trace/sema-provenance.js +11 -0
  108. package/dist/turn-activity.d.ts +32 -2
  109. package/dist/turn-activity.js +29 -4
  110. package/package.json +2 -2
package/dist/main.js CHANGED
@@ -1,13 +1,13 @@
1
1
  import { readFileSync } from "node:fs";
2
2
  import { homedir } from "node:os";
3
3
  import { join } from "node:path";
4
- import { Runner, InMemoryToolResultStore, TtlSessionStore, uuidv7, defaultTaskRegistry, createAllowDenyPolicy, workflowsCapability, probeSearchBackend, describeStaticWiring, DEFAULT_SUBAGENT_TOOL_NAME } from "@sema-agent/core";
4
+ import { Runner, InMemoryToolResultStore, TtlSessionStore, uuidv7, defaultTaskRegistry, createAllowDenyPolicy, workflowsCapability, probeSearchBackend, describeStaticWiring, expandTiers, DEFAULT_SUBAGENT_TOOL_NAME } from "@sema-agent/core";
5
5
  import { createSessionTitler } from "./session-titler.js";
6
6
  import { posIntEnv } from "./session-watch.js";
7
7
  import { selectEnvironmentTool } from "./capabilities/select-environment-tool.js";
8
8
  import { subagentSendUserFileExtraTools } from "./capabilities/send-user-file-tool.js";
9
9
  import { brainSummary } from "./brain.js";
10
- import { loadConfig, logConfigDiagnostics, resolveBindHost, splitLocalRoots, parseCapEnv, numEnv } from "./config.js";
10
+ import { loadConfig, logConfigDiagnostics, resolveBindHost, splitLocalRoots, parseCapEnv, parseNumOrFailNonNegative } from "./config.js";
11
11
  import { cardRuleScopeRoot } from "./task-cwd.js";
12
12
  import { isPricingConfigured } from "./budget.js";
13
13
  import { drainNumEnvWarnings } from "./plugins/remote-shell.js";
@@ -15,7 +15,7 @@ import { ensureChildSessionDurableWithPromotion } from "./plugins/session-store.
15
15
  import { ForkRoutingSessionStore } from "./plugins/fork-routing-session-store.js";
16
16
  import { webSearchConfigFromEnv, createWebSearchBackend, setWebSearchBadPayloadObserver, shouldProbeWebSearchOnBoot } from "./plugins/web-search.js";
17
17
  import { createAuthorizer, encodeCheckpointScope } from "./security.js";
18
- import { assertGateIntentServiceable, hasOperatorGateIntent } from "./approval.js";
18
+ import { assertGateIntentServiceable, buildForceDurableGateInertNotice, hasOperatorGateIntent } from "./approval.js";
19
19
  import { loadSkills } from "./capabilities/skills.js";
20
20
  import { createRepoClient } from "./capabilities/repo-tools.js";
21
21
  import { buildScenarios, builtinScenarioDetails } from "./capabilities/scenarios.js";
@@ -26,11 +26,13 @@ import { createRegistryJwtVerifier } from "./auth-bridge.js";
26
26
  import { createMetrics } from "./observability/metrics.js";
27
27
  import { installFailOpenRecorder } from "./observability/fail-open.js";
28
28
  import { installSecretEnvScrubRecorder } from "./observability/secret-env-scrub.js";
29
+ import { setRunTerminalObserver } from "./observability/run-terminal-log.js";
29
30
  import { setRedactionObserver, redactSecrets } from "./trace/redact.js";
30
31
  import { RateLimiter } from "./observability/rate-limit.js";
31
32
  import { createHttpServer, explicitOperatorOk, hasServiceAuth } from "./http/server.js";
32
33
  import { exportMemoryScope } from "./memory-export.js";
33
- import { workflowModelAllowlistFor } from "./task-workflow.js";
34
+ import { workflowModelAllowlistFor, selfOrchestrationDenial } from "./task-workflow.js";
35
+ import { centerEntitlementSourceWired } from "./runtime-caps-resolver.js";
34
36
  import { createMemoryBundleFaces } from "./memory-bundle-engine.js";
35
37
  import { backendOwnsControlPlane, createMemoryComplianceFaces, createMemoryOriginFaces, createSessionMemoryStatusFace } from "./memory-operator-faces.js";
36
38
  import { performMemorySync } from "./memory-sync.js";
@@ -63,6 +65,8 @@ import { createExecutionEnv } from "./boot/execution-env.js";
63
65
  import { createWorkflowOrchestration } from "./boot/workflow-orchestration.js";
64
66
  import { createLiveCoordinators } from "./boot/coordinators.js";
65
67
  import { createRuntimeCaps } from "./boot/runtime-caps.js";
68
+ import { classifierRouteFor, coreArmsAutoOnIntent } from "./auto-mode-face.js";
69
+ import { createSessionShellGateRegistry } from "./boot/session-shell-gate-registry.js";
66
70
  import { createRunnerDeps, createRunnerDepsOnAsk, createSharedRunnerDeps } from "./boot/runner-deps.js";
67
71
  import { createOrgMemoryAdmissionWiring } from "./boot/org-memory.js";
68
72
  import { createGovernanceSeams } from "./boot/governance-seams.js";
@@ -98,11 +102,17 @@ async function main() {
98
102
  ...(process.env.MODEL_VERIFIER ? { rejected: process.env.MODEL_VERIFIER, reason: "not in catalog" } : {}),
99
103
  });
100
104
  }
101
- setRedactionObserver((pattern, n) => metrics.inc("redactions_applied_total", { pattern }, n));
105
+ setRedactionObserver((pattern, n, meta) => metrics.inc("redactions_applied_total", { pattern, arm: meta.arm }, n));
102
106
  setWebSearchBadPayloadObserver((provider) => {
103
107
  metrics.inc("web_search_bad_payload_total", { provider });
104
108
  logger.warn("web_search_non_array_payload", { provider });
105
109
  });
110
+ setRunTerminalObserver((level, fields) => {
111
+ if (level === "error")
112
+ logger.error("run_failed", fields);
113
+ else
114
+ logger.info("run_failed", fields);
115
+ });
106
116
  const localRoot = config.configLocalDir ?? process.env.AGENT_DATA_DIR ?? join(homedir(), ".ai-agent");
107
117
  const rootsSplit = splitLocalRoots({
108
118
  localDataRoot: config.localDataRoot,
@@ -171,7 +181,7 @@ async function main() {
171
181
  ruleScopeRootFor: (sessionId) => cardRuleScopeRoot(sessionId !== undefined ? perSessionCwd.get(sessionId) : undefined, config),
172
182
  });
173
183
  const memoryOptOutGrantStore = backend?.memoryOptOutGrant();
174
- const { principalCaps, centerRuntimeCapsResolver, runtimeCapsResolver } = createRuntimeCaps({ config, logger, memoryOptOutGrant: memoryOptOutGrantStore });
184
+ const { principalCaps, centerRuntimeCapsResolver, runtimeCapsResolver, runtimeCapsPeekResolver } = createRuntimeCaps({ config, logger, memoryOptOutGrant: memoryOptOutGrantStore });
175
185
  const orgMemoryAdmission = createOrgMemoryAdmissionWiring({ config, logger, metrics });
176
186
  const sharedMemoryStore = orgMemoryAdmission.directory !== undefined
177
187
  ? backend?.sharedMemoryStore?.(createSharedMemoryScopeAuthorizer({
@@ -248,9 +258,11 @@ async function main() {
248
258
  });
249
259
  const mcpRevocations = configCenter.takeMcpRevocations();
250
260
  const workflowModelAllowlistSnapshot = workflowModelAllowlistFor(config) ?? [];
261
+ const sessionShellGateRegistry = createSessionShellGateRegistry();
251
262
  const runnerDeps = createRunnerDeps({
252
263
  hands: commitHands,
253
264
  mcpRevocations,
265
+ sessionShellGateExplicitlyOff: sessionShellGateRegistry.explicitlyOff,
254
266
  config, logger, metrics, localRoot, promptSource: configCenter.promptSource, rosterStore, backgroundAgentStore, mailboxStore, usageWindowStore, brain,
255
267
  pricing, tracer, outcomeSink, elicitation, question, toolApproval, sessionStore, memoryEngine,
256
268
  sessionCaptureRecordStore,
@@ -344,7 +356,7 @@ async function main() {
344
356
  const runStore = backend ? backend.run() : undefined;
345
357
  const resumeAnchorStore = backend ? backend.resumeAnchor() : undefined;
346
358
  const approvalExemptionStore = backend ? backend.approvalExemption() : undefined;
347
- const taskTimeoutSec = Math.max(0, Math.floor(numEnv("TASK_TIMEOUT_SEC", "0")));
359
+ const taskTimeoutSec = Math.floor(parseNumOrFailNonNegative("TASK_TIMEOUT_SEC", process.env.TASK_TIMEOUT_SEC || "0"));
348
360
  const capEnv = (name) => {
349
361
  const r = parseCapEnv(name, process.env[name]);
350
362
  if (r.invalid !== undefined) {
@@ -408,6 +420,17 @@ async function main() {
408
420
  logger.info("direct_door_active", { iss: config.principalJwtIss, aud: config.principalJwtAud, operators: config.operatorPrincipals.length });
409
421
  if (config.requirePrincipal && config.selfOrchestrationEnabled && !config.selfOrchestrationWorkerIsolation)
410
422
  logger.warn("self_orchestration_no_worker_isolation", { detail: "multi-tenant self-orchestration without SELF_ORCHESTRATION_WORKER_ISOLATION=true — a tenant workflow can starve the host event loop (availability risk). Set it to enable worker_thread resource isolation." });
423
+ if (selfOrchestrationDenial(config, centerEntitlementSourceWired(config)) !== null)
424
+ logger.warn("self_orchestration_no_entitlement_source", { detail: "SELF_ORCHESTRATION_ENABLED is on and REQUIRE_PRINCIPAL is on, but no per-principal entitlement source is wired (no config center, or it is in dry-run). core's allowWorkflows is tighten-only — an absent resolver leaves it ungated — so this service fail-closes self-orchestration for EVERY principal: no task on this worker can ever get the workflow engine. /v1/capabilities now reports workflows:false (workflowsGate.denial=\"entitlement_resolver_absent\") and a submit carrying selfOrchestration/settings.ultracode is refused 501 instead of silently downgraded. Wire a config center (SEMA_REGISTRY_URL + token, not dry-run), or set SELF_ORCHESTRATION_ENABLED=false." });
425
+ {
426
+ const inert = buildForceDurableGateInertNotice({
427
+ entitlementSourceWired: centerEntitlementSourceWired(config),
428
+ checkpointStorePresent: checkpointStore !== undefined,
429
+ durableApproval: config.durableApproval,
430
+ });
431
+ if (inert)
432
+ logger.warn(inert.event, inert.fields);
433
+ }
411
434
  if (config.requirePrincipal && !config.authToken && Object.keys(config.authTokens ?? {}).length === 0 && !config.traceToken)
412
435
  logger.warn("multi_tenant_no_service_token", { detail: "REQUIRE_PRINCIPAL is on but no SERVICE_AUTH_TOKEN/authTokens/TRACE_TOKEN — fleet/trace/session-list are owner-gated only (never fleet-wide, tenant-isolation safe). Set a token for cross-tenant ops visibility." });
413
436
  const rateLimiter = backend?.rateLimiter
@@ -592,6 +615,7 @@ async function main() {
592
615
  question: question ? question : undefined,
593
616
  toolApproval: toolApproval ? toolApproval : undefined,
594
617
  approvalAskAudit: approvalAskAudit ? approvalAskAudit : undefined,
618
+ approvalLateSettle: approvalAskAudit ? approvalAskAudit : undefined,
595
619
  workflowAgentRegistry: workflowAgentRegistry ? workflowAgentRegistry : undefined,
596
620
  subagentSteerRegistry,
597
621
  workflowCompletionInbox: workflowCompletionInbox ? workflowCompletionInbox : undefined,
@@ -647,6 +671,17 @@ async function main() {
647
671
  costQuota,
648
672
  fleetLease: fleetLease ? fleetLease : undefined,
649
673
  usageWindowStore: usageWindowStore ? usageWindowStore : undefined,
674
+ autoModeFace: {
675
+ seatMounted: runnerDeps.autoMode !== undefined,
676
+ engineArmsOnIntent: coreArmsAutoOnIntent(),
677
+ resolveCaps: runtimeCapsPeekResolver,
678
+ classifierModel: () => {
679
+ const route = classifierRouteFor({ models: expandTiers(config.models, config.tiers ?? {}) ?? config.models, roles: config.roles });
680
+ if (route.fault !== undefined)
681
+ logger.debug("auto_mode_classifier_route_unresolved", { fault: route.fault });
682
+ return route.id;
683
+ },
684
+ },
650
685
  };
651
686
  const storeLiveProbe = backend && backend.kind !== "local" && backend.dbNowMs && config.storeProbeIntervalMs > 0
652
687
  ? createStoreLiveProbe({ probe: () => backend.dbNowMs(), intervalMs: config.storeProbeIntervalMs, logger })
@@ -692,6 +727,7 @@ async function main() {
692
727
  getKeyResolver: () => configCenter.getKeyResolver(),
693
728
  taskAttachmentStore, perSessionCwd, setSessionCwd, setSessionShellEnv, fenceSessionOwner,
694
729
  hookLlm, hookAgent, fleetBus, hookWakeBus, resumeAnchorStore, ownerAware, taskLimitCaps, taskTimeoutSec,
730
+ noteSessionShellGate: sessionShellGateRegistry.note,
695
731
  selectEnvTool, sendUserFileToolSpec, memoryEngine, durableEnabled, approvalExemptionStore,
696
732
  singleUserAutoAcceptBaseline, checkpointStore, deploymentHooks, imageIndex, perTaskImage,
697
733
  sessionEnvSelection,
@@ -0,0 +1,6 @@
1
+ /** 把任意 rejection 值变成**一定能得到的字符串**——终局链(setTerminal 的 errorMessage 座、留痕腿)专用。
2
+ * S-107 codex r28/r31 + 第三十一/三十二轮重扫:rejection 值可以是 message getter / toString / Symbol.toPrimitive 会抛的对象,
3
+ * 或 `Error` 实例的 message getter 返回带会抛 length getter 的非字符串——裸写 `e instanceof Error ? e.message : String(e)`
4
+ * 在 catch 里自己抛,外层 catch 吞掉 setTerminal ⇒ run 卡 running、claim 悬挂到 reaper。这里**不抛、恒返字符串**。 */
5
+ export declare function errorText(e: unknown): string;
6
+ //# sourceMappingURL=err-text.d.ts.map
@@ -0,0 +1,10 @@
1
+ export function errorText(e) {
2
+ try {
3
+ const m = e instanceof Error ? e.message : e;
4
+ return typeof m === "string" ? m : String(m);
5
+ }
6
+ catch {
7
+ return "<unprintable>";
8
+ }
9
+ }
10
+ //# sourceMappingURL=err-text.js.map
@@ -13,6 +13,10 @@ export declare const FAIL_OPEN_TAGS: {
13
13
  readonly cls: "F";
14
14
  readonly note: "design/383 S-1(applyMemoryOptOutGrant):本地 grant 腿故障时的**披露钩子**(onFault → warn 日志)自己抛了。钩子只是观测面:吞掉它保住的是「resolver 不因一行日志失败而 reject」(那会把一条腿的故障扩成整车故障 —— core 的 resolver-throw 降级臂作废同一次解析里 center 已答好的 allowWorkflows/allowFork);代价是**那一行披露丢了**,而 `allowMemoryOptOut` 键缺席这件事本身不受影响(fail-closed/fail-safe 极性由 core 按姿态判)。本计数让「披露钩子一直在炸」显形。";
15
15
  };
16
+ readonly "server.memory-optout-verdict-absence.warn-hook-threw": {
17
+ readonly cls: "F";
18
+ readonly note: "S-76(applyGovernedVerdictAbsenceWarning):`governed` + center-only verdict 源的部署上,**首次**解析到 `allowMemoryOptOut` 缺席时那一行**一次性披露**钩子自己抛了。与上一条同族、同理由:钩子只是观测面,吞掉保住的是「resolver 不因一行日志失败而 reject」(core 的 resolver-throw 降级臂会作废同一次解析里 center 已答好的 allowWorkflows/allowFork);代价是**「center 从不发这个键」这件事少了一行运行期证据**(启动期那行 `memory_capture_governed_center_only_source` 仍在,拒/放的极性也不受影响 —— 缺席由 core 按姿态判 fail-closed)。本计数让「披露钩子一直在炸」显形。";
19
+ };
16
20
  readonly "server.run-store.file-hydrate-unreadable": {
17
21
  readonly cls: "P-DEBT";
18
22
  readonly note: "件6(#43 案 [4281]):`FileRunStore.hydrate()` 的 `readdirSync(runs/)` 失败 ⇒ 引擎以**空账**起动。旧形是空 catch + return:data root 不可读(权限/挂载丢失/路径被换走)时零告警,此后每一次 getRun 恒 miss —— 而「恒 miss」正是 #43 现场看到的形,一次环境故障被伪装成「本来就没有这条 run」。方向问题在于吞掉之后被读成的是「证不出有行 ⇒ 没有行」,两句话不等价。**缺省不拒启是刻意保留的行为**,所以这条留作债:响亮化(error 级启动日志带 runs 绝对路径+异常文案)+ 本计数,让它在遥测里显形而不是只活在一行日志里。⚠️ 2026-08-29(S-46 件B)起终局格已兑现:部署可用 `RUN_STORE_STRICT_HYDRATE=on` 把这一格换成 **fail-closed 启动门**(拒启,遗言同样带绝对路径+异常文案);本计数因此只在**缺省 off** 档上产生 —— 读遥测时别把「计数为零」直接读成「没有部署踩到这条路」,也可能是它们开了拒启门、根本起不来。";
@@ -61,6 +65,10 @@ export declare const FAIL_OPEN_TAGS: {
61
65
  readonly cls: "F";
62
66
  readonly note: "被拒的 CC 导入 prepare(候选超帽)未能收掉 core 已落盘的那条 pending 审批记录。放行的最坏后果 = 共享库里留一条**永远没人要的** pending 行(零权限影响:没有票就兑不动它,而且它连确认都没过)。不留痕就没人知道清理面在漏,故记 F 类;真解是给 permission_rule_approval / permission_rule_ticket 加保留期清扫腿(已列后续件)。";
63
67
  };
68
+ readonly "server.rule-offers.uncovered-detail-dropped": {
69
+ readonly cls: "F";
70
+ readonly note: "design/382 §3.5(S-44):batch offer 的 `uncoveredDetail` 明细座形不合(集外 reason / 非数组 / 行数与 count 座不等 / 超 MAX_UNCOVERED_DETAIL_ROWS)⇒ 只丢这一座、batch 本体照投。放行的最坏后果 = 卡上少几行「这段为什么还会问」的解释(count 座仍在,core 契约明写明细缺席不是断言);把整只 batch 判假才是错的方向(一只真可兑的合取批从卡上消失)。纯展示轴 ⇒ F 类;必须留痕,否则上游明细座漂形在遥测里零痕迹。";
71
+ };
64
72
  readonly "server.rules.ticket-claim-release-failed": {
65
73
  readonly cls: "F";
66
74
  readonly note: "CC 规则导入票的**认领回滚**失败(认领之后的某一步没成 ⇒ 本该把认领放回去,而这次放回本身也抛了)。放行的最坏后果 = 这张票留在已认领态、属主这一轮不能重试 —— 恰好等于加认领回滚**之前**的行为,不是新损失;票随 TTL 自然消失,属主重走一次 prepare 即可拿新票(导入按 core 的设计幂等:同一条规则再兑付只是同一个 dot 的重放)。方向上没有任何权限被放宽(规则**没有**落地才走到这条臂),故 F 类;必须留痕,否则「票为什么突然不能用了」在遥测里没有任何痕迹。";
@@ -165,6 +173,38 @@ export declare const FAIL_OPEN_TAGS: {
165
173
  readonly cls: "F";
166
174
  readonly note: "S-38 裁 (c) 审计半场:local 车道 File ask 审计店(`approval-ask-audit-store.ts`)的**运行期** append(铸造/决议/腿闭/收敛四类行)失败(盘满/只读/fd 失效)⇒ 丢**这一条**审计行,审批路径照常。方向刻意 fail-open:审批可用性不押在审计盘上 —— 盘满时人还得能批,把一次审计写失败升级成审批失败是把「痕迹缺一条」换成「门整个不可用」,方向更坏。boot 期(mkdir/重放)失败则**拒启**(File 店族同律,store 头注成文),不经本 tag。放行的最坏后果 = 该 ask 在崩溃收敛读面上缺席或分臂失真(与 A-075.55 的零痕迹病同向)—— 所以必须留痕:「审计一直在漏写」与「没有审批发生」在读面上同形,不计数则病灶永不显形。detail 带行类与异常文案。";
167
175
  };
176
+ readonly "server.run-terminal.redact-threw": {
177
+ readonly cls: "F";
178
+ readonly note: "四个发射座(errorMessage 终态文案 / 诊断文本 diag / 补丁头 patch-head / 工件 artifact,probe 前缀判别;普查 56 行):S-105 最终轮 [medium]:终态 errorMessage/blockedReason/remoteEnvFailures[].message 脱敏抛出(关键字/前缀臂对 ≥2^23 单段 RangeError,既有,S-106)⇒ 落固定占位 «redacted:oversized»——绝不落原文(安全轴 fail-closed),也绝不阻断终态翻转与 claim 释放(可用性)。方向刻意:一条超大错误体不得把 run 卡在 running。";
179
+ };
180
+ readonly "server.run-terminal.redact-capped": {
181
+ readonly cls: "F";
182
+ readonly note: "S-107:终态自由文本(errorMessage/blockedReason/remoteEnvFailures[].message/error 列/done·failed 行)长度 > REDACT_INPUT_CAP(64KB)⇒ 整段换 «redacted:oversized»(fail-closed 取证丢弃,不截断不扫);计数=多少条终态文案被整段丢弃";
183
+ };
184
+ readonly "server.run-terminal.artifact-capped": {
185
+ readonly cls: "F";
186
+ readonly note: "S-107:leader_run 行上的工件字节(salvaged[].patch / candidatePatch.patch / merge.candidatePatch / merge.rej)长度 > ARTIFACT_INPUT_CAP(256KB)⇒ 整份换 «redacted:oversized-artifact»(fail-closed 取证丢弃;原字节仍在 worker 分支/merge 咽喉);计数=多少份工件被整份丢弃";
187
+ };
188
+ readonly "server.run-terminal.diag-capped": {
189
+ readonly cls: "F";
190
+ readonly note: "S-107:诊断文本座(merge details/measure/grader trace、leader 日志座、planner 样本、journal 预览、workflow_run error cut)长度 > DIAG_INPUT_CAP(256KB)⇒ 整段换 «redacted:oversized-diagnostic» [len=N](fail-closed 取证丢弃;固定窗不 fail-closed 故不开窗);计数=多少段诊断文本被整段丢弃";
191
+ };
192
+ readonly "server.run-terminal.patch-head-cut": {
193
+ readonly cls: "F";
194
+ readonly note: "S-107:进模型的 worker 补丁(collapse 目标 / 冲突解决目标)> DIAG_INPUT_CAP(256KB)⇒ **不内联**,整份换 «redacted:oversized-patch-not-inlined» [len=N](第十三轮开的 256KB 头窗及其裁切守卫在五轮复审后撤回);计数=多少份补丁因超限未内联";
195
+ };
196
+ readonly "server.run-terminal.output-truncated": {
197
+ readonly cls: "F";
198
+ readonly note: "S-107:持久 error 脱后 UTF-8 字节 > 65,000(TiDB TEXT 65,535 字节)⇒ 脱后切并带标记(切在占位里不是泄漏);计数=多少条终态文案被字节封顶截断";
199
+ };
200
+ readonly "server.runs.terminal-ledger-write-failed": {
201
+ readonly cls: "F";
202
+ readonly note: "S-107(全量门 silent-degradation 棘轮抓出的五处静默 `catch {}`):run 终局链里**账本腿**(flush 缓冲 text/reasoning、append `failed` 行)与终态翻转(setTerminal)已不共命运——账本 INSERT 抛不再让 run 卡 running 到 reaper;但那条抛掉的账本行之前是**静默**吞的。detail `arm=` 四臂(flush-clean-cancel / append-clean-cancel / flush-catch / append-thrown-cancel / append-failed):缺的是 run_events 尾巴(缓冲文本或 failed 事件),run 行本身仍是 durable truth,读面/reaper 容忍缺失,故 F 类;计数=多少次终局账本写失败。";
203
+ };
204
+ readonly "server.redact.ppk-eat-to-end": {
205
+ readonly cls: "F";
206
+ readonly note: "S-107 第三十二轮重扫:PuTTY .ppk 容器扫描器(redact.ts redactPpkContainers)在「无合格终止 / 收口后同容器范围再见私钥字段 / 收口后到下一版本头之间夹非空白」三形下从头**吃到输入末尾**(fail-closed 取证丢弃)——终态 errorMessage 座上=整条错误文案变成 «redacted:private-key»,与 redactions_applied_total{private-key} 的诚实替换同形不可分,故要有自己的计数/探针;影子扫不计;计数=多少次整段吃尾。";
207
+ };
168
208
  readonly "server.pg-pool.idle-client-error": {
169
209
  readonly cls: "F";
170
210
  readonly note: "S-55(test [5864] 独立复现):pg 连接池里一条连接被外部终止(pg_terminate_backend / 云端故障切换 / LB 空闲回收)或自身网络错误。**两态一 tag,detail 判别**(`state=idle` / `state=checked-out`;先例=park 墓碑行的两臂 detail 判别):①idle 态 —— node-postgres 语义(pg-pool@3.14.0 makeIdleListener):池在 emit 'error' **之前**已 `_remove` 掉该 client,坏连接自然淘汰,下次 acquire 发新连接,在飞查询零影响;修前 Pool 上无 'error' 监听 ⇒ EventEmitter 把事件转 throw ⇒ uncaughtException ⇒ 整进程 exit 1。②checked-out 态(codex R1-[high] 补,同批修)—— pg-pool 借出时摘 idle 监听(index.js:344),`pool.connect()` 显式持有的连接(事务)在 checkout 期死时 client 直接 emit 'error' 同样打死进程;守卫只观察:中毒 client 的后续查询由 pg 以 `_queryable=false` 响亮拒,归还时池淘汰;同一次 checkout 的死可能双发事件(FATAL 消息 + socket end),warn 逐枚、计数按 checkout 去重。放行的最坏后果 = 下一次 acquire 多付一次建连,故 F 类。必须留痕:「连接不断被外部杀」(故障切换风暴 / 回收策略过激 / 有人在库上清连接)与「一切正常」在服务面同形;每次事件另有逐次结构化 warn(`pg_pool_idle_client_error` / `pg_pool_checked_out_client_error`,含 code/severity),本计数让频率曲线可读。`pool.query()` 快路不在洞内(pg-pool 自带 checkout 期 once('error') 守卫)。mysql2 孪生(tidb-pool)**不同病**:PoolConnection 构造器恒挂 once('error') 库内吞并淘汰、借出期同在(坐标成文在 createTidbPool 头注),故无同族 tag。";
@@ -4,6 +4,10 @@ export const FAIL_OPEN_TAGS = {
4
4
  cls: "F",
5
5
  note: "design/383 S-1(applyMemoryOptOutGrant):本地 grant 腿故障时的**披露钩子**(onFault → warn 日志)自己抛了。钩子只是观测面:吞掉它保住的是「resolver 不因一行日志失败而 reject」(那会把一条腿的故障扩成整车故障 —— core 的 resolver-throw 降级臂作废同一次解析里 center 已答好的 allowWorkflows/allowFork);代价是**那一行披露丢了**,而 `allowMemoryOptOut` 键缺席这件事本身不受影响(fail-closed/fail-safe 极性由 core 按姿态判)。本计数让「披露钩子一直在炸」显形。",
6
6
  },
7
+ "server.memory-optout-verdict-absence.warn-hook-threw": {
8
+ cls: "F",
9
+ note: "S-76(applyGovernedVerdictAbsenceWarning):`governed` + center-only verdict 源的部署上,**首次**解析到 `allowMemoryOptOut` 缺席时那一行**一次性披露**钩子自己抛了。与上一条同族、同理由:钩子只是观测面,吞掉保住的是「resolver 不因一行日志失败而 reject」(core 的 resolver-throw 降级臂会作废同一次解析里 center 已答好的 allowWorkflows/allowFork);代价是**「center 从不发这个键」这件事少了一行运行期证据**(启动期那行 `memory_capture_governed_center_only_source` 仍在,拒/放的极性也不受影响 —— 缺席由 core 按姿态判 fail-closed)。本计数让「披露钩子一直在炸」显形。",
10
+ },
7
11
  "server.run-store.file-hydrate-unreadable": {
8
12
  cls: "P-DEBT",
9
13
  note: "件6(#43 案 [4281]):`FileRunStore.hydrate()` 的 `readdirSync(runs/)` 失败 ⇒ 引擎以**空账**起动。旧形是空 catch + return:data root 不可读(权限/挂载丢失/路径被换走)时零告警,此后每一次 getRun 恒 miss —— 而「恒 miss」正是 #43 现场看到的形,一次环境故障被伪装成「本来就没有这条 run」。方向问题在于吞掉之后被读成的是「证不出有行 ⇒ 没有行」,两句话不等价。**缺省不拒启是刻意保留的行为**,所以这条留作债:响亮化(error 级启动日志带 runs 绝对路径+异常文案)+ 本计数,让它在遥测里显形而不是只活在一行日志里。⚠️ 2026-08-29(S-46 件B)起终局格已兑现:部署可用 `RUN_STORE_STRICT_HYDRATE=on` 把这一格换成 **fail-closed 启动门**(拒启,遗言同样带绝对路径+异常文案);本计数因此只在**缺省 off** 档上产生 —— 读遥测时别把「计数为零」直接读成「没有部署踩到这条路」,也可能是它们开了拒启门、根本起不来。",
@@ -52,6 +56,10 @@ export const FAIL_OPEN_TAGS = {
52
56
  cls: "F",
53
57
  note: "被拒的 CC 导入 prepare(候选超帽)未能收掉 core 已落盘的那条 pending 审批记录。放行的最坏后果 = 共享库里留一条**永远没人要的** pending 行(零权限影响:没有票就兑不动它,而且它连确认都没过)。不留痕就没人知道清理面在漏,故记 F 类;真解是给 permission_rule_approval / permission_rule_ticket 加保留期清扫腿(已列后续件)。",
54
58
  },
59
+ "server.rule-offers.uncovered-detail-dropped": {
60
+ cls: "F",
61
+ note: "design/382 §3.5(S-44):batch offer 的 `uncoveredDetail` 明细座形不合(集外 reason / 非数组 / 行数与 count 座不等 / 超 MAX_UNCOVERED_DETAIL_ROWS)⇒ 只丢这一座、batch 本体照投。放行的最坏后果 = 卡上少几行「这段为什么还会问」的解释(count 座仍在,core 契约明写明细缺席不是断言);把整只 batch 判假才是错的方向(一只真可兑的合取批从卡上消失)。纯展示轴 ⇒ F 类;必须留痕,否则上游明细座漂形在遥测里零痕迹。",
62
+ },
55
63
  "server.rules.ticket-claim-release-failed": {
56
64
  cls: "F",
57
65
  note: "CC 规则导入票的**认领回滚**失败(认领之后的某一步没成 ⇒ 本该把认领放回去,而这次放回本身也抛了)。放行的最坏后果 = 这张票留在已认领态、属主这一轮不能重试 —— 恰好等于加认领回滚**之前**的行为,不是新损失;票随 TTL 自然消失,属主重走一次 prepare 即可拿新票(导入按 core 的设计幂等:同一条规则再兑付只是同一个 dot 的重放)。方向上没有任何权限被放宽(规则**没有**落地才走到这条臂),故 F 类;必须留痕,否则「票为什么突然不能用了」在遥测里没有任何痕迹。",
@@ -156,6 +164,17 @@ export const FAIL_OPEN_TAGS = {
156
164
  cls: "F",
157
165
  note: "S-38 裁 (c) 审计半场:local 车道 File ask 审计店(`approval-ask-audit-store.ts`)的**运行期** append(铸造/决议/腿闭/收敛四类行)失败(盘满/只读/fd 失效)⇒ 丢**这一条**审计行,审批路径照常。方向刻意 fail-open:审批可用性不押在审计盘上 —— 盘满时人还得能批,把一次审计写失败升级成审批失败是把「痕迹缺一条」换成「门整个不可用」,方向更坏。boot 期(mkdir/重放)失败则**拒启**(File 店族同律,store 头注成文),不经本 tag。放行的最坏后果 = 该 ask 在崩溃收敛读面上缺席或分臂失真(与 A-075.55 的零痕迹病同向)—— 所以必须留痕:「审计一直在漏写」与「没有审批发生」在读面上同形,不计数则病灶永不显形。detail 带行类与异常文案。",
158
166
  },
167
+ "server.run-terminal.redact-threw": {
168
+ cls: "F",
169
+ note: "四个发射座(errorMessage 终态文案 / 诊断文本 diag / 补丁头 patch-head / 工件 artifact,probe 前缀判别;普查 56 行):S-105 最终轮 [medium]:终态 errorMessage/blockedReason/remoteEnvFailures[].message 脱敏抛出(关键字/前缀臂对 ≥2^23 单段 RangeError,既有,S-106)⇒ 落固定占位 «redacted:oversized»——绝不落原文(安全轴 fail-closed),也绝不阻断终态翻转与 claim 释放(可用性)。方向刻意:一条超大错误体不得把 run 卡在 running。",
170
+ },
171
+ "server.run-terminal.redact-capped": { cls: "F", note: "S-107:终态自由文本(errorMessage/blockedReason/remoteEnvFailures[].message/error 列/done·failed 行)长度 > REDACT_INPUT_CAP(64KB)⇒ 整段换 «redacted:oversized»(fail-closed 取证丢弃,不截断不扫);计数=多少条终态文案被整段丢弃" },
172
+ "server.run-terminal.artifact-capped": { cls: "F", note: "S-107:leader_run 行上的工件字节(salvaged[].patch / candidatePatch.patch / merge.candidatePatch / merge.rej)长度 > ARTIFACT_INPUT_CAP(256KB)⇒ 整份换 «redacted:oversized-artifact»(fail-closed 取证丢弃;原字节仍在 worker 分支/merge 咽喉);计数=多少份工件被整份丢弃" },
173
+ "server.run-terminal.diag-capped": { cls: "F", note: "S-107:诊断文本座(merge details/measure/grader trace、leader 日志座、planner 样本、journal 预览、workflow_run error cut)长度 > DIAG_INPUT_CAP(256KB)⇒ 整段换 «redacted:oversized-diagnostic» [len=N](fail-closed 取证丢弃;固定窗不 fail-closed 故不开窗);计数=多少段诊断文本被整段丢弃" },
174
+ "server.run-terminal.patch-head-cut": { cls: "F", note: "S-107:进模型的 worker 补丁(collapse 目标 / 冲突解决目标)> DIAG_INPUT_CAP(256KB)⇒ **不内联**,整份换 «redacted:oversized-patch-not-inlined» [len=N](第十三轮开的 256KB 头窗及其裁切守卫在五轮复审后撤回);计数=多少份补丁因超限未内联" },
175
+ "server.run-terminal.output-truncated": { cls: "F", note: "S-107:持久 error 脱后 UTF-8 字节 > 65,000(TiDB TEXT 65,535 字节)⇒ 脱后切并带标记(切在占位里不是泄漏);计数=多少条终态文案被字节封顶截断" },
176
+ "server.runs.terminal-ledger-write-failed": { cls: "F", note: "S-107(全量门 silent-degradation 棘轮抓出的五处静默 `catch {}`):run 终局链里**账本腿**(flush 缓冲 text/reasoning、append `failed` 行)与终态翻转(setTerminal)已不共命运——账本 INSERT 抛不再让 run 卡 running 到 reaper;但那条抛掉的账本行之前是**静默**吞的。detail `arm=` 四臂(flush-clean-cancel / append-clean-cancel / flush-catch / append-thrown-cancel / append-failed):缺的是 run_events 尾巴(缓冲文本或 failed 事件),run 行本身仍是 durable truth,读面/reaper 容忍缺失,故 F 类;计数=多少次终局账本写失败。" },
177
+ "server.redact.ppk-eat-to-end": { cls: "F", note: "S-107 第三十二轮重扫:PuTTY .ppk 容器扫描器(redact.ts redactPpkContainers)在「无合格终止 / 收口后同容器范围再见私钥字段 / 收口后到下一版本头之间夹非空白」三形下从头**吃到输入末尾**(fail-closed 取证丢弃)——终态 errorMessage 座上=整条错误文案变成 «redacted:private-key»,与 redactions_applied_total{private-key} 的诚实替换同形不可分,故要有自己的计数/探针;影子扫不计;计数=多少次整段吃尾。" },
159
178
  "server.pg-pool.idle-client-error": {
160
179
  cls: "F",
161
180
  note: "S-55(test [5864] 独立复现):pg 连接池里一条连接被外部终止(pg_terminate_backend / 云端故障切换 / LB 空闲回收)或自身网络错误。**两态一 tag,detail 判别**(`state=idle` / `state=checked-out`;先例=park 墓碑行的两臂 detail 判别):①idle 态 —— node-postgres 语义(pg-pool@3.14.0 makeIdleListener):池在 emit 'error' **之前**已 `_remove` 掉该 client,坏连接自然淘汰,下次 acquire 发新连接,在飞查询零影响;修前 Pool 上无 'error' 监听 ⇒ EventEmitter 把事件转 throw ⇒ uncaughtException ⇒ 整进程 exit 1。②checked-out 态(codex R1-[high] 补,同批修)—— pg-pool 借出时摘 idle 监听(index.js:344),`pool.connect()` 显式持有的连接(事务)在 checkout 期死时 client 直接 emit 'error' 同样打死进程;守卫只观察:中毒 client 的后续查询由 pg 以 `_queryable=false` 响亮拒,归还时池淘汰;同一次 checkout 的死可能双发事件(FATAL 消息 + socket end),warn 逐枚、计数按 checkout 去重。放行的最坏后果 = 下一次 acquire 多付一次建连,故 F 类。必须留痕:「连接不断被外部杀」(故障切换风暴 / 回收策略过激 / 有人在库上清连接)与「一切正常」在服务面同形;每次事件另有逐次结构化 warn(`pg_pool_idle_client_error` / `pg_pool_checked_out_client_error`,含 code/severity),本计数让频率曲线可读。`pool.query()` 快路不在洞内(pg-pool 自带 checkout 期 once('error') 守卫)。mysql2 孪生(tidb-pool)**不同病**:PoolConnection 构造器恒挂 once('error') 库内吞并淘汰、借出期同在(坐标成文在 createTidbPool 头注),故无同族 tag。",
@@ -256,7 +256,7 @@ export function createMetrics() {
256
256
  m.counter("memory_knn_query_failed_total", "Memory KNN searchScored queries that failed at runtime (S22)");
257
257
  m.counter("memory_dedup_folded_total", "Memory writes folded into an existing near-duplicate row (LOW)");
258
258
  m.counter("memory_dedup_probe_failed_total", "Memory dedup KNN probes that failed and fell through to INSERT (LOW)");
259
- m.counter("redactions_applied_total", "redactSecrets rewrites (S24), by pattern label");
259
+ m.counter("redactions_applied_total", "redactSecrets rewrites (S24), by pattern label and `arm` (S-88: full vs prefix-fragment arm — which RULE fired, NOT proof the text was upstream-truncated)");
260
260
  m.counter("stale_cache_conflict_total", "Warm session-cache entries evicted by a cross-instance write while cached (S25)");
261
261
  m.counter("breaker_writethrough_failed_total", "Circuit-breaker cross-replica write-through failures (LOW), by backend");
262
262
  m.counter("web_search_bad_payload_total", "Web-search provider responses whose results field was not an array (LOW), by provider");
@@ -0,0 +1,120 @@
1
+ import type { RunRecord } from "../plugins/store-contracts.js";
2
+ /** 一次终局写口交给观测面的原料(store 侧装配,**未脱敏** —— 脱敏在本模块内做,见 {@link notifyRunTerminal})。 */
3
+ export interface RunTerminalRecord {
4
+ /** 这条 run 的账本 id = `GET /v1/runs/:id` 的 id(本仓 run 行主键名为 taskId,同一个值)。 */
5
+ runId: string;
6
+ sessionId: string;
7
+ owner: string | null;
8
+ status: RunRecord["status"];
9
+ /** 终局结果里的引擎侧 taskId —— 只在与 {@link RunTerminalRecord.runId} **不同**时进日志(sync 腿上 core 会把它
10
+ * 铸成 sessionId,两者同值时重复一遍只是噪音)。 */
11
+ resultTaskId?: string | undefined;
12
+ model?: string | undefined;
13
+ errorCode?: string | undefined;
14
+ errorMessage?: string | undefined;
15
+ /** 行创建到终局的墙钟毫秒(store 侧用行的 createdAt 算;算不出=不传,键缺席)。 */
16
+ elapsedMs?: number | undefined;
17
+ }
18
+ /** 观测者收到的是**已判级 + 已脱敏 + 已截断**的成品——装配层只负责把它送进 logger。 */
19
+ export type RunTerminalObserver = (level: "error" | "info", fields: Record<string, unknown>) => void;
20
+ /**
21
+ * 终局行的级别判据。`undefined` = 不打行。
22
+ *
23
+ * `blocked` 刻意不打:它是**治理判决**(门拒绝了这次执行),不是失败——它有自己的门遥测与
24
+ * `GET /v1/runs/:id` 读面,混进 `run_failed` 会把「模型/网络出事了」这条读法冲淡。
25
+ */
26
+ export declare function runTerminalLogLevel(status: RunRecord["status"], errorCode: string | undefined): "error" | "info" | undefined;
27
+ /** 装配点(`main.ts` 唯一)。传 `undefined` 卸载(测试隔离)。 */
28
+ export declare function setRunTerminalObserver(fn: RunTerminalObserver | undefined): void;
29
+ /**
30
+ * store 的终局写口在 **CAS 赢了之后**调用它。未装配观测者 ⇒ 无操作(纯内存/无 boot 的单测车道)。
31
+ *
32
+ * 脱敏:`errorMessage` 过 `redactSecrets` 再截断到 {@link MESSAGE_CAP} —— provider 的错误体
33
+ * 会原样回显请求片段(取证里的 403 体带订阅链接;别的 provider 会回显 Authorization 头前缀),
34
+ * 而引擎日志是运维面、常被整条采集走。`errorCode` / `model` 是闭集式短标量,verbatim。
35
+ */
36
+ export declare function notifyRunTerminal(rec: RunTerminalRecord): void;
37
+ /** S-105(test [ref] 旁生):终态**持久化**的 `errorMessage` 与日志副本同一脱敏口——provider 错误体(403 体带回显请求头/
38
+ * 订阅链接)里的密钥形不得原文落 run 行再由 `GET /v1/runs/:id` 直出。三 run store 的 `setTerminal` 写入前同调此函数;
39
+ * `undefined` 透传(缺席仍缺席),不截断(日志上限是日志的事,行上保留全文供取证)。 */
40
+ /** r4 最终轮 [medium]:`redactSecrets` 的关键字/前缀臂对 ≥2^23 单段仍会抛 `RangeError`(既有,S-106 登记);S-105 把脱敏
41
+ * 挪到 `setTerminal` **入口**后,这个抛会先于 CAS/claim 释放 ⇒ run 卡 `running`、会话 claim 悬挂到 reaper。这里 **fail-closed
42
+ * 不抛**:脱敏失败 ⇒ 落固定占位 `«redacted:oversized»`(绝不落原文,也绝不阻断终态翻转),并走既有 fail-open 遥测 tag。 */
43
+ export declare const OVERSIZED_REDACTION_PLACEHOLDER = "\u00ABredacted:oversized\u00BB";
44
+ /** S-107 窄范围重扫 [medium]:`redactSecrets` 对 PEM 起始栅栏密集的多 MB 文案**不抛但慢**(实测 1MB 1.4s / 8MB 7.9s 同步阻塞
45
+ * 事件循环),而终态座每次写要扫两遍(appendEvent + setTerminal 入口)、每次读再扫一遍。超过本上限的自由文本**整段**换占位
46
+ * (fail-closed:不截断——截断会把密钥切成半截,[ref] 的病;不扫——O(1)),取证面丢的是「>256KB 的错误文案」这一档。 */
47
+ /** codex r2 [medium]:上限按**边界最坏耗时**定——PEM 栅栏密集形 256KB 一次扫 0.25–0.46s、终态一次写扫两遍再加读面,一行
48
+ * 能让副本卡 ~1s;64KB 时同形 ≤ ~0.1s/次。正常 provider 错误体 < 10KB,>64KB 的错误文案只剩取证价值(整段占位丢弃)。 */
49
+ export declare const REDACT_INPUT_CAP = 65536;
50
+ /** 第十七轮重扫 [medium]:TiDB `task_run.error` 是 TEXT(65,535 **字节**),而上限按**字符**(65,536)量,多字节 / 占位膨胀(`«»` 各 2 字节、
51
+ * `«redacted:key»` 比短匹配长)都能让脱后串超列 ⇒ UPDATE 抛 ER_DATA_TOO_LONG、事务回滚、claim 不释放、run 卡 running 到 reaper。
52
+ * 脱敏**之后**按字节封顶(占位已替换完,切在占位中间不是泄漏)并带标记。 */
53
+ export declare const REDACT_OUTPUT_MAX_BYTES = 65000;
54
+ export declare function redactErrorMessage(raw: string | undefined): string | undefined;
55
+ /** 同口:终态 `result` 整块也持久化(`GET /v1/runs/:id` 直出 `result.errorMessage`)——写入前把内层 `errorMessage` 脱敏,
56
+ * 其余键逐字不动;`undefined`/`null` 透传。 */
57
+ export declare function redactTerminalResult<T extends {
58
+ errorMessage?: string | undefined;
59
+ blockedReason?: string | undefined;
60
+ remoteEnvFailures?: readonly {
61
+ message?: string | undefined;
62
+ }[] | undefined;
63
+ } | null | undefined>(result: T): T;
64
+ /** S-107(第五轮合并重扫 wf_f00c00bc 两根 [high]):同一个 `TaskResult` 除 run 行外还被逐字追加进 run_events 的 `done` 行
65
+ * (bg/verify/cascade/stream/resume 五腿),`failed` 行带原文 `errorMessage`,`GET /v1/runs/:id/events` 原样回放。
66
+ * 三孪生 `appendEvent` 入口 + 事件读面(旧行纵深)同调本函数:只认 `done.result`(整块走 `redactTerminalResult`)与
67
+ * `failed.errorMessage`,其余事件类型/键逐字不动(它们各有自己的 sink 侧脱敏);形不合(非对象)原样返回,不抛。 */
68
+ export declare function redactLedgerEventData(type: string, data: unknown): unknown;
69
+ /** S-107 第三轮窄范围重扫 [high]×2:leader 面的三处原文座——①`WorkerReport.error`(生产路径经 `runWorker` 构造,`fanout.ts`
70
+ * 的 runTaskStream 回退座不是生产座);②`LeaderResult.error`(plan/provision 失败带 `e.message`);③`merge.reason`
71
+ * (coordinator push 失败把 `git clone <带凭据的 durableRemote>` 命令行整段回显)。全部随 leader_run.result JSON 落库并经
72
+ * `GET /v1/leader/:id` 直出。修=**咽喉**:fanOut 边界对每份报告脱一次(不问谁产的),endpoint 落库前 + 读面各脱一次
73
+ * (幂等;旧行纵深)。只碰这三个自由文本键,其余逐字。 */
74
+ export declare function redactWorkerReportError<T extends {
75
+ error?: string | undefined;
76
+ checkpointGate?: unknown;
77
+ }>(report: T): T;
78
+ /** codex r5 [high]:`WorkerReport.checkpointGate`(core `CheckpointGate`)整块随 LeaderResult 落库/直出——`reason` 与
79
+ * `riskDescriptor.summary`/`shadowedRule` 是自由文本([ref] 实测 summary 带 PEM 明文;[ref] R3 登记),按类型表脱这三叶;
80
+ * `kind`/`toolName`/`axes`/`severity`/`touchedPaths` 闭集或路径,逐字。非对象形原样返回。 */
81
+ export declare function redactCheckpointGate(gate: unknown): unknown;
82
+ /** 工件字节(patch / *.rej 拒绝块):codex r6 [high]——拒绝块能带 `git clone https://u:TOKEN@host` 或 `_authToken=` 行,
83
+ * 「工件保真」不能让原凭据上元数据读面。做**原位密钥形替换**(与预览同一张规则表,字节只在密钥处变)、**不截断**;
84
+ * 单份上限 256KB(`ARTIFACT_INPUT_CAP`,第八轮重扫:不封顶=worker 可控 DoS)——超过整份换占位,这是有意的取证丢弃
85
+ * (原字节仍在 worker 分支 / merge 咽喉);扫描器抛 ⇒ 占位(fail-closed)。server 无任何消费方用持久化的这份字节回放。 */
86
+ /** 诊断文本(stdout/stderr/err.message/模型输出)的「先脱后切」——第九轮重扫 [medium]×3 + 第十轮 [high]×2 + codex r9 [high]:
87
+ * ①不能把整段喂进 64KB 上限臂(长测试日志整段占位、灌水计数);②**固定 PAD 的窗不是 fail-closed**:比 PAD 长的密钥形
88
+ * (>4096 的 URL 密码 / JSON 值 / PEM 体)锚点落在窗外,窗内只剩无锚的体,任何规则都认不出;窗在脱敏下缩短时残片还会
89
+ * 落进最终切片。所以**不开窗**:文本 ≤ 256KB ⇒ 整段扫(guard,不封顶于 64KB;PEM 密集形最坏 ~0.4s/座)再切;> 256KB ⇒
90
+ * 整段换占位并带原长(fail-closed 取证丢弃,单独计数:普查第 59 行)。 */
91
+ export declare const DIAG_INPUT_CAP = 262144;
92
+ export declare const OVERSIZED_DIAG_PLACEHOLDER = "\u00ABredacted:oversized-diagnostic\u00BB";
93
+ export declare function redactDiagnosticText(text: string): string;
94
+ export declare function redactHead(text: string, keep: number): string;
95
+ /** 进模型的 worker 补丁头(collapse 目标 / 冲突解决目标):**不开窗**。第十三轮为保留大 diff 的可复用头部开了 256KB 头窗,
96
+ * 此后 codex r12–r15 与第十五至十七轮重扫五轮各证一次「窗上的裁切守卫可被打穿」(无上界的 JSON 值/URL 密码臂;脱前/脱后坐标
97
+ * 错位;PEM 逐块;前面一处脱敏会**吃掉**守卫所依赖的锚点)——同 S-103 的教训:猜边界的规则总有反例。终形=补丁 ≤256KB 整段扫
98
+ * 再切 keep(完全正确);>256KB **不内联**(占位带原长,tag `patch-head-cut`):冲突解决者仍有工作树里的冲突标记,collapse solo
99
+ * 对 lockfile 级大 diff 失去内联上下文(worker 分支仍在)——这是有意成文的安全轴取舍。 */
100
+ export declare const OVERSIZED_PATCH_PLACEHOLDER = "\u00ABredacted:oversized-patch-not-inlined\u00BB";
101
+ export declare function redactPatchHead(patch: string, keep: number): string;
102
+ export declare function redactTail(text: string, keep: number): string;
103
+ export declare const ARTIFACT_INPUT_CAP = 262144;
104
+ export declare const OVERSIZED_ARTIFACT_PLACEHOLDER = "\u00ABredacted:oversized-artifact\u00BB";
105
+ export declare function redactArtifactText(raw: string): string;
106
+ export declare function redactLeaderResult(result: unknown): unknown;
107
+ /** 同上,附带「有没有任何叶子变过」——读面记忆用它区分「本就干净的行」(只记 id,不留体)与「旧脏行」(留脱后体)。 */
108
+ export declare function redactLeaderResultWithFlag(result: unknown): {
109
+ value: unknown;
110
+ changed: boolean;
111
+ };
112
+ export declare function memoRedactedRead<T>(seat: string, raw: T, redact: (v: T) => T): T;
113
+ /** run 终态 result 的读面记忆只按**可脱敏的三叶**(errorMessage / blockedReason / remoteEnvFailures[].message)做键与留体——
114
+ * 第十六轮重扫:按整块 result 摘要每次轮询要 stringify 一份可能数 MB 的 result,且大 result 的脏行永远存不进去。三叶缺席
115
+ * ⇒ O(1) 直返(`redactTerminalResult` 本就同引用)。 */
116
+ export declare function memoRedactTerminalResult<T extends Parameters<typeof redactTerminalResult>[0]>(seat: string, result: T): T;
117
+ export declare function diffScanShadowCountForTest(): number;
118
+ export declare function resetDiffScanShadowCountForTest(): void;
119
+ export declare function resetReadRedactionMemoForTest(): void;
120
+ //# sourceMappingURL=run-terminal-log.d.ts.map