@sema-agent/server 7.56.0 → 7.58.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 (121) 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/boot/stores.js +12 -5
  30. package/dist/config-catalog.js +14 -5
  31. package/dist/config-center/http-client.js +7 -11
  32. package/dist/config-center/read-warnings.d.ts +20 -0
  33. package/dist/config-center/read-warnings.js +36 -0
  34. package/dist/config-provider.d.ts +3 -0
  35. package/dist/config-provider.js +4 -8
  36. package/dist/config-types.d.ts +41 -10
  37. package/dist/config.d.ts +21 -0
  38. package/dist/config.js +40 -5
  39. package/dist/hooks/hook-runner.js +8 -8
  40. package/dist/http/route-ctx.d.ts +75 -0
  41. package/dist/http/routes/a2a-serve.js +5 -3
  42. package/dist/http/routes/approvals-assistant.js +19 -6
  43. package/dist/http/routes/capabilities.js +21 -4
  44. package/dist/http/routes/memory-origin.d.ts +2 -2
  45. package/dist/http/routes/rules.js +3 -2
  46. package/dist/http/routes/runs.d.ts +0 -14
  47. package/dist/http/routes/runs.js +20 -8
  48. package/dist/http/routes/tasks.js +34 -8
  49. package/dist/http/routes/workflows.js +19 -4
  50. package/dist/http/server.d.ts +11 -1
  51. package/dist/http/server.js +215 -43
  52. package/dist/http/wire-types.d.ts +9 -4
  53. package/dist/leader/diffout.js +2 -1
  54. package/dist/leader/endpoint.js +45 -12
  55. package/dist/leader/fanout.js +6 -5
  56. package/dist/leader/leader.js +17 -14
  57. package/dist/leader/merge.js +17 -12
  58. package/dist/leader/planner.js +3 -2
  59. package/dist/leader/repair-oracle.js +6 -4
  60. package/dist/leader/repair-wire.js +5 -3
  61. package/dist/leader/wire.d.ts +30 -1
  62. package/dist/leader/wire.js +36 -19
  63. package/dist/main.js +43 -7
  64. package/dist/observability/err-text.d.ts +6 -0
  65. package/dist/observability/err-text.js +10 -0
  66. package/dist/observability/fail-open.d.ts +40 -0
  67. package/dist/observability/fail-open.js +19 -0
  68. package/dist/observability/metrics.js +2 -2
  69. package/dist/observability/run-terminal-log.d.ts +120 -0
  70. package/dist/observability/run-terminal-log.js +360 -0
  71. package/dist/orchestration/workflow-completion-inbox.d.ts +9 -0
  72. package/dist/orchestration/workflow-completion-inbox.js +8 -0
  73. package/dist/permission-rule-vocab.d.ts +40 -0
  74. package/dist/permission-rule-vocab.js +17 -0
  75. package/dist/plugins/checkpoint-store-sql.d.ts +14 -0
  76. package/dist/plugins/checkpoint-store-sql.js +19 -3
  77. package/dist/plugins/file-run-store.d.ts +3 -34
  78. package/dist/plugins/file-run-store.js +19 -0
  79. package/dist/plugins/local-checkpoint-store.d.ts +10 -0
  80. package/dist/plugins/local-checkpoint-store.js +3 -0
  81. package/dist/plugins/mailbox-store-sql.d.ts +28 -9
  82. package/dist/plugins/mailbox-store-sql.js +72 -10
  83. package/dist/plugins/memory-run-store.d.ts +3 -15
  84. package/dist/plugins/memory-run-store.js +19 -0
  85. package/dist/plugins/permission-rule-store-file.js +8 -3
  86. package/dist/plugins/permission-rule-store-sql.js +16 -10
  87. package/dist/plugins/remote-scratchpad.js +3 -2
  88. package/dist/plugins/run-store-sql.d.ts +3 -42
  89. package/dist/plugins/run-store-sql.js +30 -1
  90. package/dist/plugins/store-backend.d.ts +1 -1
  91. package/dist/plugins/store-contracts.d.ts +66 -1
  92. package/dist/plugins/workflow-run-store-sql.d.ts +5 -0
  93. package/dist/plugins/workflow-run-store-sql.js +10 -2
  94. package/dist/rules-consent.js +20 -13
  95. package/dist/run-cancel-context.d.ts +13 -0
  96. package/dist/run-cancel-context.js +15 -0
  97. package/dist/run-local.js +2 -2
  98. package/dist/runs.d.ts +4 -1
  99. package/dist/runs.js +41 -10
  100. package/dist/runtime-caps-resolver.d.ts +133 -17
  101. package/dist/runtime-caps-resolver.js +41 -3
  102. package/dist/task-settings.d.ts +57 -3
  103. package/dist/task-settings.js +78 -5
  104. package/dist/task-workflow.d.ts +32 -2
  105. package/dist/task-workflow.js +8 -3
  106. package/dist/tool-approval.d.ts +26 -3
  107. package/dist/tool-approval.js +96 -13
  108. package/dist/trace/core-keyset-guard.d.ts +7 -8
  109. package/dist/trace/engine-notice-wire.d.ts +1 -1
  110. package/dist/trace/engine-notice-wire.js +2 -0
  111. package/dist/trace/ledger-sink.d.ts +13 -4
  112. package/dist/trace/ledger-sink.js +14 -6
  113. package/dist/trace/project.d.ts +58 -3
  114. package/dist/trace/project.js +33 -1
  115. package/dist/trace/redact.d.ts +22 -11
  116. package/dist/trace/redact.js +660 -21
  117. package/dist/trace/sema-provenance.d.ts +35 -0
  118. package/dist/trace/sema-provenance.js +14 -0
  119. package/dist/turn-activity.d.ts +32 -2
  120. package/dist/turn-activity.js +29 -4
  121. package/package.json +3 -3
