@sema-agent/server 7.98.0 → 7.100.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 (153) hide show
  1. package/MIGRATION.md +19 -0
  2. package/README.md +1 -1
  3. package/README.zh-CN.md +1 -1
  4. package/USAGE.md +86 -60
  5. package/deploy/sema-up/lib/envf.sh +110 -42
  6. package/deploy/sema-up/sema-up.sh +76 -34
  7. package/dist/approval-ask-machine.d.ts +10 -0
  8. package/dist/approval-ask-machine.js +3 -0
  9. package/dist/approval-card.d.ts +74 -1
  10. package/dist/approval-card.js +41 -8
  11. package/dist/approval.d.ts +44 -20
  12. package/dist/approval.js +14 -6
  13. package/dist/boot/config-center.d.ts +16 -8
  14. package/dist/boot/config-center.js +206 -103
  15. package/dist/boot/execution-env.d.ts +1 -2
  16. package/dist/boot/execution-env.js +2 -2
  17. package/dist/boot/leader.js +12 -9
  18. package/dist/boot/memory-consolidation.d.ts +38 -0
  19. package/dist/boot/memory-consolidation.js +35 -0
  20. package/dist/boot/org-memory.d.ts +2 -1
  21. package/dist/boot/org-memory.js +9 -5
  22. package/dist/boot/resolve-spec.d.ts +0 -1
  23. package/dist/boot/resolve-spec.js +4 -3
  24. package/dist/boot/runner-deps.d.ts +2 -2
  25. package/dist/boot/runtime-caps.js +4 -4
  26. package/dist/boot/stage-05-execution-env.d.ts +1 -1
  27. package/dist/boot/stage-06-runners.d.ts +2 -2
  28. package/dist/boot/stage-06-runners.js +3 -3
  29. package/dist/boot/stage-07-capability-layer.d.ts +2 -3
  30. package/dist/boot/stage-07-capability-layer.js +6 -6
  31. package/dist/boot/stage-08-reapers.d.ts +3 -4
  32. package/dist/boot/stage-08-reapers.js +2 -2
  33. package/dist/boot/stage-09-leader.d.ts +2 -3
  34. package/dist/boot/stage-10-http-server.d.ts +2 -3
  35. package/dist/boot/stage-10-http-server.js +2 -2
  36. package/dist/capabilities/center-plugins.d.ts +14 -2
  37. package/dist/capabilities/center-plugins.js +13 -5
  38. package/dist/capabilities/center-prompts.d.ts +3 -0
  39. package/dist/capabilities/center-prompts.js +1 -1
  40. package/dist/capabilities/hands-lane.js +2 -1
  41. package/dist/capabilities/scenarios.js +18 -5
  42. package/dist/capabilities/tool-defer.d.ts +2 -2
  43. package/dist/center-credential.d.ts +112 -0
  44. package/dist/center-credential.js +131 -0
  45. package/dist/config-catalog.d.ts +2 -2
  46. package/dist/config-catalog.js +15 -15
  47. package/dist/config-center/apply-effective.d.ts +34 -39
  48. package/dist/config-center/apply-effective.js +100 -64
  49. package/dist/config-center/apply-ledger.d.ts +35 -16
  50. package/dist/config-center/apply-ledger.js +25 -18
  51. package/dist/config-center/center-request.d.ts +60 -0
  52. package/dist/config-center/center-request.js +71 -0
  53. package/dist/config-center/facade.d.ts +11 -5
  54. package/dist/config-center/facade.js +1 -1
  55. package/dist/config-center/hot-keys-registry.d.ts +11 -4
  56. package/dist/config-center/hot-keys-registry.js +11 -7
  57. package/dist/config-center/http-client.d.ts +17 -9
  58. package/dist/config-center/http-client.js +27 -36
  59. package/dist/config-center/restart-signal.d.ts +21 -14
  60. package/dist/config-center/restart-signal.js +16 -22
  61. package/dist/config-center/skills-mcp.d.ts +4 -1
  62. package/dist/config-center/skills-mcp.js +2 -2
  63. package/dist/config-center/types.d.ts +3 -3
  64. package/dist/config-invariants.d.ts +2 -2
  65. package/dist/config-invariants.js +0 -8
  66. package/dist/config-lkg.d.ts +47 -8
  67. package/dist/config-lkg.js +107 -20
  68. package/dist/config-provider.d.ts +8 -7
  69. package/dist/config-provider.js +5 -5
  70. package/dist/config-types.d.ts +30 -11
  71. package/dist/config.js +11 -13
  72. package/dist/cross-session-settings.js +1 -1
  73. package/dist/fleet-client.d.ts +8 -8
  74. package/dist/fleet-client.js +7 -7
  75. package/dist/fleet-lease.d.ts +11 -11
  76. package/dist/fleet-lease.js +17 -13
  77. package/dist/host-lsp-manager.d.ts +29 -0
  78. package/dist/host-lsp-manager.js +14 -0
  79. package/dist/hosted-posture.d.ts +49 -93
  80. package/dist/hosted-posture.js +1 -25
  81. package/dist/http/active-run-conflict.d.ts +2 -12
  82. package/dist/http/active-run-conflict.js +1 -6
  83. package/dist/http/admission.js +8 -0
  84. package/dist/http/resume-legs.d.ts +1 -1
  85. package/dist/http/resume-legs.js +14 -10
  86. package/dist/http/route-ctx.d.ts +2 -2
  87. package/dist/http/routes/approvals-assistant.d.ts +1 -1
  88. package/dist/http/routes/approvals-assistant.js +2 -2
  89. package/dist/http/routes/memory-compliance.js +5 -0
  90. package/dist/http/routes/memory-origin.js +5 -0
  91. package/dist/http/routes/sessions.js +6 -0
  92. package/dist/http/routes/tasks.js +7 -6
  93. package/dist/http/server.d.ts +6 -5
  94. package/dist/http/wire-types.d.ts +6 -3
  95. package/dist/leader/wire.d.ts +20 -12
  96. package/dist/leader/wire.js +7 -6
  97. package/dist/memory-layer-preflight.d.ts +46 -0
  98. package/dist/memory-layer-preflight.js +121 -0
  99. package/dist/memory-operator-faces.js +17 -1
  100. package/dist/observability/corrupt-read-seat.d.ts +21 -4
  101. package/dist/observability/corrupt-read-seat.js +31 -0
  102. package/dist/observability/fail-open.d.ts +8 -4
  103. package/dist/observability/fail-open.js +8 -4
  104. package/dist/observability/run-terminal.js +1 -0
  105. package/dist/observability/secret-env-scrub.d.ts +3 -26
  106. package/dist/observability/secret-env-scrub.js +3 -2
  107. package/dist/orchestration/workflow-completion-inbox.d.ts +31 -23
  108. package/dist/orchestration/workflow-completion-inbox.js +8 -3
  109. package/dist/plugins/approval-ask-store-file.d.ts +20 -2
  110. package/dist/plugins/approval-ask-store-file.js +21 -3
  111. package/dist/plugins/approval-ask-store-memory.d.ts +2 -0
  112. package/dist/plugins/approval-ask-store-memory.js +39 -13
  113. package/dist/plugins/approval-ask-store-sql.d.ts +24 -5
  114. package/dist/plugins/approval-ask-store-sql.js +27 -3
  115. package/dist/plugins/checkpoint-store-sql.d.ts +9 -3
  116. package/dist/plugins/checkpoint-store-sql.js +19 -2
  117. package/dist/plugins/local-checkpoint-store.js +2 -0
  118. package/dist/plugins/mailbox-store-sql.d.ts +7 -5
  119. package/dist/plugins/mailbox-store-sql.js +6 -4
  120. package/dist/plugins/permission-rule-store-file.js +3 -2
  121. package/dist/plugins/remote-env-host.d.ts +3 -1
  122. package/dist/plugins/remote-env-host.js +3 -2
  123. package/dist/plugins/sql-json-column.d.ts +16 -3
  124. package/dist/plugins/sql-json-column.js +15 -4
  125. package/dist/plugins/web-search.d.ts +42 -22
  126. package/dist/plugins/web-search.js +141 -49
  127. package/dist/project-memory.d.ts +28 -1
  128. package/dist/project-memory.js +22 -22
  129. package/dist/run-local.js +6 -7
  130. package/dist/runs.js +6 -5
  131. package/dist/runtime-caps-resolver.d.ts +8 -8
  132. package/dist/runtime-caps-resolver.js +16 -14
  133. package/dist/runtime-governance.d.ts +50 -3
  134. package/dist/runtime-governance.js +5 -0
  135. package/dist/sealed-key.js +3 -7
  136. package/dist/server-secret-env.d.ts +43 -0
  137. package/dist/server-secret-env.js +18 -0
  138. package/dist/task-settings.d.ts +1 -1
  139. package/dist/tool-approval.d.ts +144 -9
  140. package/dist/tool-approval.js +74 -18
  141. package/dist/trace/core-keyset-guard.d.ts +5 -5
  142. package/dist/trace/ledger-sink.js +2 -2
  143. package/dist/trace/project.d.ts +13 -0
  144. package/dist/trace/project.js +22 -5
  145. package/dist/trace/projection-drop.d.ts +2 -0
  146. package/dist/trace/projection-drop.js +6 -0
  147. package/dist/trace/sema-provenance.d.ts +4 -1
  148. package/dist/trace/sema-provenance.js +1 -1
  149. package/dist/trace/task-notification-facets.d.ts +65 -0
  150. package/dist/trace/task-notification-facets.js +31 -0
  151. package/dist/trace/wire-projection-faces.d.ts +3 -3
  152. package/dist/trace/wire-projection-faces.js +3 -3
  153. package/package.json +5 -4
@@ -1,3 +1,4 @@
1
+ import { type PlatformPath } from "node:path";
1
2
  import type { ProjectMemoryLoad } from "@sema-agent/core";
2
3
  interface ProjectMemoryCtx {
3
4
  cwd: string;
@@ -7,9 +8,35 @@ interface ProjectMemoryCtx {
7
8
  sessionId?: string;
8
9
  phase?: "fresh" | "resume" | "post-compact" | "post-clear";
9
10
  }
11
+ /** The instruction-file name chain: the CLAUDE family first, the AGENTS family only when no CLAUDE file is
12
+ * present. The FIRST hit is the single declared instruction source.
13
+ *
14
+ * Order follows CC 2.1.280 (clay ruling [ref], 2026-09-23, board [ref] RG-2). It replaces the core 1.302 S-1
15
+ * order, where AGENTS.md won over CLAUDE.md; [ref] supersedes that. In CC 2.1.280:
16
+ * - cli280.js:185162 — the default mode `claude-md-or-agents-md`: "a project with no CLAUDE.md of its own gets its
17
+ * AGENTS.md files instead, loaded exactly where and how CLAUDE.md would be";
18
+ * - cli280.js:245920 — the two name families: AGENTS `["AGENTS.md", ".claude/AGENTS.md"]`,
19
+ * CLAUDE `["CLAUDE.md", ".claude/CLAUDE.md", "CLAUDE.local.md"]`;
20
+ * - cli280.js:226831 — the 2.1.277 changelog entry "in a project with no CLAUDE.md, Claude Code reads AGENTS.md
21
+ * instead".
22
+ * Not followed here (CC divergences, registration drafted in the che-MN closing note): `CLAUDE.local.md` is not read;
23
+ * CC's other `instructionFiles` modes (`claude-md-and-agents-md` loads both, `managed-only`) are not offered; CC loads
24
+ * every CLAUDE file it finds, per directory level up the ancestor chain, whereas this loader reads one root and
25
+ * takes the single first hit. */
26
+ declare const INSTRUCTION_FILE_CHAIN: readonly ["CLAUDE.md", ".claude/CLAUDE.md", "AGENTS.md", ".claude/AGENTS.md"];
27
+ type ChainMember = (typeof INSTRUCTION_FILE_CHAIN)[number];
28
+ /** Recover (root, rel) for a path the LOADER declared — every declared path is `root + one chain member`.
29
+ * The probe needs this to re-run the loader's parent-directory symlink gate each round (self-review: the probe
30
+ * checked only the final component, so swapping `.claude` for a symlink out of the repo MID-RUN made the probe
31
+ * fingerprint an outside file). Exported only for its path-matrix pin (posix / win32 through `p`); the probe
32
+ * calls it with the host `node:path`. */
33
+ export declare function instructionChainCoordinates(absPath: string, p?: PlatformPath): {
34
+ root: string;
35
+ rel: ChainMember;
36
+ } | null;
10
37
  /**
11
38
  * Build a `loadProjectMemory` for the host lane. Returns null (skip) unless hands are enabled AND cwd is a local
12
- * directory. Reads the instruction-file chain (AGENTS.md → CLAUDE.md → .claude/CLAUDE.md, S-1) + (non-subagent)
39
+ * directory. Reads the instruction-file chain (CLAUDE.md → .claude/CLAUDE.md → AGENTS.md → .claude/AGENTS.md, [ref]) + (non-subagent)
13
40
  * `git log -5` / `git status`, redacts secrets, and caps the total. A subagent gets a trimmed instruction file and
14
41
  * no git narrative (cost control, H7). When an instruction file is hit the return is a `ProjectMemoryLoad`
15
42
  * declaring it in `instructionSources` (path + sha256 over the raw bounded-read window) so core's
@@ -1,11 +1,11 @@
1
1
  import { promises as fs, constants as fsConstants } from "node:fs";
2
2
  import { execFile } from "node:child_process";
3
3
  import { createHmac, randomBytes } from "node:crypto";
4
- import { isAbsolute, join, sep } from "node:path";
5
- import { scrubSecretEnv } from "@sema-agent/core";
4
+ import hostPath, { isAbsolute, join } from "node:path";
6
5
  import { recordSecretEnvScrub } from "./observability/secret-env-scrub.js";
6
+ import { scrubServerSecrets } from "./server-secret-env.js";
7
7
  import { redactSecrets } from "./trace/redact.js";
8
- const INSTRUCTION_FILE_CHAIN = ["AGENTS.md", "CLAUDE.md", ".claude/CLAUDE.md"];
8
+ const INSTRUCTION_FILE_CHAIN = ["CLAUDE.md", ".claude/CLAUDE.md", "AGENTS.md", ".claude/AGENTS.md"];
9
9
  const INSTRUCTION_READ_CAP = 24 * 1024;
10
10
  function capUtf8(s, maxBytes) {
11
11
  const buf = Buffer.from(s, "utf8");
@@ -41,15 +41,16 @@ function instructionHash(raw) {
41
41
  return `hmac-sha256:${createHmac("sha256", INSTRUCTION_HASH_KEY).update(raw).digest("hex")}`;
42
42
  }
43
43
  const ABSENT_HASH = null;
44
- function chainCoordinates(absPath) {
45
- let best = null;
46
- for (const rel of INSTRUCTION_FILE_CHAIN) {
47
- const suffix = sep + rel.split("/").join(sep);
48
- if (absPath.endsWith(suffix) && (best === null || suffix.length > best.len)) {
49
- best = { root: absPath.slice(0, -suffix.length), rel, len: suffix.length };
50
- }
44
+ export function instructionChainCoordinates(absPath, p = hostPath) {
45
+ const candidates = INSTRUCTION_FILE_CHAIN.filter((rel) => absPath.endsWith(p.sep + rel.split("/").join(p.sep))).sort((a, b) => b.length - a.length);
46
+ for (const rel of candidates) {
47
+ let root = absPath;
48
+ for (let i = 0; i < rel.split("/").length; i++)
49
+ root = p.dirname(root);
50
+ if (p.join(root, rel) === absPath)
51
+ return { root, rel };
51
52
  }
52
- return best && { root: best.root, rel: best.rel };
53
+ return null;
53
54
  }
54
55
  async function hasSymlinkedParent(root, rel) {
55
56
  const parts = rel.split("/").slice(0, -1);
@@ -62,10 +63,15 @@ async function hasSymlinkedParent(root, rel) {
62
63
  }
63
64
  return false;
64
65
  }
66
+ async function readChainMember(root, rel, maxBytes, truncatedFlag) {
67
+ if (rel.includes("/") && (await hasSymlinkedParent(root, rel)))
68
+ return null;
69
+ return readRawCapped(join(root, rel), maxBytes, truncatedFlag);
70
+ }
65
71
  function git(root, args, maxBytes) {
66
72
  const safeArgs = ["-c", "core.fsmonitor=false", "-c", "core.hooksPath=/dev/null", ...args];
67
73
  const scrubFindings = [];
68
- const scrubbedEnv = scrubSecretEnv(process.env, scrubFindings);
74
+ const scrubbedEnv = scrubServerSecrets(process.env, scrubFindings);
69
75
  recordSecretEnvScrub("server.project-memory.git-subprocess", scrubFindings);
70
76
  return new Promise((res) => {
71
77
  execFile("git", safeArgs, { cwd: root, timeout: 5000, maxBuffer: maxBytes, env: { ...scrubbedEnv, GIT_CONFIG_GLOBAL: "/dev/null", GIT_CONFIG_SYSTEM: "/dev/null" } }, (err, stdout, stderr) => {
@@ -156,9 +162,7 @@ export function makeLoadProjectMemory(opts) {
156
162
  hitIndex = -1;
157
163
  for (let i = 0; i < INSTRUCTION_FILE_CHAIN.length; i++) {
158
164
  const rel = INSTRUCTION_FILE_CHAIN[i];
159
- if (rel.includes("/") && (await hasSymlinkedParent(root, rel)))
160
- continue;
161
- const raw = await readRawCapped(join(root, rel), claudeCap, claudeTrunc);
165
+ const raw = await readChainMember(root, rel, claudeCap, claudeTrunc);
162
166
  if (raw !== null) {
163
167
  claudeRaw = capUtf8(raw.toString("utf8"), claudeCap);
164
168
  hitFile = rel;
@@ -171,8 +175,7 @@ export function makeLoadProjectMemory(opts) {
171
175
  break;
172
176
  let appeared = false;
173
177
  for (const rel of INSTRUCTION_FILE_CHAIN.slice(0, hitIndex)) {
174
- const higher = await fs.lstat(join(root, rel)).catch(() => null);
175
- if (higher && !higher.isSymbolicLink()) {
178
+ if ((await readChainMember(root, rel, claudeCap)) !== null) {
176
179
  appeared = true;
177
180
  break;
178
181
  }
@@ -229,11 +232,8 @@ export function makeProbeInstructionSources() {
229
232
  return await Promise.all(sources.map(async ({ path }) => {
230
233
  if (!isAbsolute(path))
231
234
  return { path, contentHash: null };
232
- const coords = chainCoordinates(path);
233
- if (coords && coords.rel.includes("/") && (await hasSymlinkedParent(coords.root, coords.rel))) {
234
- return { path, contentHash: null };
235
- }
236
- const raw = await readRawCapped(path, INSTRUCTION_READ_CAP);
235
+ const coords = instructionChainCoordinates(path);
236
+ const raw = coords ? await readChainMember(coords.root, coords.rel, INSTRUCTION_READ_CAP) : await readRawCapped(path, INSTRUCTION_READ_CAP);
237
237
  return { path, contentHash: raw === null ? null : instructionHash(raw) };
238
238
  }));
239
239
  }
package/dist/run-local.js CHANGED
@@ -8,8 +8,8 @@ import { realpathSync } from "node:fs";
8
8
  import { createInterface } from "node:readline/promises";
9
9
  import { fileURLToPath } from "node:url";
10
10
  import { loadRemoteExec } from "@sema-agent/settings-schema/node";
11
- import { Runner, uuidv7, parseModelMention, FileStorageBackend, NodeExecutionEnv, NodeLspManager, TtlSessionStore, CenterPromptSource, FilePromptArtifactStore, FilePromptSourceStateStore, MemoryPromptArtifactStore, MemoryPromptSourceStateStore, defaultTaskRegistry } from "@sema-agent/core";
12
- import { hasOperatorGateIntent } from "./approval.js";
11
+ import { Runner, uuidv7, parseModelMention, FileStorageBackend, NodeExecutionEnv, TtlSessionStore, CenterPromptSource, FilePromptArtifactStore, FilePromptSourceStateStore, MemoryPromptArtifactStore, MemoryPromptSourceStateStore, defaultTaskRegistry } from "@sema-agent/core";
12
+ import { singleUserAutoAcceptBaselineApplies } from "./approval.js";
13
13
  import { createBrain } from "./brain.js";
14
14
  import { assertGuardPatternsUsable, buildOnlySensitiveBaselineWarning, createApprovalBaselinePolicy, createDeploymentGovernanceInputs, } from "./deployment-governance.js";
15
15
  import { applyRuntimeGovernance } from "./runtime-governance.js";
@@ -18,7 +18,6 @@ import { taskWallClockSec } from "./task-workflow.js";
18
18
  import { applyCatalogToSource } from "./capabilities/center-prompts.js";
19
19
  import { validatePromptsDomain, CORE_ENGINE_VERSION } from "./prompts-domain-validate.js";
20
20
  import { buildUnpricedCeilingNotice, loadConfig, logConfigDiagnostics, parseNumOrFailNonNegative } from "./config.js";
21
- import { clearHostedEvidenceEnv } from "./hosted-posture.js";
22
21
  import { buildStoreOpenRefusalTail } from "./plugins/store-open-refusal.js";
23
22
  import { acquireLocalBackendWithWait } from "./plugins/store-lock-wait.js";
24
23
  import { createConfigProvider } from "./config-provider.js";
@@ -49,6 +48,7 @@ import { fatalExit } from "./boot/fatal-exit.js";
49
48
  import { installProcessExitBoundary } from "./boot/exit-boundary.js";
50
49
  import { createMetrics } from "./observability/metrics.js";
51
50
  import { installSecretEnvScrubRecorder } from "./observability/secret-env-scrub.js";
51
+ import { createHostLspManager } from "./host-lsp-manager.js";
52
52
  import { buildDisplaySafeUrl } from "./trace/redact.js";
53
53
  export function parseArgs(argv) {
54
54
  let objective;
@@ -176,7 +176,6 @@ export async function runLocal(argv, deps = {}) {
176
176
  process.env.CONFIG_PROVIDER = "local";
177
177
  process.env.SESSION_BACKEND = "memory";
178
178
  process.env.REQUIRE_PRINCIPAL = "false";
179
- clearHostedEvidenceEnv();
180
179
  const root = args.root ?? process.env.CONFIG_LOCAL_DIR ?? process.env.AGENT_DATA_DIR ?? join(homedir(), ".ai-agent");
181
180
  if (args.root)
182
181
  process.env.CONFIG_LOCAL_DIR = args.root;
@@ -197,7 +196,7 @@ export async function runLocal(argv, deps = {}) {
197
196
  for (const de of r.domainErrors ?? [])
198
197
  logger.warn("config_domain_invalid", { domain: de.domain, error: de.error, note: "domain fell to schema defaults; other domains applied normally" });
199
198
  effective = r.effective;
200
- applyEffective(config, r.effective, logger);
199
+ applyEffective(config, r.effective, logger, { leg: "boot" });
201
200
  if (effective?.mcp)
202
201
  config.mcpServers = resolveMcpServers(effective.mcp, logger);
203
202
  if (effective?.a2a)
@@ -244,7 +243,7 @@ export async function runLocal(argv, deps = {}) {
244
243
  }
245
244
  const ungatedCompensation = buildOnlySensitiveBaselineWarning(config, {
246
245
  durableEnabled: false,
247
- singleUserAutoAcceptBaseline: config.requirePrincipal !== true && !hasOperatorGateIntent(config),
246
+ singleUserAutoAcceptBaseline: singleUserAutoAcceptBaselineApplies(config),
248
247
  });
249
248
  if (ungatedCompensation)
250
249
  logger.warn(ungatedCompensation.event, ungatedCompensation.fields);
@@ -287,7 +286,7 @@ export async function runLocal(argv, deps = {}) {
287
286
  ...(hostRx.commandTimeoutMs != null ? { commandTimeoutMs: hostRx.commandTimeoutMs } : {}),
288
287
  })
289
288
  : undefined;
290
- const lspManager = hostRx && config.lspHostEnabled && config.requirePrincipal !== true ? new NodeLspManager({ log: (event, fields) => logger.info(event, fields) }) : undefined;
289
+ const lspManager = hostRx && config.lspHostEnabled && config.requirePrincipal !== true ? createHostLspManager((event, fields) => logger.info(event, fields)) : undefined;
291
290
  const memoryEngine = memoryEngineBackendFor(config, root, memoryEngineIncident);
292
291
  let memorySync;
293
292
  if (config.memorySync && memoryEngine) {
package/dist/runs.js CHANGED
@@ -1,4 +1,4 @@
1
- import { runWithVerification, runCascade, defaultTaskRegistry, DURABLE_AGENT_HANDLE_RE } from "@sema-agent/core";
1
+ import { runWithVerification, runCascade, defaultTaskRegistry, taskNotificationDedupKey, DURABLE_AGENT_HANDLE_RE } from "@sema-agent/core";
2
2
  import { failureCodeOf, sealResultForExit, outwardPlane } from "./terminal.js";
3
3
  import { GOVERNANCE_HTTP_STATUS } from "./governance-http-status.js";
4
4
  import { redactErrorMessage } from "./observability/run-terminal.js";
@@ -12,7 +12,7 @@ import { CANCELLED_CODE, mintCancelledResult } from "./run-cancel-context.js";
12
12
  import { createApprovalCardEmitter, resolveApprovalLeg } from "./tool-approval.js";
13
13
  import { fleetRunResiduals, isFleetAgentTerminalNotification } from "./fleet/fleet-bus.js";
14
14
  import { defaultSubagentTailBus } from "./fleet/subagent-tail-bus.js";
15
- import { emitPendingWorkflowCompletions, taskNotificationInboxEntry, taskNotificationStreamKey, NotifiedKeys } from "./orchestration/workflow-completion-inbox.js";
15
+ import { emitPendingWorkflowCompletions, taskNotificationInboxEntry, taskNotificationStreamKeyOf, NotifiedKeys } from "./orchestration/workflow-completion-inbox.js";
16
16
  import { recordFailOpen } from "./observability/fail-open.js";
17
17
  import { errorText as errText } from "./observability/err-text.js";
18
18
  import { LIVENESS_HEARTBEAT_MS } from "./config-invariants.js";
@@ -256,8 +256,9 @@ export async function runInBackground(runner, spec, runStore, taskId, metrics, p
256
256
  const { type, ...rest } = frame;
257
257
  const f = frame;
258
258
  const write = append(type, rest);
259
- if (f.type === "task_notification" && f.task_id)
260
- notifiedKeys.set(taskNotificationStreamKey({ task_type: f.task_type, task_id: f.task_id, status: String(f.status), seq: f.seq }), write.then(() => true, () => false));
259
+ const fKey = f.type === "task_notification" ? taskNotificationStreamKeyOf(f) : undefined;
260
+ if (fKey !== undefined)
261
+ notifiedKeys.set(fKey, write.then(() => true, () => false));
261
262
  return write;
262
263
  }, completionDiagLog ? { route: "bg-run-start", connection: "durable-append", log: completionDiagLog } : undefined);
263
264
  const runBody = async () => {
@@ -285,7 +286,7 @@ export async function runInBackground(runner, spec, runStore, taskId, metrics, p
285
286
  void workflowCompletionInbox.enqueue(taskNotificationInboxEntry(spec.sessionId, elicitOwner ?? owner, n, Date.now(), taskId, opts?.priority)).catch((err) => completionDiagLog?.("park_enqueue_failed", { route: "bg-run-start", taskId: n.task_id, err: err instanceof Error ? err.message : String(err) }));
286
287
  }
287
288
  else if (legLive) {
288
- const key = taskNotificationStreamKey(n);
289
+ const key = taskNotificationDedupKey(n);
289
290
  if (notifiedKeys.get(key) === undefined) {
290
291
  notifiedKeys.set(key, append("task_notification", taskNotificationEventData({ notification: n, ...(opts?.priority !== undefined ? { priority: opts.priority } : {}) })).then(() => true, () => false));
291
292
  }
@@ -54,6 +54,11 @@
54
54
  * `allowObservers` 是 `=== true` opt-in,缺席已等于关;`allowMemoryOptOut` 是隐私轴的 FAULT-TRANSIT 契约
55
55
  * (core types.d.ts 逐字禁止在 fault 上自造一个显式值)—— 铸 false 就是把故障臂默默换成判决臂。
56
56
  *
57
+ * 🔐 [ref] E-15(7.99.0 合并复审 F2):缓存、在飞去重与 ETag 都在**凭证身份的箱**里(`center-credential.ts`
58
+ * `createIdentityBins`,org 记忆目录同一只)——此前只按 principal 键,同机换账号后 TTL 内同一 principal 吃上一个
59
+ * 人的授予、搭上一个人的在飞请求、带上一个人的 etag 去换 304。在飞期间换了人,答复由传输层丢弃
60
+ * (`http-client.ts` `requestPrincipalView`),按这一次取数失败走下面的 fail-closed 臂(降级项落在发起那一箱)。
61
+ *
57
62
  * The resolver maps center's `EntitlementRuntimeCaps` → the SUBSET core enforces ({@link RuntimeCaps}): center
58
63
  * also carries `allowUltracode` (a shell/UX reasoning-tier cap the engine has no primitive for), which we DROP —
59
64
  * this seam feeds the ENGINE, and `allowUltracode` is enforced at the service settings-resolution layer, not here.
@@ -61,14 +66,15 @@
61
66
  import type { RuntimeCaps } from "@sema-agent/core";
62
67
  import type { EntitlementRuntimeCaps } from "@sema-agent/settings-schema";
63
68
  import { fetchPrincipalCaps, type ExecutionRuling } from "./config-center/facade.js";
69
+ import type { CenterEndpoint } from "./config-center/center-request.js";
64
70
  import type { ScenarioRuling } from "./capabilities/scenarios.js";
65
71
  import type { ServiceConfig } from "./config-types.js";
66
72
  import { type Logger } from "./observability/logger.js";
67
73
  /** test-only: reset the once-per-process trace so each test observes a fresh "first hit". */
68
74
  export declare function resetExecutionLaneDefaultTraceForTest(): void;
69
75
  export interface EntitlementsResolverOpts {
70
- baseUrl: string;
71
- token: string;
76
+ /** S-651:中心端点(基址 + 凭证**来源**;每次抓取取一次值,不持有启动快照)。 */
77
+ center: CenterEndpoint;
72
78
  /** This worker's name (`SEMA_REGISTRY_WORKER`), passed as `?worker=` so a worker-scoped `wpt_<self>` token can
73
79
  * resolve its OWN worker's principal caps (caps-only); harmless on a full token (the caps value
74
80
  * is worker-independent). Omitted ⇒ no `?worker=` (a full-token, non-fleet deployment). */
@@ -136,12 +142,6 @@ export declare function toCoreRuntimeCaps(ent: EntitlementRuntimeCaps | null | u
136
142
  * 所以本谓词为 false **不再**意味着 auto 不武装;「会不会武装」由 `auto-mode-face.ts` 的三项合取答。
137
143
  */
138
144
  export declare function centerEntitlementSourceWired(config: Pick<ServiceConfig, "configCenter">): boolean;
139
- /** A MISCONFIG diagnostic (review MEDIUM): a worker-scoped `wpt_<self>` pull token REQUIRES `?worker=<self>` to
140
- * resolve per-principal caps — so a `wpt_` token with NO `SEMA_REGISTRY_WORKER` set will 403
141
- * on every caps fetch → fail-closed DENY ALL workflows for ALL principals (correct degrade, but otherwise silent
142
- * bar per-task warns). True ⇒ the caller should log a one-shot BOOT warning so the misconfig is diagnosable.
143
- * (The orchestrator normally injects SEMA_REGISTRY_WORKER alongside a wpt_ token, so this is a hand-misconfig.) */
144
- export declare function scopedTokenNeedsWorker(token: string, worker: string | undefined): boolean;
145
145
  /** The two per-principal faces served off ONE caps fetch + cache. */
146
146
  export interface PrincipalEntitlementsClient {
147
147
  /** core's `RunnerDeps.runtimeCapsResolver` seam — FAIL-CLOSED on error (deny workflows/fork/autoMode;
@@ -1,4 +1,5 @@
1
1
  import { fetchPrincipalCaps } from "./config-center/facade.js";
2
+ import { createIdentityBins } from "./center-credential.js";
2
3
  import { HttpError } from "./security.js";
3
4
  import { createLogger } from "./observability/logger.js";
4
5
  import { recordFailOpen } from "./observability/fail-open.js";
@@ -54,9 +55,6 @@ export function toCoreRuntimeCaps(ent) {
54
55
  export function centerEntitlementSourceWired(config) {
55
56
  return config.configCenter !== undefined && !config.configCenter.dryRun;
56
57
  }
57
- export function scopedTokenNeedsWorker(token, worker) {
58
- return token.startsWith("wpt_") && !worker;
59
- }
60
58
  export function gateExecutionLane(ruling, ownLane, logger = defaultLogger) {
61
59
  if (!ruling) {
62
60
  traceExecutionLaneDefault("no_ruling", ownLane, logger);
@@ -80,11 +78,14 @@ export function createPrincipalEntitlementsClient(opts) {
80
78
  const fetchCaps = opts.fetchCaps ?? fetchPrincipalCaps;
81
79
  const now = opts.now ?? (() => Date.now());
82
80
  const onError = opts.onError ?? ((err, principal) => defaultLogger.warn("runtime_caps_resolve_failed", { principal, err: String(err) }));
83
- const cache = new Map();
84
- const inflight = new Map();
81
+ const binNow = createIdentityBins(opts.center.credential, (credential) => ({
82
+ center: { baseUrl: opts.center.baseUrl, credential },
83
+ cache: new Map(),
84
+ inflight: new Map(),
85
+ }));
85
86
  const readMintedDeny = new WeakSet();
86
87
  const MAX_CACHE = 10_000;
87
- const putCache = (key, entry) => {
88
+ const putCache = (cache, key, entry) => {
88
89
  if (cache.size >= MAX_CACHE) {
89
90
  const t = now();
90
91
  for (const [k, v] of cache)
@@ -100,15 +101,16 @@ export function createPrincipalEntitlementsClient(opts) {
100
101
  return entry;
101
102
  }
102
103
  async function loadRaw(principal, recordDeny) {
103
- const cached = cache.get(principal);
104
+ const bin = binNow();
105
+ const cached = bin.cache.get(principal);
104
106
  if (cached && cached.expiresAt > now() && !(recordDeny && readMintedDeny.has(cached)))
105
107
  return cached;
106
- const dup = inflight.get(principal);
108
+ const dup = bin.inflight.get(principal);
107
109
  if (dup)
108
110
  return dup;
109
111
  const p = (async () => {
110
112
  try {
111
- const r = await fetchCaps(opts.baseUrl, opts.token, principal, cached?.etag, fetchImpl, opts.worker);
113
+ const r = await fetchCaps(bin.center, principal, cached?.etag, fetchImpl, opts.worker);
112
114
  if (r === null) {
113
115
  const entry = {
114
116
  caps: cached?.caps,
@@ -117,7 +119,7 @@ export function createPrincipalEntitlementsClient(opts) {
117
119
  ...(cached?.etag ? { etag: cached.etag } : {}),
118
120
  expiresAt: now() + ttlMs,
119
121
  };
120
- putCache(principal, entry);
122
+ putCache(bin.cache, principal, entry);
121
123
  return entry;
122
124
  }
123
125
  if (r.executionDrift) {
@@ -133,7 +135,7 @@ export function createPrincipalEntitlementsClient(opts) {
133
135
  ...(r.etag ? { etag: r.etag } : {}),
134
136
  expiresAt: now() + ttlMs,
135
137
  };
136
- putCache(principal, entry);
138
+ putCache(bin.cache, principal, entry);
137
139
  return entry;
138
140
  }
139
141
  catch (err) {
@@ -147,7 +149,7 @@ export function createPrincipalEntitlementsClient(opts) {
147
149
  const entry = { caps: { allowWorkflows: false, allowFork: false, autoMode: false, autoModeDenySource: "org" }, expiresAt: now() + denyTtlMs };
148
150
  if (!recordDeny)
149
151
  readMintedDeny.add(entry);
150
- putCache(principal, entry);
152
+ putCache(bin.cache, principal, entry);
151
153
  try {
152
154
  onError(e, principal);
153
155
  }
@@ -155,10 +157,10 @@ export function createPrincipalEntitlementsClient(opts) {
155
157
  return entry;
156
158
  }
157
159
  finally {
158
- inflight.delete(principal);
160
+ bin.inflight.delete(principal);
159
161
  }
160
162
  })();
161
- inflight.set(principal, p);
163
+ bin.inflight.set(principal, p);
162
164
  return p;
163
165
  }
164
166
  return {
@@ -23,7 +23,7 @@
23
23
  * path-prefixed commands all bypass the argv[0] match (they route to `ask`, fail-closed, but are not blocked by
24
24
  * name). Real isolation is the `executionEnv` sandbox. Same caveat core documents on `createCoarseCommandNamePolicy`.
25
25
  */
26
- import { type TaskSpec, type ToolPolicy } from "@sema-agent/core";
26
+ import { type ShellGateDoctrine, type TaskSpec, type ToolPolicy } from "@sema-agent/core";
27
27
  import { GovernanceConfig } from "@sema-agent/settings-schema";
28
28
  import { type GovernanceAskMarks } from "./governance-ask-marks.js";
29
29
  /** 运维 autonomy 四档的**闭集**(同上)。`config.ts` 的 `parseAutonomy` 也从这里读 —— 四档词表全仓一份。 */
@@ -142,13 +142,60 @@ export declare function autonomyOverrides(autonomy: Autonomy | undefined): Parti
142
142
  * 并不要求 always),之后运维把 autonomy 热改成 `ask`,那条老行会被标成治理出身 —— 五个前提要**同时**
143
143
  * 成立,且该行仍未决。
144
144
  * 根治要**在 mint 那一刻把出身写进行里**(core 的 `CheckpointGate` 属主面,不是本仓能单方面做的);
145
- * 在那之前这一位的定位是**分诊提示**,不是裁决输入 —— 它不参与任何门/CAS/resume 判定,消费端也只拿它
146
- * 渲染徽标。登记在此,不加特征化测试(把已知残留钉成契约是另一种病)。
145
+ * 在那之前这一位的定位是**分诊提示**,不参与任何门/CAS/resume 判定 —— 唯一的例外是**只收窄**的那一个读者:
146
+ * 耐久 `/decide` 腿的会话级 remember 可记性(NP-1,7.100.0,`tool-approval.ts` 的 `parkRowStandingAnswerFacts`)。
147
+ * 在那里两个失真方向的代价都不越过修前:在场向 ⇒ 一次 `remember:"session"` 只放行本次、不铸会话免询问(安全
148
+ * 方向,代价 = 下一次再问一遍);缺席向 ⇒ 照旧可记(= NP-1 之前的行为)。它从不**放宽**任何裁决。
149
+ * 登记在此,不加特征化测试(把已知残留钉成契约是另一种病)。
147
150
  */
148
151
  export declare function governanceMandatesShellGateAlways(governance: {
149
152
  autonomy?: Autonomy;
150
153
  manualModeShellGate?: "always" | "classify";
151
154
  }): boolean;
155
+ /** [ref] 归因的部署侧入参(两个治理旋钮;判据属主见 {@link governanceMandatesShellGateAlways})。 */
156
+ export interface GovernancePosture {
157
+ autonomy?: Autonomy;
158
+ manualModeShellGate?: "always" | "classify";
159
+ }
160
+ /**
161
+ * [ref]:推「这道门是运维治理层下的吗」。判据是**合取**,两半各管一件事:
162
+ *
163
+ * 行上的取证格 `gate.riskDescriptor.shellGateDoctrine === "always"`
164
+ * ∧ 本部署的治理层本来就要求 always({@link governanceMandatesShellGateAlways},那里写着为什么单看行不够 ——
165
+ * SUP 路由姿态同样产 `"always"`)
166
+ *
167
+ * 第一半:core 在 mint 这道门时把「当时活着的 doctrine」如实写进 `riskDescriptor.shellGateDoctrine`
168
+ * (2026-08-05 取证裁定;`prepare-task` 只在**被 shell 门铸**的 ask 上写它),所以持久行自己带着证据 ——
169
+ * park 腿天然跨副本、跨重启,活卡腿那张 ALS 标记表在这里结构上够不着。
170
+ * 第二半:把「有效档 always」收窄到「治理层是真成因」,否则一条 SUP 路由出来的门会被谎报成治理强制。
171
+ *
172
+ * ⚠️ 残留(双向失真的窄在场向 + 根治路径)与「只收窄」的那一个裁决读者,都登记在
173
+ * {@link governanceMandatesShellGateAlways} 的顶注,别在这里复述。
174
+ *
175
+ * 🔴 `"classify"` 档**刻意不标**:那一档由 core 的分类器逐调用裁决,一次 shell ask 可能出自分类器(治理)
176
+ * 也可能出自 `APPROVAL_REQUIRE` 这类别的门,行上无从分辨 —— 与活卡腿同一条「宁缺毋假」纪律。
177
+ *
178
+ * 🔴 **单一属主,四个读者**(不在任何读面另写一份判据 —— 这一位恰恰是靠「行上取证 ∧ 部署姿态」的合取才不
179
+ * 谎报的,两份必漂):409 体的待批门材料(`http/active-run-conflict.ts`)、两条 durable 读面
180
+ * `GET /v1/approvals` / `/v1/approvals/stream` 的 `projectPendingForWire`([ref]②)、耐久 `/decide` 腿的
181
+ * remember 可记性(NP-1,经 `tool-approval.ts` 的 `parkRowStandingAnswerFacts`)。
182
+ * 住在本文件而不是 `http/active-run-conflict.ts` 的理由:它的另一半判据属主就在这里,而第四个读者
183
+ * (`tool-approval.ts`)从 `http/` 那侧值引会闭合一条 ESM 值环(`active-run-conflict` → `checkpoint-store-sql` →
184
+ * `tool-approval`),本文件是三者共同的叶。
185
+ *
186
+ * ⚠️ **askId 为什么不在这里**([ref] 的另一半,如实记账):待批 ask 的 `askId` 只活在 `approval_ask` 行上
187
+ * (checkpoint 行没有这一列,core 的 `CheckpointGate`/`PendingAction`/`Checkpoint` 顶层都不带它)。反向
188
+ * (checkpoint → ask)今天**没有读口**:两条 list 读口都硬过滤 `state='STREAM_PENDING'`,而 park 完的行是
189
+ * `PARKED`;唯一精确的连接键是 ask 行上的 `gate_token`,要按它反查得给店加一个新读口(SQL 面 = 双库集成
190
+ * 门)。派生 `deriveAskId(...)` 也不行:它吃 `legKey`(= 上一腿 resume token 的摘要)与 `parentToolCallId`,
191
+ * 这两维从 checkpoint 行推不出来,二腿/子代形上会算出一个**错**的 id —— 在「等的谁」这条通道上,错 id
192
+ * 比缺席坏得多(`http/active-run-conflict.ts` 的 `resumeEntryForGate` 的 default 臂是同一条纪律)。故诚实缺席。
193
+ */
194
+ export declare function governanceOriginOf(gate: {
195
+ riskDescriptor?: {
196
+ shellGateDoctrine?: ShellGateDoctrine;
197
+ };
198
+ } | undefined, governance: GovernancePosture | undefined): true | undefined;
152
199
  /** spec 序里的秩(0 起,越大越严)。入参收 `string`(fail-loud 边界:schema `trustRank` 同款姿势)——词表外 ⇒
153
200
  * RangeError,绝不 undefined/NaN 让比较静默成 false。导出只为对表钉(test/safety-merge-spec-consumption.test.ts,
154
201
  * [ref] 件4)——运行时消费仍走上表(键类型闭集)。 */
@@ -116,6 +116,11 @@ export function autonomyOverrides(autonomy) {
116
116
  export function governanceMandatesShellGateAlways(governance) {
117
117
  return autonomyOverrides(governance.autonomy).shellGate === "always" || governance.manualModeShellGate === "always";
118
118
  }
119
+ export function governanceOriginOf(gate, governance) {
120
+ if (gate?.riskDescriptor?.shellGateDoctrine !== "always")
121
+ return undefined;
122
+ return governanceMandatesShellGateAlways(governance ?? {}) ? true : undefined;
123
+ }
119
124
  function createGovernanceAskMarkingPolicy(inner, marksOf) {
120
125
  const note = (d, req) => {
121
126
  marksOf()?.setMarked(req.toolCallId, d.action === "ask");
@@ -3,6 +3,8 @@ import { chmod, mkdir, readdir, readFile, writeFile } from "node:fs/promises";
3
3
  import { join } from "node:path";
4
4
  import _sodium from "libsodium-wrappers";
5
5
  import { buildDisplaySafeUrl } from "./trace/redact.js";
6
+ import { createCenterCredentialSource } from "./center-credential.js";
7
+ import { centerFetch } from "./config-center/center-request.js";
6
8
  export const SEALED_KEY_ALG = "libsodium-sealedbox-x25519";
7
9
  const SEAL_OVERHEAD_BYTES = 48;
8
10
  const BASE64_STRICT = /^[A-Za-z0-9+/]+={0,2}$/;
@@ -114,13 +116,7 @@ export async function reportExecutionPublicKey(opts) {
114
116
  return;
115
117
  }
116
118
  try {
117
- const doFetch = opts.fetchImpl ?? fetch;
118
- const res = await doFetch(`${baseUrl.replace(/\/$/, "")}/api/config/execution-keys`, {
119
- method: "POST",
120
- headers: { authorization: `Bearer ${adminToken}`, "content-type": "application/json" },
121
- body: JSON.stringify({ publicKey: key.publicKey, publicKeyId: key.publicKeyId, ...(notes ? { notes } : {}) }),
122
- signal: AbortSignal.timeout(10_000),
123
- });
119
+ const { res } = await centerFetch({ baseUrl, credential: createCenterCredentialSource({ from: "env", token: adminToken }, undefined) }, { kind: "execution-keys" }, { method: "POST", json: { publicKey: key.publicKey, publicKeyId: key.publicKeyId, ...(notes ? { notes } : {}) }, timeoutMs: 10_000, ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}) });
124
120
  if (res.ok) {
125
121
  let idempotent = false;
126
122
  try {
@@ -0,0 +1,43 @@
1
+ /**
2
+ * 7.99.0 合并复审 F7(web-admin [ref] 实测;安全轴;DEBTS S-684)—— 子进程 env 交出去之前的**本仓**密钥剥除。
3
+ *
4
+ * ## 病
5
+ * 本仓两条子进程腿 —— 模型可驱动的 host shell(`inheritEnv:"scrub"` 缺省腿,`plugins/remote-env-host.ts`)与
6
+ * project-memory 的 git 子进程(`project-memory.ts`)—— 此前只调 core 的 `scrubSecretEnv`。它按键名**形状**判
7
+ * (`*_KEY` / `*_TOKEN` / `*_SECRET` …),而本仓自己的凭据键里有形状规则认不出的:`SERVICE_AUTH_TOKENS`(复数,
8
+ * per-system 凭据表)、`APPROVAL_HMAC_KEYS`(直连门 HMAC 密钥集)、`OTEL_EXPORTER_OTLP_HEADERS`(常载鉴权头)⇒
9
+ * 原样进子进程,模型选的一句 `printenv` 就读得到。
10
+ *
11
+ * ## 规则(一句)
12
+ * 本仓**知道**哪些 env 键是凭据 —— 配置目录(`config-catalog.ts`)里 `type:"secret"` 的每一行,从目录机器派生,
13
+ * 不手抄第二份名单 —— 这些键按名剥;其余照旧交 core 按形剥。{@link scrubServerSecrets} = 目录派生剥除 ∘ core
14
+ * `scrubSecretEnv`,本仓三条子进程腿(host shell / git 子进程 / 本地语言服务器,后者见 `host-lsp-manager.ts`)都只调它。
15
+ * core 形状规则的缺口由 core 源头修 —— 7.30.1 #1072 已补 `KEYS|TOKENS|PASSWORDS`(S-616 提货),目录臂独担的只剩
16
+ * `OTEL_EXPORTER_OTLP_HEADERS`(`*_HEADERS` 不在形状规则射程,是边界不是缺陷票)。本层不改 core 的判据,也不自造
17
+ * 第二份形状规则。目录里加一个 secret 型键,三条腿当场就剥它。
18
+ *
19
+ * ## 不变的面
20
+ * `inheritEnv:"all"`(显式全继承)与显式 allowlist 两形不经本函数,行为逐字不变;调用方显式传入的 env
21
+ * (`cfg.env` / 单次命令的 `env`)照旧原样覆盖在剥除结果之上。
22
+ */
23
+ import { type SecretEnvFinding } from "@sema-agent/core";
24
+ /** 目录派生臂的一条剥除记录(形同 core 的 `SecretEnvFinding`:键名 / 规则 / 置信度 / 来源;值永不携带)。
25
+ * `source` 用本仓自己的词 `server-config-catalog`,与 core 的 `secret-env` 分开 —— 读观测面的人要分得清
26
+ * 「形状规则剥的」与「本仓目录点名剥的」(后者多出来的那部分正是 core 形状规则的缺口)。 */
27
+ export interface CatalogSecretEnvFinding {
28
+ readonly key: string;
29
+ readonly kind: "config-catalog";
30
+ readonly confidence: "high";
31
+ readonly source: "server-config-catalog";
32
+ }
33
+ /** 本仓两条子进程腿的剥除记录:core 的形状臂 ∪ 本仓的目录臂。 */
34
+ export type ServerSecretEnvFinding = SecretEnvFinding | CatalogSecretEnvFinding;
35
+ /** 配置目录里 `type:"secret"` 的 env 名(大写 —— Windows 的 env 名不分大小写,按大写比才不会漏)。 */
36
+ export declare const CATALOG_SECRET_ENV_NAMES: ReadonlySet<string>;
37
+ /**
38
+ * 先交 core 按形剥,再按目录名剥它漏下的。`findings` 是调用方持有的收集器(交给 `recordSecretEnvScrub` 留痕):
39
+ * 形状臂剥掉的记 core 的 finding;目录臂只记**形状臂漏下、被目录点名剥掉**的键 —— 于是既有的形状臂计数不变,
40
+ * 目录臂的计数恰好就是 core 形状规则的缺口读数。
41
+ */
42
+ export declare function scrubServerSecrets(env: NodeJS.ProcessEnv, findings: ServerSecretEnvFinding[]): NodeJS.ProcessEnv;
43
+ //# sourceMappingURL=server-secret-env.d.ts.map
@@ -0,0 +1,18 @@
1
+ import { scrubSecretEnv } from "@sema-agent/core";
2
+ import { CONFIG_CATALOG } from "./config-catalog.js";
3
+ export const CATALOG_SECRET_ENV_NAMES = new Set(CONFIG_CATALOG.filter((spec) => spec.type === "secret").map((spec) => spec.name.toUpperCase()));
4
+ export function scrubServerSecrets(env, findings) {
5
+ const shapeFindings = [];
6
+ const shaped = scrubSecretEnv(env, shapeFindings);
7
+ findings.push(...shapeFindings);
8
+ const out = {};
9
+ for (const key of Object.keys(shaped)) {
10
+ if (CATALOG_SECRET_ENV_NAMES.has(key.toUpperCase())) {
11
+ findings.push({ key, kind: "config-catalog", confidence: "high", source: "server-config-catalog" });
12
+ continue;
13
+ }
14
+ out[key] = shaped[key];
15
+ }
16
+ return out;
17
+ }
18
+ //# sourceMappingURL=server-secret-env.js.map
@@ -264,7 +264,7 @@ export declare const MAX_SETTINGS_ENV_VALUE_CHARS = 32768;
264
264
  export declare const MAX_SETTINGS_ENV_KEY_CHARS = 256;
265
265
  /** 本 service 真正**消费**的 `settings` 顶层键 —— 闭集。每一个都有消费点:
266
266
  * `env`/`hooks`/`model`/`outputStyle`/`permissions`/`ultracode` 与两个 `skillListing*` 在
267
- * {@link parseTaskSettings};`webSearch` 在 `capabilities/scenarios.ts`(`webSearchConfigFromSettings`,单用户闸)。
267
+ * {@link parseTaskSettings};`webSearch` 在 `capabilities/scenarios.ts`(判决 `judgeRequestWebSearch`,单用户闸;形错在受理面 400,S-199)。
268
268
  * 加键必须同时加消费点,否则就是往闭集里塞一个新的静默丢。
269
269
  *
270
270
  * 🔴 两个 `skillListing*`([ref],7.77.0)= **CC settings 同名键**,值域抄 CC 的 schema 声明