@sema-agent/server 7.50.0 → 7.52.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (222) hide show
  1. package/README.md +1 -1
  2. package/README.zh-CN.md +1 -1
  3. package/USAGE.md +83 -20
  4. package/dist/adoption/plan.d.ts +4 -4
  5. package/dist/adoption/plan.js +11 -0
  6. package/dist/approval-card.d.ts +26 -2
  7. package/dist/approval-card.js +9 -0
  8. package/dist/approval-hmac.d.ts +1 -1
  9. package/dist/approval-reconciler.d.ts +2 -2
  10. package/dist/approval-reconciler.js +1 -1
  11. package/dist/bench/l8/artifact.d.ts +1 -1
  12. package/dist/bench/s1/arms.d.ts +1 -1
  13. package/dist/bench/s1/live-deps.d.ts +1 -1
  14. package/dist/bench/s1/oracle.d.ts +1 -1
  15. package/dist/bench/s1/row.d.ts +1 -1
  16. package/dist/boot/budget-tracing.d.ts +1 -1
  17. package/dist/boot/budget-tracing.js +2 -5
  18. package/dist/boot/coordinators.js +3 -1
  19. package/dist/boot/deferred-sandbox-path-env.d.ts +1 -1
  20. package/dist/boot/device-lane.d.ts +79 -0
  21. package/dist/boot/device-lane.js +63 -0
  22. package/dist/boot/engine-lease.d.ts +1 -1
  23. package/dist/boot/execution-env.d.ts +48 -2
  24. package/dist/boot/execution-env.js +57 -5
  25. package/dist/boot/governance-seams.d.ts +2 -2
  26. package/dist/boot/leader.d.ts +1 -1
  27. package/dist/boot/memory-boundary.d.ts +2 -2
  28. package/dist/boot/memory-consolidation.d.ts +6 -6
  29. package/dist/boot/org-memory.d.ts +1 -1
  30. package/dist/boot/parked-revive-gate.d.ts +3 -3
  31. package/dist/boot/reapers.d.ts +2 -2
  32. package/dist/boot/reapers.js +17 -4
  33. package/dist/boot/resolve-spec.d.ts +15 -0
  34. package/dist/boot/resolve-spec.js +40 -15
  35. package/dist/boot/retention-lane.d.ts +2 -2
  36. package/dist/boot/runner-deps.d.ts +17 -7
  37. package/dist/boot/runner-deps.js +4 -2
  38. package/dist/boot/runtime-caps.d.ts +23 -0
  39. package/dist/boot/runtime-caps.js +24 -2
  40. package/dist/boot/session-faces.d.ts +6 -1
  41. package/dist/boot/session-faces.js +17 -3
  42. package/dist/boot/shutdown.d.ts +8 -0
  43. package/dist/boot/shutdown.js +3 -1
  44. package/dist/boot/stores.js +40 -1
  45. package/dist/boot/task-list-lane.d.ts +2 -2
  46. package/dist/brain.js +51 -31
  47. package/dist/budget.d.ts +2 -2
  48. package/dist/capabilities/center-plugins.d.ts +1 -1
  49. package/dist/capabilities/collab-workflows.d.ts +1 -1
  50. package/dist/capabilities/hands-lane.d.ts +3 -3
  51. package/dist/capabilities/scenarios.d.ts +3 -3
  52. package/dist/capabilities/skills.d.ts +1 -1
  53. package/dist/config-center/hot-keys-registry.js +5 -0
  54. package/dist/config-center/http-client.d.ts +2 -2
  55. package/dist/config-center/read-face.d.ts +1 -1
  56. package/dist/config-center/restart-signal.d.ts +1 -1
  57. package/dist/config-center/types.d.ts +10 -3
  58. package/dist/config-invariants.d.ts +2 -2
  59. package/dist/config-invariants.js +18 -0
  60. package/dist/config-types.d.ts +90 -18
  61. package/dist/config.js +91 -19
  62. package/dist/deployment-governance.d.ts +1 -1
  63. package/dist/device-enrollment.d.ts +125 -0
  64. package/dist/device-enrollment.js +156 -0
  65. package/dist/device-store.d.ts +385 -0
  66. package/dist/device-store.js +407 -0
  67. package/dist/device-ws-hub.d.ts +182 -0
  68. package/dist/device-ws-hub.js +1012 -0
  69. package/dist/device-ws-protocol.d.ts +429 -0
  70. package/dist/device-ws-protocol.js +464 -0
  71. package/dist/env-facts.d.ts +5 -2
  72. package/dist/env-facts.js +1 -0
  73. package/dist/execution-lane-caps.d.ts +152 -0
  74. package/dist/execution-lane-caps.js +166 -0
  75. package/dist/fleet/fleet-bus.d.ts +1 -1
  76. package/dist/fleet/fleet-reconciler.d.ts +1 -1
  77. package/dist/fleet/fleet-terminal-window.d.ts +1 -1
  78. package/dist/http/active-run-conflict.d.ts +2 -2
  79. package/dist/http/principal-gate.d.ts +1 -1
  80. package/dist/http/route-ctx.d.ts +24 -4
  81. package/dist/http/routes/admin-memory-optout.d.ts +48 -0
  82. package/dist/http/routes/admin-memory-optout.js +75 -0
  83. package/dist/http/routes/agents-roster.d.ts +1 -1
  84. package/dist/http/routes/approvals-assistant.js +21 -21
  85. package/dist/http/routes/capabilities.js +27 -11
  86. package/dist/http/routes/diagnostics.d.ts +1 -1
  87. package/dist/http/routes/leader.d.ts +1 -1
  88. package/dist/http/routes/memory-bundle.d.ts +1 -1
  89. package/dist/http/routes/memory-bundle.js +3 -1
  90. package/dist/http/routes/memory-compliance.d.ts +4 -4
  91. package/dist/http/routes/memory-origin.d.ts +4 -4
  92. package/dist/http/routes/memory-origin.js +3 -1
  93. package/dist/http/routes/memory-policy.js +6 -2
  94. package/dist/http/routes/retention-ops.d.ts +1 -1
  95. package/dist/http/routes/rules.d.ts +5 -2
  96. package/dist/http/routes/rules.js +84 -5
  97. package/dist/http/routes/run-memory-capture.d.ts +34 -0
  98. package/dist/http/routes/run-memory-capture.js +97 -0
  99. package/dist/http/routes/runs.d.ts +1 -0
  100. package/dist/http/routes/runs.js +212 -6
  101. package/dist/http/routes/session-sync.d.ts +4 -6
  102. package/dist/http/routes/session-sync.js +60 -47
  103. package/dist/http/routes/sessions.js +61 -188
  104. package/dist/http/routes/shared-memory.d.ts +1 -1
  105. package/dist/http/routes/tasks.js +1 -1
  106. package/dist/http/routes/workflows.js +19 -0
  107. package/dist/http/server.d.ts +49 -13
  108. package/dist/http/server.js +109 -13
  109. package/dist/http/verify-rounds.d.ts +1 -1
  110. package/dist/http/wire-types.d.ts +41 -8
  111. package/dist/leader/endpoint.d.ts +1 -1
  112. package/dist/leader/leader.d.ts +1 -1
  113. package/dist/leader/repair-oracle.d.ts +2 -2
  114. package/dist/leader/repair-oracle.js +1 -1
  115. package/dist/leader/wire.js +4 -2
  116. package/dist/main.js +23 -9
  117. package/dist/memory-bundle-engine.d.ts +1 -1
  118. package/dist/memory-operator-faces.d.ts +5 -5
  119. package/dist/memory-origin-fence.d.ts +50 -0
  120. package/dist/memory-origin-fence.js +15 -0
  121. package/dist/memory-posture.d.ts +13 -1
  122. package/dist/memory-posture.js +1 -0
  123. package/dist/memory-scope.d.ts +6 -1
  124. package/dist/memory-scope.js +3 -2
  125. package/dist/model-select.d.ts +2 -2
  126. package/dist/observability/fail-open.d.ts +17 -5
  127. package/dist/observability/fail-open.js +17 -5
  128. package/dist/orchestration/hardened-vm-runner.d.ts +9 -2
  129. package/dist/orchestration/hardened-vm-runner.js +11 -1
  130. package/dist/orchestration/hardened-vm-worker-runner.js +2 -2
  131. package/dist/orchestration/workflow-completion-inbox.d.ts +2 -2
  132. package/dist/plugins/background-shell-support.d.ts +2 -2
  133. package/dist/plugins/blob-backend.d.ts +7 -2
  134. package/dist/plugins/blob-backend.js +22 -18
  135. package/dist/plugins/caching-session-store.d.ts +7 -3
  136. package/dist/plugins/checkpoint-store-sql.d.ts +2 -2
  137. package/dist/plugins/device-store-sql.d.ts +130 -0
  138. package/dist/plugins/device-store-sql.js +574 -0
  139. package/dist/plugins/file-history-store-sql.d.ts +170 -0
  140. package/dist/plugins/file-history-store-sql.js +827 -0
  141. package/dist/plugins/file-run-store.d.ts +44 -5
  142. package/dist/plugins/file-run-store.js +85 -13
  143. package/dist/plugins/fork-routing-session-store.d.ts +1 -1
  144. package/dist/plugins/fork-routing-session-store.js +2 -2
  145. package/dist/plugins/leader-run-store-sql.d.ts +1 -1
  146. package/dist/plugins/local-session-store.d.ts +9 -1
  147. package/dist/plugins/memory-embedder-fingerprint.d.ts +1 -1
  148. package/dist/plugins/memory-optout-grant-store-sql.d.ts +139 -0
  149. package/dist/plugins/memory-optout-grant-store-sql.js +157 -0
  150. package/dist/plugins/memory-origin-law.d.ts +2 -2
  151. package/dist/plugins/memory-run-store.d.ts +4 -3
  152. package/dist/plugins/memory-run-store.js +2 -1
  153. package/dist/plugins/permission-rule-store-file.d.ts +16 -6
  154. package/dist/plugins/permission-rule-store-file.js +77 -13
  155. package/dist/plugins/permission-rule-store-sql.d.ts +156 -8
  156. package/dist/plugins/permission-rule-store-sql.js +123 -21
  157. package/dist/plugins/pg-pool.js +59 -0
  158. package/dist/plugins/pg-safe-json.d.ts +1 -1
  159. package/dist/plugins/remote-env-device.d.ts +271 -0
  160. package/dist/plugins/remote-env-device.js +727 -0
  161. package/dist/plugins/remote-env-k8s.d.ts +1 -1
  162. package/dist/plugins/remote-scratchpad.js +1 -1
  163. package/dist/plugins/retention-lane-store-sql.d.ts +3 -3
  164. package/dist/plugins/retention-store-sql.d.ts +7 -7
  165. package/dist/plugins/retention-store-sql.js +6 -0
  166. package/dist/plugins/run-store-sql.d.ts +9 -3
  167. package/dist/plugins/run-store-sql.js +2 -2
  168. package/dist/plugins/send-user-file.d.ts +1 -1
  169. package/dist/plugins/session-placement.d.ts +1 -1
  170. package/dist/plugins/sql-errors.d.ts +1 -1
  171. package/dist/plugins/store-backend.d.ts +68 -29
  172. package/dist/plugins/store-backend.js +78 -15
  173. package/dist/plugins/store-contracts.d.ts +5 -3
  174. package/dist/plugins/task-list-store-sql.d.ts +1 -1
  175. package/dist/plugins/tidb-pool.js +59 -0
  176. package/dist/plugins/web-search.d.ts +1 -1
  177. package/dist/prompts-domain-validate.js +1 -1
  178. package/dist/resource-suspend.d.ts +1 -1
  179. package/dist/resource-window.d.ts +3 -3
  180. package/dist/rules-consent.d.ts +231 -9
  181. package/dist/rules-consent.js +411 -13
  182. package/dist/run-local.js +1 -0
  183. package/dist/runs.d.ts +15 -1
  184. package/dist/runs.js +19 -0
  185. package/dist/runtime-caps-resolver.d.ts +36 -1
  186. package/dist/runtime-caps-resolver.js +25 -0
  187. package/dist/runtime-governance.d.ts +1 -1
  188. package/dist/security.d.ts +5 -5
  189. package/dist/security.js +1 -1
  190. package/dist/session-sync.d.ts +52 -37
  191. package/dist/session-sync.js +19 -16
  192. package/dist/shared-memory-scope-authorizer.d.ts +1 -1
  193. package/dist/sighup-idle.d.ts +1 -1
  194. package/dist/spec-fields.d.ts +22 -1
  195. package/dist/spec-fields.js +11 -0
  196. package/dist/store-live-probe.d.ts +2 -2
  197. package/dist/task-a2a.d.ts +1 -1
  198. package/dist/task-cwd.d.ts +25 -0
  199. package/dist/task-cwd.js +3 -0
  200. package/dist/task-mcp.d.ts +3 -3
  201. package/dist/task-settings.d.ts +3 -3
  202. package/dist/task-settings.js +3 -1
  203. package/dist/task-workflow.d.ts +1 -1
  204. package/dist/text-bidi.d.ts +2 -2
  205. package/dist/tool-approval.d.ts +191 -9
  206. package/dist/tool-approval.js +248 -29
  207. package/dist/trace/core-keyset-guard.d.ts +1 -1
  208. package/dist/trace/engine-notice-wire.d.ts +6 -4
  209. package/dist/trace/engine-notice-wire.js +2 -0
  210. package/dist/trace/ledger-events.d.ts +1 -1
  211. package/dist/trace/ledger-sink.d.ts +2 -2
  212. package/dist/trace/project.d.ts +3 -2
  213. package/dist/trace/project.js +1 -0
  214. package/dist/turn-activity.d.ts +1 -1
  215. package/dist/wall-clock-jump-guard.d.ts +3 -3
  216. package/package.json +2 -2
  217. package/dist/http/tar.d.ts +0 -17
  218. package/dist/http/tar.js +0 -53
  219. package/dist/http/workspace-content.d.ts +0 -11
  220. package/dist/http/workspace-content.js +0 -20
  221. package/dist/plugins/file-snapshot-store-sql.d.ts +0 -170
  222. package/dist/plugins/file-snapshot-store-sql.js +0 -213
