@sema-agent/core 7.50.2 → 7.51.1
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/CHANGELOG.md +313 -0
- package/dist/agents/child-failure.d.ts +5 -16
- package/dist/agents/child-failure.js +4 -24
- package/dist/agents/subagent-steps.js +2 -2
- package/dist/agents/subagent.d.ts +4 -13
- package/dist/agents/subagent.js +94 -204
- package/dist/agents/tool-filter.d.ts +2 -2
- package/dist/brain/anthropic.d.ts +1 -1
- package/dist/brain/anthropic.js +7 -5
- package/dist/brain/open-responses.d.ts +1 -1
- package/dist/brain/open-responses.js +3 -2
- package/dist/brain/openai.d.ts +1 -1
- package/dist/brain/openai.js +3 -2
- package/dist/brain/request-route.d.ts +2 -2
- package/dist/brain/route-adjudicator.d.ts +16 -7
- package/dist/brain/route-adjudicator.js +23 -7
- package/dist/brain/route-conformance.js +23 -5
- package/dist/brain/stream-engine.js +9 -3
- package/dist/core/a2a.js +6 -8
- package/dist/core/allow-entry.d.ts +45 -26
- package/dist/core/allow-entry.js +48 -24
- package/dist/core/arg-summary.d.ts +8 -85
- package/dist/core/arg-summary.js +4 -386
- package/dist/core/ask-origin.d.ts +35 -33
- package/dist/core/ask-origin.js +9 -11
- package/dist/core/checkpoint-requirements.d.ts +6 -0
- package/dist/core/checkpoint-requirements.js +1 -0
- package/dist/core/checkpoint-store.d.ts +15 -34
- package/dist/core/checkpoint-store.js +5 -4
- package/dist/core/classifier-unavailable-arm.d.ts +12 -8
- package/dist/core/classifier-unavailable-arm.js +7 -2
- package/dist/core/credential-vocabulary.d.ts +3 -2
- package/dist/core/credential-vocabulary.js +27 -18
- package/dist/core/delegation-frames.d.ts +40 -15
- package/dist/core/delegation-frames.js +34 -0
- package/dist/core/engine-capabilities.d.ts +15 -0
- package/dist/core/engine-capabilities.js +1 -0
- package/dist/core/engine-notice.d.ts +41 -14
- package/dist/core/gate-fold.js +9 -7
- package/dist/core/gate-lanes.js +6 -4
- package/dist/core/gate-outcome.d.ts +81 -15
- package/dist/core/gate-outcome.js +168 -67
- package/dist/core/governance-codes.d.ts +7 -1
- package/dist/core/governance-codes.js +9 -0
- package/dist/core/hooks.d.ts +4 -4
- package/dist/core/hooks.js +4 -3
- package/dist/core/mcp-connection-pool.d.ts +2 -0
- package/dist/core/mcp-connection-pool.js +2 -1
- package/dist/core/mcp-failure.js +2 -1
- package/dist/core/mcp-server-spec.d.ts +27 -5
- package/dist/core/mcp.d.ts +13 -38
- package/dist/core/mcp.js +28 -23
- package/dist/core/media-byte-cap.d.ts +25 -0
- package/dist/core/media-byte-cap.js +18 -2
- package/dist/core/memory-engine/consolidation-driver.js +1 -1
- package/dist/core/mode-policy.d.ts +25 -1
- package/dist/core/mode-policy.js +28 -13
- package/dist/core/own-property-receipt.d.ts +18 -0
- package/dist/core/own-property-receipt.js +39 -0
- package/dist/core/permission-rule-model.d.ts +32 -11
- package/dist/core/permission-rule-model.js +12 -7
- package/dist/core/persisted-rule-arms.d.ts +3 -3
- package/dist/core/prompt-section-seat.d.ts +54 -0
- package/dist/core/prompt-section-seat.js +49 -0
- package/dist/core/protocol-http-auth.d.ts +44 -0
- package/dist/core/protocol-http-auth.js +45 -0
- package/dist/core/read-face-seats.d.ts +30 -0
- package/dist/core/read-face-seats.js +38 -0
- package/dist/core/redaction-passes.d.ts +82 -0
- package/dist/core/redaction-passes.js +378 -0
- package/dist/core/remote-env.d.ts +4 -3
- package/dist/core/resume-startup-cause.d.ts +56 -0
- package/dist/core/resume-startup-cause.js +43 -0
- package/dist/core/rule-denial-sentence.d.ts +2 -2
- package/dist/core/runner/announce-once-ledger.js +2 -2
- package/dist/core/runner/classifier-ladder.d.ts +17 -13
- package/dist/core/runner/classifier-ladder.js +12 -10
- package/dist/core/runner/construction-doors.js +9 -20
- package/dist/core/runner/contracts.d.ts +4 -4
- package/dist/core/runner/gate-exit.js +2 -2
- package/dist/core/runner/memory-consolidation-write-judge.d.ts +53 -9
- package/dist/core/runner/memory-consolidation-write-judge.js +39 -1
- package/dist/core/runner/permission-rule-lanes.d.ts +8 -13
- package/dist/core/runner/person-path-rows.d.ts +4 -3
- package/dist/core/runner/policy-throw.js +4 -4
- package/dist/core/runner/prepare-caps-and-workflow.d.ts +4 -3
- package/dist/core/runner/prepare-caps-and-workflow.js +8 -6
- package/dist/core/runner/prepare-gate-stations.js +4 -4
- package/dist/core/runner/prepare-hands-readface.js +1 -19
- package/dist/core/runner/prepare-inherited-gate.js +10 -9
- package/dist/core/runner/prepare-policy-chain.d.ts +3 -5
- package/dist/core/runner/prepare-policy-chain.js +10 -13
- package/dist/core/runner/prepare-project-context.d.ts +4 -4
- package/dist/core/runner/prepare-project-context.js +2 -1
- package/dist/core/runner/prepare-prompt-assembly.js +1 -0
- package/dist/core/runner/prepare-prompt-inputs.d.ts +1 -1
- package/dist/core/runner/prepare-prompt-inputs.js +4 -4
- package/dist/core/runner/prepare-protocol-tools.d.ts +4 -3
- package/dist/core/runner/prepare-protocol-tools.js +3 -2
- package/dist/core/runner/prepare-task.js +6 -4
- package/dist/core/runner/prepare-turn-wiring.d.ts +16 -13
- package/dist/core/runner/prepare-turn-wiring.js +8 -7
- package/dist/core/runner/run-harness-handlers.js +2 -2
- package/dist/core/runner/run-leg.js +10 -9
- package/dist/core/runner/run-settle-and-teardown.js +5 -5
- package/dist/core/runner/run-turn-boundary.js +3 -2
- package/dist/core/runner/runtask.d.ts +1 -2
- package/dist/core/runner/runtask.js +8 -12
- package/dist/core/runner/session-rule-policy.d.ts +15 -11
- package/dist/core/runner/session-rule-policy.js +43 -33
- package/dist/core/runner/stream-lifecycle-verbs.js +2 -2
- package/dist/core/runner/stream-settle-backstop.js +2 -1
- package/dist/core/runner/sub-agent-deny-lists.d.ts +12 -8
- package/dist/core/runner/sub-agent-deny-lists.js +23 -12
- package/dist/core/runner/terminal-account.js +3 -3
- package/dist/core/runner/tool-output-projection.d.ts +5 -0
- package/dist/core/runner/tool-output-projection.js +6 -11
- package/dist/core/runner/tool-result-media-seat.d.ts +19 -0
- package/dist/core/runner/tool-result-media-seat.js +13 -0
- package/dist/core/runner/turn-attachments.d.ts +13 -12
- package/dist/core/runner/turn-attachments.js +11 -7
- package/dist/core/runner-deps.d.ts +4 -3
- package/dist/core/safety-merge-corpus.js +21 -21
- package/dist/core/secret-env.d.ts +41 -15
- package/dist/core/secret-env.js +85 -19
- package/dist/core/session-policy-store.d.ts +89 -72
- package/dist/core/session-policy-store.js +100 -93
- package/dist/core/shell-read-silence.d.ts +38 -4
- package/dist/core/shell-read-silence.js +102 -0
- package/dist/core/side-query.d.ts +7 -8
- package/dist/core/skill-grant-binding.d.ts +32 -11
- package/dist/core/skill-grant-binding.js +11 -3
- package/dist/core/skill-grant.d.ts +4 -3
- package/dist/core/skill-grant.js +5 -4
- package/dist/core/skills-directory.d.ts +12 -1
- package/dist/core/skills-directory.js +17 -8
- package/dist/core/spec-contract.d.ts +5 -3
- package/dist/core/store-contracts/checkpoint-store-contract.d.ts +2 -0
- package/dist/core/store-contracts/checkpoint-store-contract.js +3 -0
- package/dist/core/swappable-deps.d.ts +15 -1
- package/dist/core/swappable-deps.js +25 -1
- package/dist/core/synthetic-read-verdict.d.ts +16 -14
- package/dist/core/synthetic-read-verdict.js +28 -8
- package/dist/core/task-notification.d.ts +13 -3
- package/dist/core/task-notification.js +12 -1
- package/dist/core/task-spec.d.ts +14 -15
- package/dist/core/tool-name-readings.d.ts +0 -4
- package/dist/core/tool-name-readings.js +0 -1
- package/dist/core/tool-policy.d.ts +40 -9
- package/dist/core/tool-policy.js +25 -7
- package/dist/core/types.d.ts +3 -3
- package/dist/core/types.js +1 -1
- package/dist/core/untrusted-egress.d.ts +60 -11
- package/dist/core/untrusted-egress.js +120 -9
- package/dist/core/untrusted-text.js +16 -0
- package/dist/core/wiring-manifest.d.ts +22 -8
- package/dist/core/wiring-manifest.js +12 -4
- package/dist/engine/execution-env/kill-tree.js +4 -4
- package/dist/engine/execution-env/node-execution-env.d.ts +11 -4
- package/dist/engine/execution-env/node-execution-env.js +9 -4
- package/dist/engine/harness/agent-harness.d.ts +2 -1
- package/dist/engine/harness/agent-harness.js +4 -1
- package/dist/engine/harness/types.d.ts +3 -0
- package/dist/engine/llm/types.d.ts +7 -9
- package/dist/engine/loop/agent-loop.d.ts +8 -3
- package/dist/engine/loop/agent-loop.js +26 -9
- package/dist/engine/loop/types.d.ts +10 -0
- package/dist/engine/lsp/node-lsp-manager.d.ts +2 -1
- package/dist/engine/lsp/node-lsp-manager.js +2 -2
- package/dist/index.d.ts +22 -5
- package/dist/index.js +18 -5
- package/dist/orchestration/run-workflow-tool.js +8 -8
- package/dist/orchestration/workflow.js +2 -2
- package/dist/prompt-assembly/assemble.d.ts +3 -1
- package/dist/prompt-assembly/assemble.js +6 -0
- package/dist/prompts/cowork.d.ts +42 -0
- package/dist/prompts/cowork.js +529 -0
- package/dist/prompts/default.d.ts +14 -1
- package/dist/prompts/default.js +3 -0
- package/dist/stores/file/fs-atomic.js +2 -2
- package/dist/stores/file/session-policy-store.d.ts +6 -3
- package/dist/stores/file/session-policy-store.js +5 -16
- package/dist/tools/artifact/artifact-tool.js +2 -0
- package/dist/tools/fs/image-admission.d.ts +57 -4
- package/dist/tools/fs/image-admission.js +85 -0
- package/dist/tools/fs/path-containment.d.ts +2 -0
- package/dist/tools/fs/path-containment.js +5 -0
- package/dist/tools/fs/shell-read-check.d.ts +31 -6
- package/dist/tools/fs/shell-read-check.js +2 -4
- package/dist/tools/fs/shell-read-environment.d.ts +10 -25
- package/dist/tools/fs/shell-read-environment.js +6 -18
- package/dist/tools/fs/shell-read-establish.d.ts +3 -2
- package/dist/tools/fs/shell-read-establish.js +5 -5
- package/dist/tools/fs/shell-read-judge.d.ts +38 -8
- package/dist/tools/fs/shell-read-judge.js +12 -10
- package/dist/tools/web.js +2 -2
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +112 -10
- package/wire-changes/core@7.51.0.json +2421 -0
- package/wire-changes/core@7.51.1.json +259 -0
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* A child the engine spawns may run what the MODEL chose (a tool shell, a gate over model-written code, a language
|
|
5
5
|
* server). Under the `"scrub"` policy (the default) it inherits every variable but those the judgment below names —
|
|
6
6
|
* deleted whole, never rewritten; `"all"` and a `string[]` allow list are the caller's explicit alternatives
|
|
7
|
-
* ({@link inheritedHostEnv}).
|
|
7
|
+
* ({@link inheritedHostEnv}). Whatever the policy, a name the deployment declares as its own credential
|
|
8
|
+
* ({@link declareWithheldEnv}) is never inherited. One judgment per variable ({@link classifySecretEnvEntry}), the first rule that answers:
|
|
8
9
|
* 1. exempt — git's environment config and the proxy variables, handed on as written ({@link isExemptEnvName});
|
|
9
10
|
* 2. `exact-name` — a known credential name (the vocabulary's exact env names);
|
|
10
11
|
* 3. `prefix-rule` — the OpenTelemetry settings ({@link OTEL_ENV_RE});
|
|
@@ -25,10 +26,11 @@ import { type EngineNotice } from "./engine-notice.js";
|
|
|
25
26
|
* Reads the first {@link VALUE_SCAN_LIMIT} code units. The value half of the judgment, exported apart from the env
|
|
26
27
|
* entry's exemptions ({@link isExemptEnvName}): a caller holding a value that is no environment variable asks this. */
|
|
27
28
|
export declare function looksLikeCredentialValue(value: string): boolean;
|
|
28
|
-
/** Which rule withheld an entry
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
/** Which rule withheld an entry: one of the scrub's rules, or `own-credential` — the deployment declared the name as its
|
|
30
|
+
* own credential ({@link declareWithheldEnv}), which every policy withholds. */
|
|
31
|
+
export type SecretEnvFindingKind = "exact-name" | "name-rule" | "namespace-rule" | "prefix-rule" | "value-rule" | "own-credential";
|
|
32
|
+
/** One env entry withheld (deleted) from a child's environment: its name — never its value, so the record is
|
|
33
|
+
* loggable —, the rule that withheld it, how strong that claim is, and the minting module. */
|
|
32
34
|
export interface SecretEnvFinding {
|
|
33
35
|
key: string;
|
|
34
36
|
kind: SecretEnvFindingKind;
|
|
@@ -49,16 +51,39 @@ export declare function scrubSecretEnv(env: NodeJS.ProcessEnv, findings?: Secret
|
|
|
49
51
|
/** How much of this process's environment a child spawned on this host inherits — the `inheritEnv` value. */
|
|
50
52
|
export type InheritEnvPolicy = "all" | "scrub" | string[];
|
|
51
53
|
/**
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
54
|
+
* Declare the names of this deployment's OWN credentials in this process's environment — the engine's or its host's:
|
|
55
|
+
* an approval bearer, a service token. From then on no child spawned on this host inherits one, whatever the policy:
|
|
56
|
+
* every spawn site reads its environment through {@link inheritedHostEnv}, which subtracts them from `"all"` and from
|
|
57
|
+
* the scrub's result (each one present is a finding of kind `own-credential`), and {@link inheritEnvPolicyProblem}
|
|
58
|
+
* refuses an allow list naming one. Compared as the platform compares variable names: exactly, but case-blind on
|
|
59
|
+
* Windows. A variable a host hands a child itself (a shell's `shellEnv`, a per-exec `env`, a language server's `env`, an MCP server entry's `env`)
|
|
60
|
+
* is its explicit grant and is not read against the list.
|
|
61
|
+
*
|
|
62
|
+
* Declared once, at start-up, before anything spawns; a later call REPLACES the list (a configuration reload), read
|
|
63
|
+
* from the next spawn on — a spawn whose environment is already read (one announcing its findings) keeps it. The
|
|
64
|
+
* list is copied. A malformed list — not an array, an entry that is no variable name, or a name the host hands every
|
|
65
|
+
* child anyway ({@link runtimeSuppliedEnv}) — throws and leaves the declaration as it was.
|
|
66
|
+
*/
|
|
67
|
+
export declare function declareWithheldEnv(names: readonly string[]): void;
|
|
68
|
+
/** Test seam: never called by production code (a host re-declares `[]`). */
|
|
69
|
+
export declare function __resetWithheldEnv(): void;
|
|
70
|
+
/**
|
|
71
|
+
* THE environment a child spawned ON THIS HOST inherits: the policy's arm, less the declared own credentials
|
|
72
|
+
* ({@link declareWithheldEnv}). The arms: `"scrub"` (and absent) is this process's environment through
|
|
73
|
+
* {@link scrubSecretEnv}; `"all"` is this process's environment as written; a `string[]` is exactly the named
|
|
74
|
+
* variables that are set, as written — a strict allow list, so a name it leaves out (`PATH` included) is not
|
|
75
|
+
* inherited. `findings` collects every entry withheld: the scrub's, and each declared own credential the arm held.
|
|
76
|
+
*
|
|
77
|
+
* A policy {@link inheritEnvPolicyProblem} refuses throws that reason HERE, at every spawn, read against the
|
|
78
|
+
* declaration as it stands: a value that is no policy at all (never read as the scrub), and an allow list naming a
|
|
79
|
+
* declared own credential — whenever the declaration was made, so the refusal at spawn time is the invariant. The
|
|
80
|
+
* spawn sites that take a policy (the tool shell's `NodeExecutionEnv` at construction, the oracle gate per gate) also
|
|
81
|
+
* ask first, so a host that declared before building them hears it at start-up.
|
|
59
82
|
*/
|
|
60
83
|
export declare function inheritedHostEnv(policy: InheritEnvPolicy | undefined, findings?: SecretEnvFinding[]): NodeJS.ProcessEnv;
|
|
61
|
-
/** Why `value` is not an {@link InheritEnvPolicy} (absent counts as one — the scrub), or
|
|
84
|
+
/** Why `value` is not an {@link InheritEnvPolicy} this deployment can read (absent counts as one — the scrub), or
|
|
85
|
+
* `undefined` when it is: a malformed policy, or an allow list naming one of the declared own credentials
|
|
86
|
+
* ({@link declareWithheldEnv}, compared as it says). */
|
|
62
87
|
export declare function inheritEnvPolicyProblem(value: unknown): string | undefined;
|
|
63
88
|
/** Test seam: never called by production code. */
|
|
64
89
|
export declare function __resetSecretEnvScrubNoticeAnnouncement(): void;
|
|
@@ -66,7 +91,8 @@ export declare function __resetSecretEnvScrubNoticeAnnouncement(): void;
|
|
|
66
91
|
* The ONE disclosure point for a scrub's withheld keys. Routed through {@link deliverEngineNotice} — the
|
|
67
92
|
* engine's one notice throat — so a wired `onNotice` seat REPLACES the console line exactly as every
|
|
68
93
|
* other `EngineNotice` does, and an absent seat prints the same words `console.warn` always printed here.
|
|
69
|
-
* `detail.names` carries every withheld key.
|
|
70
|
-
*
|
|
94
|
+
* `detail.names` carries every withheld key; `detail.ownCredentials`, present only when one was withheld, the
|
|
95
|
+
* subset withheld as the deployment's declared own credentials (kind `own-credential`) rather than by the scrub.
|
|
96
|
+
* Once per PROCESS, never per call or per site; an EMPTY findings array is a no-op.
|
|
71
97
|
*/
|
|
72
98
|
export declare function announceSecretEnvScrub(onNotice: ((notice: EngineNotice) => void) | undefined, findings: readonly SecretEnvFinding[]): void;
|
package/dist/core/secret-env.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { CREDENTIAL_VOCABULARY, envNameView, splitCamelCase } from "./credential-vocabulary.js";
|
|
2
|
+
import { DEFAULT_INHERITED_ENV_VARS } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
2
3
|
import { deliverEngineNotice } from "./engine-notice.js";
|
|
3
4
|
const GIT_CONFIG_ENV_RE = /^GIT_CONFIG_(?:COUNT|PARAMETERS|(?:KEY|VALUE)_[0-9]+)$/;
|
|
4
5
|
const PROXY_ENV_RE = /^(?:(?:https?|ftp|all|no)_proxy|npm_config_(?:https?_)?proxy|npm_config_noproxy|yarn_proxy|(?:yarn|global_agent|docker|claude_code)_(?:https?|no)_proxy|cloudsdk_proxy_[a-z]+|electron_get_use_proxy)$/i;
|
|
@@ -7,7 +8,7 @@ function isExemptEnvName(name) {
|
|
|
7
8
|
}
|
|
8
9
|
const ENV_NAMES = envNameView(CREDENTIAL_VOCABULARY);
|
|
9
10
|
const OTEL_ENV_RE = /^(?:INPUT_)?OTEL_/i;
|
|
10
|
-
const SEMA_NAMESPACE_SECRET_RE = new RegExp(`^SEMA_(?:.*_)
|
|
11
|
+
const SEMA_NAMESPACE_SECRET_RE = new RegExp(`^SEMA_(?:.*_)?${ENV_NAMES.segmentWord}(?:_|$)`, "is");
|
|
11
12
|
const BUNDLER_SETTINGS = ["BUILD", "LOCAL", "MIRROR", "PATH", "WITH", "WITHOUT", "CACHE", "DISABLE", "IGNORE", "ONLY"];
|
|
12
13
|
const BUNDLER_HOST = "(?:[A-Za-z0-9]+(?:___[A-Za-z0-9]+)*__)+[A-Za-z]{2,}";
|
|
13
14
|
const BUNDLER_CREDENTIAL_RE = new RegExp(`^(?:INPUT_)?BUNDLE_(?!(?:${BUNDLER_SETTINGS.join("|")})__(?!${BUNDLER_HOST}$))\\w*__`, "i");
|
|
@@ -93,6 +94,7 @@ const SECRET_ENV_KIND_CONFIDENCE = {
|
|
|
93
94
|
"exact-name": "high",
|
|
94
95
|
"prefix-rule": "high",
|
|
95
96
|
"value-rule": "high",
|
|
97
|
+
"own-credential": "high",
|
|
96
98
|
"name-rule": "medium",
|
|
97
99
|
"namespace-rule": "medium",
|
|
98
100
|
};
|
|
@@ -131,29 +133,91 @@ export function scrubSecretEnv(env, findings) {
|
|
|
131
133
|
}
|
|
132
134
|
return Object.fromEntries(kept);
|
|
133
135
|
}
|
|
136
|
+
function envNameKey(name) {
|
|
137
|
+
return process.platform === "win32" ? name.toUpperCase() : name;
|
|
138
|
+
}
|
|
139
|
+
const UNPAIRED_SURROGATE_RE = /[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/;
|
|
140
|
+
function variableNameListProblem(label, value) {
|
|
141
|
+
const bad = value.findIndex((k) => typeof k !== "string" || k.length === 0 || /[=\0\s]/.test(k) || UNPAIRED_SURROGATE_RE.test(k));
|
|
142
|
+
if (bad === -1)
|
|
143
|
+
return undefined;
|
|
144
|
+
const k = value[bad];
|
|
145
|
+
const why = typeof k !== "string" ? typeof k : k === "" ? "an empty string" : `a name holding ${k.includes("=") ? "\"=\"" : k.includes("\0") ? "a NUL" : /\s/.test(k) ? "whitespace" : "an unpaired surrogate"}`;
|
|
146
|
+
return `${label}[${bad}] is not a variable name (got ${why})`;
|
|
147
|
+
}
|
|
148
|
+
const WINDOWS_REQUIRED_ENV = Object.freeze(["HOMEDRIVE", "HOMEPATH", "LOGONSERVER", "PATH", "SYSTEMDRIVE", "SYSTEMROOT", "TEMP", "USERDOMAIN", "USERNAME", "USERPROFILE", "WINDIR"]);
|
|
149
|
+
function runtimeSuppliedEnv() {
|
|
150
|
+
return ["NODE_V8_COVERAGE", ...DEFAULT_INHERITED_ENV_VARS, ...(process.platform === "win32" ? WINDOWS_REQUIRED_ENV : [])];
|
|
151
|
+
}
|
|
152
|
+
const WITHHELD_ENV = Symbol.for("sema.core.withheldEnv");
|
|
153
|
+
const NOTHING_WITHHELD = Object.freeze([]);
|
|
154
|
+
const withheldEnvOf = () => {
|
|
155
|
+
const held = Reflect.get(globalThis, WITHHELD_ENV);
|
|
156
|
+
return Array.isArray(held) ? held : NOTHING_WITHHELD;
|
|
157
|
+
};
|
|
158
|
+
const setWithheldEnv = (names) => {
|
|
159
|
+
Reflect.set(globalThis, WITHHELD_ENV, Object.freeze([...names]));
|
|
160
|
+
};
|
|
161
|
+
export function declareWithheldEnv(names) {
|
|
162
|
+
const value = names;
|
|
163
|
+
if (!Array.isArray(value))
|
|
164
|
+
throw new Error(`declareWithheldEnv: the list must be an array of variable names (got ${typeof value === "string" ? JSON.stringify(value) : value === null ? "null" : typeof value})`);
|
|
165
|
+
const problem = variableNameListProblem("names", value);
|
|
166
|
+
if (problem !== undefined)
|
|
167
|
+
throw new Error(`declareWithheldEnv: ${problem}`);
|
|
168
|
+
const supplied = new Set(runtimeSuppliedEnv().map(envNameKey));
|
|
169
|
+
const unwithholdable = value.findIndex((k) => supplied.has(envNameKey(k)));
|
|
170
|
+
if (unwithholdable !== -1)
|
|
171
|
+
throw new Error(`declareWithheldEnv: names[${unwithholdable}] is ${value[unwithholdable]}, which a child is handed from this process's environment whatever the spawn passes (by Node, the MCP stdio transport or Windows) — it cannot be withheld`);
|
|
172
|
+
setWithheldEnv(value);
|
|
173
|
+
}
|
|
174
|
+
export function __resetWithheldEnv() {
|
|
175
|
+
setWithheldEnv([]);
|
|
176
|
+
}
|
|
177
|
+
function withholdOwnCredentials(inherited, findings) {
|
|
178
|
+
if (withheldEnvOf().length === 0)
|
|
179
|
+
return inherited;
|
|
180
|
+
const own = new Set(withheldEnvOf().map(envNameKey));
|
|
181
|
+
const kept = [];
|
|
182
|
+
for (const key of Object.keys(inherited)) {
|
|
183
|
+
if (!own.has(envNameKey(key)))
|
|
184
|
+
kept.push([key, inherited[key]]);
|
|
185
|
+
else
|
|
186
|
+
findings?.push({ key, kind: "own-credential", confidence: SECRET_ENV_KIND_CONFIDENCE["own-credential"], source: "secret-env" });
|
|
187
|
+
}
|
|
188
|
+
return Object.fromEntries(kept);
|
|
189
|
+
}
|
|
190
|
+
function policyShapeProblem(value) {
|
|
191
|
+
if (value === undefined || value === "all" || value === "scrub")
|
|
192
|
+
return undefined;
|
|
193
|
+
if (Array.isArray(value))
|
|
194
|
+
return variableNameListProblem("inheritEnv", value);
|
|
195
|
+
return `inheritEnv must be "scrub", "all" or a list of variable names (got ${typeof value === "string" ? JSON.stringify(value) : value === null ? "null" : typeof value})`;
|
|
196
|
+
}
|
|
134
197
|
export function inheritedHostEnv(policy, findings) {
|
|
198
|
+
const problem = inheritEnvPolicyProblem(policy);
|
|
199
|
+
if (problem !== undefined)
|
|
200
|
+
throw new Error(problem);
|
|
201
|
+
let arm;
|
|
135
202
|
if (policy === "all")
|
|
136
|
-
|
|
137
|
-
if (Array.isArray(policy)) {
|
|
138
|
-
|
|
203
|
+
arm = { ...process.env };
|
|
204
|
+
else if (Array.isArray(policy)) {
|
|
205
|
+
arm = {};
|
|
139
206
|
for (const k of policy)
|
|
140
207
|
if (process.env[k] !== undefined)
|
|
141
|
-
|
|
142
|
-
return picked;
|
|
208
|
+
arm[k] = process.env[k];
|
|
143
209
|
}
|
|
144
|
-
|
|
210
|
+
else
|
|
211
|
+
arm = scrubSecretEnv(process.env, findings);
|
|
212
|
+
return withholdOwnCredentials(arm, findings);
|
|
145
213
|
}
|
|
146
214
|
export function inheritEnvPolicyProblem(value) {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
const k = value[bad];
|
|
154
|
-
return `inheritEnv[${bad}] is not a variable name (got ${typeof k !== "string" ? typeof k : k === "" ? "an empty string" : `a name holding ${k.includes("=") ? "\"=\"" : "a NUL"}`})`;
|
|
155
|
-
}
|
|
156
|
-
return `inheritEnv must be "scrub", "all" or a list of variable names (got ${typeof value === "string" ? JSON.stringify(value) : value === null ? "null" : typeof value})`;
|
|
215
|
+
const shape = policyShapeProblem(value);
|
|
216
|
+
if (shape !== undefined || !Array.isArray(value) || withheldEnvOf().length === 0)
|
|
217
|
+
return shape;
|
|
218
|
+
const own = new Set(withheldEnvOf().map(envNameKey));
|
|
219
|
+
const named = value.findIndex((k) => own.has(envNameKey(k)));
|
|
220
|
+
return named === -1 ? undefined : `inheritEnv[${named}] names ${value[named]}, which is this deployment's own credential and cannot be inherited by a child (a configuration error: no retry succeeds until the policy or the declaration changes)`;
|
|
157
221
|
}
|
|
158
222
|
let secretEnvScrubNoticeAnnounced = false;
|
|
159
223
|
export function __resetSecretEnvScrubNoticeAnnouncement() {
|
|
@@ -164,9 +228,11 @@ export function announceSecretEnvScrub(onNotice, findings) {
|
|
|
164
228
|
return;
|
|
165
229
|
secretEnvScrubNoticeAnnounced = true;
|
|
166
230
|
const names = findings.map((f) => f.key);
|
|
231
|
+
const ownCredentials = findings.filter((f) => f.kind === "own-credential").map((f) => f.key);
|
|
232
|
+
const own = ownCredentials.length > 0 ? `; ${ownCredentials.length} of them this deployment's own credential(s) (${ownCredentials.join(", ")})` : "";
|
|
167
233
|
deliverEngineNotice(onNotice, {
|
|
168
234
|
code: "config.secret_env_scrubbed",
|
|
169
|
-
message: `secret_env_scrubbed: ${findings.length} env key(s) withheld from a spawned child's environment (${names.join(", ")}) — once per process; wire onNotice for the structured form`,
|
|
170
|
-
detail: { names },
|
|
235
|
+
message: `secret_env_scrubbed: ${findings.length} env key(s) withheld from a spawned child's environment (${names.join(", ")})${own} — once per process; wire onNotice for the structured form`,
|
|
236
|
+
detail: { names, ...(ownCredentials.length > 0 ? { ownCredentials } : {}) },
|
|
171
237
|
});
|
|
172
238
|
}
|
|
@@ -14,26 +14,37 @@
|
|
|
14
14
|
* (mirrors `steer.trusted`); core never adjudicates operator-ness, it only honors the flag.
|
|
15
15
|
* - **CAS rev** (optimistic concurrency): a `putRules` with a stale `expectedRev` is rejected, so two replicas
|
|
16
16
|
* can't lose an update (mirrors the checkpoint store's `resolve` OCC).
|
|
17
|
-
* - **
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
17
|
+
* - **one grammar**: the two rule buckets hold rule ENTRIES — the grammar every entry list reads, through its one reader
|
|
18
|
+
* (`readRuleEntry`, allow-entry.ts): a tool name, a protocol namespace, a shell command rule (`Bash(git push:*)`), a
|
|
19
|
+
* path rule, a name or host rule. Entries are stored as written; every station that validates, compares, matches or
|
|
20
|
+
* names one reads that reader's answer ({@link judgeSessionRulesWrite}, {@link loosenReasons}, the run-time layer).
|
|
21
|
+
* - **NOT A SANDBOX**: a shell command rule reads the command line the call carries (every program run in its connector
|
|
22
|
+
* chain); the real isolation boundary is the executionEnv, never these rules.
|
|
21
23
|
*/
|
|
22
|
-
import { type
|
|
23
|
-
/** The
|
|
24
|
+
import { type AllowEntryInertDetail, type AllowEntryInertReason } from "./allow-entry.js";
|
|
25
|
+
/** The per-session rules. All optional; an absent bucket imposes no constraint of that kind. */
|
|
24
26
|
export interface SessionPermissionRules {
|
|
25
|
-
/** If set, ONLY these
|
|
27
|
+
/** If set, ONLY the calls these entries admit are allowed — a narrowing allowlist: a call no entry admits is refused.
|
|
28
|
+
* Rule entries read in the allow direction (`Read`, `mcp__srv`, `Bash(git:*)`, `Edit(src/**)`). */
|
|
26
29
|
toolAllow?: string[];
|
|
27
|
-
/**
|
|
30
|
+
/** Entries whose calls are always refused (deny wins) — rule entries read in the deny direction (`Bash`, `Bash(rm:*)`,
|
|
31
|
+
* `mcp__srv__delete_*`, `Read(//etc/**)`). */
|
|
28
32
|
toolDeny?: string[];
|
|
29
33
|
/** If set, WRITE tools (`Write`/`Edit`) may only target paths within these dirs; a write-capable
|
|
30
34
|
* tool that can't be path-confined (e.g. `Bash`) is denied while this is set. Stored RAW; resolved at run time. */
|
|
31
35
|
allowDirs?: string[];
|
|
32
|
-
/** If set, ONLY these `Bash` command names (argv[0]) are allowed. */
|
|
33
|
-
commandAllow?: string[];
|
|
34
|
-
/** `Bash` command names (argv[0]) always denied. */
|
|
35
|
-
commandDeny?: string[];
|
|
36
36
|
}
|
|
37
|
+
/** Every bucket a rules object holds, and how its entries are read: a rule-entry list in one direction, or the write
|
|
38
|
+
* confinement's directories. A key outside this table is no bucket ({@link readSessionRulesShape} refuses it). */
|
|
39
|
+
declare const SESSION_RULE_BUCKETS: {
|
|
40
|
+
readonly toolAllow: "allow";
|
|
41
|
+
readonly toolDeny: "deny";
|
|
42
|
+
readonly allowDirs: "dirs";
|
|
43
|
+
};
|
|
44
|
+
/** A bucket that holds rule entries. */
|
|
45
|
+
export type SessionRuleEntryField = {
|
|
46
|
+
[K in keyof typeof SESSION_RULE_BUCKETS]: (typeof SESSION_RULE_BUCKETS)[K] extends "dirs" ? never : K;
|
|
47
|
+
}[keyof typeof SESSION_RULE_BUCKETS];
|
|
37
48
|
/** {@link SessionPermissionRules} plus the monotonic `rev` the store stamps (the OCC key). */
|
|
38
49
|
export interface StoredSessionRules extends SessionPermissionRules {
|
|
39
50
|
rev: number;
|
|
@@ -52,58 +63,42 @@ export interface PutRulesOptions {
|
|
|
52
63
|
* this from a verified, role-checked principal (mirrors `steer.trusted`). */
|
|
53
64
|
operator?: boolean;
|
|
54
65
|
}
|
|
55
|
-
/** The {@link SessionPolicyError.detail} of
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
export interface
|
|
59
|
-
field:
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
+
/** The {@link SessionPolicyError.detail} of an `invalid_shape` refusal: which key, and what it holds — its TYPE, never its
|
|
67
|
+
* value (`a string`, `null`, `an array holding an entry that is not a string`), or `not a rule bucket` for a key outside
|
|
68
|
+
* the buckets. `field` is empty when the rules value itself is not an object. */
|
|
69
|
+
export interface SessionRuleShapeRefusal {
|
|
70
|
+
field: string;
|
|
71
|
+
got: string;
|
|
72
|
+
}
|
|
73
|
+
/** The {@link SessionPolicyError.detail} of an `inert_entry` refusal: which bucket, which entry, and why it reads as nothing —
|
|
74
|
+
* the entry reader's own answer (`readRuleEntry`), reason and facts unchanged. */
|
|
75
|
+
export interface SessionRuleEntryRefusal extends AllowEntryInertDetail {
|
|
76
|
+
field: SessionRuleEntryField;
|
|
77
|
+
entry: string;
|
|
78
|
+
reason: AllowEntryInertReason;
|
|
66
79
|
}
|
|
67
80
|
/** Typed error from {@link SessionPolicyStore.putRules}. `conflict` = stale `expectedRev`; `loosen_forbidden`
|
|
68
|
-
* = a non-operator write tried to relax the rules; `corrupt` = a backend could not read the
|
|
69
|
-
* rev mark
|
|
70
|
-
*
|
|
71
|
-
* ({@link
|
|
81
|
+
* = a non-operator write tried to relax the rules; `corrupt` = a backend could not read what it stored (a row, or the
|
|
82
|
+
* erase-surviving rev mark of {@link SessionPolicyStore.deleteBySession}) and refuses rather than read it as fewer rules or
|
|
83
|
+
* stamp an unproven rev; `invalid_shape` = the rules value is not an object of buckets, each a list of strings
|
|
84
|
+
* ({@link readSessionRulesShape}); `inert_entry` = a bucket holds an entry the entry reader reads as nothing. `detail` is
|
|
85
|
+
* present on the last two codes and only on them ({@link SessionRuleShapeRefusal}, {@link SessionRuleEntryRefusal}). */
|
|
72
86
|
export declare class SessionPolicyError extends Error {
|
|
73
|
-
readonly code: "conflict" | "loosen_forbidden" | "corrupt" | "
|
|
74
|
-
readonly detail?:
|
|
75
|
-
constructor(code: "conflict" | "loosen_forbidden" | "corrupt" | "
|
|
87
|
+
readonly code: "conflict" | "loosen_forbidden" | "corrupt" | "invalid_shape" | "inert_entry";
|
|
88
|
+
readonly detail?: (SessionRuleShapeRefusal | SessionRuleEntryRefusal) | undefined;
|
|
89
|
+
constructor(code: "conflict" | "loosen_forbidden" | "corrupt" | "invalid_shape" | "inert_entry", message: string, detail?: (SessionRuleShapeRefusal | SessionRuleEntryRefusal) | undefined);
|
|
76
90
|
}
|
|
77
|
-
/** Which buckets hold TOOL names (the ones a run's policy audit reads as names); a new bucket is given a role here. */
|
|
78
|
-
declare const HOLDS_TOOL_NAMES: {
|
|
79
|
-
readonly toolAllow: true;
|
|
80
|
-
readonly toolDeny: true;
|
|
81
|
-
readonly allowDirs: false;
|
|
82
|
-
readonly commandAllow: false;
|
|
83
|
-
readonly commandDeny: false;
|
|
84
|
-
};
|
|
85
|
-
type SessionRuleToolNameField = {
|
|
86
|
-
[K in keyof typeof HOLDS_TOOL_NAMES]: (typeof HOLDS_TOOL_NAMES)[K] extends true ? K : never;
|
|
87
|
-
}[keyof typeof HOLDS_TOOL_NAMES];
|
|
88
91
|
/** Construction options both bundled stores take. */
|
|
89
92
|
export interface SessionPolicyStoreOptions {
|
|
90
|
-
/** The names this deployment mounts its OWN tools under — the roster the store
|
|
91
|
-
* (
|
|
92
|
-
*
|
|
93
|
+
/** The names this deployment mounts its OWN tools under — the roster the store reads a rule entry's tool name against
|
|
94
|
+
* (`readRuleEntry`): a retired name declared here is an ordinary name to the write and to the loosening check alike.
|
|
95
|
+
* Absent = none. A name carrying `__` is refused, as it is for a deployment tool. */
|
|
93
96
|
mountedToolNames?: Iterable<string>;
|
|
94
97
|
}
|
|
95
98
|
/** A store's declared roster from {@link SessionPolicyStoreOptions.mountedToolNames} — the one reader both bundled stores
|
|
96
99
|
* (and so `FileStorageBackendOptions.sessionPolicy`) construct through, refusing loudly when it is not a list of plain
|
|
97
100
|
* tool names (a bare string would iterate by character). */
|
|
98
101
|
export declare function readMountedToolNames(value: unknown): ReadonlySet<string>;
|
|
99
|
-
/**
|
|
100
|
-
* The write judge for session rules, against `mountedToolNames` (the store's declaration; empty when it has none): every
|
|
101
|
-
* entry of a tool-name bucket must be `current` ({@link judgeRuleToolName}), or the write is refused (the fourth
|
|
102
|
-
* {@link SessionPolicyError} code) — such a name matches no tool, and a run's policy audit refuses to start with it.
|
|
103
|
-
* Operator writes included. Both bundled stores call it after normalizing and before {@link loosenReasons}; a durable
|
|
104
|
-
* backend calls it at the same point. The message names the entry and the way out, never a storage location.
|
|
105
|
-
*/
|
|
106
|
-
export declare function assertSessionRuleNamesCurrent(rules: SessionPermissionRules, mountedToolNames?: ReadonlySet<string>): void;
|
|
107
102
|
/**
|
|
108
103
|
* The seam a deployment implements for DURABLE, cross-replica per-session rules. Unset on `RunnerDeps` ⇒ the
|
|
109
104
|
* feature is OFF (no rules read, zero behavior change). Keyed on `(sessionId, principal-owner)`: core passes
|
|
@@ -118,11 +113,9 @@ export interface SessionRulesRecord {
|
|
|
118
113
|
}
|
|
119
114
|
export interface SessionPolicyStore {
|
|
120
115
|
getRules(sessionId: string, principal?: string): Promise<StoredSessionRules | null>;
|
|
121
|
-
/** Write the rules for a key
|
|
122
|
-
* operator writes included), then, for a non-operator write over an existing row, the
|
|
123
|
-
* tighten-only check
|
|
124
|
-
* tools under. A bucket that is not a list of strings (a bare `"rm"` for `commandDeny`) throws a `TypeError` from
|
|
125
|
-
* {@link normalizeRules} before any of those is judged, and nothing is written. */
|
|
116
|
+
/** Write the rules for a key through the one write judge ({@link judgeSessionRulesWrite}): CAS on `expectedRev`, the
|
|
117
|
+
* shape, every entry's reading (operator writes included), then, for a non-operator write over an existing row, the
|
|
118
|
+
* tighten-only check — each refusal a {@link SessionPolicyError}, and nothing is written. */
|
|
126
119
|
putRules(sessionId: string, principal: string | undefined, rules: SessionPermissionRules, opts?: PutRulesOptions): Promise<StoredSessionRules>;
|
|
127
120
|
/**
|
|
128
121
|
* 2c session-sync: every (principal, rules) record for `sessionId` across ALL principals — `getRules`
|
|
@@ -172,21 +165,47 @@ export interface SessionPolicyStore {
|
|
|
172
165
|
* backend should keep `allowDirs` to real directories; a future symlink-aware write-time canonicalize closes it.
|
|
173
166
|
*/
|
|
174
167
|
declare function lexWithin(root: string, p: string): boolean;
|
|
175
|
-
/** Normalize a rules object's `allowDirs` (lexical `posix.normalize`) so stored rules are `..`-free and the
|
|
176
|
-
* tighten diff compares the same canonical-ish form the runtime resolves. Every bucket must be a list of strings
|
|
177
|
-
* ({@link ruleList}); a bucket that is not one throws `TypeError`, so neither bundled store (nor a durable twin that
|
|
178
|
-
* normalizes through this function) can store a rule list the matcher would read differently from the one written. */
|
|
179
|
-
export declare function normalizeRules(rules: SessionPermissionRules): SessionPermissionRules;
|
|
180
168
|
/**
|
|
181
|
-
*
|
|
182
|
-
*
|
|
183
|
-
*
|
|
184
|
-
*
|
|
185
|
-
*
|
|
186
|
-
*
|
|
187
|
-
*
|
|
169
|
+
* THE ONE SHAPE READING of a rules value — what every station that takes one reads it through: the write (incoming rules),
|
|
170
|
+
* a store's read-back of its own rows, the tighten comparison's prior, the run-time layer's served row. A rules value is an
|
|
171
|
+
* object whose keys are buckets ({@link SessionPermissionRules}); each bucket present is a list of strings (`allowDirs`: of
|
|
172
|
+
* non-blank strings — a directory is no blank). A bucket's TYPE is all this reads: what an entry means is the entry reader's.
|
|
173
|
+
* The store's own stamps (`rev`, `gen`) are passed over, and a key holding `undefined` holds nothing. Anything else is a fault,
|
|
174
|
+
* named by key and by TYPE, never by value — a bare string is never one rule and never its characters (`"rm"` spread into
|
|
175
|
+
* `["r","m"]`), and a key outside the buckets is never dropped (a dropped deny is a deny nobody enforces).
|
|
176
|
+
*/
|
|
177
|
+
export declare function readSessionRulesShape(value: unknown): {
|
|
178
|
+
rules: SessionPermissionRules;
|
|
179
|
+
} | {
|
|
180
|
+
fault: SessionRuleShapeRefusal;
|
|
181
|
+
sentence: string;
|
|
182
|
+
};
|
|
183
|
+
/** The CANONICAL stored form of a rules value: its shape read ({@link readSessionRulesShape} — a fault refuses with
|
|
184
|
+
* `invalid_shape`), every list copied (detached from the caller's arrays), and `allowDirs` lexically `posix.normalize`d so the
|
|
185
|
+
* stored form is `..`-free and the tighten diff compares the form the runtime resolves. Both bundled stores and a durable twin
|
|
186
|
+
* stamp exactly this, so a structuredClone and a JSON round-trip of it are byte-identical. */
|
|
187
|
+
export declare function normalizeRules(rules: unknown): SessionPermissionRules;
|
|
188
|
+
/**
|
|
189
|
+
* THE ONE WRITE JUDGE every store applies before it stamps a row (both bundled stores, and a durable backend at the same point):
|
|
190
|
+
* the rules to stamp, or a {@link SessionPolicyError}, in this order — `conflict` (a stale `expectedRev` against `prior`'s rev),
|
|
191
|
+
* `invalid_shape` ({@link normalizeRules}), `inert_entry` (an entry the reader reads as nothing; operator writes included — a
|
|
192
|
+
* rule that names nothing guards nothing whoever writes it), then, for a non-operator write over an existing row,
|
|
193
|
+
* `loosen_forbidden` ({@link loosenReasons}) — or `corrupt` when that prior cannot be read as rules, which the comparison never
|
|
194
|
+
* reads as fewer. `mountedToolNames` = the store's declaration ({@link SessionPolicyStoreOptions.mountedToolNames}).
|
|
195
|
+
*/
|
|
196
|
+
export declare function judgeSessionRulesWrite(prior: StoredSessionRules | null, rules: unknown, opts: PutRulesOptions | undefined, mountedToolNames?: ReadonlySet<string>): SessionPermissionRules;
|
|
197
|
+
/**
|
|
198
|
+
* Pure tighten-only diff: the reasons `next` LOOSENS `prior` (empty ⇒ `next` is a pure tightening or no-op), over the EFFECTIVE
|
|
199
|
+
* entries of each rule bucket — the list as the run-time layer enforces it ({@link effectiveEntries}). An entry that reads as
|
|
200
|
+
* nothing is in neither side's effect, so removing one relaxes nothing and adding one widens nothing (the way a session's owner
|
|
201
|
+
* takes out an entry that names nothing); an allowlist a `*` voids admits nothing, so dropping the `*` widens. toolDeny: removing an effective entry loosens. toolAllow: removing the list
|
|
202
|
+
* (→ everything allowed) or adding an effective entry outside the prior set loosens; prior-undefined → any defined next only
|
|
203
|
+
* narrows. allowDirs: removing confinement, or adding a dir not within some prior dir, loosens (coarse raw containment).
|
|
204
|
+
* Entries compare as written. Both sides are read through {@link readSessionRulesShape} first: a side it cannot read (a key that
|
|
205
|
+
* is no bucket — a retired one included) throws `invalid_shape` rather than compare without it. `mountedToolNames` = the store's
|
|
206
|
+
* declaration ({@link SessionPolicyStoreOptions.mountedToolNames}; empty when it has none) — the write judge's too.
|
|
188
207
|
*/
|
|
189
|
-
export declare function loosenReasons(
|
|
208
|
+
export declare function loosenReasons(priorValue: SessionPermissionRules, nextValue: SessionPermissionRules, mountedToolNames?: ReadonlySet<string>): string[];
|
|
190
209
|
/**
|
|
191
210
|
* Does row `a` belong to the SAME lineage as row `b` — the one question every reader asks before it compares
|
|
192
211
|
* two rows' `rev`, asked in ONE place so a `gen` that is ABSENT cannot mean three different things at three
|
|
@@ -205,8 +224,6 @@ export declare function continuesLineage(a: {
|
|
|
205
224
|
}, b: {
|
|
206
225
|
gen?: number;
|
|
207
226
|
}): boolean;
|
|
208
|
-
/** strip any caller-passed `rev` from a rules object so the store is the sole authority on it. */
|
|
209
|
-
export declare function stripRev(rules: SessionPermissionRules): SessionPermissionRules;
|
|
210
227
|
/**
|
|
211
228
|
* In-process default {@link SessionPolicyStore} (single-replica). A deployment that needs cross-replica
|
|
212
229
|
* durability supplies its own backend with the SAME CAS-rev + tighten-only semantics. Single-threaded JS makes
|