@sema-agent/server 7.103.2 → 7.105.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 (204) hide show
  1. package/MIGRATION.md +378 -2
  2. package/README.md +6 -1
  3. package/README.zh-CN.md +5 -1
  4. package/USAGE.md +320 -16
  5. package/deploy/sema-up/README.md +57 -7
  6. package/deploy/sema-up/cluster-up.sh +18 -4
  7. package/deploy/sema-up/kube-up.sh +16 -0
  8. package/deploy/sema-up/lib/dataroot.sh +142 -0
  9. package/deploy/sema-up/lib/flagshape.sh +69 -0
  10. package/deploy/sema-up/lib/lanes.py +14 -4
  11. package/deploy/sema-up/lib/shell-access.sh +41 -0
  12. package/deploy/sema-up/preflight.sh +14 -1
  13. package/deploy/sema-up/sema-up.sh +134 -37
  14. package/deploy/sema-up/smoke.sh +5 -0
  15. package/dist/auto-mode-face.js +3 -4
  16. package/dist/boot/budget-tracing.js +1 -1
  17. package/dist/boot/config-center.d.ts +6 -2
  18. package/dist/boot/config-center.js +5 -59
  19. package/dist/boot/execution-env.d.ts +11 -0
  20. package/dist/boot/execution-env.js +14 -0
  21. package/dist/boot/execution-lane.d.ts +25 -0
  22. package/dist/boot/execution-lane.js +95 -0
  23. package/dist/boot/memory-consolidation.d.ts +185 -9
  24. package/dist/boot/memory-consolidation.js +192 -32
  25. package/dist/boot/resolve-spec.d.ts +2 -0
  26. package/dist/boot/resolve-spec.js +28 -24
  27. package/dist/boot/runner-deps.d.ts +6 -2
  28. package/dist/boot/runner-deps.js +6 -1
  29. package/dist/boot/session-faces.d.ts +5 -0
  30. package/dist/boot/session-faces.js +18 -2
  31. package/dist/boot/session-incarnation-seat.d.ts +41 -0
  32. package/dist/boot/session-incarnation-seat.js +69 -0
  33. package/dist/boot/stage-01-config.js +2 -2
  34. package/dist/boot/stage-02-registry-pull.js +3 -1
  35. package/dist/boot/stage-04-stores.d.ts +2 -0
  36. package/dist/boot/stage-04-stores.js +3 -1
  37. package/dist/boot/stage-05-execution-env.d.ts +4 -0
  38. package/dist/boot/stage-05-execution-env.js +2 -2
  39. package/dist/boot/stage-06-runners.d.ts +4 -1
  40. package/dist/boot/stage-06-runners.js +7 -14
  41. package/dist/boot/stage-07-capability-layer.d.ts +4 -1
  42. package/dist/boot/stage-08-reapers.d.ts +4 -1
  43. package/dist/boot/stage-09-leader.d.ts +6 -1
  44. package/dist/boot/stage-09-leader.js +2 -1
  45. package/dist/boot/stage-10-http-server.d.ts +6 -1
  46. package/dist/boot/stage-10-http-server.js +4 -1
  47. package/dist/brain.d.ts +1 -1
  48. package/dist/config-catalog.d.ts +16 -1
  49. package/dist/config-catalog.js +9 -9
  50. package/dist/config-center/restart-signal.d.ts +4 -4
  51. package/dist/config-invariants.d.ts +7 -1
  52. package/dist/config-invariants.js +37 -6
  53. package/dist/config-provider.d.ts +42 -10
  54. package/dist/config-provider.js +18 -16
  55. package/dist/config-types.d.ts +37 -16
  56. package/dist/config-types.js +1 -0
  57. package/dist/config.d.ts +28 -30
  58. package/dist/config.js +95 -77
  59. package/dist/env-facts.d.ts +13 -3
  60. package/dist/env-facts.js +15 -3
  61. package/dist/execution-lane-caps.d.ts +15 -0
  62. package/dist/execution-lane-caps.js +8 -0
  63. package/dist/file-history-capture.d.ts +42 -35
  64. package/dist/file-history-capture.js +15 -10
  65. package/dist/file-history-disclosure.d.ts +7 -31
  66. package/dist/file-history-disclosure.js +12 -0
  67. package/dist/fleet/fleet-bus.d.ts +5 -1
  68. package/dist/http/admission.d.ts +12 -5
  69. package/dist/http/admission.js +5 -1
  70. package/dist/http/dispatch.d.ts +18 -0
  71. package/dist/http/dispatch.js +19 -6
  72. package/dist/http/principal-gate.d.ts +22 -0
  73. package/dist/http/principal-gate.js +20 -0
  74. package/dist/http/resume-legs.d.ts +1 -1
  75. package/dist/http/resume-legs.js +9 -5
  76. package/dist/http/route-table.d.ts +15 -4
  77. package/dist/http/route-table.js +6 -5
  78. package/dist/http/routes/agents-roster.d.ts +7 -0
  79. package/dist/http/routes/agents-roster.js +6 -2
  80. package/dist/http/routes/approvals-assistant.d.ts +48 -19
  81. package/dist/http/routes/approvals-assistant.js +117 -50
  82. package/dist/http/routes/capabilities.js +3 -2
  83. package/dist/http/routes/config-catalog.js +1 -0
  84. package/dist/http/routes/fleet.d.ts +3 -2
  85. package/dist/http/routes/images.js +0 -1
  86. package/dist/http/routes/memory-consolidation.d.ts +3 -2
  87. package/dist/http/routes/memory-policy.js +3 -3
  88. package/dist/http/routes/rules.js +2 -2
  89. package/dist/http/routes/runs.js +29 -10
  90. package/dist/http/routes/session-sync.js +14 -15
  91. package/dist/http/routes/sessions-list.js +3 -2
  92. package/dist/http/routes/sessions.js +49 -25
  93. package/dist/http/routes/shared-memory.js +2 -2
  94. package/dist/http/routes/side-query.js +8 -1
  95. package/dist/http/routes/tasks.js +22 -19
  96. package/dist/http/routes/trace-usage.d.ts +2 -0
  97. package/dist/http/routes/trace-usage.js +8 -13
  98. package/dist/http/send.d.ts +12 -0
  99. package/dist/http/send.js +12 -1
  100. package/dist/http/server.d.ts +30 -8
  101. package/dist/http/server.js +11 -5
  102. package/dist/http/sse-lifecycle.d.ts +1 -1
  103. package/dist/http/sse-log.d.ts +12 -9
  104. package/dist/http/sse-log.js +1 -8
  105. package/dist/http/sync-submit-body.d.ts +43 -0
  106. package/dist/http/sync-submit-body.js +4 -0
  107. package/dist/http/wire-types.d.ts +23 -5
  108. package/dist/index.d.ts +1 -0
  109. package/dist/index.js +1 -0
  110. package/dist/leader/merge.d.ts +8 -3
  111. package/dist/memory-incident-sink.js +2 -1
  112. package/dist/model-select.d.ts +9 -0
  113. package/dist/model-select.js +5 -1
  114. package/dist/observability/corrupt-read-seat.d.ts +151 -16
  115. package/dist/observability/corrupt-read-seat.js +158 -15
  116. package/dist/observability/fail-open.d.ts +17 -1
  117. package/dist/observability/fail-open.js +17 -1
  118. package/dist/observability/metrics.js +1 -1
  119. package/dist/observability/run-terminal.d.ts +60 -10
  120. package/dist/observability/run-terminal.js +119 -9
  121. package/dist/parked-decide.d.ts +8 -3
  122. package/dist/parked-decide.js +56 -20
  123. package/dist/parked-revive-surface.d.ts +14 -7
  124. package/dist/parked-revive-surface.js +5 -1
  125. package/dist/plugins/approval-ask-store-file.d.ts +4 -1
  126. package/dist/plugins/approval-ask-store-file.js +9 -12
  127. package/dist/plugins/approval-ask-store-sql.d.ts +4 -0
  128. package/dist/plugins/approval-ask-store-sql.js +4 -1
  129. package/dist/plugins/approval-exemption-store.d.ts +21 -1
  130. package/dist/plugins/approval-exemption-store.js +36 -7
  131. package/dist/plugins/approval-nonce-store.d.ts +16 -2
  132. package/dist/plugins/approval-nonce-store.js +17 -7
  133. package/dist/plugins/caching-session-store.d.ts +5 -0
  134. package/dist/plugins/caching-session-store.js +3 -0
  135. package/dist/plugins/file-resume-anchor-store.d.ts +7 -2
  136. package/dist/plugins/file-resume-anchor-store.js +19 -5
  137. package/dist/plugins/file-run-store.d.ts +21 -8
  138. package/dist/plugins/file-run-store.js +60 -38
  139. package/dist/plugins/image-bake-store-sql.d.ts +0 -2
  140. package/dist/plugins/image-bake-store-sql.js +0 -5
  141. package/dist/plugins/local-session-store.d.ts +22 -0
  142. package/dist/plugins/local-session-store.js +74 -11
  143. package/dist/plugins/memory-run-store.d.ts +7 -5
  144. package/dist/plugins/memory-run-store.js +11 -4
  145. package/dist/plugins/one-time-migrations.d.ts +38 -1
  146. package/dist/plugins/one-time-migrations.js +143 -15
  147. package/dist/plugins/permission-rule-store-file.d.ts +9 -2
  148. package/dist/plugins/permission-rule-store-file.js +37 -51
  149. package/dist/plugins/pg-session-storage.d.ts +3 -0
  150. package/dist/plugins/pg-session-storage.js +19 -10
  151. package/dist/plugins/retention-store-sql.js +1 -0
  152. package/dist/plugins/run-store-sql.d.ts +15 -6
  153. package/dist/plugins/run-store-sql.js +15 -13
  154. package/dist/plugins/schema-indexes.js +1 -0
  155. package/dist/plugins/send-file-ledger.d.ts +5 -1
  156. package/dist/plugins/send-file-ledger.js +11 -6
  157. package/dist/plugins/session-event-payload.d.ts +14 -4
  158. package/dist/plugins/session-event-payload.js +5 -2
  159. package/dist/plugins/session-policy-store-sql.js +18 -5
  160. package/dist/plugins/store-backend.d.ts +13 -7
  161. package/dist/plugins/store-backend.js +10 -6
  162. package/dist/plugins/store-contracts.d.ts +7 -2
  163. package/dist/plugins/tidb-pool.js +12 -5
  164. package/dist/plugins/tidb-session-store.d.ts +6 -1
  165. package/dist/plugins/tidb-session-store.js +17 -10
  166. package/dist/project-identity.d.ts +4 -6
  167. package/dist/read-face-posture.d.ts +21 -0
  168. package/dist/read-face-posture.js +9 -0
  169. package/dist/request-key-closure.js +1 -1
  170. package/dist/request-value-closure.d.ts +72 -3
  171. package/dist/request-value-closure.js +12 -4
  172. package/dist/roster-bound-http-status.d.ts +6 -0
  173. package/dist/roster-bound-http-status.js +6 -0
  174. package/dist/rules-consent.js +0 -11
  175. package/dist/run-local.js +22 -27
  176. package/dist/runs.d.ts +22 -2
  177. package/dist/runs.js +29 -7
  178. package/dist/sealed-key.d.ts +31 -4
  179. package/dist/sealed-key.js +9 -0
  180. package/dist/security.d.ts +5 -1
  181. package/dist/session-children-retire.d.ts +92 -0
  182. package/dist/session-children-retire.js +85 -0
  183. package/dist/session-error-wire.d.ts +58 -0
  184. package/dist/session-error-wire.js +38 -0
  185. package/dist/session-incarnation.d.ts +61 -0
  186. package/dist/session-incarnation.js +43 -0
  187. package/dist/session-policy-wire.d.ts +98 -12
  188. package/dist/session-policy-wire.js +71 -8
  189. package/dist/session-records-skipped-notice.d.ts +20 -0
  190. package/dist/session-records-skipped-notice.js +27 -0
  191. package/dist/tool-approval.d.ts +65 -0
  192. package/dist/tool-approval.js +36 -9
  193. package/dist/trace/core-keyset-guard.d.ts +8 -7
  194. package/dist/trace/engine-notice-wire.d.ts +3 -1
  195. package/dist/trace/engine-notice-wire.js +2 -0
  196. package/dist/trace/project.d.ts +11 -6
  197. package/dist/trace/project.js +12 -3
  198. package/dist/trace/redact.d.ts +7 -0
  199. package/dist/trace/redact.js +153 -61
  200. package/dist/trace/wire-projection-faces.d.ts +10 -3
  201. package/dist/trace/wire-projection-faces.js +8 -2
  202. package/package.json +2 -2
  203. package/dist/plugins/memory-session-policy-store.d.ts +0 -46
  204. package/dist/plugins/memory-session-policy-store.js +0 -52
@@ -20,6 +20,8 @@ export declare function runStage09(ctx: Awaited<ReturnType<typeof runStage08>>):
20
20
  deleted: boolean;
21
21
  } | {
22
22
  active: string;
23
+ } | {
24
+ childrenAlive: readonly string[];
23
25
  }>) | undefined;
24
26
  registryJwtVerifier: import("../auth-bridge.js").RegistryJwtVerifier | undefined;
25
27
  sessionAudit: ((sessionId: string) => Promise<{
@@ -74,7 +76,6 @@ export declare function runStage09(ctx: Awaited<ReturnType<typeof runStage08>>):
74
76
  approvalAskAudit: import("../approval-ask-audit-store.js").FileApprovalAskAuditStore | undefined;
75
77
  centerRuntimeCapsResolver: ((principal: string | undefined) => Promise<import("@sema-agent/core").RuntimeCaps | undefined>) | undefined;
76
78
  checkpointStore: import("../plugins/store-backend.js").CheckpointStoreFull | undefined;
77
- consolidationSeatWatch: import("./memory-consolidation.js").ConsolidationSeatWatch | undefined;
78
79
  durableEnabled: boolean;
79
80
  elicitation: import("../elicitation.js").ElicitationCoordinator | undefined;
80
81
  fleetBus: import("../fleet/fleet-bus.js").FleetEventBus;
@@ -128,14 +129,18 @@ export declare function runStage09(ctx: Awaited<ReturnType<typeof runStage08>>):
128
129
  executionEnvFactory: import("@sema-agent/core").ExecutionEnvFactory | undefined;
129
130
  fenceSessionOwner: (sid: string, owner: string | null) => void;
130
131
  lspManager: import("@sema-agent/core").NodeLspManager | import("../lsp/manager.js").E2bLspManager | undefined;
132
+ noteSessionIncarnation: (sid: string, incarnation: string | undefined) => void;
131
133
  perSessionCwd: Map<string, string>;
132
134
  perTaskImage: import("../per-task-image.js").PerTaskImageRegistry;
133
135
  sendUserFileTaskEnvs: import("../capabilities/sandbox-file-send.js").TaskEnvRegistry | undefined;
134
136
  sessionEnvSelection: import("../capabilities/select-environment-tool.js").SessionEnvironmentSelection;
137
+ sessionIncarnationOf: (sid: string) => string | undefined;
135
138
  setSessionCwd: (sid: string, cwd: string) => void;
136
139
  setSessionShellEnv: (sid: string, env: Record<string, string>) => void;
137
140
  worktreeReap: (() => Promise<void>) | undefined;
138
141
  brain: import("@sema-agent/core").Brain;
142
+ consolidationSeatWatch: import("./memory-consolidation.js").ConsolidationSeatWatch | undefined;
143
+ consolidationValve: import("./memory-consolidation.js").ConsolidationValve;
139
144
  costQuota: import("../observability/cost-quota.js").CostQuota | import("../plugins/pg-cost-quota.js").PgCostQuota | import("../plugins/tidb-cost-quota.js").TiDBCostQuota;
140
145
  degradeRouteWatch: import("../brain.js").DegradeRouteWatch;
141
146
  counterDegradeHook: (info: {
@@ -6,13 +6,14 @@ import { createLeaderFace } from "./leader.js";
6
6
  import { assertStaticWiringConsistent } from "../http/routes/diagnostics.js";
7
7
  import { buildDisplaySafeUrl } from "../trace/redact.js";
8
8
  export function runStage09(ctx) {
9
- const { approvalExemptionStore, backend, brain, checkpointStore, config, configCenter, deviceLane, dropSessionScoped, durableEnabled, executionEnvFactory, fileHistoryErasure, governanceSeams, localRoot, logger, mailboxRecipientLifecycle, mailboxStore, metrics, pricing, resumeAnchorStore, runStore, runnerDeps, sessionPolicyStore, sessionStore, streamApprovalGate, taskAttachmentStore, taskListLane, toolResultStore, tracer, usageWindowStore, workflowCompletionInbox } = ctx;
9
+ const { approvalExemptionStore, backend, backgroundAgentStore, brain, checkpointStore, config, configCenter, deviceLane, dropSessionScoped, durableEnabled, executionEnvFactory, fileHistoryErasure, governanceSeams, localRoot, logger, mailboxRecipientLifecycle, mailboxStore, metrics, pricing, resumeAnchorStore, runStore, runnerDeps, sessionPolicyStore, sessionStore, streamApprovalGate, taskAttachmentStore, taskListLane, toolResultStore, tracer, usageWindowStore, workflowCompletionInbox } = ctx;
10
10
  const { ownerAware, sessionAudit, sessionWatchRegistry, purgeSession, instrumentDegenerate, planCacheProbe } = createSessionFaces({
11
11
  deviceStore: deviceLane?.store,
12
12
  dropSessionScoped,
13
13
  config, logger, metrics, localRoot, backend, sessionStore, runStore, checkpointStore, toolResultStore,
14
14
  resumeAnchorStore, approvalExemptionStore, sessionPolicyStore, taskAttachmentStore, fileHistoryErasure,
15
15
  workflowCompletionInbox, taskListLane, mailboxStore, mailboxRecipientLifecycle,
16
+ backgroundAgentStore,
16
17
  });
17
18
  const leaderEndpoint = createLeaderFace({ config, logger, brain, pricing, executionEnvFactory, toolResultStore, sessionStore, checkpointStore, governanceSeams, backend, tracer, usageWindowStore, promptSource: configCenter.promptSource, onError: runnerDeps.onError });
18
19
  const drainState = { draining: false };
@@ -20,6 +20,8 @@ export declare function runStage10(ctx: Awaited<ReturnType<typeof runStage09>>):
20
20
  deleted: boolean;
21
21
  } | {
22
22
  active: string;
23
+ } | {
24
+ childrenAlive: readonly string[];
23
25
  }>) | undefined;
24
26
  registryJwtVerifier: import("../auth-bridge.js").RegistryJwtVerifier | undefined;
25
27
  sessionAudit: ((sessionId: string) => Promise<{
@@ -74,7 +76,6 @@ export declare function runStage10(ctx: Awaited<ReturnType<typeof runStage09>>):
74
76
  approvalAskAudit: import("../approval-ask-audit-store.js").FileApprovalAskAuditStore | undefined;
75
77
  centerRuntimeCapsResolver: ((principal: string | undefined) => Promise<import("@sema-agent/core").RuntimeCaps | undefined>) | undefined;
76
78
  checkpointStore: import("../plugins/store-backend.js").CheckpointStoreFull | undefined;
77
- consolidationSeatWatch: import("./memory-consolidation.js").ConsolidationSeatWatch | undefined;
78
79
  durableEnabled: boolean;
79
80
  elicitation: import("../elicitation.js").ElicitationCoordinator | undefined;
80
81
  fleetBus: import("../fleet/fleet-bus.js").FleetEventBus;
@@ -128,14 +129,18 @@ export declare function runStage10(ctx: Awaited<ReturnType<typeof runStage09>>):
128
129
  executionEnvFactory: import("@sema-agent/core").ExecutionEnvFactory | undefined;
129
130
  fenceSessionOwner: (sid: string, owner: string | null) => void;
130
131
  lspManager: import("@sema-agent/core").NodeLspManager | import("../lsp/manager.js").E2bLspManager | undefined;
132
+ noteSessionIncarnation: (sid: string, incarnation: string | undefined) => void;
131
133
  perSessionCwd: Map<string, string>;
132
134
  perTaskImage: import("../per-task-image.js").PerTaskImageRegistry;
133
135
  sendUserFileTaskEnvs: import("../capabilities/sandbox-file-send.js").TaskEnvRegistry | undefined;
134
136
  sessionEnvSelection: import("../capabilities/select-environment-tool.js").SessionEnvironmentSelection;
137
+ sessionIncarnationOf: (sid: string) => string | undefined;
135
138
  setSessionCwd: (sid: string, cwd: string) => void;
136
139
  setSessionShellEnv: (sid: string, env: Record<string, string>) => void;
137
140
  worktreeReap: (() => Promise<void>) | undefined;
138
141
  brain: import("@sema-agent/core").Brain;
142
+ consolidationSeatWatch: import("./memory-consolidation.js").ConsolidationSeatWatch | undefined;
143
+ consolidationValve: import("./memory-consolidation.js").ConsolidationValve;
139
144
  costQuota: import("../observability/cost-quota.js").CostQuota | import("../plugins/pg-cost-quota.js").PgCostQuota | import("../plugins/tidb-cost-quota.js").TiDBCostQuota;
140
145
  degradeRouteWatch: import("../brain.js").DegradeRouteWatch;
141
146
  counterDegradeHook: (info: {
@@ -24,7 +24,7 @@ import { bindServerLateSeats } from "./late-binds.js";
24
24
  import { serviceVersion } from "../service-version.js";
25
25
  import { buildDisplaySafeUrl } from "../trace/redact.js";
26
26
  export async function runStage10(ctx) {
27
- const { approvalAskAudit, approvalExemptionStore, approvalNonceStore, authorize, backend, backgroundAgentStore, brain, breakerState, centerRuntimeCapsResolver, checkpointStore, config, configCenter, costQuota, deploymentHooks, deviceLane, drainState, durableEnabled, elicitation, fenceSessionOwner, fileHistoryStore, fleetBus, fleetLease, fleetUsage, governanceSeams, handslessRunner, hookAgent, hookLlm, hookWakeBus, imageBakes, imageIndex, instanceId, instrumentDegenerate, leaderEndpoint, localRoot, logger, lspManager, mcpRevocations, memoryBundleFaces, memoryComplianceFaces, memoryConsolidationFaces, memoryEngine, memoryExportBackend, memoryOptOutGrantStore, memoryOriginFaces, memoryPosture, memorySyncCursors, metrics, modelUsageTracker, orgMemoryAdmission, otelExporter, outcomeSink, ownerAware, parkedReviveInheritedGate, parkedReviveSurface, perSessionCwd, perTaskImage, planCacheProbe, pricing, principalCaps, promptManifestTracker, purgeSession, question, rateLimiter, reaper, registryJwtVerifier, resumeAnchorStore, retentionLaneStarted, retentionLaneStore, ruleConsent, runStore, runner, runnerDeps, runtimeCapsPeekResolver, scenarioDetails, scenarios, selectEnvTool, sendFileLedger, sendUserFileEmitter, sendUserFileToolSpec, sessionAudit, sessionEnvSelection, sessionMemoryStatusFace, sessionPolicyStore, sessionShellGateRegistry, sessionTitler, sessionWatchRegistry, setSessionCwd, setSessionShellEnv, sharedMemoryStore, sideQueryAccounting, sqlEngineFacts, sqlWorkflowRunStore, staticWiring, storeBackendDegraded, subRunner, subagentSteerRegistry, taskAttachmentStore, taskLimitCaps, taskTimeoutSec, toolApproval, toolResultStore, usageWindowStore, webSearchProvider, workflowAgentRegistry, workflowAgentSessionIndex, workflowCompletionInbox, workflowJournalStore, workflowModelAllowlistSnapshot, workflowNotifyGate, workflowNotifyJournal, workflowRecoverOpts, workflowRunStore, workflowsCapable } = ctx;
27
+ const { approvalAskAudit, approvalExemptionStore, approvalNonceStore, authorize, backend, backgroundAgentStore, brain, breakerState, centerRuntimeCapsResolver, checkpointStore, config, configCenter, consolidationValve, costQuota, deploymentHooks, deviceLane, drainState, durableEnabled, elicitation, fenceSessionOwner, noteSessionIncarnation, fileHistoryStore, fleetBus, fleetLease, fleetUsage, governanceSeams, handslessRunner, hookAgent, hookLlm, hookWakeBus, imageBakes, imageIndex, instanceId, instrumentDegenerate, leaderEndpoint, localRoot, logger, lspManager, mcpRevocations, memoryBundleFaces, memoryComplianceFaces, memoryConsolidationFaces, memoryEngine, memoryExportBackend, memoryOptOutGrantStore, memoryOriginFaces, memoryPosture, memorySyncCursors, metrics, modelUsageTracker, orgMemoryAdmission, otelExporter, outcomeSink, ownerAware, parkedReviveInheritedGate, parkedReviveSurface, perSessionCwd, perTaskImage, planCacheProbe, pricing, principalCaps, promptManifestTracker, purgeSession, question, rateLimiter, reaper, registryJwtVerifier, resumeAnchorStore, retentionLaneStarted, retentionLaneStore, ruleConsent, runStore, runner, runnerDeps, runtimeCapsPeekResolver, scenarioDetails, scenarios, selectEnvTool, sendFileLedger, sendUserFileEmitter, sendUserFileToolSpec, sessionAudit, sessionEnvSelection, sessionMemoryStatusFace, sessionPolicyStore, sessionShellGateRegistry, sessionTitler, sessionWatchRegistry, setSessionCwd, setSessionShellEnv, sharedMemoryStore, sideQueryAccounting, sqlEngineFacts, sqlWorkflowRunStore, staticWiring, storeBackendDegraded, subRunner, subagentSteerRegistry, taskAttachmentStore, taskLimitCaps, taskTimeoutSec, toolApproval, toolResultStore, usageWindowStore, webSearchProvider, workflowAgentRegistry, workflowAgentSessionIndex, workflowCompletionInbox, workflowJournalStore, workflowModelAllowlistSnapshot, workflowNotifyGate, workflowNotifyJournal, workflowRecoverOpts, workflowRunStore, workflowsCapable } = ctx;
28
28
  const stores = {
29
29
  runStore,
30
30
  resumeAnchorStore: resumeAnchorStore ? resumeAnchorStore : undefined,
@@ -87,6 +87,7 @@ export async function runStage10(ctx) {
87
87
  memoryOriginFace: memoryOriginFaces,
88
88
  sessionMemoryStatus: sessionMemoryStatusFace,
89
89
  memoryAutoConsolidationArmed: autoRunOnRecommendationEffective(runnerDeps),
90
+ memoryConsolidationValve: consolidationValve,
90
91
  memoryConsolidation: memoryConsolidationFaces,
91
92
  memoryOptOutGrant: memoryOptOutGrantStore,
92
93
  orgMemoryDirectory: orgMemoryAdmission.directory,
@@ -139,6 +140,7 @@ export async function runStage10(ctx) {
139
140
  sqlEngineFacts,
140
141
  writeProtection: buildWriteProtectionPosture(config),
141
142
  webSearchProvider,
143
+ sealedKeyPublic: configCenter.sealedKeyPublic,
142
144
  instanceId,
143
145
  capabilities: {
144
146
  version: serviceVersion(),
@@ -171,6 +173,7 @@ export async function runStage10(ctx) {
171
173
  getCenterPrompts: () => configCenter.getCenterPrompts(),
172
174
  getKeyResolver: () => configCenter.getKeyResolver(),
173
175
  taskAttachmentStore, perSessionCwd, setSessionCwd, setSessionShellEnv, fenceSessionOwner,
176
+ noteSessionIncarnation,
174
177
  hookLlm, hookAgent, fleetBus, hookWakeBus, resumeAnchorStore, ownerAware, taskLimitCaps, taskTimeoutSec,
175
178
  noteSessionShellGate: sessionShellGateRegistry.note,
176
179
  selectEnvTool, sendUserFileToolSpec, memoryEngine, durableEnabled, approvalExemptionStore,
package/dist/brain.d.ts CHANGED
@@ -69,7 +69,7 @@ export interface DegradeRouteWatch {
69
69
  /**
70
70
  * `create*`:返回带捕获状态(组壳那一刻的指纹)的活对象。**须在 {@link createBrain} 的同一时刻、同一份 config 上调**
71
71
  * (生产:boot/budget-tracing.ts 紧随 createBrain)—— 否则 `baked` 就不是壳真焊进去的那一只。与 boot/memory-consolidation.ts
72
- * `createConsolidationSeatWatch` 同形。
72
+ * `createConsolidationValveWatch` 同形(`{ read(): { baked, current } }`)。
73
73
  */
74
74
  export declare function createDegradeRouteWatch(config: DegradeRoutePlane): DegradeRouteWatch;
75
75
  /**
@@ -74,7 +74,7 @@ export interface ConfigCatalogSpec {
74
74
  * env 现算,如 WebSearch 后端),而目录的纪律是「回显属主算出来的值」—— 只能把同一条实算腿的入参放宽,
75
75
  * 不能在目录里再抄一遍属主的读法(S-100 / codex r1 F3 的成案:抄一遍就是第二个写者,当场分歧)。
76
76
  */
77
- readonly resolve?: (config: ServiceConfig, env: NodeJS.ProcessEnv) => CatalogValue | undefined;
77
+ readonly resolve?: (config: ServiceConfig, env: NodeJS.ProcessEnv, boot: CatalogBootFacts) => CatalogValue | undefined;
78
78
  readonly center?: CatalogCenterSeatSpec;
79
79
  /** 缺省 "deploy";retired/零消费键标 "inert"(设计稿 ⚪ 的 env 腿投影)。 */
80
80
  readonly envTiming?: "inert";
@@ -123,9 +123,24 @@ export interface ConfigCatalogRow {
123
123
  readonly scope?: "bootstrap-only";
124
124
  readonly enumValues?: readonly string[];
125
125
  }
126
+ /**
127
+ * 只有 **boot 装配**才知道、`ServiceConfig` 上没有的**生效事实** —— 目录实算腿的第三参(车PI 修复轮 ④)。
128
+ * 本面的契约是「本进程实际生效值」:一根旋钮的生效值若在装配期才定(出厂缺省按装配结果派生),实算腿就得读装配的判决,
129
+ * 而不是回表态原词、未设回 `null`。入参对象本身**必填**(每个调用点都得表态这次有哪些装配事实)。
130
+ */
131
+ export interface CatalogBootFacts {
132
+ /** 记忆整理阀门的生效值(boot 第 4 段 `createConsolidationValve` 的判决:显式词,或 S-824 / [ref] 的出厂缺省派生)。
133
+ * 缺席 = 这次装配没有这件事实(只有不经真装配的桩服务:`ServiceDeps` 平铺视图里组字段是可选的;真装配的分组形里它必填)
134
+ * ⇒ 实算腿答「不知道」(落 env 原文 / null),不猜。 */
135
+ readonly memoryConsolidationValve?: {
136
+ readonly enabled: boolean;
137
+ };
138
+ }
126
139
  export interface ConfigCatalogInputs {
127
140
  readonly config: ServiceConfig;
128
141
  readonly env: NodeJS.ProcessEnv;
142
+ /** boot 装配的生效事实({@link CatalogBootFacts})。 */
143
+ readonly boot: CatalogBootFacts;
129
144
  readonly knobs: readonly ConfigKnobRecord[];
130
145
  /** settings-schema 现行 DOMAINS(调用方传入 —— 本模块保持纯数据层,零包依赖)。 */
131
146
  readonly centerDomains: readonly string[];
@@ -2,7 +2,7 @@ import { CROSS_SESSION_INBOUND_SETTINGS, CROSS_SESSION_DIALOG_EXPIRY_VALUES } fr
2
2
  import { CENTER_CREDENTIAL_ENV } from "@sema-agent/settings-schema";
3
3
  import { CENTER_CREDENTIAL_RENEW_LEAD_DEFAULT_MS, CENTER_CREDENTIAL_RENEW_LEAD_MAX_MS, CENTER_CREDENTIAL_RENEW_LEAD_MIN_MS } from "./center-credential-renewer.js";
4
4
  import { THINKING_FORMATS } from "./model-compat.js";
5
- import { AUTO_MODE_CLASSIFIER_TIMEOUT_BOUNDS, DRAIN_SECOND_SIGNAL_MIN_GAP_BOUNDS, DRAIN_SECOND_SIGNAL_MIN_GAP_DEFAULT_MS, FIRST_BYTE_TIMEOUT_BOUNDS, RESILIENCE_DEFAULTS, TASK_WRITE_FACE_ESCALATIONS } from "./config-types.js";
5
+ import { AUTO_MODE_CLASSIFIER_TIMEOUT_BOUNDS, CONFIG_PROVIDER_WORDS, DRAIN_SECOND_SIGNAL_MIN_GAP_BOUNDS, DRAIN_SECOND_SIGNAL_MIN_GAP_DEFAULT_MS, FIRST_BYTE_TIMEOUT_BOUNDS, RESILIENCE_DEFAULTS, TASK_WRITE_FACE_ESCALATIONS } from "./config-types.js";
6
6
  import { webSearchProviderFromEnv, WEB_SEARCH_PROVIDERS } from "./plugins/web-search.js";
7
7
  import { AUTONOMY_MODES } from "./runtime-governance.js";
8
8
  import { REMOTE_EXEC_PROVIDER_WORDS } from "./execution-lane-caps.js";
@@ -102,7 +102,7 @@ export const CONFIG_CATALOG = [
102
102
  r("CONFIG_LKG_ENABLED", "integrations", "boolean", "last-known-good 配置盘缓存读写开关(仅远程 provider 有意义)", { staticDefault: "true" }),
103
103
  r("CONFIG_LKG_PATH", "integrations", "path", "LKG 落盘路径", { derivedDefaultNote: "缺席 ⇒ SEMA_CONFIG_DIR(~/.sema)下按 worker 名派生" }),
104
104
  r("CONFIG_LOCAL_DIR", "integrations", "path", "本地配置源根(config.d/<domain>.json);兼作数据根回落链一环", { configKey: "configLocalDir", danger: DATA }),
105
- r("CONFIG_PROVIDER", "integrations", "enum", "配置源双模选择", { enumValues: ["remote", "local"], derivedDefaultNote: "缺席:SEMA_REGISTRY_URL 在场 ⇒ remote,否则纯 env(无管道)", configKey: "configProvider" }),
105
+ r("CONFIG_PROVIDER", "integrations", "enum", "配置源双模选择(闭集,逐字比较;认不出的词 ⇒ 拒启,S-841)", { enumValues: CONFIG_PROVIDER_WORDS, derivedDefaultNote: "缺席:SEMA_REGISTRY_URL 在场 ⇒ remote,否则纯 env(无管道)", configKey: "configProvider" }),
106
106
  r("CONFIG_REQUIRE_ROSTER", "integrations", "boolean", "远程 registry 非 dryRun 时布防 ready 门:roster 未落地 ⇒ 计费提交 503 + /health ready:false(严格 'true')", { staticDefault: "false" }),
107
107
  r("CORS_ORIGIN", "auth", "csv", "CORS 白名单(单值 v1 兼容回显;多值按请求 Origin 匹配 + Vary;未设=无 CORS 面)", { danger: SEC, configKey: "corsOrigins" }),
108
108
  r("COST_QUOTA_WINDOW_SEC", "limitsHttp", "number", "per-principal 成本配额窗长(秒);SQL 腿窗长进桶主键 ⇒ center 腿显式 deferred(重启生效)", { staticDefault: "86400", danger: BILL, configKey: "costQuotaWindowSec", center: cLimits("costQuotaWindowSec", { timing: "restart", restartReason: "cost-quota-window" }) }),
@@ -151,7 +151,7 @@ export const CONFIG_CATALOG = [
151
151
  r("ENGINE_LINGER_MS", "limitsHttp", "number", "父还活着时自退前三合取须持续成立的静默窗(ms;[1000,24h] 拒启带界)", { staticDefault: "60000", configKey: "engineLingerMs" }),
152
152
  r("ENGINE_ORPHAN_LINGER_MS", "limitsHttp", "number", "父进程已不在(孤儿)时的静默窗(ms;[1000,24h] 拒启带界;SEMA_PARENT_PID 缺席则不生效)", { staticDefault: "2000", configKey: "engineOrphanLingerMs" }),
153
153
  r("EXPERIMENTAL_OBSERVER_AGENTS", "orchestration", "boolean", "observer 开闸线(实验面 ship-dark;多租户只认 center caps,env 被忽略)", { staticDefault: "false", configKey: "experimentalObserverAgents" }),
154
- r("FILE_HISTORY_CAPTURE", "store", "boolean", "文件历史**捕获**总开关(三条车道同义)。交换:关 = 不再捕获任何改动文件的首次快照,同时没有 rewind / restoreFiles / rewindFilesTo / 2c session-sync(对应能力位与 `fileHistoryCapture` 同源翻 off)。关掉 ≠ 删掉:已捕获的历史不被批量删除、也不再被读用,仍按会话删除与留存规则清理 —— 要清存量走 DELETE 会话或留存旋钮", { staticDefault: "true", danger: DATA, configKey: "fileHistoryCapture" }),
154
+ r("FILE_HISTORY_CAPTURE", "store", "boolean", "文件历史**捕获**总开关(三条车道同义)。关 = 不再捕获任何改动文件的首次快照(落 core 的部署捕获位 `RunnerDeps.fileHistoryCapture: \"off\"`,能力词 `fileHistoryCapture` 翻 off);S-724(7.104.0)起**只关捕获**:历史店照接,已存历史照常 restoreFiles / rewindFilesTo / 2c session-sync。关掉 ≠ 删掉:已捕获的历史不被批量删除,仍按会话删除与留存规则清理 —— 要清存量走 DELETE 会话或留存旋钮;没有「整库历史不可用」的旋钮", { staticDefault: "true", danger: DATA, configKey: "fileHistoryCapture" }),
155
155
  r("FILE_HISTORY_RETENTION_KEEP", "store", "number", "per-file rewind 历史的保留上限(与 core FileStorageBackend 同域键形,三条车道一套语义)", { danger: DATA, derivedDefaultNote: "缺席 ⇒ core 缺省 100" }),
156
156
  r("FLEET_ADVERTISE_ADDRESS", "integrations", "url", "fleet 面向 center 公告的本 worker 可达地址(与 center 腿同在 ⇒ 启 fleet client;坏形拒启)"),
157
157
  r("FORK_ENABLED", "orchestration", "boolean", "CC /fork(core Fork 工具)装配开关", { derivedDefaultNote: "posture 三态:缺席 = 单用户 ⇒ on / 多租户 ⇒ off(per-principal entitlement 另判)", configKey: "forkEnabled" }),
@@ -228,9 +228,9 @@ export const CONFIG_CATALOG = [
228
228
  r("MCP_ELICITATION_MAX_TOTAL", "approval", "number", "elicitation 每 run 总量帽", { derivedDefaultNote: "缺席 ⇒ core 出厂窗" }),
229
229
  r("MCP_ELICITATION_MIN_INTERVAL_MS", "approval", "number", "elicitation per-server 最小间隔(ms)", { derivedDefaultNote: "缺席 ⇒ core 出厂窗" }),
230
230
  r("MCP_ELICITATION_TTL_MS", "approval", "number", "elicitation 无人应答释放窗(ms)", { derivedDefaultNote: "缺席 ⇒ core 出厂窗" }),
231
- r("MEMORY_AUTO_CONSOLIDATION", "memory", "enum", "记忆自动整理部署席(on=每个建议任务后整库整理跑,记忆内容自动外流到整理模型;off=显式关,胜过引擎缺省;未设=读引擎具名缺省)", { enumValues: ["on", "off"], derivedDefaultNote: "缺席 ⇒ 引擎具名缺省(**本版装的引擎是开**)——不配不等于关;前置=MEMORY_CONSOLIDATION_DRIVER=on", danger: SEC, resolve: (c) => c.memoryConsolidationDriver.autoRunOnRecommendation, booleanEnum: { true: "on", false: "off" } }),
231
+ r("MEMORY_AUTO_CONSOLIDATION", "memory", "enum", "记忆自动整理部署席(on=每个建议任务后整库整理跑,记忆内容自动外流到整理模型;off=显式关,胜过引擎缺省;未设=读引擎具名缺省)", { enumValues: ["on", "off"], derivedDefaultNote: "缺席 ⇒ 引擎具名缺省(**本版装的引擎是开**)——不配不等于关;前置=整理阀门开着(MEMORY_CONSOLIDATION_DRIVER=on,或未设而出厂缺省派生成开)", danger: SEC, resolve: (c) => c.memoryConsolidationDriver.autoRunOnRecommendation, booleanEnum: { true: "on", false: "off" } }),
232
232
  r("MEMORY_CAPTURE_POLICY", "memory", "enum", "记忆 capture 姿态(open=声明按面值;governed=按 per-principal verdict;capture-required=恒采)", { enumValues: ["open", "governed", "capture-required"], staticDefault: "open", danger: SEC, configKey: "memoryCapturePolicy" }),
233
- r("MEMORY_CONSOLIDATION_DRIVER", "memory", "enum", "记忆折叠阀门(off=零装配零探针,两 admin 口 404 族)", { enumValues: ["on", "off"], staticDefault: "off", danger: BILL, resolve: (c) => c.memoryConsolidationDriver.enabled, booleanEnum: { true: "on", false: "off" } }),
233
+ r("MEMORY_CONSOLIDATION_DRIVER", "memory", "enum", "记忆折叠阀门(off=零装配零探针,两 admin 口 404 族;on=座解析 + 两 admin 口 + 引擎面整理两席)", { enumValues: ["on", "off"], derivedDefaultNote: "缺席 ⇒ 起服时派生:整理座不落主模型(MODEL_CHEAP_ID 在场且异于 MODEL_ID,或目录给 consolidate / summarize / flash 档位绑了别的模型)⇒ on;落主模型 ⇒ off + 起服 warn memory_consolidation_default_off_no_cheap_model;结构上跑不起来(无记忆引擎 / pg·tidb 记忆后端 / MEMORY_PROVENANCE=off)⇒ 恒 off 零提示。显式 on 而落主模型 ⇒ 照开 + warn memory_consolidation_on_main_model(按主模型计费)", danger: BILL, resolve: (_c, _env, boot) => (boot.memoryConsolidationValve === undefined ? undefined : boot.memoryConsolidationValve.enabled ? "on" : "off") }),
234
234
  r("MEMORY_CONSOLIDATION_INTERVAL_SEC", "memory", "number", "折叠周期腿节律(秒;本 build 任何正值拒启 —— 周期腿未落地,恒 0)", { staticDefault: "0" }),
235
235
  r("MEMORY_CONSOLIDATION_SCOPES", "memory", "csv", "折叠阀门 scope 表(恰一条时=admin 口缺省;不是授权边界)"),
236
236
  r("MEMORY_DELEGATION_EVIDENCE", "memory", "enum", "委派臂污染标记的证据标准", { enumValues: ["static-face", "attested-only"], staticDefault: "static-face", danger: SEC, configKey: "memoryDelegationEvidence" }),
@@ -274,7 +274,7 @@ export const CONFIG_CATALOG = [
274
274
  r("MODEL_CHEAP_COST_CACHE_WRITE", "modelPlane", "number", "cheap 槽 cache-write 单价", { danger: BILL, derivedDefaultNote: "缺席 ⇒ 继承主模型", center: cModels("models[].cost.cacheWrite") }),
275
275
  r("MODEL_CHEAP_COST_INPUT", "modelPlane", "number", "cheap 槽 input 单价", { danger: BILL, derivedDefaultNote: "缺席 ⇒ 继承主模型", center: cModels("models[].cost.input") }),
276
276
  r("MODEL_CHEAP_COST_OUTPUT", "modelPlane", "number", "cheap 槽 output 单价", { danger: BILL, derivedDefaultNote: "缺席 ⇒ 继承主模型", center: cModels("models[].cost.output") }),
277
- r("MODEL_CHEAP_ID", "modelPlane", "string", "cheap 槽模型 id(subagent/summarize 两 role 走它;未设=全落主模型)", { danger: BILL, center: cModels("models[].id") }),
277
+ r("MODEL_CHEAP_ID", "modelPlane", "string", "cheap 槽模型 id(subagent/summarize 两 role 走它;未设或与 MODEL_ID 同 id=全落主模型,同 id 不铸 cheap 槽)", { danger: BILL, center: cModels("models[].id") }),
278
278
  r("MODEL_CHEAP_MAX_TOKENS", "modelPlane", "number", "cheap 槽输出上限", { derivedDefaultNote: "缺席 ⇒ 继承主模型", center: cModels("models[].maxTokens") }),
279
279
  r("MODEL_CHEAP_REASONING", "modelPlane", "boolean", "cheap 槽 reasoning 声明(三态:缺席继承主模型)", { derivedDefaultNote: "缺席 ⇒ 继承主模型" }),
280
280
  r("MODEL_CHEAP_VISION", "modelPlane", "boolean", "cheap 槽 vision 声明(三态:缺席继承主模型;text-only cheap 必设 false 让预检真拦)", { derivedDefaultNote: "缺席 ⇒ 继承主模型" }),
@@ -384,7 +384,7 @@ export const CONFIG_CATALOG = [
384
384
  r("S3_PUBLIC_BUCKET", "store", "string", "SendUserFile 公链桶", { derivedDefaultNote: "缺席 ⇒ sema-public" }),
385
385
  r("S3_PUBLIC_ENDPOINT", "store", "url", "SendUserFile 公网面 endpoint(跨仓契约名,禁改)"),
386
386
  r("SANDBOX_BASE_REF", "orchestration", "string", "bake 缺省 base ref(缺席回落 index 最新 sandbox-base)"),
387
- r("SANDBOX_ENV_FACTS", "orchestration", "boolean", "沙箱 envFacts 注入(# Environment 块)", { staticDefault: "true", configKey: "envFactsEnabled" }),
387
+ r("SANDBOX_ENV_FACTS", "orchestration", "boolean", "沙箱事实注入(# Environment 块的 profile/capabilities/pkgSource/egress/resume 事实);会话 scratchpad 的宣告不归本键管(S-842:它是写面的宣告,关事实不关 scratchpad)", { staticDefault: "true", configKey: "envFactsEnabled" }),
388
388
  r("SANDBOX_PKG_SOURCE", "orchestration", "enum", "沙箱包源选择(cn/global/custom;none=完全不注入)", { enumValues: ["global", "cn", "custom", "none"], staticDefault: "global" }),
389
389
  r("SCHEDULER_ENABLED", "orchestration", "boolean", "自唤醒 scheduler(host 车道单用户形)", { derivedDefaultNote: "posture 三态:缺席 = 单用户 ⇒ on / 多租户 ⇒ off(调度归 center)", configKey: "schedulerEnabled" }),
390
390
  r("SCHEDULER_SESSION_LIFETIME", "orchestration", "boolean", "session-lifetime 记录 host-signal(headless 壳注 false ⇒ CronCreate 前门拒)", { staticDefault: "true", configKey: "schedulerSessionLifetime" }),
@@ -535,7 +535,7 @@ function valueClassOf(type) {
535
535
  return "plain";
536
536
  }
537
537
  export function buildConfigCatalog(inputs) {
538
- const { config, env, knobs, centerDomains, centerManagedKeys } = inputs;
538
+ const { config, env, boot, knobs, centerDomains, centerManagedKeys } = inputs;
539
539
  const knobByName = new Map(knobs.map((k) => [k.name, k]));
540
540
  const rows = [...CONFIG_CATALOG]
541
541
  .sort((a, b) => (a.name < b.name ? -1 : 1))
@@ -560,7 +560,7 @@ export function buildConfigCatalog(inputs) {
560
560
  effectiveValue = envHeld ? { present: true, length: raw.length } : { present: false };
561
561
  }
562
562
  else {
563
- const resolved = spec.resolve !== undefined ? spec.resolve(config, env) : spec.configKey !== undefined ? primitiveConfigValue(config, spec.configKey) : undefined;
563
+ const resolved = spec.resolve !== undefined ? spec.resolve(config, env, boot) : spec.configKey !== undefined ? primitiveConfigValue(config, spec.configKey) : undefined;
564
564
  const picked = resolved !== undefined ? resolved : knob !== undefined ? knob.value : envHeld ? raw : spec.staticDefault ?? null;
565
565
  const sanitized = sanitizeValue(spec, picked);
566
566
  effectiveValue = sanitized.value;
@@ -24,10 +24,10 @@ export interface RestartSliceCtx {
24
24
  * 新增/改址/域整体出现或消失仍签(新 mount / 改址只能 restart 才生效)。缺席/false = 旧语义照签
25
25
  * (fail-closed:没接席的部署里删除向没人承接,restart 是它唯一的生效路径)。 */
26
26
  mcpRevocationLive?: boolean;
27
- /** S-668 波 0(过渡;**波 1 转热后删**):记忆整理驱动席的两侧读数 —— `baked` = boot 期 core 解析器选中的
28
- * 那只 Model 的指纹,`current` = 同一只解析器对本拍活配置再解一次的指纹(读法见
29
- * boot/memory-consolidation.ts `createConsolidationSeatWatch`)。缺席 = 阀门关着(`MEMORY_CONSOLIDATION_DRIVER`
30
- * 不是 on,或记忆引擎没接线)⇒ 没有 boot 烤死的席 ⇒ `memory-consolidation` 片恒不签。 */
27
+ /** S-668 波 0(过渡;**波 1 转热后删**)+ 车PI 修复轮 ⑥:记忆整理**阀门判决**的两侧读数(一只 watch,读法见
28
+ * boot/memory-consolidation.ts `createConsolidationValveWatch`)—— `baked` = boot 判决的投影,`current` = 拿本拍活配置
29
+ * 重判一次的投影;投影 = 开着 ⇒ `{ valve: "on", seat: 整理座指纹 }`、关着 ⇒ `{ valve: "off" }`。判决翻了(派生开 ↔ 关)或
30
+ * 开着而座漂了 ⇒ 两侧不等 ⇒ 签。缺席 = 判决不随目录变(显式 off,或结构上跑不起来的派生关)⇒ 恒不签。 */
31
31
  consolidationSeat?: {
32
32
  readonly baked: unknown;
33
33
  readonly current: unknown;
@@ -12,6 +12,12 @@
12
12
  * 双向对表 —— 新增一条拒启却不登记 = 门红。boot 的判据文本与拒启行为**逐字不变**(本批不改拒启),
13
13
  * 表在这一侧承担的是「不许有表外不变量」的登记职责。
14
14
  *
15
+ * 🔴 **报文次序**(车PI 验修轮 F7,如实登记):boot 期一次只报一条。条目按 `bootAnchor` 分住几个区 —— `config.ts` 的 loadConfig 区与
16
+ * `boot/stage-01-config.ts`(第 1 段)、`boot/execution-lane.ts`(第 2 段开头的车道合成步)、`boot/stage-02-registry-pull.ts`(第 2 段
17
+ * 目录应用之后)。**跨区次序 = 段序,区内次序 = 表序**:同时撞上两个区的两条,先到的区先报,与表序无关(例:`LEADER_ENABLED` + host
18
+ * + 无 SQL 先报 loadConfig 区的 `leader-durable-store`,改完再在合成步撞 `leader-host-lane`)。锚点按「读什么键、那个键在哪一段定形」定,
19
+ * 不为报文次序挪区 —— 多一次往返,没有安全后果。
20
+ *
15
21
  * 🔴 判据形状:每条不变量收 `{live, next}` 两个视图。有些条目判的是**状态**(next 自身是否自洽),有些
16
22
  * 判的是**转换**(宽→窄、门姿态翻转)。boot 期没有「上一代」,调用方传 `live === next`,于是转换型条目
17
23
  * 在 boot 天然惰性 —— 这正确:boot 不存在「热切」这回事。
@@ -28,7 +34,7 @@ import type { ServiceConfigFlat } from "./config-types.js";
28
34
  */
29
35
  export declare const LIVENESS_HEARTBEAT_MS = 30000;
30
36
  /** 闭集:新增一条不变量必须在这里加词(拼错 = 编译红,而不是运行期悄悄不判)。 */
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" | "model-provider-word" | "gateway-url-userinfo" | "model-route-endpoint" | "default-model-resolvable" | "refused-names-not-minted" | "require-execution-lane";
37
+ 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" | "multi-tenant-shared-lane" | "leader-durable-store" | "device-durable-store" | "device-durable-approval" | "model-provider-word" | "gateway-url-userinfo" | "model-route-endpoint" | "default-model-resolvable" | "refused-names-not-minted" | "require-execution-lane";
32
38
  /** 表读的键集(取自平铺配置型 ⇒ 键名/类型永不与真配置漂)。 */
33
39
  export type CrossInvariantView = Readonly<Pick<ServiceConfigFlat, "runStaleSec" | "authToken" | "authTokens" | "allowUnauthedWrites" | "imageBakes" | "directApprovalDoor" | "durableApproval" | "approvalHmacKeys" | "principalJwtPubkeys" | "principalJwtIss" | "principalJwtAud" | "requirePrincipal" | "operatorPrincipals" | "sensitiveWritePatterns" | "leaderEnabled" | "remoteExec" | "requireExecutionLane" | "dbBackend" | "tidb" | "pg" | "envModel" | "gatewayBaseUrl" | "gatewayFallbackUrls" | "anthropic" | "models" | "modelRefusals" | "tiers">>;
34
40
  export interface CrossInvariantInput {
@@ -3,10 +3,19 @@ import { modelProviderRefusal } from "./model-provider.js";
3
3
  import { modelRouteUrlRefusal } from "./model-route-url.js";
4
4
  import { modelRouteEndpointRefusal } from "./model-route-endpoint.js";
5
5
  import { defaultModelRefusal, refusedNameMintedRefusal } from "./model-entry-refusal.js";
6
+ import { executionLaneCaps, executionLaneOf, laneCapabilityHolds, REMOTE_EXEC_PROVIDER_WORDS } from "./execution-lane-caps.js";
6
7
  export const LIVENESS_HEARTBEAT_MS = 30_000;
7
8
  const anyServiceAuth = (v) => Boolean(v.authToken) || Object.keys(v.authTokens ?? {}).length > 0;
8
9
  const mustNarrowBind = (v) => Boolean(v.allowUnauthedWrites) && !anyServiceAuth(v);
9
10
  const directDoorConjuncts = (v) => Boolean(v.directApprovalDoor) && Boolean(v.durableApproval) && v.approvalHmacKeys.length > 0 && v.principalJwtPubkeys.length > 0 && Boolean(v.principalJwtIss) && Boolean(v.principalJwtAud);
11
+ function multiTenantSharedLaneRefusal(v) {
12
+ const lane = executionLaneOf(v.remoteExec?.provider);
13
+ if (laneCapabilityHolds(executionLaneCaps(lane).tenantSeparation, v))
14
+ return undefined;
15
+ const outlets = REMOTE_EXEC_PROVIDER_WORDS.filter((w) => executionLaneCaps(w).tenantSeparation === "supported");
16
+ return (`REQUIRE_PRINCIPAL=true (multi-tenant posture) with the "${lane}" execution lane runs every tenant's Bash on one shared target — ` +
17
+ `this lane does not separate tenants; set REMOTE_EXEC to ${outlets.join(" / ")}, or unset REQUIRE_PRINCIPAL for a single-user box`);
18
+ }
10
19
  export const CONFIG_CROSS_INVARIANTS = [
11
20
  {
12
21
  id: "run-stale-heartbeat",
@@ -110,11 +119,24 @@ export const CONFIG_CROSS_INVARIANTS = [
110
119
  id: "leader-host-lane",
111
120
  keys: ["leaderEnabled", "remoteExec"],
112
121
  restartReason: "leader-lane",
113
- bootAnchor: { file: "src/config.ts", marker: "CROSS-INVARIANT:leader-host-lane" },
122
+ bootAnchor: { file: "src/boot/execution-lane.ts", marker: "CROSS-INVARIANT:leader-host-lane" },
114
123
  check: ({ next }) => next.leaderEnabled && next.remoteExec?.provider === "host"
115
- ? "LEADER_ENABLED with REMOTE_EXEC=host shares ONE absolute workspace across concurrent sub-workers (silent git-state corruption)"
124
+ ? "LEADER_ENABLED=true on the host execution lane (REMOTE_EXEC=host, CONFIG_PROVIDER=local with REMOTE_EXEC unset, or a " +
125
+ "config.d/remote-exec.json host override) is refused: the leader wire pins ONE absolute workspace path " +
126
+ "(LEADER_WORKSPACE / the /home/user default) for every sub-worker, while the host adapter gives each env a " +
127
+ "DIFFERENT random directory — so N concurrent host workers would share one physical git worktree and " +
128
+ "corrupt each other's state SILENTLY (per-worker workspace is not wired on this lane yet). Run the leader " +
129
+ "on an isolated lane (REMOTE_EXEC=e2b or k8s), or turn the leader off (unset LEADER_ENABLED) " +
130
+ "(#193 车4 件2 / staleness ledger P1-10 boot invariant)"
116
131
  : undefined,
117
132
  },
133
+ {
134
+ id: "multi-tenant-shared-lane",
135
+ keys: ["requirePrincipal", "remoteExec"],
136
+ restartReason: "execution-lane",
137
+ bootAnchor: { file: "src/boot/execution-lane.ts", marker: "CROSS-INVARIANT:multi-tenant-shared-lane" },
138
+ check: ({ next }) => multiTenantSharedLaneRefusal(next),
139
+ },
118
140
  {
119
141
  id: "leader-durable-store",
120
142
  keys: ["leaderEnabled", "dbBackend"],
@@ -128,18 +150,27 @@ export const CONFIG_CROSS_INVARIANTS = [
128
150
  id: "device-durable-store",
129
151
  keys: ["remoteExec", "dbBackend", "tidb", "pg"],
130
152
  restartReason: "device-lane",
131
- bootAnchor: { file: "src/config.ts", marker: "CROSS-INVARIANT:device-durable-store" },
153
+ bootAnchor: { file: "src/boot/execution-lane.ts", marker: "CROSS-INVARIANT:device-durable-store" },
132
154
  check: ({ next }) => next.remoteExec?.provider === "device" && !((next.dbBackend === "mysql" && next.tidb !== undefined) || (next.dbBackend === "pg" && next.pg !== undefined))
133
- ? `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)`
155
+ ? `REMOTE_EXEC=device with DB_BACKEND=${next.dbBackend} and no SQL connection coordinates built is refused ` +
156
+ "[device-durable-store]: the device registry, the session↔device binding and the device audit trail are all " +
157
+ "SQL-backed — without a real SQL store the binding lives in this process only, so a restart forgets WHICH device " +
158
+ "a parked run must resume onto and a second replica cannot see it at all. Note DB_BACKEND alone is not enough: " +
159
+ "SESSION_BACKEND=memory (or auto with no MYSQL_HOST/PG_HOST) builds no coordinates at all. Configure a durable " +
160
+ "store (DB_BACKEND=mysql|pg + its *_HOST/*_USER/*_DATABASE), or unset REMOTE_EXEC " +
161
+ "(device-executor-lane-v2 §4.7 boot invariant)"
134
162
  : undefined,
135
163
  },
136
164
  {
137
165
  id: "device-durable-approval",
138
166
  keys: ["remoteExec", "durableApproval"],
139
167
  restartReason: "device-lane",
140
- bootAnchor: { file: "src/config.ts", marker: "CROSS-INVARIANT:device-durable-approval" },
168
+ bootAnchor: { file: "src/boot/execution-lane.ts", marker: "CROSS-INVARIANT:device-durable-approval" },
141
169
  check: ({ next }) => next.remoteExec?.provider === "device" && !next.durableApproval
142
- ? "REMOTE_EXEC=device without DURABLE_APPROVAL=true has no park exit for an approval that outlives the device's offline window"
170
+ ? "REMOTE_EXEC=device without DURABLE_APPROVAL=true is refused [device-durable-approval]: a personal device goes " +
171
+ "offline (lid closed, network drop) at arbitrary points, so an in-flight approval that outlives the window MUST " +
172
+ "have a park exit — without the durable approval store there is none and the run is stuck with nowhere to persist. " +
173
+ "Set DURABLE_APPROVAL=true, or unset REMOTE_EXEC (device-executor-lane-v2 §4.7 boot invariant)"
143
174
  : undefined,
144
175
  },
145
176
  {
@@ -3,6 +3,7 @@ import { FileConfigStore } from "@sema-agent/settings-schema/node";
3
3
  import { type EffectiveConfig as AgentConfigEffective, type EffectiveReadWarning } from "@sema-agent/settings-schema";
4
4
  import { fetchEffective as remoteFetchEffective, fetchSkillContent as remoteFetchSkillContent, type EffectiveConfig } from "./config-center/facade.js";
5
5
  import type { CenterEndpoint } from "./config-center/center-request.js";
6
+ import type { ConfigProviderWord } from "./config-types.js";
6
7
  /** Result of a `fetchEffective` — EXACTLY the config-center's return: the effective config + its etag, or
7
8
  * `null` for "unchanged" (remote 304; local: the version matched the caller's prior etag).
8
9
  * `domainErrors`([ref] registry-core 0.10.12 tolerant seam,local lane only):catalog 域坏文件不再
@@ -79,14 +80,15 @@ export interface ConfigProviderInput {
79
80
  worker?: string;
80
81
  dryRun?: boolean;
81
82
  };
82
- /** `CONFIG_PROVIDER` env — a CLOSED enum `local` | `remote` | undefined. `local` forces the local
83
- * backend; `remote` selects the center (falls through to local if no URL); any OTHER value is treated as
84
- * a misconfiguration and defaults to local (safe, non-network) with a warn. Normalize/trim the raw env
85
- * string at parse time in config.ts when this knob is actually wired. */
86
- provider?: string;
87
- /** `CONFIG_LOCAL_DIR` env — root holding `config.d/<domain>.json` for the local backend. Defaults to
88
- * `./config.d`'s parent convention (the FileConfigStore takes the ROOT, and reads `<root>/config.d`). */
89
- localDir?: string;
83
+ /** `CONFIG_PROVIDER` —— 闭集 `local` | `remote` | 缺席(S-841:词表属主 `CONFIG_PROVIDER_WORDS`,认不出的词在
84
+ * `loadConfig` 就拒启,本层**不可能**再收到第三个词 —— 修前那条「认不出 ⇒ 当 local + warn」的臂随之删除)。
85
+ * `local` forces the local backend; `remote` selects the center (falls through to local if no URL). */
86
+ provider?: ConfigProviderWord;
87
+ /** 本地配置源的根(持有 `config.d/<domain>.json`;FileConfigStore 吃根、读 `<root>/config.d`)。🔴 S-841:**必填**,
88
+ * 值恒来自 {@link configLocalRoot}(入口算一次,工厂与执行车道合成步读同一个)—— 修前是可选、缺席回落
89
+ * `process.cwd()`,而工厂只在 `CONFIG_PROVIDER === "local"` 字面上递它 ⇒ 另一条走到 local 的路上配置源读 cwd、
90
+ * 合成步读 localRoot,两个根分家。一个远程源也收它:`remote` 而无中心 URL 的形照样落回 local。 */
91
+ localDir: string;
90
92
  }
91
93
  /**
92
94
  * RemoteConfigProvider — DELEGATES to the exported config-center HTTP functions. No HTTP is reimplemented;
@@ -142,8 +144,38 @@ export declare function mapToServiceEffective(eff: AgentConfigEffective, version
142
144
  * `CONFIG_PROVIDER=remote` with no URL is a misconfiguration — we fall through to local rather than
143
145
  * constructing a remote provider with no endpoint (the caller would have skipped the remote pull entirely
144
146
  * under the old code path; local-with-empty-config.d degrades to env the same way). An UNRECOGNIZED value
145
- * (typo / wrong case) is NOT treated as remote: it defaults to local (non-network) with a warn, so a
146
- * fat-fingered knob can never silently source config from an unintended/stale network endpoint.
147
+ * never reaches this function: `loadConfig` refuses to boot on it (S-841), so a fat-fingered knob can
148
+ * neither source config from an unintended network endpoint nor be read as "local" here while the
149
+ * execution-lane default reads it as "not local".
147
150
  */
148
151
  export declare function createConfigProvider(input: ConfigProviderInput): ConfigProvider;
152
+ /**
153
+ * S-841 —— **本地配置根的唯一来源**:`CONFIG_LOCAL_DIR` → `AGENT_DATA_DIR` → `~/.ai-agent`。两个入口(HTTP 服务的第 1 段、
154
+ * run-local)各调一次,算出来的那一个值同时交给 config-center 工厂(`createConfigProvider` 的 `localDir`)与执行车道合成步
155
+ * (`composeExecutionLane` 的 `localRoot`)—— 修前两个入口各写一份这条链(run-local 还多一个与之等价的三元),工厂又在
156
+ * 字面 `"local"` 之外回落 `process.cwd()`。
157
+ *
158
+ * ⚠️ 与**数据根**(`config.localDataRoot`,多一节 `LOCAL_DATA_ROOT`)不是同一条链:两根分家的那一形由第 1 段的
159
+ * `local_roots_split` 告警说出来(`splitLocalRoots`),合一旋钮 = `CONFIG_LOCAL_DIR`。
160
+ */
161
+ export declare function configLocalRoot(config: {
162
+ readonly configLocalDir?: string | undefined;
163
+ }): string;
164
+ /**
165
+ * {@link createConfigProvider} 会造出**哪一种**配置源 —— 判据的唯一属主(构造器与执行车道合成步读同一只:
166
+ * local 形 `config.d/remote-exec.json` 的 file-wins 覆写只在配置源是 local 时生效,车PI 修复轮 ①)。
167
+ * `local` 或没配中心 ⇒ local;其余 ⇒ remote。(S-841 起入参是闭集:「认不出的词 ⇒ local」那一臂随拒启删除。)
168
+ */
169
+ export declare function configProviderKind(input: {
170
+ readonly provider?: ConfigProviderWord | undefined;
171
+ readonly configCenter?: unknown;
172
+ }): "local" | "remote";
173
+ /**
174
+ * HTTP 服务这一侧的配置源:config-center 工厂**建不建** provider(`CONFIG_PROVIDER=local` 或配了中心才建)、建出来是哪一种。
175
+ * `undefined` = 纯 env 部署(没有配置源)。工厂与执行车道合成步读这同一只(车PI 修复轮 ①:合成步排在工厂之前)。
176
+ */
177
+ export declare function serviceConfigSourceKind(config: {
178
+ readonly configProvider?: ConfigProviderWord | undefined;
179
+ readonly configCenter?: unknown;
180
+ }): "local" | "remote" | undefined;
149
181
  //# sourceMappingURL=config-provider.d.ts.map
@@ -5,8 +5,8 @@ import { FileConfigStore } from "@sema-agent/settings-schema/node";
5
5
  import { findSkillContent, skillContentHash, refIntegrityIssues, createSiblingResolver, formatConfigPath, REF_CHECKED_DOMAINS, } from "@sema-agent/settings-schema";
6
6
  import { redactSecrets } from "./trace/redact.js";
7
7
  import { fetchEffective as remoteFetchEffective, fetchSkillContent as remoteFetchSkillContent, ConfigCenterHttpError, } from "./config-center/facade.js";
8
- import { createLogger } from "./observability/logger.js";
9
- const logger = createLogger();
8
+ import { homedir } from "node:os";
9
+ import { join } from "node:path";
10
10
  const OPERAND_FINGERPRINT_KEY = randomBytes(16);
11
11
  function redactConfigError(text) {
12
12
  return redactSecrets(text).replace(/"[^"]*"|'[^']*'/g, (m) => `"#${createHmac("sha256", OPERAND_FINGERPRINT_KEY).update(m.slice(1, -1)).digest("hex").slice(0, 8)}"`);
@@ -205,19 +205,21 @@ export function mapToServiceEffective(eff, version) {
205
205
  };
206
206
  }
207
207
  export function createConfigProvider(input) {
208
- const p = input.provider;
209
- if (p !== undefined && p !== "local" && p !== "remote") {
210
- logger.warn("config_provider_unrecognized", {
211
- provider: p,
212
- action: "defaulting to local (safe, non-network)",
213
- });
214
- return new LocalConfigProvider(input.localDir ?? process.cwd());
215
- }
216
- const forceLocal = p === "local";
217
- if (forceLocal || !input.configCenter) {
218
- const root = input.localDir ?? process.cwd();
219
- return new LocalConfigProvider(root);
220
- }
221
- return new RemoteConfigProvider(input.configCenter);
208
+ return configProviderKind(input) === "remote" && input.configCenter !== undefined
209
+ ? new RemoteConfigProvider(input.configCenter)
210
+ : new LocalConfigProvider(input.localDir);
211
+ }
212
+ export function configLocalRoot(config) {
213
+ return config.configLocalDir ?? process.env.AGENT_DATA_DIR ?? join(homedir(), ".ai-agent");
214
+ }
215
+ export function configProviderKind(input) {
216
+ if (input.provider === "local" || input.configCenter === undefined)
217
+ return "local";
218
+ return "remote";
219
+ }
220
+ export function serviceConfigSourceKind(config) {
221
+ if (config.configCenter === undefined && config.configProvider !== "local")
222
+ return undefined;
223
+ return configProviderKind({ provider: config.configProvider, configCenter: config.configCenter });
222
224
  }
223
225
  //# sourceMappingURL=config-provider.js.map