package/dist/brain.js CHANGED
@@ -1,8 +1,6 @@
1
- import { createAnthropicBrain, createCircuitBreakerBrain, createDegradingBrain, createFailoverBrain, createOpenAIBrain, createRoutingBrain, } from "@sema-agent/core";
1
+ import { adjudicateModelRoute, createAnthropicBrain, createCircuitBreakerBrain, createDegradingBrain, createFailoverBrain, createOpenAIBrain, createRoutingBrain, hasAuthCarrier, resolveRouteCredential, routeRefusalText, } from "@sema-agent/core";
2
2
  import { normalizeBaseUrl } from "@sema-agent/core";
3
3
  import { resolveModelApiKey } from "./key-resolver.js";
4
- import { isSealedKeyPoison } from "./sealed-key.js";
5
- import { recordFailOpen } from "./observability/fail-open.js";
6
4
  function isOffDeploymentRoot(baseUrl, config) {
7
5
  if (!baseUrl)
8
6
  return false;
@@ -11,6 +9,10 @@ function isOffDeploymentRoot(baseUrl, config) {
11
9
  return false;
12
10
  return ![config.gatewayBaseUrl, ...config.gatewayFallbackUrls].some((u) => normalizeBaseUrl(u) === entry);
13
11
  }
12
+ function degradeDeclaredRoot(fallbackBaseUrl, config) {
13
+ const armed = Boolean(config.gatewayApiKey) && isOffDeploymentRoot(fallbackBaseUrl, config);
14
+ return armed ? config.gatewayBaseUrl : (fallbackBaseUrl || config.gatewayBaseUrl);
15
+ }
14
16
  export const FAST_FAIL_MAX_RETRIES = 2;
15
17
  const DEFAULT_RESILIENCE = {
16
18
  connectTimeoutMs: 30_000,
@@ -61,7 +63,7 @@ export function createBrain(config, deps = {}) {
61
63
  ...(config.anthropic.authToken
62
64
  ? { headers: { Authorization: `Bearer ${config.anthropic.authToken}` } }
63
65
  : { apiKey: config.anthropic.apiKey }),
64
- baseUrl: config.anthropic.baseUrl,
66
+ baseUrl: config.anthropic.baseUrl ?? "https://api.anthropic.com",
65
67
  version: config.anthropic.version,
66
68
  cacheBreakpoints: config.anthropic.cacheBreakpoints,
67
69
  ...(config.anthropic.maxRetries !== undefined ? { maxRetries: config.anthropic.maxRetries } : {}),
@@ -76,44 +78,42 @@ export function createBrain(config, deps = {}) {
76
78
  const degradeTo = config.degrade.to;
77
79
  const fallbackModel = config.models[degradeTo];
78
80
  if (fallbackModel) {
79
- const fbApiKey = () => {
80
- const own = resolveModelApiKey(fallbackModel.name, config.modelApiKeyEnv ?? {}, process.env, config.modelApiKeys ?? {}) ??
81
- resolveModelApiKey(degradeTo, config.modelApiKeyEnv ?? {}, process.env, config.modelApiKeys ?? {});
82
- return own ?? config.gatewayApiKey;
83
- };
84
- const hopBorrowsGatewayKey = () => {
85
- const keys = config.modelApiKeys ?? {};
86
- if (isSealedKeyPoison(keys[fallbackModel.name]) || isSealedKeyPoison(keys[degradeTo]))
87
- return false;
88
- const own = resolveModelApiKey(fallbackModel.name, config.modelApiKeyEnv ?? {}, process.env, keys) ??
89
- resolveModelApiKey(degradeTo, config.modelApiKeyEnv ?? {}, process.env, keys);
90
- return own === undefined && config.gatewayApiKey !== undefined && isOffDeploymentRoot(fallbackModel.baseUrl, config);
91
- };
81
+ const fbOwnApiKey = () => resolveModelApiKey(fallbackModel.name, config.modelApiKeyEnv ?? {}, process.env, config.modelApiKeys ?? {}) ??
82
+ resolveModelApiKey(degradeTo, config.modelApiKeyEnv ?? {}, process.env, config.modelApiKeys ?? {});
92
83
  const anthropicLaneHop = fallbackModel.provider === "anthropic" && anthropicBrain !== undefined;
93
84
  const fallback = fallbackModel.provider === "anthropic" && anthropicBrain
94
85
  ?
95
86
  anthropicBrain
96
87
  : createOpenAIBrain({
97
- baseUrl: fallbackModel.baseUrl || config.gatewayBaseUrl,
88
+ get baseUrl() {
89
+ return degradeDeclaredRoot(fallbackModel.baseUrl, config);
90
+ },
98
91
  get apiKey() {
99
- return fbApiKey();
92
+ return config.gatewayApiKey || undefined;
100
93
  },
101
94
  ...(config.gatewayMaxRetries !== undefined ? { maxRetries: config.gatewayMaxRetries } : {}),
102
95
  fetchImpl,
103
96
  ...timeouts,
104
97
  });
105
- const fallbackWithDebtProbe = anthropicLaneHop
106
- ? fallback
107
- : {
108
- stream: (m, c, o) => {
109
- if (hopBorrowsGatewayKey())
110
- recordFailOpen("server.model-key.degrade-hop-borrows-gateway-key", fallbackModel.name);
111
- return fallback.stream(m, c, o);
112
- },
113
- ...(fallback.adjudicateRoute !== undefined ? { adjudicateRoute: (m, a) => fallback.adjudicateRoute?.(m, a) } : {}),
114
- ...(fallback.complete !== undefined ? { complete: (m, c, o) => fallback.complete(m, c, o) } : {}),
115
- };
116
- brain = createDegradingBrain({ primary: brain, fallbacks: [{ brain: fallbackWithDebtProbe, model: fallbackModel }], downgradeOn: config.degrade.downgradeOn });
98
+ const degradeEntryModel = {
99
+ ...fallbackModel,
100
+ get headers() {
101
+ const own = fbOwnApiKey();
102
+ if (own === undefined)
103
+ return fallbackModel.headers;
104
+ const kept = {};
105
+ for (const [k, v] of Object.entries(fallbackModel.headers ?? {})) {
106
+ if (!hasAuthCarrier({ [k]: v }))
107
+ kept[k] = v;
108
+ }
109
+ return { ...kept, authorization: `Bearer ${own}` };
110
+ },
111
+ };
112
+ brain = createDegradingBrain({
113
+ primary: brain,
114
+ fallbacks: [{ brain: fallback, model: anthropicLaneHop ? fallbackModel : degradeEntryModel }],
115
+ downgradeOn: config.degrade.downgradeOn,
116
+ });
117
117
  }
118
118
  }
119
119
  return brain;
@@ -143,6 +143,26 @@ export function brainSummary(config) {
143
143
  reactiveDegradeBaseUrl: config.degrade?.reactive && config.models?.[config.degrade.to]
144
144
  ? config.models[config.degrade.to].baseUrl || config.gatewayBaseUrl
145
145
  : null,
146
+ ...((() => {
147
+ const target = config.degrade?.reactive ? config.models?.[config.degrade.to] : undefined;
148
+ if (!target)
149
+ return { reactiveDegradeRoutePairing: null, reactiveDegradeRouteRefusal: null };
150
+ if (target.provider === "anthropic" && config.anthropic) {
151
+ return { reactiveDegradeRoutePairing: "anthropic-lane", reactiveDegradeRouteRefusal: null };
152
+ }
153
+ const declaresOwnCredential = config.modelApiKeyEnv?.[target.name] !== undefined ||
154
+ config.modelApiKeyEnv?.[config.degrade.to] !== undefined ||
155
+ config.modelApiKeys?.[target.name] !== undefined ||
156
+ config.modelApiKeys?.[config.degrade.to] !== undefined ||
157
+ hasAuthCarrier(target.headers);
158
+ if (declaresOwnCredential) {
159
+ return { reactiveDegradeRoutePairing: "per-model-declared", reactiveDegradeRouteRefusal: null };
160
+ }
161
+ const verdict = adjudicateModelRoute({ id: target.id, baseUrl: target.baseUrl }, resolveRouteCredential({ ...(config.gatewayApiKey ? { configApiKey: config.gatewayApiKey } : {}) }), { baseUrl: degradeDeclaredRoot(target.baseUrl, config) });
162
+ return verdict.ok
163
+ ? { reactiveDegradeRoutePairing: `ok:${verdict.posture}`, reactiveDegradeRouteRefusal: null }
164
+ : { reactiveDegradeRoutePairing: `broken:${verdict.code}`, reactiveDegradeRouteRefusal: routeRefusalText(verdict) };
165
+ })()),
146
166
  };
147
167
  }
148
168
  //# sourceMappingURL=brain.js.map
package/dist/budget.d.ts CHANGED
@@ -27,7 +27,7 @@ export declare function buildPricing(models: Record<string, Model>): Record<stri
27
27
  export declare function isPricingConfigured(pricing: Record<string, ModelPricing>): boolean;
28
28
  /**
29
29
  * Bound the cardinality of a free-form metric label value (BL-11/BL-21). A label fed by untrusted/unbounded
30
- * input — MCP tool names (a misconfigured/malicious server can emit thousands of dynamic names) or center
30
+ * input — MCP tool names (a misconfigured/untrusted server can emit thousands of dynamic names) or center
31
31
  * model ids (a malformed roster) — would otherwise mint an unbounded number of metric series: a slow memory
32
32
  * leak in the registry + a /metrics scrape blow-up. We track the DISTINCT values seen; once the cap is hit,
33
33
  * further NEW values collapse into one `__other__` bucket (already-seen values still report exactly). The
@@ -66,7 +66,7 @@ export declare class ModelUsageTracker {
66
66
  private readonly active;
67
67
  /** Mark a TOP-LEVEL run active so its `brain.call` usage is tracked. **Required** — `record` ignores any taskId
68
68
  * that isn't registered. This is the leak fence: delegated SUB-TASKS (team members,
69
- * adversarial verifiers) run on throwaway core-minted sessionIds that the run lifecycle never registers/drains,
69
+ * independent verifiers) run on throwaway core-minted sessionIds that the run lifecycle never registers/drains,
70
70
  * so without this gate their usage would accumulate under ids nothing ever clears → an unbounded process-lifetime
71
71
  * leak. Sub-task spend is intentionally NOT in the per-run `modelUsage` echo (it lands in `TaskStats.nested`);
72
72
  * `modelUsage` is the TOP-LEVEL run's own per-model usage. Call at run start; {@link clear} unregisters. */
@@ -29,7 +29,7 @@ export type PluginValidation = {
29
29
  };
30
30
  /** 条款 1/2/4:https-only、host 全等白名单、sha 40hex。纯函数,worker 侧纵深复验(不信任 wire 已验过)。 */
31
31
  export declare function validatePluginDecl(decl: PluginDeclWire, allowHosts: readonly string[]): PluginValidation;
32
- /** 条款 3/5:clone 计划(argv 序列 + 隔离 env)。纯数据,让"姿势"可被钉死。 */
32
+ /** 条款 3/5:clone 计划(argv 序列 + 隔离 env)。纯数据,让"姿势"可被锚定。 */
33
33
  export declare function gitClonePlan(o: {
34
34
  url: string;
35
35
  dest: string;
@@ -37,7 +37,7 @@ export declare function registerCollabWorkflows(workflows: Record<string, Collab
37
37
  /** composite WorkflowScriptStore 的 resolveName 前段:collab 命中 ⇒ 内置 team-discussion 脚本+本模板
38
38
  * defaultArgs(同名 SHADOW core 内置,design/140 §6 1c)。🔴 Object.hasOwn — `name` 是模型供给的
39
39
  * (run-workflow-tool 原样传 rawName):裸索引会让 Object.prototype 成员名("constructor"/"toString"/
40
- * "__proto__"…)假命中 ⇒ 零模板注册也能烧真讨论、且同名文件 store 条目被遮蔽(对抗评审 2026-07-11 MED)。 */
40
+ * "__proto__"…)假命中 ⇒ 零模板注册也能烧真讨论、且同名文件 store 条目被遮蔽(交叉评审 2026-07-11 MED)。 */
41
41
  export declare function resolveCollabWorkflow(name: string): {
42
42
  script: string;
43
43
  defaultArgs: unknown;
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * #196 场景 hands lane —— 「要手的场景」与「无手的场景」在装配层分家。
3
3
  *
4
- * 病根(设计小票 §1,案源 [3208]/[3209]/[3212]):core 的手带工具面是 **per-Runner 表态**
4
+ * 根本问题(设计小票 §1,案源 [3208]/[3209]/[3212]):core 的手带工具面是 **per-Runner 表态**
5
5
  * (`prepare-task`:`handsEnabled = ownedEnv || deps.executionEnv`,ownedEnv 只要挂了
6
6
  * `executionEnvFactory` 就每任务必铸)。server 此前全场景共享一只 runner + 一只 subRunner,
7
7
  * 于是任何 REMOTE_EXEC 部署里,声明 clone-free 的 `scan`、只有 `run_council` 的评审团、
@@ -37,7 +37,7 @@ export declare function pickHandsRunner(hands: ScenarioHands, pair: HandsRunnerP
37
37
  *
38
38
  * 两段来源,都不是拍脑袋:
39
39
  * · `HAND_TOOL_EFFECTS`(core 导出面,`tools/fs`)—— band 的 effect 表,11 名。
40
- * · 下方钉死补集 —— `createHandsToolkit` / 手带块还会铸但**不在** effect 表里的 5 名(实测差集,见
40
+ * · 下方锚定补集 —— `createHandsToolkit` / 手带块还会铸但**不在** effect 表里的 5 名(实测差集,见
41
41
  * test/scenario-hands-lane.test.ts 的对表格:它用真 Runner 跑「有手 roster ∖ 无手 roster」,与本常量逐名对账,
42
42
  * core 加/改 band 成员时红,防漂移)。
43
43
  *
@@ -50,7 +50,7 @@ export declare function pickHandsRunner(hands: ScenarioHands, pair: HandsRunnerP
50
50
  * `hands: none` 三场景(scan/code-review/team)在**所有部署**上零 turn 全灭。core 5.21.1 已删
51
51
  * 那条死条目([3249]),这道 filter 是结构性防御:将来 core 再退役任何 band 名都不会重演
52
52
  * (test/scenario-hands-lane.test.ts 格7/格8 双钉:真 Runner 过审计 + 零交集门)。
53
- * filter 打在**整表**(effect 表 + 钉死补集)之后 —— caret floor 下装置会解析到更新的 core,
53
+ * filter 打在**整表**(effect 表 + 锚定补集)之后 —— caret floor 下装置会解析到更新的 core,
54
54
  * 补集名哪天被退役,只滤前半就是同一次故障换个名字(codex 复审 finding,2026-08-09)。
55
55
  */
56
56
  export declare const HANDS_BAND_TOOL_NAMES: readonly string[];
@@ -65,7 +65,7 @@ export interface ScenarioDeps {
65
65
  skills: LoadedSkill[];
66
66
  repoClient?: RepoReadClient;
67
67
  /** Multi-tenant deployment (REQUIRE_PRINCIPAL). Gates the full-body `WebFetch` tool out of the
68
- * default roster — core says a multi-tenant deployment MUST allowlist hosts (an injected/malicious tenant model
68
+ * default roster — core says a multi-tenant deployment MUST allowlist hosts (an injected/untrusted tenant model
69
69
  * could exfil via an arbitrary public URL; the SSRF floor only blocks internal). Single-user TOC keeps WebFetch. */
70
70
  requirePrincipal?: boolean;
71
71
  /** Deployment-injected WebSearch backend (Brave/Tavily/SearXNG via `WEB_SEARCH_PROVIDER`). When set,
@@ -164,7 +164,7 @@ export declare function semaIdentityProvider(): PromptProvider;
164
164
  * `code` 场景的提示词提供者([849] 造,[891] clay 裁定改名 autonomous→code):role 基底 = core
165
165
  * {@link CODE_SYSTEM_PROMPT}(core 1.298 [891] 改名批正名,旧名 FULL_BODY_SYSTEM_PROMPT=恒等
166
166
  * 别名过渡窗;CODE_AGENT persona + AUTONOMY_SELF_AUDIT + ANTI_VERBOSITY +
167
- * TOOL_PARAM_JSON;[891] 血缘定论 = CC 生产编码提示词的蒸馏去牌版,底本随 CC 版本对表 cc-anchors,
167
+ * TOOL_PARAM_JSON;[891] 谱系定论 = CC 生产编码提示词的蒸馏去牌版,底本随 CC 版本对表 cc-anchors,
168
168
  * 非自研)——与 bench 的请求组装完全同源,引用 core 常量随版本走,绝不复制字符串(tb bench 实测:
169
169
  * 产品默认请求与同引擎 bench 态的差距根因就在这三件 persona/自检/终验)。品牌层与
170
170
  * {@link semaIdentityProvider} 同构:brandIdentity 开时 SEMA_IDENTITY 叠在 role 基底之上;任务自带
@@ -252,7 +252,7 @@ export declare function centerScenarios(deps: ScenarioDeps, specs: CenterScenari
252
252
  * by the HTTP layer (validateUserSkills); this stays pure merge policy.
253
253
  */
254
254
  export declare function mergeUserSkills(scenarioSkills: SkillSpec[] | undefined, userSkills: SkillSpec[] | undefined, logger?: Logger): SkillSpec[] | undefined;
255
- /** center caps view 的解析结果两键(形状钉死)。`scenario` null = 三层皆未配。 */
255
+ /** center caps view 的解析结果两键(形状锚定)。`scenario` null = 三层皆未配。 */
256
256
  export interface ScenarioRuling {
257
257
  scenario: string | null;
258
258
  /** 纯场景名集合(principal/组名不上 wire);空 = 该 principal 无任何指派 = 无治理。 */
@@ -35,7 +35,7 @@ export interface LoadedSkill {
35
35
  * ——那个便利是有意的(`foo.md -> 共享文件`)。`confineTo` 在场(**不可信 lane**:第三方 git checkout,
36
36
  * 见 center-plugins 的 plugin 装载)= 每个被读条目 realpath 后必须仍在该根之内,逃逸即抛。
37
37
  * 🔴 为什么信任级必须是**参数**而不是注释里的假设:本函数的头注原写着「skills dir 全受控」,而
38
- * center-plugins 把它原样复用在 attacker-controlled git checkout 上——git 原样保留仓里的 symlink,
38
+ * center-plugins 把它原样复用在 不可信来源的 git checkout 上——git 原样保留仓里的 symlink,
39
39
  * 于是 checkout 里某个 skill 条目(`任意 .md -> /宿主/任意文件`)会被读进 spec.files 进入提示词。resolvePluginSkillsRoot
40
40
  * 只 realpath 守了 skills **根**,根下逐条目没守。 */
41
41
  export interface LoadSkillsOptions {
@@ -17,6 +17,11 @@ export const BATCH1_LIMIT_KEYS = [
17
17
  ];
18
18
  const B_SETTER_KEYS = ["rateLimitPerMin", "maxPrincipalCostUsd"];
19
19
  export const DEFERRED_LIMIT_KEYS = [
20
+ {
21
+ key: "workspaceFileMaxBytes",
22
+ restartReason: "workspace-face-retired",
23
+ why: "the workspace browse face was retired with the whole-tree snapshot epoch (S-15) — this knob has no consumer left; the value is still validated and recorded for center-push compatibility, but applies to nothing (restart does not change that). Removal follows the settings-schema deprecation lane",
24
+ },
20
25
  {
21
26
  key: "costQuotaWindowSec",
22
27
  restartReason: "cost-quota-window",
@@ -68,7 +68,7 @@ export declare class ConfigCenterHttpError extends Error {
68
68
  }
69
69
  /**
70
70
  * Fetch one skill's body by content hash (`GET /api/config/skills/content/<hash>`, Bearer) and VERIFY its
71
- * sha256 matches the manifest hash (integrity + tamper check). The content is immutable per hash, so the
71
+ * sha256 matches the manifest hash (integrity + rewrite check). The content is immutable per hash, so the
72
72
  * caller caches by hash and never re-fetches an unchanged skill. Throws a {@link ConfigCenterHttpError}
73
73
  * (carrying `.status`) on an HTTP error — INCLUDING 404 — or a plain Error on hash mismatch. Callers that
74
74
  * want "unknown hash → undefined" parity with the local backend inspect `.status === 404` and swallow it;
@@ -77,7 +77,7 @@ export declare class ConfigCenterHttpError extends Error {
77
77
  export declare function fetchSkillContent(baseUrl: string, token: string, contentHash: string, fetchImpl?: typeof fetch): Promise<string>;
78
78
  /** [985]b / [1285]① — §9.4 by-digest 补拉二函(fetchSkillContent 同姿势:HTTP+Bearer+8s 超时+
79
79
  * 消费侧自证)。artifact=envelope JSON(验真=调用方过 core verifyPromptArtifact,canonical 重算——
80
- * 本函数只做传输与形读,不越权解释);blob=段文本 raw-byte(sha256 逐位自证,fetchSkillContent 同款)。
80
+ * 本函数只做传输与形读,不超范围解释);blob=段文本 raw-byte(sha256 逐位自证,fetchSkillContent 同款)。
81
81
  * digest 参数收带前缀/裸 hex 两形([1285] 参数口径)。 */
82
82
  export declare function fetchPromptArtifact(baseUrl: string, token: string, artifactDigest: string, fetchImpl?: typeof fetch): Promise<unknown>;
83
83
  export declare function fetchPromptBlob(baseUrl: string, token: string, contentDigest: string, fetchImpl?: typeof fetch): Promise<string>;
@@ -8,7 +8,7 @@ import type { CenterReadFace, EffectiveConfig } from "./types.js";
8
8
  * 返回值 = **本机 env 显式占住的 center 键名**(与本次发布内容无关的部署事实)。调用方把它喂给
9
9
  * `RestartSliceCtx.readFaceEnvHeld`,`read-face` restart 片于是只指纹「真会 boot-bake 的那部分」——
10
10
  * 否则 center 改一个本机永远用不上的键,整片 fleet 会为一次对自己毫无影响的发布滚动重启一遍
11
- * (codex 对抗复审 finding 2,验真后采纳的那一半)。
11
+ * (codex 交叉复审 finding 2,验真后采纳的那一半)。
12
12
  */
13
13
  export declare function applyCenterReadFace(config: ServiceConfig, eff: EffectiveConfig, logger?: Logger): readonly (keyof CenterReadFace)[];
14
14
  //# sourceMappingURL=read-face.d.ts.map
@@ -7,7 +7,7 @@ export interface RestartSliceCtx {
7
7
  /** `config.degrade.to` when REACTIVE degrade is on (env `MODEL_DEGRADE_TO` + `MODEL_DEGRADE_REACTIVE`);
8
8
  * undefined = the lane is off ⇒ no degrading brain was composed ⇒ nothing is boot-frozen. */
9
9
  reactiveDegradeTo?: string;
10
- /** #A4(codex 对抗复审 finding 2,验真后采纳):本机 env **显式占住**的 readFace 键
10
+ /** #A4(codex 交叉复审 finding 2,验真后采纳):本机 env **显式占住**的 readFace 键
11
11
  * (`applyCenterReadFace` 的返回值,由 boot 存下)。这些键的 center 值在本机**永不 boot-bake**,
12
12
  * 所以它们的变化不该产生 restart 理由 —— 否则 center 改一个本机用不上的键,整片 fleet 会为一次
13
13
  * 对自己毫无影响的发布滚动重启一遍。缺席 = 没有 env 覆盖 ⇒ 整域都算数(与旧行为一致)。 */
@@ -353,10 +353,17 @@ export interface EffectiveConfig {
353
353
  /** (stage7 P5,registry-core 0.8 `execution` 域): the center-resolved per-principal execution
354
354
  * ruling riding the caps view. `required=false` / ruling absent = no enforcement (legacy). `allowedLanes` is
355
355
  * an OPEN value domain (adding one is never BREAKING; consumers tolerate unknown names). Known lane names:
356
- * `host`/`e2b`/`k8s`/`ssh`/`adb`/`local-docker` (the REMOTE_EXEC providers) **plus `in-process`** — the
357
- * REMOTE_EXEC-unset fleet posture (OA/review-style workers; named distinctly from the explicit
356
+ * `host`/`e2b`/`k8s`/`ssh`/`adb`/`local-docker`/`device` (the REMOTE_EXEC providers) **plus `in-process`**
357
+ * — the REMOTE_EXEC-unset fleet posture (OA/review-style workers; named distinctly from the explicit
358
358
  * `host` lane on purpose). ⚠️ Admins writing `required=true` policies must include `in-process` when such
359
- * workers should stay admittable — center UI/doc 词表待补齐。 */
359
+ * workers should stay admittable — center UI/doc 词表待补齐。
360
+ *
361
+ * 🔴 **`device` 是 per-principal 执行面上最需要写清的一条**(device-executor-lane-v2 §4.1 的 config-center
362
+ * 行,车A-4 补):它是「在**员工自己的机器**上执行」的那条车道 —— 允许它 ≠ 允许一台云沙箱,而是允许
363
+ * 这个 principal 把裁决过的指令投到一台**受管设备**上。⚠️ 但**别把它当设备准入门用**:本 ruling 的
364
+ * 粒度是 lane 名,它表达不了 principal×device 绑定,而且 no-ruling 是 **fail-open** 的
365
+ * (`runtime-caps-resolver.ts` 的 P 类债)。device lane 的真准入是自建的 fail-closed 查表
366
+ * (绑定行 owner 谓词 + 设备 active + presence,§4.3.2)—— center 白名单是**外加**的一层,不是替代。 */
360
367
  export interface ExecutionRuling {
361
368
  required: boolean;
362
369
  allowedLanes: string[];
@@ -28,9 +28,9 @@ import type { ServiceConfigFlat } from "./config-types.js";
28
28
  */
29
29
  export declare const LIVENESS_HEARTBEAT_MS = 30000;
30
30
  /** 闭集:新增一条不变量必须在这里加词(拼错 = 编译红,而不是运行期悄悄不判)。 */
31
- export type CrossInvariantId = "run-stale-heartbeat" | "bind-posture" | "bake-runner-credential" | "direct-door-anchors" | "operator-principals-required" | "bake-operator-principals" | "sensitive-write-patterns" | "leader-host-lane" | "leader-durable-store";
31
+ export type CrossInvariantId = "run-stale-heartbeat" | "bind-posture" | "bake-runner-credential" | "direct-door-anchors" | "operator-principals-required" | "bake-operator-principals" | "sensitive-write-patterns" | "leader-host-lane" | "leader-durable-store" | "device-durable-store" | "device-durable-approval";
32
32
  /** 表读的键集(取自平铺配置型 ⇒ 键名/类型永不与真配置漂)。 */
33
- export type CrossInvariantView = Readonly<Pick<ServiceConfigFlat, "runStaleSec" | "authToken" | "authTokens" | "allowUnauthedWrites" | "imageBakes" | "directApprovalDoor" | "durableApproval" | "approvalHmacKeys" | "principalJwtPubkeys" | "principalJwtIss" | "principalJwtAud" | "requirePrincipal" | "operatorPrincipals" | "sensitiveWritePatterns" | "leaderEnabled" | "remoteExec" | "dbBackend">>;
33
+ export type CrossInvariantView = Readonly<Pick<ServiceConfigFlat, "runStaleSec" | "authToken" | "authTokens" | "allowUnauthedWrites" | "imageBakes" | "directApprovalDoor" | "durableApproval" | "approvalHmacKeys" | "principalJwtPubkeys" | "principalJwtIss" | "principalJwtAud" | "requirePrincipal" | "operatorPrincipals" | "sensitiveWritePatterns" | "leaderEnabled" | "remoteExec" | "dbBackend" | "tidb" | "pg">>;
34
34
  export interface CrossInvariantInput {
35
35
  /** 现役配置(boot 期 = 候选自身)。 */
36
36
  readonly live: CrossInvariantView;
@@ -111,6 +111,24 @@ export const CONFIG_CROSS_INVARIANTS = [
111
111
  ? `LEADER_ENABLED with DB_BACKEND=${next.dbBackend} (no external SQL store) degrades the leader-run registry to a per-process Map`
112
112
  : undefined,
113
113
  },
114
+ {
115
+ id: "device-durable-store",
116
+ keys: ["remoteExec", "dbBackend", "tidb", "pg"],
117
+ restartReason: "device-lane",
118
+ bootAnchor: { file: "src/config.ts", marker: "CROSS-INVARIANT:device-durable-store" },
119
+ check: ({ next }) => next.remoteExec?.provider === "device" && !((next.dbBackend === "mysql" && next.tidb !== undefined) || (next.dbBackend === "pg" && next.pg !== undefined))
120
+ ? `REMOTE_EXEC=device with DB_BACKEND=${next.dbBackend} and no SQL connection coordinates leaves the device registry/binding/audit in a per-process map — a restart forgets which device a parked run must resume onto (DB_BACKEND alone is only an engine selector)`
121
+ : undefined,
122
+ },
123
+ {
124
+ id: "device-durable-approval",
125
+ keys: ["remoteExec", "durableApproval"],
126
+ restartReason: "device-lane",
127
+ bootAnchor: { file: "src/config.ts", marker: "CROSS-INVARIANT:device-durable-approval" },
128
+ check: ({ next }) => next.remoteExec?.provider === "device" && !next.durableApproval
129
+ ? "REMOTE_EXEC=device without DURABLE_APPROVAL=true has no park exit for an approval that outlives the device's offline window"
130
+ : undefined,
131
+ },
114
132
  ];
115
133
  export function checkCrossInvariants(input) {
116
134
  for (const inv of CONFIG_CROSS_INVARIANTS) {
@@ -6,7 +6,7 @@
6
6
  * compiling unchanged. All imports below are top-level `import type` (erased — never a load edge,
7
7
  * see test/module-cycle-gate.test.ts header on verbatimModuleSyntax). */
8
8
  import type { SealedKeyPoison } from "./sealed-key.js";
9
- import type { A2aServerSpec, CompliancePosture, LockedKey, McpServerSpec, Model, ModelRoles, RetentionPolicy } from "@sema-agent/core";
9
+ import type { A2aServerSpec, CompliancePosture, FileHistoryRetentionPolicy, LockedKey, McpServerSpec, Model, ModelRoles, RetentionPolicy } from "@sema-agent/core";
10
10
  import type { ApprovalHmacKey, PrincipalJwtKey } from "./auth-keys.js";
11
11
  import type { ElicitationThrottle } from "./elicitation.js";
12
12
  import type { QuestionThrottle } from "./question.js";
@@ -183,6 +183,26 @@ export interface StreamApprovalConfig {
183
183
  /** 判据 5(遗孤最终可判)的上界,量 immutable 的 `createdAtMs`。`STREAM_APPROVAL_ORPHAN_TTL_MS`,
184
184
  * 默认 **7d**,有界 [60000, 90d]。 */
185
185
  orphanTtlMs: number;
186
+ /**
187
+ * S-43(案A,#374 / core #481)—— **零活流铸造**的 ask 的悬挂窗。`UNREACHED_ASK_TTL_MS`,
188
+ * 默认 **3600000**(1h),有界 [60000, 86400000](60s ~ 24h),坏值/越界**拒启**(#210 A 档)。
189
+ *
190
+ * 三问(behavior-facing 硬条款):
191
+ * · **谁需要**:workflow 恒后台跑(CC 220 parity)⇒ 子代 ask 到达时 (owner, session) 键域常无活 SSE。
192
+ * 改形前那一刻即答 `"unavailable"`,合成整条命令死拒([5443] 车道2 实证)。悬挂让人稍后经
193
+ * `/v1/approvals` / 壳队列 / attach 补投三通道回来答它。1h 是「人不在场但当天会回来」的尺度。
194
+ * · **谁被伤**:悬挂 ask 占一格写侧准入配额(`STREAM_APPROVAL_ADMIT_MAX_PER_TASK`,默认 32)且占一条
195
+ * 持久行,窗越长积压越久;显式声明无人值守的部署(`UNATTENDED_APPROVAL_POLICY=deny`)**完全不受
196
+ * 本键影响**(那条腿在铸造时刻即拒,追记 F1)。
197
+ * · **补偿**:本键本身(调小即回到接近旧行为;60s 下界不给「等于零窗」的形——那等价于关掉案A,
198
+ * 要那个语义请用 `deny` 政策或 `STREAM_ASK_WINDOW_MS=0` 的 windowZero 腿)。
199
+ *
200
+ * 🔴 跨旋钮不变量(与 `WINDOW + ADHOC_GRACE < ORPHAN_TTL` 同族、同理由):
201
+ * **`UNREACHED_ASK_TTL_MS + ADHOC_GRACE` 必须严格小于 `ORPHAN_TTL`** —— 悬挂行的 `expiresAtMs ≈
202
+ * createdAtMs + 本键`,判据 4 在 `expiresAtMs + 宽限` 触发而判据 5 在 `createdAtMs + orphanTtl` 触发;
203
+ * 不等式不成立 ⇒ 遗孤兜底必先到,每条悬挂行的归因都被写成 `orphan_ttl_exceeded`。boot 期校验,拒启。
204
+ */
205
+ unreachedTtlMs: number;
186
206
  }
187
207
  export interface ImageBakeConfig {
188
208
  enabled: boolean;
@@ -251,7 +271,7 @@ export interface ServiceConfigFlat {
251
271
  /** Prompt-cache breakpoints on system + last tool (stable prefix). Default true. */
252
272
  cacheBreakpoints: boolean;
253
273
  /** `ANTHROPIC_MAX_RETRIES` —— **缺席不铸键**,同 `gatewayMaxRetries` 的理由(见其顶注):
254
- * env 没配时以前会铸成 `2` 再显式传给引擎,把引擎默认钉死在 server 这一层。 */
274
+ * env 没配时以前会铸成 `2` 再显式传给引擎,把引擎默认写死在 server 这一层。 */
255
275
  maxRetries?: number;
256
276
  };
257
277
  /** Resilience brain stack (1.38, design/27): tiered timeouts + an optional circuit breaker layered
@@ -348,10 +368,6 @@ export interface ServiceConfigFlat {
348
368
  /** B1 /health 活体 store 探测:后台缓存探针间隔 ms(STORE_PROBE_INTERVAL_MS;0=显式关,负/非数 boot
349
369
  * 响亮拒)。只对 SQL 后端接线;探针原语=backend.dbNowMs()(S10 时钟探针的真 DB 往返,零新 SQL 面)。 */
350
370
  storeProbeIntervalMs: number;
351
- /** Rewind snapshot bounds: byte cap in MB (env REWIND_SNAPSHOT_MAX_MB). Unset = core DEFAULT_SNAPSHOT_BOUNDS
352
- * (256 MiB / 10000 files as of core 2026-06-26 — was 2000/64MiB). BYTE-ONLY knob: raises OR lowers just the byte
353
- * cap; file-count/ignoreDirs stay core defaults (more knobs when a case shows up). */
354
- rewindSnapshotMaxMb?: number;
355
371
  /** Long-term memory (design/138 S1, clay 2026-07-08): the injection-first file-based memory ENGINE
356
372
  * (core `RunnerDeps.memoryBackend` + `MemoryEngine`) is the ONLY memory plane — the legacy MemoryStore
357
373
  * path (MEMORY_BACKEND/EMBEDDING_* SQL/file note stores) was dropped without migration. Default ON;
@@ -378,13 +394,13 @@ export interface ServiceConfigFlat {
378
394
  * · `true` ⇒ 部署自证有推断**看不见**的持久通道(自定义 writer / MCP 记忆服务 / remote lane 与记忆根
379
395
  * 共享挂载)。它撤回引擎的只读披露,并在 remote lane 上**恢复** `# Memory` 写指令(core 5.26.0 r13/r14
380
396
  * 收窄的官方恢复路径);
381
- * · `false` ⇒ 强制披露只读/无持久面,并关掉文件工具往记忆根的写通道(披露不会被存储事实打脸)。
397
+ * · `false` ⇒ 强制披露只读/无持久面,并关掉文件工具往记忆根的写通道(披露不会被存储事实推翻)。
382
398
  *
383
399
  * 🔴 **`false` 自 core 5.27.0 起还多一层**([3612] F2,#231 提货):declared-false 的会话是**受限会话**
384
400
  * —— materialize/search/harvest 一律按**已提交账**(ledger + shadow)供给,磁盘上无事务背书的分歧
385
401
  * 既不收编也不供给,而是留盘 + 响亮点名(`restricted_divergence`,`HarvestRejectionCode` 新成员),
386
402
  * 等下一次**非受限**会话走正常门收编。这补上了 5.26.0 自己登记的那条残余(读侧入站同步收编磁盘分歧
387
- * **与会话意图无关**)——在那之前,这里写的「披露不会被存储事实打脸」只是半真话:披露说「本机存不下」,
403
+ * **与会话意图无关**)——在那之前,这里写的「披露不会被存储事实推翻」只是半真话:披露说「本机存不下」,
388
404
  * 引擎却仍可能把别处落进记忆根的字节收进这次会话的账。
389
405
  * ⚠️ **这一层的射程 = file 记忆引擎**(`MEMORY_ENGINE_BACKEND` 未设的默认单机形):受限视图是
390
406
  * `FileMemoryEngineBackend.restrictedAdoptionView` 的实装,而 `engine.materialize` 取它走的是
@@ -482,6 +498,28 @@ export interface ServiceConfigFlat {
482
498
  * ⚠️ **不冻进 checkpoint**:resume 的那条腿跟**当前**部署配置走(core 成文)。
483
499
  * 运维读面 = `GET /v1/diagnostics/wiring` 的 `memoryPosture.provenance`(`null`=本部署未设)。 */
484
500
  memoryProvenance?: "off" | "carry";
501
+ /** design/383 §3.1(core 7.0.0 片3,S-49 S-1b):本部署的记忆 **capture 姿态** —— per-principal
502
+ * `RuntimeCaps.allowMemoryOptOut` verdict 被读进的那个框。env `MEMORY_CAPTURE_POLICY`,三词闭集:
503
+ * · `open`(≡ 缺席时 core 的缺省)—— `TaskSpec.memory.capture:"off"` 声明按面值生效;只有 resolver 的
504
+ * **显式** `false` 拒它(`memory.capture_optout_denied`,terminal)。resolver **故障**(throw / 非
505
+ * boolean / 缺席)⇒ opt-out **仍生效** + `onError({phase:"config"})` 具名披露 —— 极性反转臂:隐私轴上
506
+ * 不可逆的一侧是「把用户明说不要记的会话记进去」,fail-safe 朝它的反面;
507
+ * · `governed` —— per-principal verdict **必答**(混合车队:部分用户强制留存)。`true` 放行、`false` 拒;
508
+ * resolver 故障/verdict 缺席 ⇒ **拒跑任务**(fail-closed:部署显式声明合规侧的不可逆更重);
509
+ * · `capture-required` —— 全体声明具名拒(部署级强制留存,一行配置,不依赖 resolver)。
510
+ *
511
+ * 🔴 **缺席 = 不铸键**(不复制上游缺省;`memoryProvenance` 同姿态)。坏值 ⇒ 启动期响亮拒:`governed` 与
512
+ * `open` 的故障极性**相反**,一个被静默折成缺省的拼写错误会让合规部署在故障窗里放行留存必需用户的 opt-out。
513
+ * core 侧 `prepareConfigDoors` 另有同判门(`config.memory_capture_policy`),两道同向不冲突。
514
+ *
515
+ * 🔴 **`governed` 的半配置拒启**:该姿态要求本部署有 per-principal verdict 源。本批唯一源 = SQL 后端上的
516
+ * `memory_optout_grant` 表(S-2;center 键 `EntitlementRuntimeCaps.allowMemoryOptOut` 是 settings-schema
517
+ * 的独立小件,到货前不算源)。governed 而无源 ⇒ 每个 opt-out 声明都被 core 拒且没人知道为什么 ⇒
518
+ * `boot/runtime-caps.ts` 的 `assertMemoryCapturePolicyWirable` 在启动期拒启(与 consolidation 阀门 D1 同姿态)。
519
+ *
520
+ * ⚠️ **部署席 ONLY**:TaskSpec 无同名键;不冻进 checkpoint(resume 腿跟当前部署配置走,core 成文)。
521
+ * 运维读面 = `GET /v1/diagnostics/wiring` 的 `memoryPosture.capturePolicy`(`null`=本部署未设)。 */
522
+ memoryCapturePolicy?: "open" | "governed" | "capture-required";
485
523
  /**
486
524
  * design/378(core 5.58 design/376 片②)—— 记忆 **consolidation driver 阀门**的三根旋钮。
487
525
  * **恒在场**(不是可选块,与 {@link retentionSweep} 同姿态):三根都有文档化的缺省,而缺省本身
@@ -577,6 +615,18 @@ export interface ServiceConfigFlat {
577
615
  provider: "host";
578
616
  workspaceBase?: string;
579
617
  commandTimeoutMs?: number;
618
+ } | {
619
+ provider: "device";
620
+ /** `DEVICE_DELIVERY_TIMEOUT_MS`(默认 10s)= 等在途槽的上限;到期 = `device.busy`(§5.5)。 */
621
+ deliveryTimeoutMs?: number;
622
+ /** `DEVICE_RECONNECT_GRACE_MS`(默认 120s)= 断连后在途不判丢的窗(§5.4 同 epoch 重连)。 */
623
+ reconnectGraceMs?: number;
624
+ /** `DEVICE_EXEC_TIMEOUT_MS`(默认 1800s)= 指令级默认超时;core 的 per-command `timeout` 可收紧。 */
625
+ execTimeoutMs?: number;
626
+ /** `DEVICE_MAX_INFLIGHT_PER_DEVICE`(§11-O3 clay 已裁 = 4)。 */
627
+ maxInflightPerDevice?: number;
628
+ /** `DEVICE_SUPERSEDE`(默认 `stale_only`)= 同 deviceId 二连的顶替策略(§5.2)。 */
629
+ supersede?: "stale_only" | "deny";
580
630
  } | {
581
631
  provider: "local-docker";
582
632
  image: string;
@@ -650,6 +700,19 @@ export interface ServiceConfigFlat {
650
700
  * service and a run-local on the same box agree on one data dir (boot-locked single-instance). Only the
651
701
  * `local` backend reads it. */
652
702
  localDataRoot?: string;
703
+ /**
704
+ * S-46 件B(`RUN_STORE_STRICT_HYDRATE`,普查表第 29 行的**终局格**)—— file 后端的**可选** fail-closed
705
+ * 启动门。`true` ⇒ `FileRunStore.hydrate()` 列不出 `runs/` 时**拒启**(error 级遗言带绝对路径 + 异常
706
+ * 文案,然后抛);缺省 **false** ⇒ 件6 的既有形逐字不变(带病起动 + 一行 error + P-DEBT 计数)。
707
+ *
708
+ * 为什么默认是 off:件6 当年把「不拒启」写成了刻意保留的行为,默认翻面等于替所有存量部署做主。
709
+ * 为什么值得有这根旋钮:空账起动之后每一次 `getRun` 恒 miss,而「恒 miss」与「本来就没有这条 run」
710
+ * 在 wire 上同形(#43 现场就是这么被误读的)—— 把 run 账本当真账本用的部署有权要求「读不出就别起」。
711
+ * 坏值**拒启**(#210 A 档):静默回默认会让 operator 要的 fail-closed 被无声吃掉。
712
+ *
713
+ * 只有 `local`(file)后端读它;SQL 后端的 hydrate 不经这条路。
714
+ */
715
+ runStoreStrictHydrate: boolean;
653
716
  /** PostgreSQL connection (used iff dbBackend === "pg"). Mirror of `tidb`. Env: PG_HOST/PG_PORT/PG_USER/
654
717
  * PG_PASSWORD/PG_DATABASE/PG_POOL_SIZE. */
655
718
  pg?: {
@@ -691,6 +754,15 @@ export interface ServiceConfigFlat {
691
754
  * 实证,大字节归对象存储);`SNAPSHOT_BLOB_ALLOW_SQL_BYTES=true` 显式接受 SQL 店 + (a) 的帽
692
755
  * (单机/测试台形)。严格 "true" 判——这是安全姿势开关,不收宽松真值。 */
693
756
  snapshotBlobAllowSql: boolean;
757
+ /** S-15 twin 保留条款(core design/381 §片2):per-file rewind 历史的**边界上限**,键形与 core
758
+ * `FileStorageBackendOptions.fileHistory.retention.keep` **同域同名**(local 腿原样透传给 core 的 File 店;
759
+ * SQL 双生店构造期同调 core `resolveFileHistoryRetention` —— 一套语义,三条车道)。缺席 = core 缺省
760
+ * (100,由 core 兑现,本仓不复制那个数);`{ keep: "unbounded" }` 是唯一的「不剪」拼法。坏值在
761
+ * loadConfig 就以 core 的 `config.retention_policy_invalid` 拒(运维接线处),绝不静默回落。
762
+ * env:`FILE_HISTORY_RETENTION_KEEP`。 */
763
+ fileHistory?: {
764
+ retention?: FileHistoryRetentionPolicy;
765
+ };
694
766
  /** plugins 域 clone host 白名单(`PLUGINS_ALLOW_HOSTS`,缺省 github.com;契约 [1361]① 双保险面,
695
767
  * 消费在 capabilities/center-plugins.ts)。 */
696
768
  pluginsAllowHosts: string[];
@@ -706,8 +778,8 @@ export interface ServiceConfigFlat {
706
778
  * 复审 F10:同 tick 邻居(workflowOrphanGraceMs/attachmentUnboundTtlMs/scratchpadSweepTtlMs)都有
707
779
  * 旋钮——存量对象巨大的部署要调慢/停掉,不该被迫取消 MinIO 配置(那会连带关掉整个附件面)。 */
708
780
  attachmentOrphanGraceMs: number;
709
- /** workspace 浏览面(#3,[1894]①)单文件读上限(字节;`WORKSPACE_FILE_MAX_BYTES`,默认 8MiB)。
710
- * 超限=413 结构化信封 {sizeBytes, limit} 指路 archive 口。软旋钮:坏值走默认。 */
781
+ /** ⚠️ INERT since S-15(workspace 浏览面随整树快照纪元退役,消费点归零)。键**保留**:center 热推
782
+ * 可能仍携带它,删键会让整份推送按未知键处置——正式摘除走 settings-schema 的弃用车道。 */
711
783
  workspaceFileMaxBytes: number;
712
784
  /** SendUserFile dual-track public-link issuance (design settled cross-repo 2026-07-14; S3_PUBLIC_* are
713
785
  * [766]② cross-repo contract names — never rename). Enabled iff the adapter-held MinIO/S3 creds are set:
@@ -752,7 +824,7 @@ export interface ServiceConfigFlat {
752
824
  * service-to-service token, else anything in-cluster can submit & bill directly). `ALLOW_UNAUTHED_WRITES`.
753
825
  * 有意设计(文档化):本开关只豁免「零凭证形态」的 fail-closed 503;一旦配了凭证
754
826
  * (SERVICE_AUTH_TOKEN 或 TOKENS 名录),凭证必须使用——unauthed 仍 401,两 env 不叠加
755
- * (半配置绕行=后门,刻意优先级)。 */
827
+ * (半配置绕行=隐藏入口,刻意优先级)。 */
756
828
  allowUnauthedWrites?: boolean;
757
829
  /** Optional READ-ONLY token that ALSO authorizes GET /metrics + /metrics/summary (in addition to
758
830
  * authToken). Set the SAME value fleet-wide so a control plane (sema-registry) can pull every worker's
@@ -881,7 +953,7 @@ export interface ServiceConfigFlat {
881
953
  * 它是**零进展 slice 循环**的兜底(两条总额才是主约束),与重启环的 `maxSuspends` 是两个独立计数。
882
954
  * core 的 JSDoc 逐字点名了本仓这个角色:"a host driving resumes unattended **should set this**"。
883
955
  *
884
- * 🔴 **它与两条 totals 的冻结语义不同**(codex 对抗复审 R1-[medium],验真后改口):core **不把它写进
956
+ * 🔴 **它与两条 totals 的冻结语义不同**(codex 交叉复审 R1-[medium],验真后改口):core **不把它写进
885
957
  * 账本**(`debitLedger` 的返回形里没有这一格),每片现读 `spec.resourceSuspend.maxSlices` 去比账本上
886
958
  * 累计的 `sliceCount`。⇒ 本仓在**每一片**(含续跑)供值是**载荷不是冗余**:只在首片供值等于让续跑腿
887
959
  * 供 `undefined` = 上限当场消失。代价如实登记:滚动部署改这根 env,已 park 的链按**新**值判 —— 部署
@@ -1277,7 +1349,7 @@ export interface ServiceConfigFlat {
1277
1349
  * 放行、不会减**,而车二**没有撤销面**——列出 / 删除自己已存规则的口子没建,core 的
1278
1350
  * `removePersistedRule` 在包里但本仓零调用点。一次误导入的全局规则因此会一直生效,租户自己拿它没
1279
1351
  * 办法,只能找运维改库。#203 交付的正是那条撤销面本身(`GET /v1/rules` + `DELETE /v1/rules`,后者
1280
- * 恒经 `removePersistedRule` 产墓碑)加上 operator 的越权回收腿 ⇒ **前置条件兑现,默认翻转**。
1352
+ * 恒经 `removePersistedRule` 产墓碑)加上 operator 的超范围回收腿 ⇒ **前置条件兑现,默认翻转**。
1281
1353
  * 记这段史不是怀旧:哪天有人想把某条「只加放行」的车道默认打开,判据就是这一条 —— 收得回来才敢开。
1282
1354
  *
1283
1355
  * 显式 `PERMISSION_RULES_ENABLED=false` 仍然关得掉(默认翻转不吃部署侧的表态);升级到本版的既有
@@ -1345,7 +1417,7 @@ export interface ServiceConfigFlat {
1345
1417
  * 🔴 **射程边界(成文,不是遗漏)**:
1346
1418
  * · 唯一让本旋钮**无处施加**的键是 `TOOL_APPROVAL_ENABLED=false`(协调器整体不构造,core 走它自己的
1347
1419
  * headless 姿态)。**不造推断腿**:旋钮只在协调器在场时有意义,这一句是成文而不是代码分支。
1348
- * · ⚠️ **与 `STREAM_APPROVAL_ENABLED` 正交**(codex 对抗复审 R2-F2 验真后纠正 —— 本注上一版把两者
1420
+ * · ⚠️ **与 `STREAM_APPROVAL_ENABLED` 正交**(codex 交叉复审 R2-F2 验真后纠正 —— 本注上一版把两者
1349
1421
  * 写成了一回事,是错的):协议开关关掉只是不注入 `askStore`/协议窗,协调器照常在场,于是 R-13 的
1350
1422
  * D1 窗到期臂**照样**走 park 路由。这不是漏网:设计稿 §2 明写 park 的承载是 **core checkpoint**
1351
1423
  * 而不是 server 的 ask 行,「无 askStore 但有 durable 设施」正是 R-13 点名的**主受益部署**。
@@ -1370,7 +1442,7 @@ export interface ServiceConfigFlat {
1370
1442
  workflowAgentsReadOnly: boolean;
1371
1443
  /** Sandbox-image-pool BAKE job API (IMAGE-API-DESIGN.md §P2): enable /v1/images/bakes* (operator-submit + the
1372
1444
  * bake-runner claim/ingest control plane). Requires a TiDB pool (the durable bake row/event/lease tables) AND
1373
- * a NON-EMPTY `operatorPrincipals` (boot-asserted — never a world-writable build-host-RCE door). Default OFF.
1445
+ * a NON-EMPTY `operatorPrincipals` (boot-asserted — never an unrestricted-write build-host exec door). Default OFF.
1374
1446
  * `IMAGE_BAKES_ENABLED=true`. */
1375
1447
  imageBakes: ImageBakeConfig;
1376
1448
  /** prove-before-scale stopgap: log every tool call (pre+post, incl. resumed legs) for diagnosis. `TOOL_TRACE=true`. Default OFF. */
@@ -1378,7 +1450,7 @@ export interface ServiceConfigFlat {
1378
1450
  /** Task Trace API (S1): persist the agent's reasoning/thinking into the durable run event log (for the CC-style
1379
1451
  * trace UI). Default ON; `TRACE_THINKING=false` disables capture for sensitive deployments. Redacted on persist. */
1380
1452
  traceThinking: boolean;
1381
- /** #246(core 5.30.0 readFace 部署座席):READ 容纳面。缺席=不铸(core 引擎默认 roots,server 不钉死
1453
+ /** #246(core 5.30.0 readFace 部署座席):READ 容纳面。缺席=不铸(core 引擎默认 roots,server 不写死
1382
1454
  * 上游默认);显式坏词启动期响亮拒。只影响读面,写面永不受它管(core 契约)。 */
1383
1455
  readFace?: "open" | "roots";
1384
1456
  /** #246:部署级 READ deny 追加表(additive-only,core 内建表永不可缩)。env=JSON 数组
@@ -1497,14 +1569,14 @@ export interface ServiceConfigFlat {
1497
1569
  configLocalDir?: string;
1498
1570
  }
1499
1571
  /** 组:store(持久化)—— DB 引擎三态、session 后端、SQL coords、快照 BLOB / SendUserFile 对象存储。 */
1500
- export type ServiceStoreConfig = Pick<ServiceConfigFlat, "sessionBackend" | "sessionCacheTtlSec" | "storeProbeIntervalMs" | "rewindSnapshotMaxMb" | "dbBackend" | "dbBackendExplicit" | "localDataRoot" | "tidb" | "pg" | "dbQueryTimeoutMs" | "snapshotBlobStore" | "snapshotBlobSqlMaxBytes" | "snapshotBlobAllowSql" | "sendUserFile">;
1572
+ export type ServiceStoreConfig = Pick<ServiceConfigFlat, "sessionBackend" | "sessionCacheTtlSec" | "storeProbeIntervalMs" | "dbBackend" | "dbBackendExplicit" | "localDataRoot" | "runStoreStrictHydrate" | "tidb" | "pg" | "dbQueryTimeoutMs" | "snapshotBlobStore" | "snapshotBlobSqlMaxBytes" | "snapshotBlobAllowSql" | "fileHistory" | "sendUserFile">;
1501
1573
  /** 组:modelPlane(模型面)—— 网关坐标、Anthropic 路线、韧性旋钮、主/廉价 model entry、role 表、降级梯。 */
1502
1574
  export type ServiceModelPlaneConfig = Pick<ServiceConfigFlat, "gatewayBaseUrl" | "gatewayApiKey" | "gatewayFallbackUrls" | "gatewayMaxRetries" | "anthropic" | "resilience" | "model" | "models" | "modelApiKeyEnv" | "modelApiKeys" | "modelQuotaWeights" | "tiers" | "projects" | "roles" | "atModelAllowlist" | "cascadeLadder" | "degrade">;
1503
1575
  /** 组:approval(审批 / HITL 门)。`directDoorActive` 无 env 解析腿(装配层三域合取的产物),但语义上
1504
1576
  * 就是本组的门状态,故进组;`parseApprovalDomain` 的返回类型相应是 `Omit<…, "directDoorActive">`。 */
1505
1577
  export type ServiceApprovalConfig = Pick<ServiceConfigFlat, "approvalRequire" | "approvalDeny" | "approvalTimeoutSec" | "approvalAutoBudget" | "approvalNeverAuto" | "approvalHmacKeys" | "durableApproval" | "directApprovalDoor" | "directDoorActive" | "resourceSuspend" | "resourceSuspendTtlSec" | "resourceSuspendTotalTokens" | "resourceSuspendTotalBudgetUsd" | "resourceSuspendMaxSlices" | "askQuestionEnabled" | "questionThrottle" | "toolApprovalEnabled" | "permissionRulesEnabled" | "permissionRulesEnabledExplicit" | "streamAskWindowMarginMs" | "streamApproval" | "unattendedApprovalPolicy" | "mcpElicitation" | "sensitiveWritePatterns" | "manualModeShellGate">;
1506
1578
  /** 组:memory(记忆面 + TOC 同步腿)。 */
1507
- export type ServiceMemoryConfig = Pick<ServiceConfigFlat, "memoryEngineEnabled" | "memoryEngineDir" | "memoryEngineRemoteLaneAllowed" | "memoryEngineBackend" | "memoryScope" | "memoryPersistenceCapable" | "memorySync" | "memoryEmbedder" | "memoryOrgAdmissionMode" | "memoryDelegationEvidence" | "memoryProvenance" | "memoryConsolidationDriver" | "memoryOrgDirectoryJson" | "memoryOrgGrantTtlMs" | "memoryOrgUnavailableBackoffMs" | "projectMemoryEnabled" | "syncImportLeaseStaleSec">;
1579
+ export type ServiceMemoryConfig = Pick<ServiceConfigFlat, "memoryEngineEnabled" | "memoryEngineDir" | "memoryEngineRemoteLaneAllowed" | "memoryEngineBackend" | "memoryScope" | "memoryPersistenceCapable" | "memorySync" | "memoryEmbedder" | "memoryOrgAdmissionMode" | "memoryDelegationEvidence" | "memoryProvenance" | "memoryCapturePolicy" | "memoryConsolidationDriver" | "memoryOrgDirectoryJson" | "memoryOrgGrantTtlMs" | "memoryOrgUnavailableBackoffMs" | "projectMemoryEnabled" | "syncImportLeaseStaleSec">;
1508
1580
  /** 组:auth(鉴权 / 身份 / 治理棒)。`commandPolicy` 只有 sema-registry 腿(无 env 标量形),故 env 解析
1509
1581
  * 函数不产出它,但它与 `autonomy` 是同一根治理棒的两半,归本组。
1510
1582
  * design/170 的三件部署治理声明(`compliancePosture`/`lockedConfigKeys`/`retentionPolicy`)同归本组: