@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.
Files changed (200) hide show
  1. package/CHANGELOG.md +313 -0
  2. package/dist/agents/child-failure.d.ts +5 -16
  3. package/dist/agents/child-failure.js +4 -24
  4. package/dist/agents/subagent-steps.js +2 -2
  5. package/dist/agents/subagent.d.ts +4 -13
  6. package/dist/agents/subagent.js +94 -204
  7. package/dist/agents/tool-filter.d.ts +2 -2
  8. package/dist/brain/anthropic.d.ts +1 -1
  9. package/dist/brain/anthropic.js +7 -5
  10. package/dist/brain/open-responses.d.ts +1 -1
  11. package/dist/brain/open-responses.js +3 -2
  12. package/dist/brain/openai.d.ts +1 -1
  13. package/dist/brain/openai.js +3 -2
  14. package/dist/brain/request-route.d.ts +2 -2
  15. package/dist/brain/route-adjudicator.d.ts +16 -7
  16. package/dist/brain/route-adjudicator.js +23 -7
  17. package/dist/brain/route-conformance.js +23 -5
  18. package/dist/brain/stream-engine.js +9 -3
  19. package/dist/core/a2a.js +6 -8
  20. package/dist/core/allow-entry.d.ts +45 -26
  21. package/dist/core/allow-entry.js +48 -24
  22. package/dist/core/arg-summary.d.ts +8 -85
  23. package/dist/core/arg-summary.js +4 -386
  24. package/dist/core/ask-origin.d.ts +35 -33
  25. package/dist/core/ask-origin.js +9 -11
  26. package/dist/core/checkpoint-requirements.d.ts +6 -0
  27. package/dist/core/checkpoint-requirements.js +1 -0
  28. package/dist/core/checkpoint-store.d.ts +15 -34
  29. package/dist/core/checkpoint-store.js +5 -4
  30. package/dist/core/classifier-unavailable-arm.d.ts +12 -8
  31. package/dist/core/classifier-unavailable-arm.js +7 -2
  32. package/dist/core/credential-vocabulary.d.ts +3 -2
  33. package/dist/core/credential-vocabulary.js +27 -18
  34. package/dist/core/delegation-frames.d.ts +40 -15
  35. package/dist/core/delegation-frames.js +34 -0
  36. package/dist/core/engine-capabilities.d.ts +15 -0
  37. package/dist/core/engine-capabilities.js +1 -0
  38. package/dist/core/engine-notice.d.ts +41 -14
  39. package/dist/core/gate-fold.js +9 -7
  40. package/dist/core/gate-lanes.js +6 -4
  41. package/dist/core/gate-outcome.d.ts +81 -15
  42. package/dist/core/gate-outcome.js +168 -67
  43. package/dist/core/governance-codes.d.ts +7 -1
  44. package/dist/core/governance-codes.js +9 -0
  45. package/dist/core/hooks.d.ts +4 -4
  46. package/dist/core/hooks.js +4 -3
  47. package/dist/core/mcp-connection-pool.d.ts +2 -0
  48. package/dist/core/mcp-connection-pool.js +2 -1
  49. package/dist/core/mcp-failure.js +2 -1
  50. package/dist/core/mcp-server-spec.d.ts +27 -5
  51. package/dist/core/mcp.d.ts +13 -38
  52. package/dist/core/mcp.js +28 -23
  53. package/dist/core/media-byte-cap.d.ts +25 -0
  54. package/dist/core/media-byte-cap.js +18 -2
  55. package/dist/core/memory-engine/consolidation-driver.js +1 -1
  56. package/dist/core/mode-policy.d.ts +25 -1
  57. package/dist/core/mode-policy.js +28 -13
  58. package/dist/core/own-property-receipt.d.ts +18 -0
  59. package/dist/core/own-property-receipt.js +39 -0
  60. package/dist/core/permission-rule-model.d.ts +32 -11
  61. package/dist/core/permission-rule-model.js +12 -7
  62. package/dist/core/persisted-rule-arms.d.ts +3 -3
  63. package/dist/core/prompt-section-seat.d.ts +54 -0
  64. package/dist/core/prompt-section-seat.js +49 -0
  65. package/dist/core/protocol-http-auth.d.ts +44 -0
  66. package/dist/core/protocol-http-auth.js +45 -0
  67. package/dist/core/read-face-seats.d.ts +30 -0
  68. package/dist/core/read-face-seats.js +38 -0
  69. package/dist/core/redaction-passes.d.ts +82 -0
  70. package/dist/core/redaction-passes.js +378 -0
  71. package/dist/core/remote-env.d.ts +4 -3
  72. package/dist/core/resume-startup-cause.d.ts +56 -0
  73. package/dist/core/resume-startup-cause.js +43 -0
  74. package/dist/core/rule-denial-sentence.d.ts +2 -2
  75. package/dist/core/runner/announce-once-ledger.js +2 -2
  76. package/dist/core/runner/classifier-ladder.d.ts +17 -13
  77. package/dist/core/runner/classifier-ladder.js +12 -10
  78. package/dist/core/runner/construction-doors.js +9 -20
  79. package/dist/core/runner/contracts.d.ts +4 -4
  80. package/dist/core/runner/gate-exit.js +2 -2
  81. package/dist/core/runner/memory-consolidation-write-judge.d.ts +53 -9
  82. package/dist/core/runner/memory-consolidation-write-judge.js +39 -1
  83. package/dist/core/runner/permission-rule-lanes.d.ts +8 -13
  84. package/dist/core/runner/person-path-rows.d.ts +4 -3
  85. package/dist/core/runner/policy-throw.js +4 -4
  86. package/dist/core/runner/prepare-caps-and-workflow.d.ts +4 -3
  87. package/dist/core/runner/prepare-caps-and-workflow.js +8 -6
  88. package/dist/core/runner/prepare-gate-stations.js +4 -4
  89. package/dist/core/runner/prepare-hands-readface.js +1 -19
  90. package/dist/core/runner/prepare-inherited-gate.js +10 -9
  91. package/dist/core/runner/prepare-policy-chain.d.ts +3 -5
  92. package/dist/core/runner/prepare-policy-chain.js +10 -13
  93. package/dist/core/runner/prepare-project-context.d.ts +4 -4
  94. package/dist/core/runner/prepare-project-context.js +2 -1
  95. package/dist/core/runner/prepare-prompt-assembly.js +1 -0
  96. package/dist/core/runner/prepare-prompt-inputs.d.ts +1 -1
  97. package/dist/core/runner/prepare-prompt-inputs.js +4 -4
  98. package/dist/core/runner/prepare-protocol-tools.d.ts +4 -3
  99. package/dist/core/runner/prepare-protocol-tools.js +3 -2
  100. package/dist/core/runner/prepare-task.js +6 -4
  101. package/dist/core/runner/prepare-turn-wiring.d.ts +16 -13
  102. package/dist/core/runner/prepare-turn-wiring.js +8 -7
  103. package/dist/core/runner/run-harness-handlers.js +2 -2
  104. package/dist/core/runner/run-leg.js +10 -9
  105. package/dist/core/runner/run-settle-and-teardown.js +5 -5
  106. package/dist/core/runner/run-turn-boundary.js +3 -2
  107. package/dist/core/runner/runtask.d.ts +1 -2
  108. package/dist/core/runner/runtask.js +8 -12
  109. package/dist/core/runner/session-rule-policy.d.ts +15 -11
  110. package/dist/core/runner/session-rule-policy.js +43 -33
  111. package/dist/core/runner/stream-lifecycle-verbs.js +2 -2
  112. package/dist/core/runner/stream-settle-backstop.js +2 -1
  113. package/dist/core/runner/sub-agent-deny-lists.d.ts +12 -8
  114. package/dist/core/runner/sub-agent-deny-lists.js +23 -12
  115. package/dist/core/runner/terminal-account.js +3 -3
  116. package/dist/core/runner/tool-output-projection.d.ts +5 -0
  117. package/dist/core/runner/tool-output-projection.js +6 -11
  118. package/dist/core/runner/tool-result-media-seat.d.ts +19 -0
  119. package/dist/core/runner/tool-result-media-seat.js +13 -0
  120. package/dist/core/runner/turn-attachments.d.ts +13 -12
  121. package/dist/core/runner/turn-attachments.js +11 -7
  122. package/dist/core/runner-deps.d.ts +4 -3
  123. package/dist/core/safety-merge-corpus.js +21 -21
  124. package/dist/core/secret-env.d.ts +41 -15
  125. package/dist/core/secret-env.js +85 -19
  126. package/dist/core/session-policy-store.d.ts +89 -72
  127. package/dist/core/session-policy-store.js +100 -93
  128. package/dist/core/shell-read-silence.d.ts +38 -4
  129. package/dist/core/shell-read-silence.js +102 -0
  130. package/dist/core/side-query.d.ts +7 -8
  131. package/dist/core/skill-grant-binding.d.ts +32 -11
  132. package/dist/core/skill-grant-binding.js +11 -3
  133. package/dist/core/skill-grant.d.ts +4 -3
  134. package/dist/core/skill-grant.js +5 -4
  135. package/dist/core/skills-directory.d.ts +12 -1
  136. package/dist/core/skills-directory.js +17 -8
  137. package/dist/core/spec-contract.d.ts +5 -3
  138. package/dist/core/store-contracts/checkpoint-store-contract.d.ts +2 -0
  139. package/dist/core/store-contracts/checkpoint-store-contract.js +3 -0
  140. package/dist/core/swappable-deps.d.ts +15 -1
  141. package/dist/core/swappable-deps.js +25 -1
  142. package/dist/core/synthetic-read-verdict.d.ts +16 -14
  143. package/dist/core/synthetic-read-verdict.js +28 -8
  144. package/dist/core/task-notification.d.ts +13 -3
  145. package/dist/core/task-notification.js +12 -1
  146. package/dist/core/task-spec.d.ts +14 -15
  147. package/dist/core/tool-name-readings.d.ts +0 -4
  148. package/dist/core/tool-name-readings.js +0 -1
  149. package/dist/core/tool-policy.d.ts +40 -9
  150. package/dist/core/tool-policy.js +25 -7
  151. package/dist/core/types.d.ts +3 -3
  152. package/dist/core/types.js +1 -1
  153. package/dist/core/untrusted-egress.d.ts +60 -11
  154. package/dist/core/untrusted-egress.js +120 -9
  155. package/dist/core/untrusted-text.js +16 -0
  156. package/dist/core/wiring-manifest.d.ts +22 -8
  157. package/dist/core/wiring-manifest.js +12 -4
  158. package/dist/engine/execution-env/kill-tree.js +4 -4
  159. package/dist/engine/execution-env/node-execution-env.d.ts +11 -4
  160. package/dist/engine/execution-env/node-execution-env.js +9 -4
  161. package/dist/engine/harness/agent-harness.d.ts +2 -1
  162. package/dist/engine/harness/agent-harness.js +4 -1
  163. package/dist/engine/harness/types.d.ts +3 -0
  164. package/dist/engine/llm/types.d.ts +7 -9
  165. package/dist/engine/loop/agent-loop.d.ts +8 -3
  166. package/dist/engine/loop/agent-loop.js +26 -9
  167. package/dist/engine/loop/types.d.ts +10 -0
  168. package/dist/engine/lsp/node-lsp-manager.d.ts +2 -1
  169. package/dist/engine/lsp/node-lsp-manager.js +2 -2
  170. package/dist/index.d.ts +22 -5
  171. package/dist/index.js +18 -5
  172. package/dist/orchestration/run-workflow-tool.js +8 -8
  173. package/dist/orchestration/workflow.js +2 -2
  174. package/dist/prompt-assembly/assemble.d.ts +3 -1
  175. package/dist/prompt-assembly/assemble.js +6 -0
  176. package/dist/prompts/cowork.d.ts +42 -0
  177. package/dist/prompts/cowork.js +529 -0
  178. package/dist/prompts/default.d.ts +14 -1
  179. package/dist/prompts/default.js +3 -0
  180. package/dist/stores/file/fs-atomic.js +2 -2
  181. package/dist/stores/file/session-policy-store.d.ts +6 -3
  182. package/dist/stores/file/session-policy-store.js +5 -16
  183. package/dist/tools/artifact/artifact-tool.js +2 -0
  184. package/dist/tools/fs/image-admission.d.ts +57 -4
  185. package/dist/tools/fs/image-admission.js +85 -0
  186. package/dist/tools/fs/path-containment.d.ts +2 -0
  187. package/dist/tools/fs/path-containment.js +5 -0
  188. package/dist/tools/fs/shell-read-check.d.ts +31 -6
  189. package/dist/tools/fs/shell-read-check.js +2 -4
  190. package/dist/tools/fs/shell-read-environment.d.ts +10 -25
  191. package/dist/tools/fs/shell-read-environment.js +6 -18
  192. package/dist/tools/fs/shell-read-establish.d.ts +3 -2
  193. package/dist/tools/fs/shell-read-establish.js +5 -5
  194. package/dist/tools/fs/shell-read-judge.d.ts +38 -8
  195. package/dist/tools/fs/shell-read-judge.js +12 -10
  196. package/dist/tools/web.js +2 -2
  197. package/package.json +1 -1
  198. package/test/export-surface.snapshot.json +112 -10
  199. package/wire-changes/core@7.51.0.json +2421 -0
  200. 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}). One judgment per variable ({@link classifySecretEnvEntry}), the first rule that answers:
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
- export type SecretEnvFindingKind = "exact-name" | "name-rule" | "namespace-rule" | "prefix-rule" | "value-rule";
30
- /** One env entry the `"scrub"` policy withheld (deleted): its name — never its value, so the record is loggable —, the
31
- * rule that withheld it, how strong that claim is, and the minting module. */
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
- * The environment a child spawned ON THIS HOST inherits, by an {@link InheritEnvPolicy}: `"scrub"` (and absent) is
53
- * this process's environment through {@link scrubSecretEnv}; `"all"` is this process's environment
54
- * verbatim (the explicit opt-out); a `string[]` is exactly the named variables that are set, verbatim — a strict allow
55
- * list, so a name it leaves out (`PATH` included) is not inherited. `findings` collects what the scrub withheld, and
56
- * only the `"scrub"` arm adds to it. Any other value reads as `"scrub"`; both spawn sites that take a policy — the
57
- * tool shell's `NodeExecutionEnv` (at construction) and the oracle gate (per gate) — refuse a malformed one through
58
- * {@link inheritEnvPolicyProblem} before it gets here.
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 `undefined` when it is. */
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. Once per PROCESS, never per call or per site; an EMPTY
70
- * findings array is a no-op.
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;
@@ -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_(?:.*_)?(?:KEY|KEYS|TOKEN|${ENV_NAMES.countedPlural}|SECRET|SECRETS|PASSWORD|PASSWORDS|PASSWD|CREDENTIAL|CREDENTIALS|PRIVATEKEY|APIKEY)(?:_|$)`, "is");
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
- return { ...process.env };
137
- if (Array.isArray(policy)) {
138
- const picked = {};
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
- picked[k] = process.env[k];
142
- return picked;
208
+ arm[k] = process.env[k];
143
209
  }
144
- return scrubSecretEnv(process.env, findings);
210
+ else
211
+ arm = scrubSecretEnv(process.env, findings);
212
+ return withholdOwnCredentials(arm, findings);
145
213
  }
146
214
  export function inheritEnvPolicyProblem(value) {
147
- if (value === undefined || value === "all" || value === "scrub")
148
- return undefined;
149
- if (Array.isArray(value)) {
150
- const bad = value.findIndex((k) => typeof k !== "string" || k.length === 0 || /[=\0]/.test(k));
151
- if (bad === -1)
152
- return undefined;
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
- * - **structured fields only**: core interprets `toolAllow/Deny`, `allowDirs`, `commandAllow/Deny` (argv[0]
18
- * names). A richer rule DSL is the service's/profile's job — core grows no rule interpreter.
19
- * - **NOT A SANDBOX**: command rules inherit the coarse argv[0] matching (wrappers like `sh -c`/`sudo` circumvention);
20
- * the real isolation boundary is the executionEnv, never these rules.
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 RuleToolNameVerdict } from "./tool-name-aliases.js";
23
- /** The structured per-session rules. All optional; an absent field imposes no constraint of that kind. */
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 tool names are allowed (others denied). A narrowing allowlist. */
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
- /** Tool names always denied (deny wins). */
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 a tool-name refusal: which bucket, which entry, the judgment's word, the judgment's
56
- * one-line sentence when it has one, and the spelling to write instead when the judgment has one (a retired name's 1:1
57
- * successor, an unmintable segment's minted name). */
58
- export interface SessionRuleNameRefusal {
59
- field: SessionRuleToolNameField;
60
- name: string;
61
- verdict: Exclude<RuleToolNameVerdict["kind"], "current">;
62
- /** The `guidance` of the judgment ({@link judgeRuleToolName}), byte for byte — the same string the refusal's sentence quotes. Present for
63
- * `retired` and `unmintable_segment`, the two verdicts that carry one; absent for `unprefixed_namespace`, whose judgment has no sentence of its own. */
64
- guidance?: string;
65
- currentName?: string;
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 erase-surviving
69
- * rev mark ({@link SessionPolicyStore.deleteBySession}) and refuses rather than stamp an unproven rev; the fourth
70
- * code = a tool-name bucket names a retired name, a prefix-less `__` name or a segment no mint spells (an empty one, or a peer spelled otherwise)
71
- * ({@link assertSessionRuleNamesCurrent}) — `detail` is present on that code and only on it. */
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" | "legacy_tool_name";
74
- readonly detail?: SessionRuleNameRefusal | undefined;
75
- constructor(code: "conflict" | "loosen_forbidden" | "corrupt" | "legacy_tool_name", message: string, detail?: SessionRuleNameRefusal | undefined);
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 judges a rule's tool names against
91
- * ({@link judgeRuleToolName}): a retired name declared here is an ordinary name to the write and to the loosening
92
- * check alike. Absent = none. A name carrying `__` is refused, as it is for a deployment tool. */
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: CAS on `expectedRev`, then the name judge ({@link assertSessionRuleNamesCurrent} —
122
- * operator writes included), then, for a non-operator write over an existing row, the
123
- * tighten-only check ({@link loosenReasons}) — both against the names the deployment declares it mounts its own
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
- * Pure tighten-only diff: the reasons `next` LOOSENS `prior` (empty ⇒ `next` is a pure tightening or no-op).
182
- * Direction per field — deny-lists: removing an entry loosens, except a `toolDeny` entry that is not `current` against
183
- * `mountedToolNames` ({@link judgeRuleToolName}: it matches no tool, so removing it relaxes nothing — the way a session's owner
184
- * takes out a stale entry that stops every run). allowlists: removing the list (→ everything allowed) or adding an
185
- * entry outside the prior set loosens; prior-undefined → any defined next only narrows. allowDirs: removing
186
- * confinement, or adding a dir not within some prior dir, loosens (coarse raw containment). `mountedToolNames` = the
187
- * store's declaration ({@link SessionPolicyStoreOptions.mountedToolNames}; empty when it has none) — the write judge's too.
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(prior: SessionPermissionRules, next: SessionPermissionRules, mountedToolNames?: ReadonlySet<string>): string[];
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