@@ -0,0 +1,99 @@
1
+ import { createRequire } from "node:module";
2
+ import { readFileSync } from "node:fs";
3
+ import { dirname, join } from "node:path";
4
+ import { z } from "zod";
5
+ import { resolveTaskModel } from "@sema-agent/core";
6
+ export function classifierRouteFor(deps) {
7
+ try {
8
+ return { id: resolveTaskModel({ modelRole: "classifier" }, deps).model.id };
9
+ }
10
+ catch (err) {
11
+ return { id: undefined, fault: err instanceof Error ? err.message : String(err) };
12
+ }
13
+ }
14
+ export const AUTO_MODE_UNARMED_REASONS = ["mode_not_auto", "deployment_incapable", "org_denied", "local_denied", "settings_denied", "resolver_fault"];
15
+ export const AUTO_MODE_UNARMED_REASON_SEMANTICS = {
16
+ mode_not_auto: "本次意图不是 auto(壳发的 permissionMode 不是 \"auto\";座缺席)——只属意图武装式,旧式 core 不读模式",
17
+ deployment_incapable: "装配面没挂 RunnerDeps.autoMode 分类器席位(本部署/本进程形不具备分类器);旧式 core 下另含「无 center 源 = 零授予路径」",
18
+ org_denied: "per-principal caps.autoMode === false 且本地 deny 未设:center 显式 deny / center 硬失败 fail-closed 拒项;旧式 core 下另含「center 在场但未授予」",
19
+ local_denied: "本地 catalog 键 PERMISSIONS_DISABLE_AUTO_MODE=true(center/settings 键名 permissions.disableAutoMode)把 caps.autoMode 折成 false",
20
+ settings_denied: "settings 层 permissions.disableAutoMode 取 disable 或 true(两套已发布拼写同义;用户/managed 层 kill-switch)把本 run 折成非 auto —— capabilities 查询串不带 settings,本词由 run 级面产([6128] PM 裁定第六员)",
21
+ resolver_fault: "per-principal caps resolver 抛错/答非记录(core 同形全拒:autoMode:false + onError phase config)",
22
+ };
23
+ function armingProbeFailure(what) {
24
+ return new Error(`auto-mode arming probe cannot decide (${what}). This probe answers ONE question — does the INSTALLED @sema-agent/core arm ` +
25
+ `auto mode from the task's INTENT seat (core #521: spec.autoModeRequested ∧ seat ∧ caps !== false), or from the older ` +
26
+ `"org grants it" polarity (runtimeCaps.autoMode === true ∧ seat)? The two polarities are OPPOSITE on the permission axis, ` +
27
+ `so guessing would silently put this deployment on the wrong one — either arming runs the org never granted, or reporting ` +
28
+ `"not armed" at /v1/capabilities while the engine really is arming (#157: no silent fail-open on the permission axis). ` +
29
+ `Refusing to start instead. Fix: reinstall the engine (rm -rf node_modules && npm ci) so the package ships its full dist, ` +
30
+ `or — if core moved/renamed the arming site — update the probe in src/auto-mode-face.ts to read the new one.`);
31
+ }
32
+ export function coreArmsAutoOnIntentProbe(distDir) {
33
+ const dist = distDir ?? dirname(createRequire(import.meta.url).resolve("@sema-agent/core"));
34
+ const armingSite = join(dist, "core", "runner", "prepare-task.js");
35
+ let armingSrc;
36
+ try {
37
+ armingSrc = readFileSync(armingSite, "utf8");
38
+ }
39
+ catch (err) {
40
+ throw armingProbeFailure(`the engine's arming site is unreadable at ${armingSite}: ${err instanceof Error ? err.message : String(err)}`);
41
+ }
42
+ if (armingSrc.includes(CORE_AUTO_MODE_INTENT_SEAT))
43
+ return true;
44
+ const snapPath = join(dist, "..", "test", "export-surface.snapshot.json");
45
+ let snap;
46
+ try {
47
+ const parsed = ExportSurfaceSnapshotSchema.safeParse(JSON.parse(readFileSync(snapPath, "utf8")));
48
+ if (!parsed.success)
49
+ throw new Error(`export-surface snapshot shape mismatch: ${parsed.error.issues.map((i) => i.path.join(".") + ":" + i.message).join("; ")}`);
50
+ snap = parsed.data;
51
+ }
52
+ catch (err) {
53
+ throw armingProbeFailure(`the arming seat is absent from ${armingSite} and the corroborating export-surface snapshot is unreadable at ${snapPath}: ${err instanceof Error ? err.message : String(err)}`);
54
+ }
55
+ if (!snap.exports || typeof snap.exports !== "object")
56
+ throw armingProbeFailure(`core export-surface snapshot malformed at ${snapPath}`);
57
+ if (typeof snap.exports.mailboxCrossProcessMountVerdict === "string") {
58
+ throw armingProbeFailure(`contradiction — the installed core's export surface is at/after the batch that shipped #521, but its arming site ` +
59
+ `(${armingSite}) never names \`${CORE_AUTO_MODE_INTENT_SEAT}\`, so the engine either moved the arming site or renamed the seat`);
60
+ }
61
+ return false;
62
+ }
63
+ const CORE_AUTO_MODE_INTENT_SEAT = "autoModeRequested";
64
+ let armsOnIntentMemo;
65
+ const ExportSurfaceSnapshotSchema = z.object({ exports: z.record(z.string(), z.string()).optional() }).passthrough();
66
+ export function coreArmsAutoOnIntent() {
67
+ if (armsOnIntentMemo === undefined)
68
+ armsOnIntentMemo = coreArmsAutoOnIntentProbe();
69
+ return armsOnIntentMemo;
70
+ }
71
+ export async function judgeAutoModeArming(face, input) {
72
+ const route = face?.seatMounted ? face.classifierModel?.() : undefined;
73
+ const model = route !== undefined ? { model: route } : {};
74
+ if (face?.engineArmsOnIntent && input.requestedMode !== undefined && input.requestedMode !== "auto")
75
+ return { armed: false, reason: "mode_not_auto", ...model };
76
+ if (face === undefined || !face.seatMounted)
77
+ return { armed: false, reason: "deployment_incapable" };
78
+ let caps;
79
+ try {
80
+ caps = face.resolveCaps !== undefined ? await face.resolveCaps(input.principal) : undefined;
81
+ }
82
+ catch (err) {
83
+ return { armed: false, reason: "resolver_fault", ...model, fault: err instanceof Error ? err.message : String(err) };
84
+ }
85
+ const deniedWord = input.localDeny ? "local_denied" : "org_denied";
86
+ if (face.engineArmsOnIntent) {
87
+ if (caps?.autoMode === false)
88
+ return { armed: false, reason: deniedWord, ...model };
89
+ return { armed: true, ...model };
90
+ }
91
+ if (caps?.autoMode === true)
92
+ return { armed: true, ...model };
93
+ if (caps?.autoMode === false)
94
+ return { armed: false, reason: deniedWord, ...model };
95
+ if (!input.entitlementSource)
96
+ return { armed: false, reason: "deployment_incapable", ...model };
97
+ return { armed: false, reason: "org_denied", ...model };
98
+ }
99
+ //# sourceMappingURL=auto-mode-face.js.map
@@ -1,4 +1,4 @@
1
- import { leafBudgetFields } from "./runner-ctx.js";
1
+ import { leafBudgetFields, s1DebugGit } from "./runner-ctx.js";
2
2
  import {} from "./tasks.js";
3
3
  import { decideApproval, decidePlan } from "./reviewer.js";
4
4
  import { oracleTrulyCorrect, pickOracleVerdicts, armTag, S1_SCHEMA_VERSION, S1_CORE_VERSION, } from "./row.js";
@@ -164,7 +164,7 @@ export async function runArm(arm, trap, seed, ctx, deps, supStore) {
164
164
  vr = await deps.runWithVerification(implSpec);
165
165
  }
166
166
  catch (e) {
167
- if (process.env.S1_DEBUG_GIT)
167
+ if (s1DebugGit())
168
168
  console.error("[S1 SOLO infra throw]", e instanceof Error ? (e.stack ?? e.message) : String(e));
169
169
  return assembleRow({
170
170
  arm, trap, seed, ctx,
@@ -176,7 +176,7 @@ export async function runArm(arm, trap, seed, ctx, deps, supStore) {
176
176
  finishedAt: ctx.clock.now(),
177
177
  });
178
178
  }
179
- if (process.env.S1_DEBUG_GIT)
179
+ if (s1DebugGit())
180
180
  console.error("[S1 SOLO vr.status]", vr.status, "| stats:", JSON.stringify(vr.stats)?.slice(0, 200));
181
181
  const oracle = pickOracleVerdicts(await deps.runOracle());
182
182
  return assembleRow({
@@ -210,7 +210,7 @@ export async function runArm(arm, trap, seed, ctx, deps, supStore) {
210
210
  }
211
211
  }
212
212
  catch (e) {
213
- if (process.env.S1_DEBUG_GIT)
213
+ if (s1DebugGit())
214
214
  console.error(`[S1 SUP infra throw driver=${trap.supDriver}]`, e instanceof Error ? (e.stack ?? e.message) : String(e));
215
215
  return assembleRow({
216
216
  arm, trap, seed, ctx,
@@ -222,7 +222,7 @@ export async function runArm(arm, trap, seed, ctx, deps, supStore) {
222
222
  finishedAt: ctx.clock.now(),
223
223
  });
224
224
  }
225
- if (process.env.S1_DEBUG_GIT)
225
+ if (s1DebugGit())
226
226
  console.error(`[S1 SUP vr.status driver=${trap.supDriver}]`, vr.status, "| repairTerminal:", repairTerminal, "| verdict:", vr.verification?.verdict, "| unverifiedReason:", vr.verification?.unverifiedReason, "| checkpointGate:", JSON.stringify(vr.checkpointGate), "| error:", vr.error, "| stats:", JSON.stringify(vr.stats)?.slice(0, 180));
227
227
  const oracle = pickOracleVerdicts(await deps.runOracle());
228
228
  const runStatus = classifyRunStatus({ status: vr.status, repairTerminal });
@@ -7,7 +7,7 @@ import { createDurableAskPolicy } from "../../approval.js";
7
7
  import { createLeaderRunner } from "../../leader/wire.js";
8
8
  import { runOracle as runStandaloneOracle } from "./oracle.js";
9
9
  import { makeRepairOracle } from "./repair-oracle-adapter.js";
10
- import { assertDistinctEnvs } from "./runner-ctx.js";
10
+ import { assertDistinctEnvs, s1DebugGit } from "./runner-ctx.js";
11
11
  const W = "/home/user";
12
12
  const REPO = `${W}/repo`;
13
13
  const BASE_REF = "s1-base";
@@ -112,7 +112,7 @@ async function transferWorkerTreeToGrader(worker, grader) {
112
112
  const ext = await sh(grader)(`set -e; test -s /tmp/s1-wt.tar; rm -rf ${REPO}; mkdir -p ${REPO}; tar -xf /tmp/s1-wt.tar -C ${REPO}; echo "EXTRACTED=$(ls -A ${REPO} | wc -l)"`);
113
113
  if (ext.code !== 0 || !ext.out.includes("EXTRACTED="))
114
114
  throw new Error(`s1 grader tar extract failed: ${ext.out.slice(-400)}`);
115
- if (process.env.S1_DEBUG_GIT)
115
+ if (s1DebugGit())
116
116
  console.error(`[S1_DEBUG_TAR] head=${head.slice(0, 8)} base=${base.slice(0, 8)} tarBytes=${buf.length} ${ext.out.slice(-60)}`);
117
117
  return { head, base };
118
118
  }
@@ -141,7 +141,7 @@ async function importBundleBinaryToGrader(grader, tarBuffer, shas) {
141
141
  const ext = await sh(grader)(`set -e; test -s /tmp/s1-wt.tar; rm -rf ${REPO}; mkdir -p ${REPO}; tar -xf /tmp/s1-wt.tar -C ${REPO}; echo "EXTRACTED=$(ls -A ${REPO} | wc -l)"`);
142
142
  if (ext.code !== 0 || !ext.out.includes("EXTRACTED="))
143
143
  throw new Error(`s1 grader tar extract failed: ${ext.out.slice(-400)}`);
144
- if (process.env.S1_DEBUG_GIT)
144
+ if (s1DebugGit())
145
145
  console.error(`[S1_DEBUG_TAR team] head=${shas.head.slice(0, 8)} base=${shas.base.slice(0, 8)} ${ext.out.slice(-60)}`);
146
146
  return shas;
147
147
  }
@@ -196,7 +196,7 @@ export function buildLiveDeps(rt, trap, seed, cellId) {
196
196
  state.shas = await transferWorkerTreeToGrader(worker, grader);
197
197
  const task = { ...oracleTaskFromTrap(trap), ...(state.shas ? { deliveredShas: state.shas } : {}) };
198
198
  const v = await runStandaloneOracle(grader, graderTransport(grader), worker, task);
199
- if (process.env.S1_DEBUG_GIT)
199
+ if (s1DebugGit())
200
200
  console.error(`[S1_DEBUG_ORACLE ${trap.id}] delivered=${v.delivered} tests=${v.hiddenTestsGreen} build=${v.buildPassed} inv=${v.invariantsOk} | raw=`, JSON.stringify(v.raw)?.slice(0, 700));
201
201
  return v;
202
202
  };
@@ -250,27 +250,27 @@ export function buildLiveDeps(rt, trap, seed, cellId) {
250
250
  execFileSync("git", ["clone", "-q", "--branch", branch, remoteUrl, cloneDir], { stdio: ["ignore", "ignore", "pipe"] });
251
251
  }
252
252
  catch (e1) {
253
- if (process.env.S1_DEBUG_GIT)
253
+ if (s1DebugGit())
254
254
  console.error(`[S1_DEBUG_IMPORT] cp clone --branch ${branch} failed: ${e1.stderr?.toString().slice(-200)}; retrying default`);
255
255
  execFileSync("git", ["clone", "-q", remoteUrl, cloneDir], { stdio: ["ignore", "ignore", "pipe"] });
256
256
  }
257
257
  const baseHash = execFileSync("git", ["-C", cloneDir, "rev-list", "--max-parents=0", "HEAD"]).toString().trim().split("\n").pop().trim();
258
258
  const head = execFileSync("git", ["-C", cloneDir, "rev-parse", "HEAD"]).toString().trim();
259
259
  if (!/^[0-9a-f]{40}$/.test(baseHash) || head === baseHash) {
260
- if (process.env.S1_DEBUG_GIT)
260
+ if (s1DebugGit())
261
261
  console.error(`[S1_DEBUG_IMPORT] cp integrated tree not delivered: head=${head} base=${baseHash}`);
262
262
  return false;
263
263
  }
264
264
  const bundlePath = join(tmp, "team.bundle");
265
265
  execFileSync("git", ["-C", cloneDir, "archive", "--format=tar", "-o", bundlePath, "HEAD"], { stdio: ["ignore", "ignore", "pipe"] });
266
266
  const buffer = readFileSync(bundlePath);
267
- if (process.env.S1_DEBUG_GIT)
267
+ if (s1DebugGit())
268
268
  console.error(`[S1_DEBUG_IMPORT] cp integrated head=${head} base=${baseHash} tar bytes=${buffer.length}`);
269
269
  state.teamBundle = { buffer, head, base: baseHash };
270
270
  return true;
271
271
  }
272
272
  catch (e) {
273
- if (process.env.S1_DEBUG_GIT)
273
+ if (s1DebugGit())
274
274
  console.error(`[S1_DEBUG_IMPORT] control-plane THREW:`, e instanceof Error ? e.message : String(e));
275
275
  return false;
276
276
  }
@@ -361,7 +361,7 @@ async function runLeaderForTrap(rt, brain, models, roles, pricing, trap, cellId,
361
361
  process.env.LEADER_CONFLICT_ROUNDS = process.env.LEADER_CONFLICT_ROUNDS ?? "2";
362
362
  const run = createLeaderRunner({
363
363
  brain, models, roles, pricing, e2bApiKey: rt.e2bApiKey, workspace: W,
364
- ...(process.env.S1_DEBUG_GIT ? { logger: { warn: (m, x) => console.error(`[S1_DEBUG_LEADER warn] ${m}`, x ? JSON.stringify(x).slice(0, 300) : ""), info: (m, x) => console.error(`[S1_DEBUG_LEADER info] ${m}`, x ? JSON.stringify(x).slice(0, 200) : "") } } : {}),
364
+ ...(s1DebugGit() ? { logger: { warn: (m, x) => console.error(`[S1_DEBUG_LEADER warn] ${m}`, x ? JSON.stringify(x).slice(0, 300) : ""), info: (m, x) => console.error(`[S1_DEBUG_LEADER info] ${m}`, x ? JSON.stringify(x).slice(0, 200) : "") } } : {}),
365
365
  });
366
366
  const teamSeedWrite = (trap.seedFiles ?? [])
367
367
  .map((f) => {
@@ -415,13 +415,13 @@ async function runLeaderForTrap(rt, brain, models, roles, pricing, trap, cellId,
415
415
  });
416
416
  }
417
417
  catch (e) {
418
- if (process.env.S1_DEBUG_GIT)
418
+ if (s1DebugGit())
419
419
  console.error(`[S1_DEBUG_LEADER ${trap.id}] run() THREW:`, e instanceof Error ? (e.stack ?? e.message) : String(e));
420
420
  killDaemon();
421
421
  rmSync(baseDir, { recursive: true, force: true });
422
422
  return { ok: false, reports: [], infraFailed: true };
423
423
  }
424
- if (process.env.S1_DEBUG_GIT)
424
+ if (s1DebugGit())
425
425
  console.error(`[S1_DEBUG_LEADER ${trap.id}] ok=${res.ok} error=${JSON.stringify(res.error)} cancelled=${res.cancelled} merge=${JSON.stringify(res.merge)} repairTerminal=${res.repairTerminal} reports=${(res.reports ?? []).length} reportsDetail=${JSON.stringify((res.reports ?? []).map((r) => ({ w: r.workerId, ok: r.ok, err: r.error })))?.slice(0, 400)}`);
426
426
  let imported = false;
427
427
  const merged = res.merge?.ok === true;
@@ -7,6 +7,7 @@ import { makeBenchClock } from "./runner-ctx.js";
7
7
  import { buildS1Report, S1_SCHEMA_VERSION, S1_CORE_VERSION } from "./row.js";
8
8
  import { armTag } from "./row.js";
9
9
  import { buildLiveDeps, liveRuntimeConfigFromEnv } from "./live-deps.js";
10
+ import { setS1DebugGit } from "./runner-ctx.js";
10
11
  export function enumerateCells(seeds, filter = {}) {
11
12
  const cells = [];
12
13
  const source = filter.only ? ALL_TRAPS : FIRM_TRAPS;
@@ -184,6 +185,7 @@ function parseArgs(argv) {
184
185
  const cellTimeoutMs = positiveIntArg(get("cell-timeout-ms"), 15 * 60_000, "--cell-timeout-ms");
185
186
  return {
186
187
  dryRun: argv.includes("--dry-run"),
188
+ debugGit: argv.includes("--debug-git"),
187
189
  seeds,
188
190
  cellTimeoutMs,
189
191
  ledgerPath: get("ledger") ?? path.resolve(`./s1-out/${runId}.ledger.jsonl`),
@@ -234,6 +236,7 @@ function dryRunMockFactory() {
234
236
  }
235
237
  export async function main(argv = process.argv.slice(2)) {
236
238
  const args = parseArgs(argv);
239
+ setS1DebugGit(args.debugGit);
237
240
  const liveRuntime = liveRuntimeConfigFromEnv();
238
241
  if (!args.dryRun && !liveRuntime) {
239
242
  console.error("[s1 run-firm] LIVE path refused: E2B_API_KEY + DEEPSEEK_API_KEY are required (the brain gateway). " +
@@ -17,6 +17,10 @@
17
17
  * + the clock; a deterministic shape test exercises it with a MOCK runner (no real agents, no keys).
18
18
  */
19
19
  import type { ExecutionEnv } from "@sema-agent/core";
20
+ /** 由 bench 入口(`run-firm.ts` 的 `--debug-git`)翻开;不调 = 关。 */
21
+ export declare function setS1DebugGit(on: boolean): void;
22
+ /** tar/import/oracle/leader 四族 stderr 追踪开着没有(读侧单点)。 */
23
+ export declare function s1DebugGit(): boolean;
20
24
  /**
21
25
  * The budget block the budget-match guard compares. NOT a s1.v1 RawRow field (the contract carries no budget) —
22
26
  * it is a PRODUCER-side fairness descriptor (one per emitted cell). `teamWorkerBudgetSumUsd` is the bigger-pie
@@ -1,3 +1,10 @@
1
+ let debugGit = false;
2
+ export function setS1DebugGit(on) {
3
+ debugGit = on;
4
+ }
5
+ export function s1DebugGit() {
6
+ return debugGit;
7
+ }
1
8
  export function budgetDescriptor(budget, arm, teamWorkerBudgetSumUsd) {
2
9
  return {
3
10
  modelId: budget.modelId,
@@ -14,6 +14,7 @@ import { configLkgEnabled } from "../config.js";
14
14
  import { defaultLkgPath, defaultSkillCacheDir, saveLkg, loadLkg } from "../config-lkg.js";
15
15
  import { centerManagedConfigKeys } from "../config-center/apply-effective.js";
16
16
  import { createConfigProvider, raceBootFetch, BOOT_FETCH_DEFERRED } from "../config-provider.js";
17
+ import { executionLaneCaps, executionLaneOf } from "../execution-lane-caps.js";
17
18
  import { createKeyResolver } from "../key-resolver.js";
18
19
  import { ensureSealedKeyStore, reportExecutionPublicKey } from "../sealed-key.js";
19
20
  import { applyEffective, applyCenterReadFace, mutateInPlace, logEffectiveDiff, applyCenterSkills, resolveMcpServers, resolveA2aPeers, restartReasons, modelPlaneChanged, planeHasActiveTiers, fetchPromptArtifact } from "../config-center/facade.js";
@@ -366,12 +367,32 @@ export async function createConfigCenterRuntime(ctx) {
366
367
  try {
367
368
  const fileRx = await loadRemoteExec(localRoot);
368
369
  if (fileRx) {
370
+ const preemptedLane = config.remoteExec?.provider ?? "in-process";
371
+ const isolatedLane = (lane) => {
372
+ const word = executionLaneOf(lane === "in-process" ? undefined : lane);
373
+ return word !== undefined && executionLaneCaps(word).isolation === "supported";
374
+ };
375
+ const disclosePreemption = (winner) => {
376
+ if (winner === preemptedLane)
377
+ return;
378
+ const downgrade = isolatedLane(preemptedLane) && !isolatedLane(winner);
379
+ logger.warn("remote_exec_lane_preempted_by_file", {
380
+ preemptedLane,
381
+ effectiveLane: winner,
382
+ root: localRoot,
383
+ isolationDowngrade: downgrade,
384
+ note: `config.d/remote-exec.json (CONFIG_PROVIDER=local, file-wins) replaced the env-derived execution lane ` +
385
+ `"${preemptedLane}" with "${winner}" — any REMOTE_EXEC/lane coordinate env for "${preemptedLane}" is now INERT` +
386
+ (downgrade ? `. This is an ISOLATION DOWNGRADE: tasks run on a non-isolated target. Delete/fix the file, or unset the lane env if the file is the intended source.` : `. Delete/fix the file, or unset the lane env if the file is the intended source.`),
387
+ });
388
+ };
369
389
  if (fileRx.provider === "host") {
370
390
  config.remoteExec = {
371
391
  provider: "host",
372
392
  ...(fileRx.workdir ? { workspaceBase: fileRx.workdir } : {}),
373
393
  ...(fileRx.commandTimeoutMs != null ? { commandTimeoutMs: fileRx.commandTimeoutMs } : {}),
374
394
  };
395
+ disclosePreemption("host");
375
396
  logger.info("remote_exec_from_file", { provider: "host", root: localRoot });
376
397
  }
377
398
  else if (fileRx.provider === "local-docker") {
@@ -383,10 +404,11 @@ export async function createConfigCenterRuntime(ctx) {
383
404
  ...(fileRx.cpus != null ? { cpus: fileRx.cpus } : {}),
384
405
  ...(fileRx.network ? { network: fileRx.network } : {}),
385
406
  };
407
+ disclosePreemption("local-docker");
386
408
  logger.info("remote_exec_from_file", { provider: "local-docker", root: localRoot, image: fileRx.image });
387
409
  }
388
410
  else {
389
- logger.warn("remote_exec_file_isolated_lane_unwired", { provider: fileRx.provider, note: "e2b/k8s/ssh/adb are sourced from REMOTE_EXEC env until file-source NAME→value resolution lands (center field map)" });
411
+ logger.warn("remote_exec_file_isolated_lane_unwired", { provider: fileRx.provider, effectiveLane: preemptedLane, note: "e2b/k8s/ssh/adb are sourced from REMOTE_EXEC env until file-source NAME→value resolution lands (center field map) — the env-derived lane above stays in force" });
390
412
  }
391
413
  }
392
414
  }
@@ -26,6 +26,17 @@ export function createLiveCoordinators(ctx) {
26
26
  checkpointStore: backend?.checkpoint !== undefined,
27
27
  durableApproval: config.durableApproval === true,
28
28
  toolApproval: config.toolApprovalEnabled === true,
29
+ askWindowMs: config.streamApproval.windowMs,
30
+ });
31
+ }
32
+ if (config.toolApprovalEnabled === true && config.streamApproval.windowMs === 0) {
33
+ logger.warn("stream_ask_window_zero", {
34
+ unattendedPolicy: config.unattendedApprovalPolicy,
35
+ parkFacility: durableEnabled,
36
+ streamApproval: streamApprovalGate.active ? "active" : streamApprovalGate.reason,
37
+ note: durableEnabled && config.unattendedApprovalPolicy === "park"
38
+ ? "STREAM_ASK_WINDOW_MS=0 — no approval card is emitted on ANY leg; every gated ask parks immediately (answer it via POST /v1/approvals/:sessionId/decide). Set a positive window (ms) to get live cards back."
39
+ : "STREAM_ASK_WINDOW_MS=0 — no approval card is emitted on ANY leg, and this deployment has no park destination (UNATTENDED_APPROVAL_POLICY=deny and/or no checkpoint store), so every gated tool call is refused fail-closed. Set a positive window (ms), or wire DURABLE_APPROVAL + a checkpoint store.",
29
40
  });
30
41
  }
31
42
  const approvalAskAudit = config.toolApprovalEnabled && backend?.kind === "local" && config.localDataRoot !== undefined
@@ -45,8 +56,9 @@ export function createLiveCoordinators(ctx) {
45
56
  }
46
57
  const toolApproval = config.toolApprovalEnabled
47
58
  ? new ToolApprovalCoordinator({
59
+ ttlMs: config.streamApproval.windowMs,
48
60
  ...(streamApprovalGate.active
49
- ? { askStore: streamApprovalGate.askStore, ttlMs: config.streamApproval.windowMs, unreachedTtlMs: config.streamApproval.unreachedTtlMs }
61
+ ? { askStore: streamApprovalGate.askStore, unreachedTtlMs: config.streamApproval.unreachedTtlMs }
50
62
  : {}),
51
63
  windowMarginMs: config.streamAskWindowMarginMs,
52
64
  admitMaxPerTask: config.streamApproval.admitMaxPerTask,
@@ -57,9 +57,14 @@ export interface ParkedReviveGateDeps {
57
57
  * 手抄一个等价签名会让 core 改这条 seam 的那天变成静默漂移而不是编译期事件)。
58
58
  */
59
59
  readonly resolveRuntimeCaps?: RunnerDeps["runtimeCapsResolver"];
60
- /** `RunnerDeps.autoMode` 分类器面(信任门半场)在不在场 —— core 的武装条件是
61
- * `runtimeCaps.autoMode === true` **∧** 这一面在场,两个半场缺一不武装(prepare-task 原式) */
60
+ /** `RunnerDeps.autoMode` 分类器面(信任门半场)在不在场 —— core 7.2.0 的武装条件是
61
+ * `runtimeCaps.autoMode === true` **∧** 这一面在场(prepare-task 原式)。⚠️ [ref] / core [ref] 起武装式换成
62
+ * `spec.autoModeRequested === true ∧ 席位 ∧ runtimeCaps?.autoMode !== false`——本文件的镜像
63
+ * {@link autoModePostureOf} **未随改**(赎回行无父 run 的 auto 意图可读),见该函数头注的未闭环登记。 */
62
64
  readonly autoModeSeatMounted?: boolean;
65
+ /** [ref]:父 run 的 auto 意图(`TaskSpec.autoModeRequested` / 链位)——意图武装式下重建 decider 的第三项。⚠️ 今日
66
+ * **无持久源**(core [ref] 链位 live-only;RunRecord 无 body),生产装配不递 ⇒ 恒缺席;座留给 core 持久化到货。 */
67
+ readonly parentAutoModeIntent?: ((row: BackgroundAgentRecord) => boolean) | undefined;
63
68
  readonly logger: {
64
69
  info(event: string, fields?: Record<string, unknown>): void;
65
70
  warn(event: string, fields?: Record<string, unknown>): void;
@@ -1,3 +1,4 @@
1
+ import { coreArmsAutoOnIntent } from "../auto-mode-face.js";
1
2
  import { join } from "node:path";
2
3
  import { createAutoModeDecider, NodeExecutionEnv } from "@sema-agent/core";
3
4
  import { recordFailOpen } from "../observability/fail-open.js";
@@ -66,8 +67,18 @@ async function resolveCapsFor(deps, identity) {
66
67
  }
67
68
  }
68
69
  function autoModePostureOf(deps, row, caps) {
69
- if (deps.autoModeSeatMounted !== true || caps?.autoMode !== true)
70
+ if (deps.autoModeSeatMounted !== true)
70
71
  return {};
72
+ if (!coreArmsAutoOnIntent()) {
73
+ if (caps?.autoMode !== true)
74
+ return {};
75
+ }
76
+ else {
77
+ if (caps?.autoMode === false)
78
+ return {};
79
+ if (deps.parentAutoModeIntent?.(row) !== true)
80
+ return {};
81
+ }
71
82
  return { autoMode: { decider: createUnreachableAncestorClassifier(deps, row) } };
72
83
  }
73
84
  function createUnreachableAncestorClassifier(deps, row) {
@@ -50,6 +50,9 @@ export interface ResolveSpecCtx {
50
50
  hookWakeBus: {
51
51
  deliver?: (sessionId: string, text: string) => Promise<boolean>;
52
52
  };
53
+ /** [ref] 件⑤:per-session「显式 bypassPermissions」登记簿的写腿(`boot/session-shell-gate-registry.ts`);
54
+ * 阶段④ 算出 effMode 的那一行登记,runner-deps 的 config 忠告汇点按 sessionId 回查定级。缺席(桩)= 不登记。 */
55
+ noteSessionShellGate?: ((sessionId: string | undefined, explicitOff: boolean) => void) | undefined;
53
56
  resumeAnchorStore: ReturnType<StoreBackend["resumeAnchor"]> | undefined;
54
57
  ownerAware: OwnerAwareSessionStore;
55
58
  taskLimitCaps: {
@@ -36,7 +36,7 @@ import { redactSecrets } from "../trace/redact.js";
36
36
  import { DeferredSandboxPathEnv, isSandboxPathAdjudicationLane, sandboxPathEnvSlots } from "./deferred-sandbox-path-env.js";
37
37
  import { effectiveMemoryPersistenceCapable } from "./memory-boundary.js";
38
38
  export function createResolveSpec(ctx) {
39
- const { config, logger, metrics, localRoot, scenarios, principalCaps, centerRuntimeCapsResolver, handsLanes, getCenterPrompts, getKeyResolver, taskAttachmentStore, perSessionCwd, setSessionCwd, setSessionShellEnv, fenceSessionOwner, hookLlm, hookAgent, fleetBus, hookWakeBus, resumeAnchorStore, ownerAware, taskLimitCaps, taskTimeoutSec, selectEnvTool, sendUserFileToolSpec, memoryEngine, durableEnabled, approvalExemptionStore, singleUserAutoAcceptBaseline, checkpointStore, deploymentHooks, imageIndex, perTaskImage, sessionEnvSelection, liveQuestionFace, approverSeat, lockedKeys, } = ctx;
39
+ const { config, logger, metrics, localRoot, scenarios, principalCaps, centerRuntimeCapsResolver, handsLanes, getCenterPrompts, getKeyResolver, taskAttachmentStore, perSessionCwd, setSessionCwd, setSessionShellEnv, fenceSessionOwner, hookLlm, hookAgent, fleetBus, hookWakeBus, noteSessionShellGate, resumeAnchorStore, ownerAware, taskLimitCaps, taskTimeoutSec, selectEnvTool, sendUserFileToolSpec, memoryEngine, durableEnabled, approvalExemptionStore, singleUserAutoAcceptBaseline, checkpointStore, deploymentHooks, imageIndex, perTaskImage, sessionEnvSelection, liveQuestionFace, approverSeat, lockedKeys, } = ctx;
40
40
  assertGuardPatternsUsable(config);
41
41
  const onlySensitiveBaseline = buildOnlySensitiveBaselineWarning(config, { durableEnabled, singleUserAutoAcceptBaseline });
42
42
  if (onlySensitiveBaseline)
@@ -52,6 +52,7 @@ export function createResolveSpec(ctx) {
52
52
  assertRequestA2aUnlocked(body.a2aPeers, lockedKeys);
53
53
  const gated = await gateScenarioAndAppend(body, auth, opts);
54
54
  const effMode = effectivePermissionMode(body, gated.parsedSettings.settings);
55
+ noteSessionShellGate?.(auth?.sessionId, effMode === "bypassPermissions");
55
56
  const lane = bindSettingsCwdEnvAndModel(body, auth, gated, effMode, opts);
56
57
  const anchors = await resolveHistoryAnchors(body, auth);
57
58
  const spec = assembleSpecLiteral(body, auth, req, opts, { ...gated, ...lane, ...anchors }, effMode);
@@ -278,6 +279,7 @@ export function createResolveSpec(ctx) {
278
279
  additionalReadDirectories,
279
280
  enablePlanMode: config.planModeEnabled ? true : undefined,
280
281
  ...(effMode ? { shellGate: shellGateForMode(effMode) } : {}),
282
+ ...(effMode === "auto" ? { autoModeRequested: true } : {}),
281
283
  selfOrchestration: selfOrchestrationFromBody({ selfOrchestration: body.selfOrchestration === true || parsedSettings.settings?.ultracode === true }, config, Boolean(centerRuntimeCapsResolver)),
282
284
  forwardSubagentEvents: body.forwardSubagentEvents === true ? true : undefined,
283
285
  retainSubagentSessions: normalizeRetainSubagentSessions(body.retainSubagentSessions),
@@ -52,6 +52,14 @@ import type { MemorySyncRunner } from "../memory-sync-client.js";
52
52
  */
53
53
  export type RunnerDepsOnAsk = (req: Parameters<ToolApprovalCoordinator["ask"]>[0], signal?: AbortSignal) => Promise<AskOutcome>;
54
54
  export declare function createRunnerDepsOnAsk(toolApproval: ToolApprovalCoordinator | undefined): RunnerDepsOnAsk | undefined;
55
+ /** [ref] 件⑤:core `onError(phase:"config")` 的 `classification` 已知词表。⚠️ core d.ts(`RunnerDeps.onError` 注)只登记了
56
+ * prompt-cache 相的五词,config 相的词**只在 dist js 出现**(7.2.0 亲扫 dist 下全部 js 的 `phase: "config"` 发射点:五词——
57
+ * `interaction-posture-refused`(prepare-config-doors)/ `memory-tools-not-mounted` / `sandbox-admission-unavailable` /
58
+ * `shared-memory-not-mounted` / `shell-gate-off`(prepare-hands-readface));[ref] 去幻觉轮真抓:首版只写了 shell-gate-off
59
+ * 一词,其余四词会被误标 unknown。所以这是「按实现亲读」的开集快照,不是 core 类型闭集——词表外的值原样进日志并标
60
+ * `classificationKnown:false`(指标标签折 `unknown`),不吞不编;与安装包的锁步由 test/boot-runner-deps-shared-base.test.ts
61
+ * 的 dist 扫描格钉住(core 加词/收进 d.ts 那天先红)。 */
62
+ export declare const CONFIG_ADVISORY_CLASSIFICATIONS: ReadonlySet<string>;
55
63
  export interface RunnerDepsCtx {
56
64
  config: ServiceConfig;
57
65
  logger: Logger;
@@ -113,6 +121,10 @@ export interface RunnerDepsCtx {
113
121
  * 来源=configCenter.takeMcpRevocations()(取走即声明接线,restart 豁免与承接绑同一动作)。
114
122
  * 缺席=undefined=pre-338 语义(纯 env 部署无中心撤销面)。 */
115
123
  mcpRevocations: RunnerDeps["mcpRevocations"];
124
+ /** [ref] 件⑤([ref] server ③):per-session「显式 bypassPermissions」登记簿的读腿 —— `onError(phase:"config",
125
+ * classification:"shell-gate-off")` 在本 session 显式声明 bypass 时降级 info(正常态不打 warn)。缺席(桩 /
126
+ * run-local)= 照 warn(退化方向=多一条告警)。 */
127
+ sessionShellGateExplicitlyOff?: ((sessionId: string) => boolean) | undefined;
116
128
  /**
117
129
  * 交接件⑤ —— commit 尾注的署名座(`RunnerDeps.hands.commitCoAuthor`)。
118
130
  *
@@ -15,6 +15,13 @@ export function createRunnerDepsOnAsk(toolApproval) {
15
15
  return undefined;
16
16
  return (req, signal) => toolApproval.ask(req, signal);
17
17
  }
18
+ export const CONFIG_ADVISORY_CLASSIFICATIONS = new Set([
19
+ "interaction-posture-refused",
20
+ "memory-tools-not-mounted",
21
+ "sandbox-admission-unavailable",
22
+ "shared-memory-not-mounted",
23
+ "shell-gate-off",
24
+ ]);
18
25
  export function createEngineNoticeForwarder(logger, router = defaultEngineNoticeRouter) {
19
26
  return (notice) => {
20
27
  logger.warn("engine_notice", { code: notice.code, message: notice.message, ...(notice.detail !== undefined ? { detail: notice.detail } : {}) });
@@ -71,7 +78,7 @@ export function createSharedRunnerDeps(ctx) {
71
78
  return shared;
72
79
  }
73
80
  export function createRunnerDeps(ctx) {
74
- const { config, logger, metrics, localRoot, outcomeSink, elicitation, question, toolApproval, sessionStore, memoryEngine, sessionCaptureRecordStore, memorySyncRunner, runtimeCapsResolver, fileHistoryStore, legacyRewindBoundaryProbe, permissionRuleStore, fleetBus, workflowRunStore, workflowJournalStore, workflowAgentRegistry, workflowNotifyGate, workflowCompletionInbox, deliverWorkflowCompletion, getRunStore, } = ctx;
81
+ const { config, logger, metrics, localRoot, outcomeSink, elicitation, question, toolApproval, sessionStore, memoryEngine, sessionCaptureRecordStore, memorySyncRunner, runtimeCapsResolver, fileHistoryStore, legacyRewindBoundaryProbe, permissionRuleStore, fleetBus, workflowRunStore, workflowJournalStore, workflowAgentRegistry, workflowNotifyGate, workflowCompletionInbox, deliverWorkflowCompletion, getRunStore, sessionShellGateExplicitlyOff, } = ctx;
75
82
  const sharedRunnerDeps = createSharedRunnerDeps(ctx);
76
83
  const workflowModelAllowlist = workflowModelAllowlistFor(config);
77
84
  const selfOrchestrationDeps = config.selfOrchestrationEnabled
@@ -216,8 +223,26 @@ export function createRunnerDeps(ctx) {
216
223
  return;
217
224
  }
218
225
  if (ctx.phase === "config") {
219
- logger.warn("runner_config_advisory", { sessionId: ctx.sessionId, info: String(err) });
220
- metrics.inc("runner_config_advisory_total");
226
+ const code = typeof err?.code === "string" ? err.code : undefined;
227
+ const classification = ctx.classification;
228
+ const classificationKnown = classification === undefined ? undefined : CONFIG_ADVISORY_CLASSIFICATIONS.has(classification);
229
+ const expectedShellGateOff = classification === "shell-gate-off" && sessionShellGateExplicitlyOff?.(ctx.sessionId) === true;
230
+ const fields = {
231
+ sessionId: ctx.sessionId,
232
+ ...(code !== undefined ? { code } : {}),
233
+ ...(classification !== undefined ? { classification, classificationKnown } : {}),
234
+ ...(expectedShellGateOff ? { expected: "bypassPermissions declared by the caller for this session — shell gate off is the requested posture, not a misconfiguration" } : {}),
235
+ info: String(err),
236
+ };
237
+ if (expectedShellGateOff)
238
+ logger.info("runner_config_advisory", fields);
239
+ else
240
+ logger.warn("runner_config_advisory", fields);
241
+ metrics.inc("runner_config_advisory_total", {
242
+ ...(code !== undefined ? { code } : {}),
243
+ ...(classification !== undefined ? { classification: classificationKnown ? classification : "unknown" } : {}),
244
+ level: expectedShellGateOff ? "info" : "warn",
245
+ });
221
246
  return;
222
247
  }
223
248
  logger.error("runner_error", { phase: ctx.phase, sessionId: ctx.sessionId, err: String(err) });
@@ -9,7 +9,7 @@
9
9
  */
10
10
  import type { ServiceConfig } from "../config.js";
11
11
  import type { Logger } from "../observability/logger.js";
12
- import { type MemoryOptOutVerdictSource } from "../runtime-caps-resolver.js";
12
+ import { type EntitlementsResolverFn, type MemoryOptOutVerdictSource } from "../runtime-caps-resolver.js";
13
13
  export interface RuntimeCapsCtx {
14
14
  config: ServiceConfig;
15
15
  logger: Logger;
@@ -22,24 +22,34 @@ export interface RuntimeCapsCtx {
22
22
  * D1 / `boot/retention-lane.ts` 同姿态:安全/合规控件不得半开)。
23
23
  *
24
24
  * governed 的语义是「per-principal verdict **必答**」:core 对 verdict 缺席一律**拒跑**(fail-closed,部署显式
25
- * 选的方向)。而 verdict 只能来自一个源 —— 本批唯一源 = SQL 后端上的 `memory_optout_grant` 表(三层折叠后
26
- * 恒答显式 boolean)。governed 而无源 每一个 `capture:"off"` 声明都被 core 以
25
+ * 选的方向)verdict 源**两条**(析取,[ref] 起):①SQL 后端上的 `memory_optout_grant` 表(三层折叠后
26
+ * 恒答显式 boolean),②非 dry-run config center `EntitlementRuntimeCaps.allowMemoryOptOut`。
27
+ * governed 而两源皆无 ⇒ 每一个 `capture:"off"` 声明都被 core 以
27
28
  * `memory.capture_optout_denied` 拒掉,而运维看到的只是一台「启动成功、opt-out 全拒」的机器 —— 正是
28
- * 「阀门开着而配不出来 ⇒ 悄悄读成别的」那一形。所以在**启动期**点名:要么接 SQL 后端(表随中央
29
- * ensureSchema 建),要么改姿态。`open`/`capture-required`/缺席 不依赖源(前者故障放行、后者全拒,
30
- * 都不问 verdict)。
29
+ * 「阀门开着而配不出来 ⇒ 悄悄读成别的」那一形。所以在**启动期**点名:接 SQL 后端(表随中央
30
+ * ensureSchema 建)、接非 dry-run center,或改姿态。`open`/`capture-required`/缺席 不依赖源(前者故障放行、
31
+ * 后者全拒,都不问 verdict)。
31
32
  *
32
33
  * ⚠️ center 键 `EntitlementRuntimeCaps.allowMemoryOptOut` 是 settings-schema 的独立小件([ref] §四 开放问题①),
33
- * 已到货(1.4.0,[ref])——析取臂已加(centerEntitlementPresent),判据不变;dry-run 不算源(不 APPLY 即不 ENFORCE 同律)。
34
+ * 已到货(1.4.0,[ref])——析取臂已加,dry-run 不算源(不 APPLY 即不 ENFORCE 同律)。
35
+ *
36
+ * 🔴 **[ref] —— 第三臂的辖域**:它证的是「center 这个**源**在场」,**不是**「center 会为每个 principal
37
+ * **发** `allowMemoryOptOut`」。该键在 settings-schema 里是**可选**的(`z.ZodOptional<z.ZodBoolean>`),
38
+ * `toCoreRuntimeCaps` 对缺席不铸键 ⇒ 一台 center 从不发该键的 governed worker 照样起动、然后每一个
39
+ * `memory.capture:"off"` 恒拒(core: verdict absent)。这条**不能**在启动期证死(部署级布尔对 per-principal
40
+ * 的授权说不了话,且 boot 期去问 center 会把拒启挂在一次网络上),所以取「拒启臂放行 + 两处响亮」:
41
+ * 启动期一行 `memory_capture_governed_center_only_source`(这一形有风险)+ 运行期首次真缺席一行
42
+ * (见 `applyGovernedVerdictAbsenceWarning`,一次性、不改判)。
34
43
  */
35
44
  export declare function assertMemoryCapturePolicyWirable(input: {
36
45
  policy: ServiceConfig["memoryCapturePolicy"];
37
46
  grantSourceWired: boolean;
38
- centerEntitlementPresent?: boolean;
47
+ centerEntitlementSourceWired?: boolean;
39
48
  }): void;
40
49
  export declare function createRuntimeCaps(ctx: RuntimeCapsCtx): {
41
50
  principalCaps: import("../runtime-caps-resolver.js").PrincipalEntitlementsClient | undefined;
42
51
  centerRuntimeCapsResolver: ((principal: string | undefined) => Promise<import("@sema-agent/core").RuntimeCaps | undefined>) | undefined;
43
- runtimeCapsResolver: import("../runtime-caps-resolver.js").EntitlementsResolverFn | undefined;
52
+ runtimeCapsResolver: EntitlementsResolverFn | undefined;
53
+ runtimeCapsPeekResolver: EntitlementsResolverFn | undefined;
44
54
  };
45
55
  //# sourceMappingURL=runtime-caps.d.ts.map