@sema-agent/server 7.43.0 → 7.44.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.
- package/USAGE.md +18 -0
- package/dist/adoption/plan.js +31 -192
- package/dist/adoption/quiesce.js +3 -86
- package/dist/adoption/runner.js +3 -140
- package/dist/adoption/sql.js +0 -74
- package/dist/adoption/wire.js +0 -80
- package/dist/approval-ask-machine.js +0 -75
- package/dist/approval-card.js +0 -323
- package/dist/approval-deny-reasons.js +0 -42
- package/dist/approval-hmac.js +0 -35
- package/dist/approval-reconciler.js +8 -166
- package/dist/approval.js +4 -67
- package/dist/audit.js +1 -44
- package/dist/auth-bridge.js +4 -47
- package/dist/auth-keys.js +0 -23
- package/dist/bake-runner/main.js +4 -65
- package/dist/bake-runner/protocol.js +3 -58
- package/dist/bake-runner/runner.js +5 -91
- package/dist/bench/l8/artifact.js +2 -61
- package/dist/bench/l8/escape.js +0 -25
- package/dist/bench/l8/index.js +0 -14
- package/dist/bench/l8/probes.js +0 -60
- package/dist/bench/l8/run-probes.js +2 -45
- package/dist/bench/s1/arms.js +14 -189
- package/dist/bench/s1/live-deps.js +11 -253
- package/dist/bench/s1/oracle.js +1 -36
- package/dist/bench/s1/repair-oracle-adapter.js +0 -30
- package/dist/bench/s1/reviewer.js +0 -34
- package/dist/bench/s1/row.js +0 -62
- package/dist/bench/s1/run-firm.js +4 -70
- package/dist/bench/s1/runner-ctx.js +0 -40
- package/dist/bench/s1/tasks.js +0 -115
- package/dist/boot/adoption.js +0 -20
- package/dist/boot/budget-tracing.js +3 -55
- package/dist/boot/config-center.js +43 -551
- package/dist/boot/coordinators.js +1 -69
- package/dist/boot/crash-last.js +0 -18
- package/dist/boot/deferred-sandbox-path-env.js +3 -119
- package/dist/boot/execution-env.js +5 -171
- package/dist/boot/governance-seams.js +0 -92
- package/dist/boot/leader.js +0 -69
- package/dist/boot/limit-sync.js +0 -2
- package/dist/boot/memory-boundary.js +3 -91
- package/dist/boot/org-memory.js +1 -25
- package/dist/boot/parked-revive-gate.js +0 -150
- package/dist/boot/permission-rules-audit.js +1 -56
- package/dist/boot/reapers.js +15 -273
- package/dist/boot/resolve-spec.js +9 -768
- package/dist/boot/retention-lane.js +4 -87
- package/dist/boot/runner-deps.js +3 -297
- package/dist/boot/runtime-caps.js +0 -40
- package/dist/boot/session-faces.js +2 -133
- package/dist/boot/shutdown.js +9 -83
- package/dist/boot/side-query-lane.js +2 -137
- package/dist/boot/stores.js +15 -215
- package/dist/boot/task-list-lane.js +0 -18
- package/dist/boot/webfetch-summarize-lane.js +0 -47
- package/dist/boot/workflow-orchestration.js +3 -99
- package/dist/boot-reclaim.js +0 -20
- package/dist/bounded-session-map.js +0 -19
- package/dist/brain.js +2 -139
- package/dist/budget.js +9 -244
- package/dist/capabilities/builtin-tools.js +0 -3
- package/dist/capabilities/center-plugins.js +6 -51
- package/dist/capabilities/center-prompts.js +6 -71
- package/dist/capabilities/code-review-council.js +7 -40
- package/dist/capabilities/collab-workflows.js +1 -44
- package/dist/capabilities/hands-lane.js +0 -65
- package/dist/capabilities/memory-notice.js +0 -70
- package/dist/capabilities/prompt.js +0 -14
- package/dist/capabilities/prompts/code-review.js +0 -14
- package/dist/capabilities/prompts/identity.js +0 -6
- package/dist/capabilities/prompts/team.js +0 -4
- package/dist/capabilities/repo-tools.js +0 -34
- package/dist/capabilities/sandbox-file-send.js +3 -92
- package/dist/capabilities/scenarios.d.ts +0 -1
- package/dist/capabilities/scenarios.js +18 -279
- package/dist/capabilities/select-environment-tool.js +0 -32
- package/dist/capabilities/send-user-file-tool.js +2 -66
- package/dist/capabilities/skills.d.ts +1 -2
- package/dist/capabilities/skills.js +3 -21
- package/dist/capabilities/team.d.ts +3 -11
- package/dist/capabilities/team.js +3 -51
- package/dist/capabilities/tool-defer.js +0 -3
- package/dist/config-center/apply-effective.js +15 -330
- package/dist/config-center/apply-ledger.js +5 -38
- package/dist/config-center/facade.js +0 -41
- package/dist/config-center/hot-keys-registry.js +0 -20
- package/dist/config-center/http-client.js +2 -125
- package/dist/config-center/mcp-revocation.js +2 -34
- package/dist/config-center/read-face.js +0 -59
- package/dist/config-center/restart-signal.js +1 -85
- package/dist/config-center/skills-mcp.d.ts +1 -1
- package/dist/config-center/skills-mcp.js +9 -76
- package/dist/config-center/stage-limits.js +8 -35
- package/dist/config-invariants.js +0 -16
- package/dist/config-lkg.js +0 -42
- package/dist/config-provider.js +3 -186
- package/dist/config-types.js +0 -5
- package/dist/config.js +107 -1145
- package/dist/degenerate-instrument.js +3 -67
- package/dist/deployment-governance.js +0 -124
- package/dist/digest-form.js +0 -12
- package/dist/elicitation.js +3 -86
- package/dist/env-facts.js +7 -75
- package/dist/fleet/fleet-bus.js +34 -507
- package/dist/fleet/fleet-reconciler.js +9 -149
- package/dist/fleet/fleet-terminal-window.js +10 -178
- package/dist/fleet/subagent-tail-bus.js +3 -72
- package/dist/fleet-client.js +10 -70
- package/dist/fleet-lease.js +5 -79
- package/dist/git-api-kind.js +0 -3
- package/dist/governance-ask-marks.js +2 -78
- package/dist/hooks/branch-transcript.js +0 -74
- package/dist/hooks/cc-agent-hook-prompt.js +0 -29
- package/dist/hooks/cc-stop-prompt.js +1 -46
- package/dist/hooks/hook-llm.js +1 -53
- package/dist/hooks/hook-runner.js +20 -414
- package/dist/http/active-run-conflict.js +4 -129
- package/dist/http/cursor-fingerprint.d.ts +5 -0
- package/dist/http/cursor-fingerprint.js +5 -0
- package/dist/http/idempotency.js +0 -37
- package/dist/http/principal-gate.js +3 -40
- package/dist/http/route-ctx.js +0 -9
- package/dist/http/routes/a2a-serve.js +7 -319
- package/dist/http/routes/admin-config-refresh.js +0 -4
- package/dist/http/routes/admin-drain.js +0 -10
- package/dist/http/routes/adoption.js +1 -29
- package/dist/http/routes/agents-roster.js +1 -47
- package/dist/http/routes/approvals-assistant.js +32 -395
- package/dist/http/routes/attachments.js +4 -22
- package/dist/http/routes/capabilities.js +3 -420
- package/dist/http/routes/diagnostics.js +1 -81
- package/dist/http/routes/fleet.js +9 -185
- package/dist/http/routes/images.js +14 -239
- package/dist/http/routes/leader.js +0 -13
- package/dist/http/routes/memory-bundle.js +1 -61
- package/dist/http/routes/memory-policy.js +9 -99
- package/dist/http/routes/notify-wake.js +3 -37
- package/dist/http/routes/observability.js +2 -19
- package/dist/http/routes/retention-ops.js +2 -34
- package/dist/http/routes/rules.js +1 -93
- package/dist/http/routes/runs.js +64 -873
- package/dist/http/routes/session-sync.js +19 -258
- package/dist/http/routes/sessions-list.js +17 -43
- package/dist/http/routes/sessions.js +34 -226
- package/dist/http/routes/shared-memory.js +5 -36
- package/dist/http/routes/side-query.js +1 -87
- package/dist/http/routes/tasks.js +52 -722
- package/dist/http/routes/trace-usage.js +32 -195
- package/dist/http/routes/workflows.js +19 -195
- package/dist/http/run-meta.js +0 -6
- package/dist/http/send.js +0 -32
- package/dist/http/server.js +92 -1597
- package/dist/http/sse-lifecycle.js +2 -13
- package/dist/http/sse-log.js +3 -48
- package/dist/http/tar.js +5 -21
- package/dist/http/verify-rounds.js +0 -5
- package/dist/http/wire-gate.js +0 -9
- package/dist/http/workspace-content.js +0 -10
- package/dist/images/bake-validate.js +1 -70
- package/dist/images/manifest.js +1 -6
- package/dist/index.js +0 -21
- package/dist/key-resolver.js +2 -17
- package/dist/leader/diffout.js +1 -20
- package/dist/leader/diffup.js +0 -47
- package/dist/leader/endpoint.js +2 -57
- package/dist/leader/fanout.js +3 -45
- package/dist/leader/grader-env-factory.js +3 -72
- package/dist/leader/leader.js +5 -156
- package/dist/leader/merge.js +8 -115
- package/dist/leader/planner.js +3 -54
- package/dist/leader/repair-oracle.js +1 -60
- package/dist/leader/repair-wire.js +2 -79
- package/dist/leader/wire.js +8 -307
- package/dist/lsp/e2b-bridge.js +4 -64
- package/dist/lsp/e2b-manager.js +6 -94
- package/dist/lsp/lsp-frames.js +0 -12
- package/dist/lsp/manager.js +4 -96
- package/dist/lsp/ws-transport.js +5 -55
- package/dist/lsp-evict.js +1 -15
- package/dist/main.js +57 -765
- package/dist/memory-bundle-engine.js +0 -55
- package/dist/memory-export.js +0 -4
- package/dist/memory-posture.js +1 -15
- package/dist/memory-scope.js +11 -148
- package/dist/memory-sync-client.js +2 -44
- package/dist/memory-sync.js +1 -80
- package/dist/model-select.js +3 -80
- package/dist/observability/cost-quota.js +1 -17
- package/dist/observability/cost-taxonomy.js +0 -34
- package/dist/observability/fail-open.js +7 -86
- package/dist/observability/logger.js +0 -6
- package/dist/observability/metrics.js +0 -94
- package/dist/observability/otel-exporter.js +3 -13
- package/dist/observability/principal-context.js +0 -9
- package/dist/observability/prompt-manifest.js +1 -37
- package/dist/observability/rate-limit.js +0 -4
- package/dist/observability/secret-env-scrub.js +2 -56
- package/dist/observability/tool-trace.js +1 -70
- package/dist/orchestration/hardened-vm-runner.js +4 -118
- package/dist/orchestration/hardened-vm-worker-runner.js +1 -26
- package/dist/orchestration/hardened-vm-worker.js +0 -27
- package/dist/orchestration/subagent-steer.js +1 -45
- package/dist/orchestration/workflow-agent-steer.js +1 -80
- package/dist/orchestration/workflow-completion-inbox.js +32 -285
- package/dist/orchestration/workflow-notify-journal.js +16 -259
- package/dist/org-memory-admission.js +3 -47
- package/dist/parent-watch.js +2 -48
- package/dist/parked-decide.js +1 -109
- package/dist/per-task-image.js +0 -57
- package/dist/plan-cache-probe.js +3 -27
- package/dist/plugins/adoption-log-sql.js +2 -119
- package/dist/plugins/approval-ask-store-memory.js +3 -38
- package/dist/plugins/approval-ask-store-sql.js +6 -188
- package/dist/plugins/approval-exemption-store.js +2 -28
- package/dist/plugins/background-agent-store-sql.js +4 -105
- package/dist/plugins/background-shell-support.js +14 -122
- package/dist/plugins/blob-backend.js +6 -169
- package/dist/plugins/breaker-state-sql.js +8 -46
- package/dist/plugins/caching-session-store.js +4 -106
- package/dist/plugins/checkpoint-store-sql.js +18 -505
- package/dist/plugins/e2b-orphan-reclaim.js +0 -45
- package/dist/plugins/file-outcome-sink.js +0 -9
- package/dist/plugins/file-resume-anchor-store.js +4 -43
- package/dist/plugins/file-run-store.js +26 -364
- package/dist/plugins/file-snapshot-store-sql.js +10 -181
- package/dist/plugins/fork-routing-session-store.js +8 -111
- package/dist/plugins/host-platform.js +2 -91
- package/dist/plugins/image-bake-store-sql.js +7 -250
- package/dist/plugins/image-index-sql.js +4 -123
- package/dist/plugins/k8s-bg-scripts.js +4 -88
- package/dist/plugins/k8s-exec-protocol.js +0 -41
- package/dist/plugins/leader-run-store-sql.js +0 -101
- package/dist/plugins/local-checkpoint-store.js +8 -128
- package/dist/plugins/local-session-store.js +34 -296
- package/dist/plugins/local-task-attachment-store.js +2 -16
- package/dist/plugins/mailbox-store-sql.js +8 -65
- package/dist/plugins/memory-embedder-fingerprint.js +5 -166
- package/dist/plugins/memory-embedder.js +4 -70
- package/dist/plugins/memory-engine-pg.js +6 -170
- package/dist/plugins/memory-engine-tidb.js +7 -155
- package/dist/plugins/memory-engine-vector-util.js +0 -10
- package/dist/plugins/memory-key-guards.js +0 -34
- package/dist/plugins/memory-origin-law.js +0 -187
- package/dist/plugins/memory-resume-anchor-store.js +0 -17
- package/dist/plugins/memory-run-store.js +12 -89
- package/dist/plugins/memory-session-policy-store.js +0 -17
- package/dist/plugins/memory-sync-store-pg.js +4 -49
- package/dist/plugins/memory-sync-store-tidb.js +3 -35
- package/dist/plugins/outcome-ledger-sql.js +3 -97
- package/dist/plugins/permission-rule-store-file.js +5 -133
- package/dist/plugins/permission-rule-store-sql.d.ts +3 -0
- package/dist/plugins/permission-rule-store-sql.js +50 -307
- package/dist/plugins/pg-cost-quota.js +0 -7
- package/dist/plugins/pg-pool.js +0 -92
- package/dist/plugins/pg-rate-limiter.js +2 -13
- package/dist/plugins/pg-safe-json.js +4 -40
- package/dist/plugins/pg-session-storage.js +25 -189
- package/dist/plugins/posix-shell-fs.js +1 -31
- package/dist/plugins/remote-env-adb.js +12 -101
- package/dist/plugins/remote-env-e2b.js +40 -371
- package/dist/plugins/remote-env-file-error.js +0 -37
- package/dist/plugins/remote-env-host.js +63 -483
- package/dist/plugins/remote-env-k8s.js +32 -326
- package/dist/plugins/remote-env-local-docker.js +24 -157
- package/dist/plugins/remote-env-ssh.js +20 -128
- package/dist/plugins/remote-scratchpad.js +2 -32
- package/dist/plugins/remote-shell.js +1 -32
- package/dist/plugins/resume-anchor-store-sql.js +0 -11
- package/dist/plugins/retention-lane-store-sql.js +0 -108
- package/dist/plugins/retention-store-sql.js +4 -383
- package/dist/plugins/roster-store-sql.js +0 -55
- package/dist/plugins/run-store-sql.js +14 -251
- package/dist/plugins/s3-presign.js +2 -49
- package/dist/plugins/scheduler-support.js +3 -80
- package/dist/plugins/send-file-ledger.js +4 -53
- package/dist/plugins/send-user-file.js +4 -94
- package/dist/plugins/session-placement.js +1 -89
- package/dist/plugins/session-policy-store-sql.js +3 -81
- package/dist/plugins/session-store.js +0 -57
- package/dist/plugins/shared-memory-store-sql.js +4 -167
- package/dist/plugins/sql-driver.js +0 -17
- package/dist/plugins/sql-errors.js +0 -7
- package/dist/plugins/sql-escape.js +0 -8
- package/dist/plugins/sql-row-helpers.js +0 -25
- package/dist/plugins/store-backend.js +42 -222
- package/dist/plugins/store-contracts.js +2 -46
- package/dist/plugins/task-attachment-store.js +3 -49
- package/dist/plugins/task-list-store-sql.js +0 -79
- package/dist/plugins/tidb-cost-quota.js +1 -4
- package/dist/plugins/tidb-pool.js +1 -207
- package/dist/plugins/tidb-rate-limiter.js +3 -9
- package/dist/plugins/tidb-session-storage.js +4 -70
- package/dist/plugins/tidb-session-store.js +19 -341
- package/dist/plugins/tool-result-store-sql.js +4 -179
- package/dist/plugins/usage-window-store-sql.js +0 -8
- package/dist/plugins/web-search.js +10 -132
- package/dist/plugins/workflow-journal-store-sql.js +2 -58
- package/dist/plugins/workflow-run-store-sql.js +6 -91
- package/dist/plugins/worktree-isolation.js +6 -126
- package/dist/plugins/write-behind-counter.js +16 -75
- package/dist/principal-jwt.js +5 -60
- package/dist/project-memory.js +15 -146
- package/dist/prompts-domain-validate.js +1 -51
- package/dist/question.js +2 -111
- package/dist/resource-suspend.js +0 -18
- package/dist/router/route-orchestration.js +0 -77
- package/dist/rules-consent.d.ts +44 -5
- package/dist/rules-consent.js +75 -215
- package/dist/run-local.js +14 -381
- package/dist/runs.js +29 -617
- package/dist/runtime-caps-resolver.js +6 -132
- package/dist/runtime-governance.js +1 -232
- package/dist/sandbox-pkg-source.js +0 -37
- package/dist/sealed-key.js +3 -68
- package/dist/security.js +6 -291
- package/dist/session-leaf-bus.js +0 -32
- package/dist/session-sync-content.js +1 -69
- package/dist/session-sync-kernel.js +3 -60
- package/dist/session-sync.js +3 -66
- package/dist/session-titler.js +7 -41
- package/dist/session-watch.js +9 -73
- package/dist/shared-memory-scope-authorizer.js +0 -18
- package/dist/sighup-idle.js +1 -12
- package/dist/spec-fields.js +4 -143
- package/dist/store-live-probe.js +3 -49
- package/dist/task-a2a.js +1 -126
- package/dist/task-cwd.js +1 -103
- package/dist/task-mcp.js +1 -89
- package/dist/task-settings.js +10 -300
- package/dist/task-workflow.js +8 -75
- package/dist/tool-approval.d.ts +28 -1
- package/dist/tool-approval.js +70 -1430
- package/dist/trace/artifacts.js +5 -20
- package/dist/trace/engine-notice-wire.js +6 -138
- package/dist/trace/ledger-sink.js +7 -96
- package/dist/trace/project.js +4 -448
- package/dist/trace/redact.js +9 -77
- package/dist/turn-activity.js +1 -27
- package/dist/usage-analytics.js +4 -34
- package/dist/wall-clock-jump-guard.js +1 -68
- package/package.json +3 -3
- package/dist/capabilities/scenario-alias.d.ts +0 -27
- package/dist/capabilities/scenario-alias.js +0 -61
|
@@ -2,19 +2,7 @@ import { fetchPrincipalCaps } from "./config-center/facade.js";
|
|
|
2
2
|
import { HttpError } from "./security.js";
|
|
3
3
|
import { createLogger } from "./observability/logger.js";
|
|
4
4
|
const defaultLogger = createLogger();
|
|
5
|
-
/** #157 / [2762]§五(sema-comms `audits/failopen-governance-176.md` §7 "已知在办 1 条"):module-scope,
|
|
6
|
-
* once-per-process record of which {@link gateExecutionLane} fail-open DEFAULT form has already been traced.
|
|
7
|
-
* Two forms are counted SEPARATELY (both currently resolve to the same "pass" default, but they are different
|
|
8
|
-
* facts about the world — a caller must be able to tell "we never got a ruling for this principal" apart from
|
|
9
|
-
* "center ruled, and explicitly said not-required"):
|
|
10
|
-
* - `"no_ruling"` — `ruling === undefined` (no principal / center absent-or-blip / older center missing the key).
|
|
11
|
-
* - `"required_false"` — a ruling WAS resolved and it explicitly says `required: false`.
|
|
12
|
-
* Each form logs ONCE per process at first hit (a debug line — this is an expected, non-alarming default, not a
|
|
13
|
-
* warning); every subsequent hit of the SAME form only increments the counter (no repeat log line — this is a
|
|
14
|
-
* per-task gate, so without the throttle a busy deployment would log once per task forever). Pure record-keeping:
|
|
15
|
-
* never changes {@link gateExecutionLane}'s pass/deny outcome. */
|
|
16
5
|
const executionLaneDefaultTrace = new Map();
|
|
17
|
-
/** test-only: reset the once-per-process trace so each test observes a fresh "first hit". */
|
|
18
6
|
export function resetExecutionLaneDefaultTraceForTest() {
|
|
19
7
|
executionLaneDefaultTrace.clear();
|
|
20
8
|
}
|
|
@@ -22,14 +10,8 @@ function traceExecutionLaneDefault(form, ownLane, logger) {
|
|
|
22
10
|
const hitCount = (executionLaneDefaultTrace.get(form) ?? 0) + 1;
|
|
23
11
|
executionLaneDefaultTrace.set(form, hitCount);
|
|
24
12
|
if (hitCount > 1)
|
|
25
|
-
return;
|
|
13
|
+
return;
|
|
26
14
|
try {
|
|
27
|
-
// codex 交叉复审第二轮真发现(2026-08-05):a throwing `logger.debug` must NEVER turn this pure
|
|
28
|
-
// observability step into gateExecutionLane itself throwing — that would silently convert a fail-open
|
|
29
|
-
// PASS into an uncaught exception, the exact inversion this module's whole design fights elsewhere
|
|
30
|
-
// (see the FAIL-CLOSED/FAIL-OPEN posture doc above). The hit-count above already advanced, so a retry
|
|
31
|
-
// of the SAME form stays silent rather than storming — the trade-off is "lose one trace line", not
|
|
32
|
-
// "lose one task admission".
|
|
33
15
|
logger.debug("execution_lane_default_applied", {
|
|
34
16
|
dimension: "execution_lane",
|
|
35
17
|
form,
|
|
@@ -41,34 +23,12 @@ function traceExecutionLaneDefault(form, ownLane, logger) {
|
|
|
41
23
|
});
|
|
42
24
|
}
|
|
43
25
|
catch {
|
|
44
|
-
// tracing must never affect the fail-open admission outcome — see comment above.
|
|
45
26
|
}
|
|
46
27
|
}
|
|
47
|
-
/** Map center's `EntitlementRuntimeCaps` → the subset core's engine enforces. `allowUltracode` (shell/UX) is
|
|
48
|
-
* dropped on purpose (not a core primitive); `allowWorkflows` / `forceDurableGate` / `allowFork` are the three the
|
|
49
|
-
* engine enforces. Returns undefined when no enforceable cap is set (= no restriction). FAIL-CLOSED defense-in-depth:
|
|
50
|
-
* do NOT trust the wire to be boolean. center serializes these as zod-validated booleans, but a schema drift / proxy
|
|
51
|
-
* mangling that sent a TRUTHY non-boolean (e.g. the STRING "false") would slip past core's strict `=== false` deny
|
|
52
|
-
* check and silently GRANT. So a cap that is PRESENT but not a real boolean is a contract breach → THROW (the
|
|
53
|
-
* resolver's catch then fail-closes to deny). `allowFork` (registry-core 0.1.47): the per-principal Fork
|
|
54
|
-
* ceiling — core mounts Fork only when `runtimeCaps.allowFork !== false` (prepare-task), tighten-only like allowWorkflows.
|
|
55
|
-
*
|
|
56
|
-
* `allowObservers`(observer 开闸线,core 1.270.0/1.273.0):**极性与 allow* 相反** —
|
|
57
|
-
* core 是 `=== true` 显式 opt-in(默认 OFF,undefined ships dark)。registry-core 0.10.3 起该键已收编进
|
|
58
|
-
* `EntitlementRuntimeCaps` 类型(此前 0.10.2 走 unknown 宽读透传,现为类型直读,行为零变)。
|
|
59
|
-
* **缺键=不写键**(绝不把缺键补成显式 false —— core 是唯一默认源,补 false 语义上等价但会伪造「center
|
|
60
|
-
* 说过话」的显式判决,污染 env-baseline 合成序,见 applyObserverEnvOptIn)。非 boolean 虽然骗不过 core
|
|
61
|
-
* 的 `=== true`(不会误 GRANT),但 PRESENT-非-boolean 与 allow* 同属同一 body 的契约破损信号 → 同款 THROW。
|
|
62
|
-
*
|
|
63
|
-
* `autoMode`(core 1.276.0 `RuntimeCaps.autoMode`):auto-mode 分类器 per-principal
|
|
64
|
-
* 开闸线,极性同 allowObservers(`=== true` 显式 opt-in,默认 OFF;真执行还要 deployment 装配
|
|
65
|
-
* `RunnerDeps.autoMode` 分类器面=信任门,center 键只是 entitlement 半场)。[2373]B-6a:registry-core
|
|
66
|
-
* 0.13.0 已把 autoMode 收进 EntitlementRuntimeCaps(types.d.ts:1786)——宽读逃逸期结束,现为类型直读
|
|
67
|
-
* (allowObservers 同款收编先例)。 */
|
|
68
28
|
export function toCoreRuntimeCaps(ent) {
|
|
69
29
|
if (!ent)
|
|
70
30
|
return undefined;
|
|
71
|
-
const autoMode = ent.autoMode;
|
|
31
|
+
const autoMode = ent.autoMode;
|
|
72
32
|
const badCap = (v) => v !== undefined && typeof v !== "boolean";
|
|
73
33
|
if (badCap(ent.allowWorkflows) || badCap(ent.forceDurableGate) || badCap(ent.allowFork) || badCap(ent.allowObservers) || badCap(autoMode)) {
|
|
74
34
|
throw new Error("runtimeCaps from center carries a non-boolean cap (contract breach — fail closed)");
|
|
@@ -86,43 +46,12 @@ export function toCoreRuntimeCaps(ent) {
|
|
|
86
46
|
caps.autoMode = autoMode;
|
|
87
47
|
return Object.keys(caps).length ? caps : undefined;
|
|
88
48
|
}
|
|
89
|
-
/**
|
|
90
|
-
* #A5([4147] 件2)—— 「这台 worker 上**存在** per-principal entitlement 源吗」的**单一谓词**。
|
|
91
|
-
*
|
|
92
|
-
* 判据逐字 = `boot/runtime-caps.ts` 建 caps 客户端的那一条(**配了 center ∧ 非 dry-run**):dry-run 的部署
|
|
93
|
-
* 拉取并记录 center 配置但**不应用、不执法**(observe-only),所以它解不出任何 per-principal caps —— 把它
|
|
94
|
-
* 算成源会让能力面对着一台永不武装的机器说「有源」。
|
|
95
|
-
*
|
|
96
|
-
* 为什么要抽成函数:这条判据现在有**两个**读者 —— boot 的装配点,与 `/v1/capabilities` 的
|
|
97
|
-
* `permissionModeAuto.entitlementSource` 披露位。两处各写一份条件,就是本仓在册的
|
|
98
|
-
* 「诊断页说亮、能力位说暗」病族的入口(装配与披露对同一件事各算各的)。
|
|
99
|
-
*
|
|
100
|
-
* ⚠️ **辖域**:说的是「源在不在」,不是「某个 principal 被授权了」。授权是 per-principal 的
|
|
101
|
-
* (`RuntimeCaps.autoMode === true` 逐 principal 解),任何部署级布尔都对它说不了话。
|
|
102
|
-
*/
|
|
103
49
|
export function centerEntitlementSourceWired(config) {
|
|
104
50
|
return config.configCenter !== undefined && !config.configCenter.dryRun;
|
|
105
51
|
}
|
|
106
|
-
/** A MISCONFIG diagnostic (review MEDIUM): a worker-scoped `wpt_<self>` pull token REQUIRES `?worker=<self>` to
|
|
107
|
-
* resolve per-principal caps — so a `wpt_` token with NO `SEMA_REGISTRY_WORKER` set will 403
|
|
108
|
-
* on every caps fetch → fail-closed DENY ALL workflows for ALL principals (correct degrade, but otherwise silent
|
|
109
|
-
* bar per-task warns). True ⇒ the caller should log a one-shot BOOT warning so the misconfig is diagnosable.
|
|
110
|
-
* (The orchestrator normally injects SEMA_REGISTRY_WORKER alongside a wpt_ token, so this is a hand-misconfig.) */
|
|
111
52
|
export function scopedTokenNeedsWorker(token, worker) {
|
|
112
53
|
return token.startsWith("wpt_") && !worker;
|
|
113
54
|
}
|
|
114
|
-
/**
|
|
115
|
-
* Task-admission enforcement of the per-principal execution policy(stage7 P5 的「真强制」
|
|
116
|
-
* service 半场——壳侧锁定是易用性强制可绕,这里是引擎闸)。The lane is a WORKER-PROCESS identity
|
|
117
|
-
* (REMOTE_EXEC is boot wiring; there is no per-task lane switch), so the gate is "this worker's lane is not
|
|
118
|
-
* permitted for this principal ⇒ reject the task": typed 403 `execution_lane_not_allowed` carrying the
|
|
119
|
-
* allowlist + this worker's lane (machine code on the wire, prose stays with the shell/web).
|
|
120
|
-
* No ruling / required=false / caps blip ⇒ pass (fail-open + audit). `allowedLanes` is an
|
|
121
|
-
* OPEN domain — unknown names simply never match this worker's lane.
|
|
122
|
-
*
|
|
123
|
-
* `logger` (optional, defaults to this module's `defaultLogger`) is an injection seam purely for the
|
|
124
|
-
* once-per-process default trace below (test observability) — it does not change enforcement.
|
|
125
|
-
*/
|
|
126
55
|
export function gateExecutionLane(ruling, ownLane, logger = defaultLogger) {
|
|
127
56
|
if (!ruling) {
|
|
128
57
|
traceExecutionLaneDefault("no_ruling", ownLane, logger);
|
|
@@ -139,13 +68,6 @@ export function gateExecutionLane(ruling, ownLane, logger = defaultLogger) {
|
|
|
139
68
|
});
|
|
140
69
|
}
|
|
141
70
|
}
|
|
142
|
-
/**
|
|
143
|
-
* Build the per-principal caps client: `resolveRuntimeCaps` matches core's seam signature
|
|
144
|
-
* `(principal) => Promise<RuntimeCaps | undefined>`; `scenarioRuling` rides the SAME fetch/cache/in-flight
|
|
145
|
-
* (zero extra center RTT — center attaches the ruling to the caps body). Wire ONLY when a center is
|
|
146
|
-
* configured (and not in dry-run) — absent ⇒ core sees `undefined` ⇒ NO per-principal restriction, and the
|
|
147
|
-
* scenario gate sees `undefined` ⇒ legacy body-or-default chain.
|
|
148
|
-
*/
|
|
149
71
|
export function createPrincipalEntitlementsClient(opts) {
|
|
150
72
|
const ttlMs = opts.ttlMs ?? 60_000;
|
|
151
73
|
const denyTtlMs = opts.denyTtlMs ?? Math.min(ttlMs, 10_000);
|
|
@@ -155,9 +77,6 @@ export function createPrincipalEntitlementsClient(opts) {
|
|
|
155
77
|
const onError = opts.onError ?? ((err, principal) => defaultLogger.warn("runtime_caps_resolve_failed", { principal, err: String(err) }));
|
|
156
78
|
const cache = new Map();
|
|
157
79
|
const inflight = new Map();
|
|
158
|
-
// Bound cache growth (review L1-1): `principal` is a per-user identity, so a long-lived worker — or a direct-door
|
|
159
|
-
// caller spoofing many principals — would accumulate one entry forever. On write, when the map crosses a soft cap,
|
|
160
|
-
// sweep logically-expired entries (TTL is a freshness gate, not eviction). Caps retention to ~the active set.
|
|
161
80
|
const MAX_CACHE = 10_000;
|
|
162
81
|
const putCache = (key, entry) => {
|
|
163
82
|
if (cache.size >= MAX_CACHE) {
|
|
@@ -168,25 +87,17 @@ export function createPrincipalEntitlementsClient(opts) {
|
|
|
168
87
|
}
|
|
169
88
|
cache.set(key, entry);
|
|
170
89
|
};
|
|
171
|
-
// ONE loader behind both faces: fresh-cached entry → as-is; else fetch (in-flight-deduped) → cache → entry.
|
|
172
90
|
async function load(principal) {
|
|
173
91
|
const cached = cache.get(principal);
|
|
174
92
|
if (cached && cached.expiresAt > now())
|
|
175
93
|
return cached;
|
|
176
|
-
// Collapse concurrent prepare-time lookups for the SAME principal onto one in-flight fetch.
|
|
177
94
|
const dup = inflight.get(principal);
|
|
178
95
|
if (dup)
|
|
179
96
|
return dup;
|
|
180
97
|
const p = (async () => {
|
|
181
98
|
try {
|
|
182
|
-
// `cached?.etag` is set only on a real prior entry (an error entry clears it) → an `if-none-match` here can
|
|
183
|
-
// only 304-confirm a REAL value, never resurrect a stale deny. Pass `opts.worker` as `?worker=<self>` so a
|
|
184
|
-
// worker-scoped `wpt_<self>` token can resolve its OWN worker's principal caps; harmless
|
|
185
|
-
// on a full token (caps are worker-independent — the worker only scopes the config view we ignore).
|
|
186
99
|
const r = await fetchCaps(opts.baseUrl, opts.token, principal, cached?.etag, fetchImpl, opts.worker);
|
|
187
100
|
if (r === null) {
|
|
188
|
-
// 304 — unchanged. We only sent if-none-match because `cached` was a real entry → reuse caps AND the
|
|
189
|
-
// rulings (same body, same generation), refresh TTL.
|
|
190
101
|
const entry = {
|
|
191
102
|
caps: cached?.caps,
|
|
192
103
|
...(cached?.scenario ? { scenario: cached.scenario } : {}),
|
|
@@ -197,14 +108,11 @@ export function createPrincipalEntitlementsClient(opts) {
|
|
|
197
108
|
putCache(principal, entry);
|
|
198
109
|
return entry;
|
|
199
110
|
}
|
|
200
|
-
// execution ruling shape drift = ruling dropped (fail-open) + THIS audit
|
|
201
|
-
// line — the "fail-open + audit" posture now covers the wire-drift direction too (previously a
|
|
202
|
-
// drifted allowedLanes coerced to [] = silent fleet-wide deny-all). Routed through onError (never throws).
|
|
203
111
|
if (r.executionDrift) {
|
|
204
112
|
try {
|
|
205
113
|
onError(new Error(`execution ruling wire drift: ${r.executionDrift}`), principal);
|
|
206
114
|
}
|
|
207
|
-
catch {
|
|
115
|
+
catch { }
|
|
208
116
|
}
|
|
209
117
|
const entry = {
|
|
210
118
|
caps: toCoreRuntimeCaps(r.runtimeCaps),
|
|
@@ -217,11 +125,6 @@ export function createPrincipalEntitlementsClient(opts) {
|
|
|
217
125
|
return entry;
|
|
218
126
|
}
|
|
219
127
|
catch (err) {
|
|
220
|
-
// codex 交叉复审第三轮真发现(2026-08-05):a poison rejection reason (throwing `Symbol.toPrimitive`/
|
|
221
|
-
// `toString`) must not make THIS normalization line itself throw — this catch block exists to
|
|
222
|
-
// GUARANTEE the documented FAIL-CLOSED degrade below runs; if normalizing `err` throws, that
|
|
223
|
-
// guarantee is exactly what breaks (resolveRuntimeCaps/scenarioRuling/executionRuling would reject
|
|
224
|
-
// instead of resolving to their fail-closed/fail-open defaults on a degraded center response).
|
|
225
128
|
let e;
|
|
226
129
|
try {
|
|
227
130
|
e = err instanceof Error ? err : new Error(String(err));
|
|
@@ -229,18 +132,12 @@ export function createPrincipalEntitlementsClient(opts) {
|
|
|
229
132
|
catch {
|
|
230
133
|
e = new Error("center fetch failed with an unstringifiable rejection reason");
|
|
231
134
|
}
|
|
232
|
-
// Caps face FAIL CLOSED: deny the OPTIONAL amplification caps (workflows + fork); never force the durable
|
|
233
|
-
// gate on error. Scenario face FAIL OPEN: `scenario` stays undefined (no governance — never pin users to
|
|
234
|
-
// the default scenario on a center blip; deliberate posture split, see PrincipalEntitlementsClient). Cache the
|
|
235
|
-
// deny for a SHORT window (bound center hammering on a sustained outage) WITHOUT an etag — so the next
|
|
236
|
-
// fetch is a full 200 pull that re-reads the real caps the moment center recovers (a 304 can't resurrect
|
|
237
|
-
// this deny). Write the deny BEFORE onError so a throwing observability hook can't escape before recording.
|
|
238
135
|
const entry = { caps: { allowWorkflows: false, allowFork: false }, expiresAt: now() + denyTtlMs };
|
|
239
136
|
putCache(principal, entry);
|
|
240
137
|
try {
|
|
241
138
|
onError(e, principal);
|
|
242
139
|
}
|
|
243
|
-
catch {
|
|
140
|
+
catch { }
|
|
244
141
|
return entry;
|
|
245
142
|
}
|
|
246
143
|
finally {
|
|
@@ -251,48 +148,25 @@ export function createPrincipalEntitlementsClient(opts) {
|
|
|
251
148
|
return p;
|
|
252
149
|
}
|
|
253
150
|
return {
|
|
254
|
-
// No principal = an anonymous/system run with no per-principal identity → no per-principal restriction
|
|
255
|
-
// (tighten-only: absence falls back to the deployment-level default; core treats undefined as "no caps").
|
|
256
151
|
resolveRuntimeCaps: async (principal) => (principal ? (await load(principal)).caps : undefined),
|
|
257
152
|
scenarioRuling: async (principal) => (principal ? (await load(principal)).scenario : undefined),
|
|
258
153
|
executionRuling: async (principal) => (principal ? (await load(principal)).execution : undefined),
|
|
259
154
|
};
|
|
260
155
|
}
|
|
261
|
-
/** Back-compat face for core's seam-only consumers: the caps half of {@link createPrincipalEntitlementsClient}. */
|
|
262
156
|
export function createEntitlementsResolver(opts) {
|
|
263
157
|
return createPrincipalEntitlementsClient(opts).resolveRuntimeCaps;
|
|
264
158
|
}
|
|
265
|
-
/**
|
|
266
|
-
* observer 开闸线的 env 半场:`EXPERIMENTAL_OBSERVER_AGENTS=true` 时把
|
|
267
|
-
* `RuntimeCaps.allowObservers: true` 作为**部署基线**合成进 caps resolver(core 1.270.0/1.273.0 的
|
|
268
|
-
* observer 面是 `=== true` 显式 opt-in、默认 OFF —— 命名先例=CC 的 `CLAUDE_CODE_EXPERIMENTAL_OBSERVER_AGENTS`
|
|
269
|
-
* env 对儿)。租户姿势(与 FORK_ENABLED/SELF_ORCHESTRATION 同款判别):
|
|
270
|
-
* - **单用户部署**(`requirePrincipal !== true`,operator 即用户):env=true 直开 —— 无 center 时静态
|
|
271
|
-
* `{allowObservers:true}`;有 center 时作 BASELINE,**center caps 带键则 center 赢**(含显式 false;
|
|
272
|
-
* 缺键=center 没说话 → env 基线生效。这依赖 toCoreRuntimeCaps 缺键不补 false 的纪律 —— core 是唯一
|
|
273
|
-
* 默认源,center 只有真说了话才覆盖)。fail-closed 错误 entry(deny)不带 allowObservers 键 → 单用户
|
|
274
|
-
* env 基线在 center blip 期间维持(operator 在自己盒子上显式 opt-in 过;observers 非 workflows/fork
|
|
275
|
-
* 级的放大面,且 deny entry 的两把闸不受影响)。
|
|
276
|
-
* - **多租户**(`requirePrincipal === true`):env **不认**,只认 center caps(半配置 fail-loud:置了
|
|
277
|
-
* env 却多租户 → 一次 boot warn,行为零变——与 MEMORY_ENGINE_BACKEND 半配置同款姿势)。
|
|
278
|
-
* 返回合成后的 resolver;两边都无源(无 center、env 无效)⇒ undefined(RunnerDeps 不接线,零行为差)。
|
|
279
|
-
* ⚠️ 调用方注意:`Boolean(centerResolver)` 才是「entitlement resolver wired」语义(enableForkFromBody /
|
|
280
|
-
* selfOrchestrationFromBody 的多租户 fail-close 判别),不要改用本函数的返回值判 —— env-only lane 不是
|
|
281
|
-
* center 背书的 entitlement 源。
|
|
282
|
-
*/
|
|
283
159
|
export function applyObserverEnvOptIn(centerResolver, opts) {
|
|
284
160
|
if (!opts.experimentalObserverAgents)
|
|
285
|
-
return centerResolver;
|
|
161
|
+
return centerResolver;
|
|
286
162
|
if (opts.requirePrincipal) {
|
|
287
|
-
// 多租户不认 env(observer 是 per-principal 治理面,只认 center caps)——半配置 fail-loud 一次。
|
|
288
163
|
opts.warn?.("observer_env_opt_in_ignored", {
|
|
289
164
|
reason: "EXPERIMENTAL_OBSERVER_AGENTS=true is single-user only; a multi-tenant deployment (REQUIRE_PRINCIPAL=true) honors ONLY center caps allowObservers — env ignored, behavior unchanged",
|
|
290
165
|
});
|
|
291
166
|
return centerResolver;
|
|
292
167
|
}
|
|
293
168
|
if (!centerResolver)
|
|
294
|
-
return () => ({ allowObservers: true });
|
|
295
|
-
// 单用户 + center:env 作 baseline,center 带键(true/false 皆)覆盖;缺键 → baseline 生效。
|
|
169
|
+
return () => ({ allowObservers: true });
|
|
296
170
|
return async (principal) => ({ allowObservers: true, ...(await centerResolver(principal)) });
|
|
297
171
|
}
|
|
298
172
|
//# sourceMappingURL=runtime-caps-resolver.js.map
|
|
@@ -1,117 +1,34 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Runtime governance compiler — the "second baton" (center §10, core 1.114.0): turn the
|
|
3
|
-
* operator's declarative `runtime.autonomy` + `runtime.commandPolicy` (`@sema-agent/registry-core`,
|
|
4
|
-
* carried to the service via sema-registry `EffectiveConfig.runtime`) into TIGHTEN-ONLY overrides on the
|
|
5
|
-
* per-task `TaskSpec`. Pure so it is unit-testable in isolation (the spec is otherwise assembled inside
|
|
6
|
-
* main.ts's `resolveSpec`).
|
|
7
|
-
*
|
|
8
|
-
* 🪤 TWO TRAPS this module exists to NOT fall into (see core
|
|
9
|
-
* `tool-policy.ts` / `tighten-task-spec.ts` JSDoc):
|
|
10
|
-
*
|
|
11
|
-
* TRAP #1 — `TaskSpec.toolPolicy` OVERRIDES (not merges) `RunnerDeps.toolPolicy` (the runner does
|
|
12
|
-
* `spec.toolPolicy ?? deps.toolPolicy`). A bare `spec.toolPolicy = createCoarseCommandNamePolicy(…)` would
|
|
13
|
-
* SILENTLY DISCARD the deployment's approval/durable baseline. We compose via {@link tightenTaskSpec}, whose
|
|
14
|
-
* toolPolicy rule is `combinePolicies(base, override)` (deny-wins) — the command gate can only ADD denies/asks
|
|
15
|
-
* on top of the baseline, never clear it.
|
|
16
|
-
*
|
|
17
|
-
* TRAP #2 — `autonomy` must EXPAND INTO the three core primitives (`handsReadOnly` / `shellGate` / a
|
|
18
|
-
* `toolPolicy`), NOT become a parallel enum on the spec (design-research §10: a parallel `autonomy` field +
|
|
19
|
-
* `handsReadOnly:true` would conflict). The expansion is tighten-only and routed through `tightenTaskSpec`, so
|
|
20
|
-
* `auto` (the loosest mode) can never LOOSEN a stricter base — `tightenTaskSpec` THROWS instead.
|
|
21
|
-
*
|
|
22
|
-
* ⚠️ `commandPolicy` is a COARSE name-level filter, NOT a sandbox. `sh -c …` / `$(…)` / pipes / `env X=…` /
|
|
23
|
-
* path-prefixed commands all bypass the argv[0] match (they route to `ask`, fail-closed, but are not blocked by
|
|
24
|
-
* name). Real isolation is the `executionEnv` sandbox. Same caveat core documents on `createCoarseCommandNamePolicy`.
|
|
25
|
-
*/
|
|
26
1
|
import { combinePolicies, COARSE_SHELL_TOOLS as CORE_COARSE_SHELL_TOOLS, createCoarseCommandNamePolicy, DEFAULT_SUBAGENT_TOOL_NAME, parseLeadingCommandName, tightenTaskSpec, } from "@sema-agent/core";
|
|
27
2
|
import { currentGovernanceAskMarks } from "./governance-ask-marks.js";
|
|
28
|
-
/** True when `name` names a DELEGATION tool — the subagent tool(CC-187 canonical `"Agent"`,core
|
|
29
|
-
* `DEFAULT_SUBAGENT_TOOL_NAME`)。5.0.0 RB-476:折叠面退役,RAW 比对 core 单源常量——旧名("Task")
|
|
30
|
-
* 在 core roster 层响亮 miss,不再需要归一;单源常量本身就防了当年 1.202 改名时裸字符串失配的
|
|
31
|
-
* fail-open(那个教训的解=引用常量,不是折叠)。 */
|
|
32
3
|
export function isDelegationToolName(name) {
|
|
33
4
|
return name === DEFAULT_SUBAGENT_TOOL_NAME;
|
|
34
5
|
}
|
|
35
|
-
/** The hands-read-only DELEGATION STRIP (the main.ts governed-spec site, extracted here so the actual filter —
|
|
36
|
-
* not just the name predicate — is pinned by tests): a read-only (plan-mode) run does not mount a delegation
|
|
37
|
-
* tool at all.
|
|
38
|
-
*
|
|
39
|
-
* 🔴 原始理由已被上游收编(2026-08-05 亲验安装包 core 5.13.x):子任务 spec 现在**继承** `handsReadOnly`
|
|
40
|
-
* (`dist/agents/subagent.js:1501` 的 `...(ctx.handsReadOnly === true ? { handsReadOnly: true } : {})`,resume
|
|
41
|
-
* 腿 `:405` 同款)与 `shellGate`(`dist/core/runner/prepare-task.js` 的 `inheritedGateForChildren`),所以
|
|
42
|
-
* 「子任务会拿到可写的手」这个当年的缺口在 core 侧已经补上。本 strip 因此是**同向的第二层**(更严,不更松):
|
|
43
|
-
* 它保证 read-only 一档下连委派工具面都不铸,与继承是否存在无关。拆掉它是一次行为变更(read-only 任务将
|
|
44
|
-
* 重新能委派),需要单独决定——不要因为「上游修好了」就顺手删。
|
|
45
|
-
*
|
|
46
|
-
* Identity (the same array) when nothing matches, so the caller's spec-object churn stays minimal. */
|
|
47
6
|
export function stripDelegationTools(tools) {
|
|
48
7
|
const kept = tools.filter((t) => !isDelegationToolName(t.name));
|
|
49
8
|
return kept.length === tools.length ? tools : kept;
|
|
50
9
|
}
|
|
51
|
-
/** The shell tool names a command-name gate parses — **core 的 {@link COARSE_SHELL_TOOLS} 本尊**,不再是镜像。
|
|
52
|
-
* 本仓的 ask-list 策略因此恒门住与 `createCoarseCommandNamePolicy` **同一批**工具。
|
|
53
|
-
* Q6 (core 1.161): tool names align to CC — `bash`→`Bash`; the old `bash_readonly` MERGED into `Bash` (read-only is
|
|
54
|
-
* now a toolEffect, not a separate tool).
|
|
55
|
-
*
|
|
56
|
-
* 🔴 A-033.1(#256)换装记:本常量此前是**手抄镜像** `["Bash","Monitor"] as const`,注释自称「core 未导出」。
|
|
57
|
-
* 那句话在 core RB-153(server [1708] 请托)之后就过期了 —— core 现在**公开导出**这份名单(index.d.ts:134),
|
|
58
|
-
* 它自己的原话是「The list itself is the fact; publishing it removes the guess」。镜像的真实代价有前科:
|
|
59
|
-
* core 1.414 把默认从 `["Bash"]` 扩到 `["Bash","Monitor"]`(Monitor 走同一条执行 seam),镜像漏跟一轮 ⇒
|
|
60
|
-
* **blocklist 模式下本仓的 ask-list 不覆盖 Monitor 而 core 的 deny 覆盖**(core 端到端实证:同一条
|
|
61
|
-
* `rm -rf $(cat targets)`,Bash 被拒、Monitor 零门执行)。换真 import 之后这一整类漏跟在结构上消失。
|
|
62
|
-
* `test/coarse-shell-tools-mirror.test.ts` 的钉相应从「行为反推 + 值比对」改成**同源断言**(见那里顶注)。 */
|
|
63
10
|
const COARSE_SHELL_TOOLS = CORE_COARSE_SHELL_TOOLS;
|
|
64
|
-
/** 导出给 `test/coarse-shell-tools-mirror.test.ts` 的同源钉用(见那里的顶注)。 */
|
|
65
11
|
export const COARSE_SHELL_TOOLS_MIRROR = COARSE_SHELL_TOOLS;
|
|
66
|
-
/**
|
|
67
|
-
* A per-command ask-list ToolPolicy: a parsed shell command whose `argv[0]` is in `askCommands` tightens to
|
|
68
|
-
* `ask`; everything else passes (`allow`). core's `createCoarseCommandNamePolicy` has NO per-command ask list
|
|
69
|
-
* (only `allow[]` / `deny[]` / `defaultAction`), so blocklist-mode "ask before THIS command, pass the rest"
|
|
70
|
-
* cannot be expressed by it — this thin policy carries the `ask` decisions. It reuses core's
|
|
71
|
-
* `parseLeadingCommandName` (the SINGLE source-of-truth simple-command parser used by the `Bash` tool and
|
|
72
|
-
* `createCoarseCommandNamePolicy`), so the argv[0]-bypass surface (operators / pipes / `env=` / path-prefix)
|
|
73
|
-
* routes to `ask` (fail toward the gate), never silent-allow.
|
|
74
|
-
*/
|
|
75
12
|
function createCommandAskListPolicy(askCommands, tools = COARSE_SHELL_TOOLS) {
|
|
76
13
|
const askSet = new Set(askCommands);
|
|
77
14
|
const shellTools = new Set(tools);
|
|
78
15
|
const ask = (message) => ({ action: "ask", message, decisionReason: "rule" });
|
|
79
16
|
return {
|
|
80
17
|
check(req) {
|
|
81
|
-
// Out of scope for non-shell tools: this gate only speaks about shell command names. RAW(5.0.0):
|
|
82
|
-
// live 名恒 canonical,pre-rename 重放由 core 响亮拒——折叠臂退役。
|
|
83
18
|
if (!shellTools.has(req.toolName))
|
|
84
19
|
return { action: "allow" };
|
|
85
20
|
const command = req.args?.command;
|
|
86
21
|
if (typeof command !== "string")
|
|
87
22
|
return ask(`tool "${req.toolName}" call has no parseable command string`);
|
|
88
23
|
const parsed = parseLeadingCommandName(command);
|
|
89
|
-
// Un-parseable (operators / path-prefix / env-assignment) = exactly the argv[0]-bypass surface → gate it.
|
|
90
24
|
if ("reject" in parsed)
|
|
91
25
|
return ask(`command is not a single simple command (${parsed.reject})`);
|
|
92
26
|
return askSet.has(parsed.name) ? ask(`command "${parsed.name}" requires approval by policy`) : { action: "allow" };
|
|
93
27
|
},
|
|
94
28
|
};
|
|
95
29
|
}
|
|
96
|
-
/** A valid `commandPolicy` rule targets a bare argv[0] command NAME. core's `createCoarseCommandNamePolicy` does
|
|
97
|
-
* EXACT `Set` matching (NOT glob, NOT last-match-wins — the §10 verdict deliberately downgraded the original
|
|
98
|
-
* `createCommandGlobPolicy` idea), so a glob like `git*` or a path `/usr/bin/rm` or `npm i`
|
|
99
|
-
* would SILENTLY never match a parsed argv[0] → the command would slip the gate. We accept only bare-name tokens
|
|
100
|
-
* (letters/digits and `. _ - +`, covering `python3.11`/`docker-compose`/`g++`) and reject the rest FAIL-LOUD at
|
|
101
|
-
* config-apply. 🔴 Kept ALIGNED with the `@sema-agent/registry-core` contract-layer regex (center fixed the
|
|
102
|
-
* glob→exact contract bug in 0.1.26 + publish-validates with this SAME pattern) so the contract gate and this
|
|
103
|
-
* defense-in-depth gate agree. [2373]B-11:旧注靠「dep=^0.1.26 additive 窗」论证对齐,地板抬到 ^0.13.0 后
|
|
104
|
-
* 该论证失效——现由机器钉逐字节对表(test/runtime-governance.test.ts 读安装包正则比对),lockstep 不再
|
|
105
|
-
* 是注释请求。2026-08-02 亲核 0.13.0 dist/types.js:703:两正则逐字节相同。 */
|
|
106
30
|
const VALID_COMMAND_NAME = /^[A-Za-z0-9._+-]+$/;
|
|
107
|
-
/** The three legal per-command decisions (mirrors the registry `runtime.commandPolicy[].decision` enum). */
|
|
108
31
|
const VALID_DECISIONS = new Set(["allow", "ask", "deny"]);
|
|
109
|
-
/** Validate `commandPolicy` rules — the shape (array of {command, decision}), the `command` (a bare argv[0] name,
|
|
110
|
-
* see {@link VALID_COMMAND_NAME}), AND the `decision` enum. Returns the list of human-readable errors (empty =
|
|
111
|
-
* OK). Called at config-apply (config-center/facade.ts `applyRuntimeHot`) so a malformed rule that BYPASSED the
|
|
112
|
-
* registry schema (a hand-edited config.d / a non-conformant publish) is rejected LOUDLY rather than
|
|
113
|
-
* silently mis-compiling — review MEDIUM: an out-of-enum `decision` (e.g. "DENY") would otherwise fall through
|
|
114
|
-
* {@link compileCommandPolicy}'s `decision==="deny"?…:"ask"` to ASK, silently WEAKENING an intended deny. */
|
|
115
32
|
export function validateCommandRules(rules) {
|
|
116
33
|
const errors = [];
|
|
117
34
|
if (!Array.isArray(rules))
|
|
@@ -132,31 +49,9 @@ export function validateCommandRules(rules) {
|
|
|
132
49
|
}
|
|
133
50
|
return errors;
|
|
134
51
|
}
|
|
135
|
-
/**
|
|
136
|
-
* Compile `runtime.commandPolicy` (per-command 3-way rules) into a single ToolPolicy, or `undefined` when there
|
|
137
|
-
* is nothing to govern. The registry model (per-command `allow`/`ask`/`deny`) and core's helper model
|
|
138
|
-
* (`allow[]` + `deny[]` + `defaultAction`) don't map 1:1, so the compilation picks a mode by the rules present:
|
|
139
|
-
*
|
|
140
|
-
* - **Allowlist mode** — triggered when ANY rule resolves to `allow`. Only `allow` commands pass; `deny` wins;
|
|
141
|
-
* EVERY other command — `ask`-decision AND unlisted — falls to `defaultAction:"ask"`.
|
|
142
|
-
* 🔴 OPERATOR FOOTGUN (documented contract): adding even one `allow` rule flips the whole policy to a strict
|
|
143
|
-
* allowlist — previously-passing unlisted commands now ASK.
|
|
144
|
-
* - **Blocklist mode** — no `allow` rules. Only `deny` commands are blocked; the rest pass THROUGH to the
|
|
145
|
-
* deployment baseline. Per-command `ask` is carried by {@link createCommandAskListPolicy} (the coarse helper
|
|
146
|
-
* can't express it). Un-parseable bypass commands fail toward `ask` in both the deny gate and the ask-list.
|
|
147
|
-
*
|
|
148
|
-
* Duplicate rules for the SAME command collapse to the STRICTEST decision (`deny > ask > allow`) BEFORE the mode
|
|
149
|
-
* split, so e.g. a command listed both `allow` and `ask` resolves to `ask` — honoring `combinePolicies`'
|
|
150
|
-
* strictest-wins (without this, the `allow` rule would shadow the `ask` for that command in allowlist mode).
|
|
151
|
-
*
|
|
152
|
-
* Either way the result only ever ADDS asks/denies — composed onto the baseline via `combinePolicies`
|
|
153
|
-
* (deny-wins) by {@link applyRuntimeGovernance}, so it is tighten-only. (Assumes commands are pre-validated by
|
|
154
|
-
* {@link validateCommandRules}; an un-matchable command here is simply inert, never a parse error.)
|
|
155
|
-
*/
|
|
156
52
|
export function compileCommandPolicy(rules) {
|
|
157
53
|
if (!rules || rules.length === 0)
|
|
158
54
|
return undefined;
|
|
159
|
-
// Collapse per command to the strictest decision (deny > ask > allow) so a duplicate can't shadow a stricter one.
|
|
160
55
|
const RANK = { deny: 3, ask: 2, allow: 1 };
|
|
161
56
|
const strictest = new Map();
|
|
162
57
|
for (const r of rules) {
|
|
@@ -172,20 +67,9 @@ export function compileCommandPolicy(rules) {
|
|
|
172
67
|
}
|
|
173
68
|
const policies = [];
|
|
174
69
|
if (allow.length > 0) {
|
|
175
|
-
// Allowlist mode: `allow` passes, `deny` wins, `ask`-decision + unlisted fall to defaultAction "ask".
|
|
176
70
|
policies.push(createCoarseCommandNamePolicy({ allow, deny, defaultAction: "ask" }));
|
|
177
71
|
}
|
|
178
72
|
else {
|
|
179
|
-
// Blocklist mode: only `deny` blocks; per-command `ask` via the ask-list; an unlisted **simple** command
|
|
180
|
-
// passes through.
|
|
181
|
-
// ⚠️ #184([3038]/[3047] 双探针实证):「unlisted pass through」只对**单纯命令**成立。带 shell 运算符的
|
|
182
|
-
// 复合命令(`a && b`、管道、`;`)`parseLeadingCommandName` 判 reject,而 `createCoarseCommandNamePolicy`
|
|
183
|
-
// 的 `defaultAction` **缺省就是 `"ask"`**(core tool-policy.js:`opts.defaultAction ?? "ask"`)—— 于是它们
|
|
184
|
-
// 在 deny 门上折 **ask**,不是放行(无 command 串的调用同理)。这与 allowlist 模式那句「fall to
|
|
185
|
-
// defaultAction」是同一个兜底,只是这里没显式传参所以容易读漏。上方 docblock 的「Un-parseable bypass
|
|
186
|
-
// commands fail toward `ask` in both the deny gate and the ask-list」说的就是这一条。零行为码改动。
|
|
187
|
-
// (NOTE: never pass `allow: []` to the coarse helper — an empty array is truthy there and would turn it
|
|
188
|
-
// into an allowlist that asks on EVERYTHING. Omit allow entirely instead.)
|
|
189
73
|
if (deny.length > 0)
|
|
190
74
|
policies.push(createCoarseCommandNamePolicy({ deny }));
|
|
191
75
|
if (ask.length > 0)
|
|
@@ -195,15 +79,6 @@ export function compileCommandPolicy(rules) {
|
|
|
195
79
|
return undefined;
|
|
196
80
|
return policies.length === 1 ? policies[0] : combinePolicies(...policies);
|
|
197
81
|
}
|
|
198
|
-
/**
|
|
199
|
-
* Expand an autonomy mode into TIGHTEN-ONLY `TaskSpec` safety overrides (the three primitives — TRAP #2):
|
|
200
|
-
* - `read-only` / `plan` → `handsReadOnly: true` (read + propose, never mutate; the §6 verifier read-only
|
|
201
|
-
* boundary). `plan` shares the SAME safety boundary as `read-only`; the "present a plan for approval" intent
|
|
202
|
-
* is higher up (UX), not a distinct TaskSpec safety field, so both map to read-only hands.
|
|
203
|
-
* - `ask` → `shellGate: "always"` (every `Bash` command tightens to an `irreversible_ask` durable suspend —
|
|
204
|
-
* the fail-closed default for an unattended deployment with no parsed classifier).
|
|
205
|
-
* - `auto` / `undefined` → `{}` (no extra tightening; still subject to the deployment baseline + commandPolicy).
|
|
206
|
-
*/
|
|
207
82
|
export function autonomyOverrides(autonomy) {
|
|
208
83
|
switch (autonomy) {
|
|
209
84
|
case "read-only":
|
|
@@ -216,53 +91,11 @@ export function autonomyOverrides(autonomy) {
|
|
|
216
91
|
return {};
|
|
217
92
|
}
|
|
218
93
|
}
|
|
219
|
-
/**
|
|
220
|
-
* #220:**部署级**判据 —— 本部署的治理层自己是否把 shellGate 抬到 `"always"`。
|
|
221
|
-
*
|
|
222
|
-
* 用途 = 持久门(park 行)的出身归因。活卡腿判「这只 ask 是治理层产的」靠 ALS 标记表(见本文件
|
|
223
|
-
* `createGovernanceAskMarkingPolicy` / `createGovernanceShellGateMarkPolicy`),而 park 腿天然跨副本、
|
|
224
|
-
* 跨重启,进程内的表在那里结构上够不着;行上唯一的取证格是 core 写的
|
|
225
|
-
* `gate.riskDescriptor.shellGateDoctrine`。
|
|
226
|
-
*
|
|
227
|
-
* 🔴 **为什么单看行上那一格不够**:有效档 `"always"` **不止治理层一个产地** —— `resolve-spec` 的 SUP 路由
|
|
228
|
-
* 姿态(`supPostureOverrides`)在 governance **之后**叠,同样产 `"always"`(本文件 `manualModeShellGate`
|
|
229
|
-
* 合成段的注释里已如实记着这条时序)。只看行 ⇒ 一条 SUP 路由出来的门会被谎报成「运维治理层强制」,
|
|
230
|
-
* 而那恰是这个信号要回答的那个问题。所以归因取**合取**:行上是 `always` **∧** 本部署的治理层本来就
|
|
231
|
-
* 要求 `always`(后者成立时,治理层就是一个真成因,SUP 是否也抬过不改变这句话的真假)。
|
|
232
|
-
*
|
|
233
|
-
* 判据逐字对着两个产地(与 `applyRuntimeGovernance` 里 `overrides.shellGate` 的取值同源):
|
|
234
|
-
* `autonomyOverrides(autonomy).shellGate`(`AUTONOMY=ask`)与 `MANUAL_MODE_SHELL_GATE` 旋钮,取「有一个
|
|
235
|
-
* 是 always」。`commandPolicy` 不入判据:它产的是 `toolPolicy` 的 ask,不抬 shellGate 档,与本格无关。
|
|
236
|
-
*
|
|
237
|
-
* ⚠️ **已知残留(codex 对抗复审 2026-08-11 逮到,如实登记而不是掩盖)**:本判据读的是**当下**的
|
|
238
|
-
* config,而 `config.autonomy` 是**热改**的(`config-center/apply-effective.ts` 就地写 `config.autonomy`)。
|
|
239
|
-
* 于是失真是**双向**的,不是我原先写的「只往缺席方向」:
|
|
240
|
-
* · 缺席向(常见):mint 之后运维把 `AUTONOMY=ask` 撤了 ⇒ 老 park 行归不出治理出身,少标一个;
|
|
241
|
-
* · **在场向**(窄):一条**纯 SUP 路由**产的门(`ROUTER_ENABLED` 开 ∧ 路由判 supervisor ∧ 当时治理层
|
|
242
|
-
* 并不要求 always),之后运维把 autonomy 热改成 `ask`,那条老行会被标成治理出身 —— 五个前提要**同时**
|
|
243
|
-
* 成立,且该行仍未决。
|
|
244
|
-
* 根治要**在 mint 那一刻把出身写进行里**(core 的 `CheckpointGate` 属主面,不是本仓能单方面做的);
|
|
245
|
-
* 在那之前这一位的定位是**分诊提示**,不是裁决输入 —— 它不参与任何门/CAS/resume 判定,消费端也只拿它
|
|
246
|
-
* 渲染徽标。登记在此,不加特征化测试(把已知残留钉成契约是另一种病)。
|
|
247
|
-
*/
|
|
248
94
|
export function governanceMandatesShellGateAlways(governance) {
|
|
249
95
|
return autonomyOverrides(governance.autonomy).shellGate === "always" || governance.manualModeShellGate === "always";
|
|
250
96
|
}
|
|
251
|
-
/**
|
|
252
|
-
* [2942]/[2943] `governanceForced` 的**写侧** —— 把内层策略(治理层自己合成的那些)产的 `ask` 记进标记表。
|
|
253
|
-
*
|
|
254
|
-
* 决议本身**逐字透传**:本包装器只观察,不改 action / message / updatedInput / decisionReason,也不吞异常
|
|
255
|
-
* (内层抛就照抛,fail-closed 的属主仍是内层)。同步内层保持同步返回(不无谓地把一条同步策略异步化)。
|
|
256
|
-
*
|
|
257
|
-
* 为什么标在这一层而不是让 `decisionReason` 上 wire:core 的 ask 铸造点不透传 `PermissionResult`
|
|
258
|
-
* (侦察结论逐字见 `governance-ask-marks.ts` 顶注),这条缝在当前 core 上根本不存在。
|
|
259
|
-
*/
|
|
260
97
|
function createGovernanceAskMarkingPolicy(inner, marksOf) {
|
|
261
|
-
// 🔴 codex 交叉复审 round2 [medium](验真):**如实覆写**,不是只加不减 —— core 的
|
|
262
|
-
// `recheckApprovedEdit` 拿同一个 `toolCallId` 重跑策略链(人编辑 args 之后),「原始 args 治理层要问 /
|
|
263
|
-
// 编辑后治理层放行、由别的策略提第二问」会让第二张卡挂陈旧治理标。理由全文见 GovernanceAskMarks.setMarked。
|
|
264
98
|
const note = (d, req) => {
|
|
265
|
-
// 表在**裁决那一刻**解析(不是构造时):策略闭包铸于 resolveSpec,真正的作用域要到 run 腿跑起来才在。
|
|
266
99
|
marksOf()?.setMarked(req.toolCallId, d.action === "ask");
|
|
267
100
|
return d;
|
|
268
101
|
};
|
|
@@ -273,72 +106,22 @@ function createGovernanceAskMarkingPolicy(inner, marksOf) {
|
|
|
273
106
|
},
|
|
274
107
|
};
|
|
275
108
|
}
|
|
276
|
-
/**
|
|
277
|
-
* [2942]/[2943] shellGate 腿的写侧 —— **纯观察器**(恒 `allow` = `combinePolicies` 折叠里的弃权,见
|
|
278
|
-
* `applyRuntimeGovernance` 顶注对 sensitivePathPolicy 那段 allow 语义的同款说明)。
|
|
279
|
-
*
|
|
280
|
-
* 只在治理层把 shellGate 抬到 **`"always"`** 时装:该档下 core 对**每一次** shell 调用都要人批
|
|
281
|
-
* (`prepare-task.js` 把 Bash/Monitor 的 irreversibility tier 置 `"always"`),所以「这是一次 shell 调用」
|
|
282
|
-
* 与「这只 ask 是治理层门出来的」等价——推断是紧的,零错标。
|
|
283
|
-
*
|
|
284
|
-
* 🔴 `"classify"` 档**故意不装**:那一档由 core 的分类器逐调用裁决,一次 shell ask 可能来自分类器
|
|
285
|
-
* (治理),也可能来自 `APPROVAL_REQUIRE` 这类别的门(非治理),在 `AskRequest` 上无从分辨。按本信号
|
|
286
|
-
* 「只做正向断言」的语义,分不清就**让键缺席**,不去猜(`governance-ask-marks.ts` 语义 1)。
|
|
287
|
-
*/
|
|
288
109
|
function createGovernanceShellGateMarkPolicy(marksOf, tools = COARSE_SHELL_TOOLS) {
|
|
289
110
|
const shellTools = new Set(tools);
|
|
290
111
|
return {
|
|
291
112
|
check(req) {
|
|
292
|
-
// 只对 shell 工具**置位**,非 shell 一格不碰(那格的属主是上面的 ask 观察器)。本策略在合成里
|
|
293
|
-
// 恒排在 ask 观察器**之后**(见 applyRuntimeGovernance 的 combinePolicies 顺序):`always` 档下
|
|
294
|
-
// 每一次 shell 调用都被治理层门住,所以它对 shell 那一格的裁定压过「治理策略这次没产 ask」。
|
|
295
113
|
if (shellTools.has(req.toolName))
|
|
296
114
|
marksOf()?.setMarked(req.toolCallId, true);
|
|
297
115
|
return { action: "allow" };
|
|
298
116
|
},
|
|
299
117
|
};
|
|
300
118
|
}
|
|
301
|
-
/** core `tighten-task-spec` 的 SHELL_GATE_RANK 镜像(单一属主:#153 搬家后本文件是 server 侧唯一副本;
|
|
302
|
-
* task-settings 的旧副本随 settings 折叠臂一并拆除)。tightenTaskSpec 的契约是「override 自身必须收紧」
|
|
303
|
-
* 而非「取两者较严」——低于 base 的 override 直接 throw,所以施加前要按 rank 只升不降。 */
|
|
304
119
|
const SHELL_GATE_RANK = { off: 0, classify: 1, always: 2 };
|
|
305
|
-
/**
|
|
306
|
-
* Apply the operator's runtime governance (autonomy + commandPolicy + manualModeShellGate) onto a base
|
|
307
|
-
* `TaskSpec`, TIGHTEN-ONLY, in a SINGLE {@link tightenTaskSpec} call: commandPolicy compiles to a `toolPolicy`
|
|
308
|
-
* override (combined onto the baseline by `tightenTaskSpec` — TRAP #1), and autonomy expands to
|
|
309
|
-
* `handsReadOnly` / `shellGate` overrides (TRAP #2). `tightenTaskSpec` THROWS `TaskSpecTightenError` if any
|
|
310
|
-
* override would LOOSEN a stricter base safety field — surfacing a misconfiguration loudly instead of silently
|
|
311
|
-
* weakening the gate. Returns `base` untouched when there is nothing to apply.
|
|
312
|
-
*
|
|
313
|
-
* #153([2713] server 分单②):`MANUAL_MODE_SHELL_GATE` 从 settings 折叠搬到这里**无条件施加**。旧家
|
|
314
|
-
* (task-settings deriveSettingsPolicy 的 default/auto/acceptEdits 三臂)让一个**部署级 tighten-only 旋钮
|
|
315
|
-
* 的生死由客户端表态在场性决定**:body 无 settings/permissionMode(cli print/headless 腿按设计不 stamp)
|
|
316
|
-
* ⇒ 旋钮静默失效(clay 实机 Monitor 案「门根本没铸」);bypassPermissions 一句话掀掉部署闸;非 host lane
|
|
317
|
-
* fsWriteGate wiring 不建 ⇒ 沙箱 lane 全失效。governance 层与 autonomy 同拍施加后三者全闭。rank 合成:
|
|
318
|
-
* autonomy 派生值与旋钮取大(ask→"always" 压过 "classify"),再对 base 只升不降(防 tightenTaskSpec throw)。
|
|
319
|
-
*
|
|
320
|
-
* #177([2951] issue #29,**邻仓 test AI 的 46 格真机矩阵**实证——不是本仓的钉,别到本仓文件里数 46;
|
|
321
|
-
* 本仓的类级门是 test/operator-knob-client-posture-matrix.test.ts 的 旋钮×表态×lane 矩阵):
|
|
322
|
-
* `SENSITIVE_WRITE_PATTERNS` 是 #153 那次搬家漏下的兄弟旋钮——同一个病灶原样复发在 sensitive-path
|
|
323
|
-
* DENY 腿上(唯一合成点在 deriveSettingsPolicy 的 fsWriteGate 闭包,bypassPermissions/键缺席/settings
|
|
324
|
-
* 整缺席三形整条腿不建)。同方修:调用方(resolve-spec)把 operator 守卫集预铸成 `sensitivePathPolicy`
|
|
325
|
-
* 从这里**无条件施加**——它只在守卫段上产 deny(core 语义 deny/"safety"),对其余目标返回的是
|
|
326
|
-
* `action:"allow"`(**不是**「无意见」这种第三态:core 的 ToolPolicy 没有弃权值。它在
|
|
327
|
-
* `combinePolicies` 的 deny/ask 优先折叠里不构成一票,所以在**折叠语境**下等价于弃权;若日后有人把
|
|
328
|
-
* 这条策略单独当成唯一 policy 用,那个 allow 就是真放行——别照抄这句话去别的语境)。于是对 bypass
|
|
329
|
-
* 只挡守卫段、不给普通目标加 ask 门([820] 表第四行「bypass=不加门非开门」保持逐字)。策略在这里
|
|
330
|
-
* 只组合不构造:裁决 env 的 lane 分形(host 真 fs / 沙箱 deferred 代理)是 resolve-spec 的属主知识,
|
|
331
|
-
* governance 层不重复它。
|
|
332
|
-
*/
|
|
333
120
|
export function applyRuntimeGovernance(base, governance) {
|
|
334
|
-
// 显式注入(测试/将来的多实例形)优先;否则**每次裁决时**取当前 run 腿的 ALS 表(顶注「作用域」段)。
|
|
335
121
|
const marksOf = () => governance.askMarks ?? currentGovernanceAskMarks();
|
|
336
122
|
const overrides = { ...autonomyOverrides(governance.autonomy) };
|
|
337
123
|
const coarse = compileCommandPolicy(governance.commandPolicy);
|
|
338
124
|
const governancePolicies = [coarse, governance.sensitivePathPolicy].filter((p) => p !== undefined);
|
|
339
|
-
// [2942]/[2943]:治理层自己产的 `ask` 打标 —— 包在**合成之后**,于是 commandPolicy 的两模式
|
|
340
|
-
// (allowlist 的 defaultAction:"ask" / blocklist 的 ask-list)与 sensitivePathPolicy 走同一个铸点,
|
|
341
|
-
// 不会漏掉将来加进这个数组的第四条腿。
|
|
342
125
|
const governancePolicy = governancePolicies.length > 0
|
|
343
126
|
? createGovernanceAskMarkingPolicy(governancePolicies.length === 1 ? governancePolicies[0] : combinePolicies(...governancePolicies), marksOf)
|
|
344
127
|
: undefined;
|
|
@@ -348,31 +131,17 @@ export function applyRuntimeGovernance(base, governance) {
|
|
|
348
131
|
const candidate = overrides.shellGate !== undefined && SHELL_GATE_RANK[overrides.shellGate] >= SHELL_GATE_RANK[governance.manualModeShellGate]
|
|
349
132
|
? overrides.shellGate
|
|
350
133
|
: governance.manualModeShellGate;
|
|
351
|
-
// base 已更严 ⇒ 省略,让 base 原样保留(省略=行为等价,不 throw)。**仍然装配上不可达、纯防御**
|
|
352
|
-
// ——但论证已随 design/201 换了一条:
|
|
353
|
-
// · 前提(改):resolveSpec 的 spec 字面量**现在会写 shellGate** —— 显式 permissionMode 的翻译档
|
|
354
|
-
// (`shellGateForMode`,阶段④,governance 之前),所以 base.shellGate 不再恒缺席。
|
|
355
|
-
// · 结论(不变):这一支照旧不可达。翻译表的**上限是 classify**(rank 1),而能走到这里的 candidate
|
|
356
|
-
// 下限也是 classify(旋钮词表只有 classify/always,且 candidate 已取过与 autonomy 派生值的较大者)
|
|
357
|
-
// ⇒ candidate ≥ base 恒成立。SUP 路由姿态(resolve-spec 的 supPostureOverrides)是 governance 之
|
|
358
|
-
// **后**才叠的,够不到这里的 base。
|
|
359
|
-
// 若将来有人给 base 种上更严的值(例:翻译表新增一个 ⇒ always 的模式词),这里省略而不是让
|
|
360
|
-
// tightenTaskSpec 因「override 更松」throw——candidate 已是 autonomy 派生值与旋钮的较大者,省略它不会
|
|
361
|
-
// 丢掉 autonomy 那一半(autonomy 只产 "always",即最高 rank,永远不会落进这一支)。
|
|
362
134
|
if (SHELL_GATE_RANK[candidate] >= SHELL_GATE_RANK[base.shellGate ?? "off"])
|
|
363
135
|
overrides.shellGate = candidate;
|
|
364
136
|
else
|
|
365
137
|
delete overrides.shellGate;
|
|
366
138
|
}
|
|
367
|
-
// [2942]/[2943] shellGate 腿:治理层**自己**把门抬到 `"always"` 时,挂上纯观察器给 shell 调用打标
|
|
368
|
-
// (理由与 `"classify"` 为何不装,见 createGovernanceShellGateMarkPolicy 顶注)。判据取
|
|
369
|
-
// `overrides.shellGate` 而不是有效档 —— base 已经更严时上面那支会 `delete`,那时门不是治理层给的。
|
|
370
139
|
if (overrides.shellGate === "always") {
|
|
371
140
|
const shellObserver = createGovernanceShellGateMarkPolicy(marksOf);
|
|
372
141
|
overrides.toolPolicy = governancePolicy === undefined ? shellObserver : combinePolicies(governancePolicy, shellObserver);
|
|
373
142
|
}
|
|
374
143
|
if (overrides.toolPolicy === undefined && overrides.handsReadOnly === undefined && overrides.shellGate === undefined) {
|
|
375
|
-
return base;
|
|
144
|
+
return base;
|
|
376
145
|
}
|
|
377
146
|
return tightenTaskSpec(base, overrides);
|
|
378
147
|
}
|