@sema-agent/server 7.99.0 → 7.100.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 (115) hide show
  1. package/MIGRATION.md +13 -0
  2. package/USAGE.md +75 -52
  3. package/dist/approval-ask-machine.d.ts +10 -0
  4. package/dist/approval-ask-machine.js +3 -0
  5. package/dist/approval-card.d.ts +74 -1
  6. package/dist/approval-card.js +41 -8
  7. package/dist/approval.d.ts +44 -20
  8. package/dist/approval.js +14 -6
  9. package/dist/boot/config-center.d.ts +16 -8
  10. package/dist/boot/config-center.js +39 -81
  11. package/dist/boot/execution-env.d.ts +1 -2
  12. package/dist/boot/execution-env.js +2 -2
  13. package/dist/boot/leader.js +12 -9
  14. package/dist/boot/memory-consolidation.d.ts +38 -0
  15. package/dist/boot/memory-consolidation.js +35 -0
  16. package/dist/boot/resolve-spec.d.ts +0 -1
  17. package/dist/boot/resolve-spec.js +4 -3
  18. package/dist/boot/runner-deps.d.ts +2 -2
  19. package/dist/boot/stage-05-execution-env.d.ts +1 -1
  20. package/dist/boot/stage-06-runners.d.ts +2 -2
  21. package/dist/boot/stage-06-runners.js +3 -3
  22. package/dist/boot/stage-07-capability-layer.d.ts +2 -3
  23. package/dist/boot/stage-07-capability-layer.js +6 -6
  24. package/dist/boot/stage-08-reapers.d.ts +3 -4
  25. package/dist/boot/stage-08-reapers.js +2 -2
  26. package/dist/boot/stage-09-leader.d.ts +2 -3
  27. package/dist/boot/stage-10-http-server.d.ts +2 -3
  28. package/dist/boot/stage-10-http-server.js +2 -2
  29. package/dist/capabilities/center-plugins.d.ts +14 -2
  30. package/dist/capabilities/center-plugins.js +13 -5
  31. package/dist/capabilities/hands-lane.js +2 -1
  32. package/dist/capabilities/scenarios.js +18 -5
  33. package/dist/capabilities/tool-defer.d.ts +2 -2
  34. package/dist/config-catalog.d.ts +2 -2
  35. package/dist/config-catalog.js +12 -13
  36. package/dist/config-center/apply-effective.d.ts +34 -39
  37. package/dist/config-center/apply-effective.js +100 -64
  38. package/dist/config-center/apply-ledger.d.ts +35 -16
  39. package/dist/config-center/apply-ledger.js +25 -18
  40. package/dist/config-center/facade.d.ts +11 -5
  41. package/dist/config-center/facade.js +1 -1
  42. package/dist/config-center/hot-keys-registry.d.ts +11 -4
  43. package/dist/config-center/hot-keys-registry.js +11 -7
  44. package/dist/config-center/restart-signal.d.ts +21 -14
  45. package/dist/config-center/restart-signal.js +16 -22
  46. package/dist/config-center/types.d.ts +3 -3
  47. package/dist/config-types.d.ts +22 -7
  48. package/dist/cross-session-settings.js +1 -1
  49. package/dist/host-lsp-manager.d.ts +29 -0
  50. package/dist/host-lsp-manager.js +14 -0
  51. package/dist/http/active-run-conflict.d.ts +2 -12
  52. package/dist/http/active-run-conflict.js +1 -6
  53. package/dist/http/admission.js +8 -0
  54. package/dist/http/resume-legs.d.ts +1 -1
  55. package/dist/http/resume-legs.js +14 -10
  56. package/dist/http/route-ctx.d.ts +2 -2
  57. package/dist/http/routes/approvals-assistant.d.ts +1 -1
  58. package/dist/http/routes/approvals-assistant.js +2 -2
  59. package/dist/http/routes/memory-compliance.js +5 -0
  60. package/dist/http/routes/memory-origin.js +5 -0
  61. package/dist/http/routes/sessions.js +6 -0
  62. package/dist/http/routes/tasks.js +7 -6
  63. package/dist/http/server.d.ts +6 -5
  64. package/dist/http/wire-types.d.ts +6 -3
  65. package/dist/leader/wire.d.ts +20 -12
  66. package/dist/leader/wire.js +7 -6
  67. package/dist/memory-layer-preflight.d.ts +46 -0
  68. package/dist/memory-layer-preflight.js +121 -0
  69. package/dist/memory-operator-faces.js +17 -1
  70. package/dist/observability/corrupt-read-seat.d.ts +21 -4
  71. package/dist/observability/corrupt-read-seat.js +31 -0
  72. package/dist/observability/fail-open.d.ts +8 -4
  73. package/dist/observability/fail-open.js +8 -4
  74. package/dist/observability/run-terminal.js +1 -0
  75. package/dist/observability/secret-env-scrub.d.ts +1 -0
  76. package/dist/observability/secret-env-scrub.js +1 -0
  77. package/dist/orchestration/workflow-completion-inbox.d.ts +31 -23
  78. package/dist/orchestration/workflow-completion-inbox.js +8 -3
  79. package/dist/plugins/approval-ask-store-file.d.ts +20 -2
  80. package/dist/plugins/approval-ask-store-file.js +21 -3
  81. package/dist/plugins/approval-ask-store-memory.d.ts +2 -0
  82. package/dist/plugins/approval-ask-store-memory.js +39 -13
  83. package/dist/plugins/approval-ask-store-sql.d.ts +24 -5
  84. package/dist/plugins/approval-ask-store-sql.js +27 -3
  85. package/dist/plugins/checkpoint-store-sql.d.ts +9 -3
  86. package/dist/plugins/checkpoint-store-sql.js +19 -2
  87. package/dist/plugins/local-checkpoint-store.js +2 -0
  88. package/dist/plugins/mailbox-store-sql.d.ts +7 -5
  89. package/dist/plugins/mailbox-store-sql.js +6 -4
  90. package/dist/plugins/permission-rule-store-file.js +3 -2
  91. package/dist/plugins/sql-json-column.d.ts +16 -3
  92. package/dist/plugins/sql-json-column.js +15 -4
  93. package/dist/plugins/web-search.d.ts +42 -22
  94. package/dist/plugins/web-search.js +141 -49
  95. package/dist/run-local.js +6 -5
  96. package/dist/runs.js +6 -5
  97. package/dist/runtime-governance.d.ts +50 -3
  98. package/dist/runtime-governance.js +5 -0
  99. package/dist/server-secret-env.d.ts +4 -2
  100. package/dist/task-settings.d.ts +1 -1
  101. package/dist/tool-approval.d.ts +107 -19
  102. package/dist/tool-approval.js +64 -15
  103. package/dist/trace/core-keyset-guard.d.ts +5 -5
  104. package/dist/trace/ledger-sink.js +2 -2
  105. package/dist/trace/project.d.ts +13 -0
  106. package/dist/trace/project.js +22 -5
  107. package/dist/trace/projection-drop.d.ts +2 -0
  108. package/dist/trace/projection-drop.js +6 -0
  109. package/dist/trace/sema-provenance.d.ts +4 -1
  110. package/dist/trace/sema-provenance.js +1 -1
  111. package/dist/trace/task-notification-facets.d.ts +65 -0
  112. package/dist/trace/task-notification-facets.js +31 -0
  113. package/dist/trace/wire-projection-faces.d.ts +3 -3
  114. package/dist/trace/wire-projection-faces.js +3 -3
  115. package/package.json +3 -3
@@ -17,7 +17,6 @@ export declare function runStage07(ctx: Awaited<ReturnType<typeof runStage06>>):
17
17
  scenarioDetails: Record<string, import("../capabilities/scenarios.js").ScenarioDetail>;
18
18
  scenarios: Record<string, import("../capabilities/scenarios.js").Scenario>;
19
19
  selectEnvTool: import("@sema-agent/core").ToolSpec<import("@sema-agent/core").TSchema> | undefined;
20
- singleUserAutoAcceptBaseline: boolean;
21
20
  taskLimitCaps: {
22
21
  timeoutSec: number | undefined;
23
22
  maxOutputTokens: number | undefined;
@@ -28,6 +27,7 @@ export declare function runStage07(ctx: Awaited<ReturnType<typeof runStage06>>):
28
27
  approvalAskAudit: import("../approval-ask-audit-store.js").FileApprovalAskAuditStore | undefined;
29
28
  centerRuntimeCapsResolver: ((principal: string | undefined) => Promise<import("@sema-agent/core").RuntimeCaps | undefined>) | undefined;
30
29
  checkpointStore: import("../plugins/store-backend.js").CheckpointStoreFull | undefined;
30
+ consolidationSeatWatch: import("./memory-consolidation.js").ConsolidationSeatWatch | undefined;
31
31
  durableEnabled: boolean;
32
32
  elicitation: import("../elicitation.js").ElicitationCoordinator | undefined;
33
33
  fleetBus: import("../fleet/fleet-bus.js").FleetEventBus;
@@ -48,7 +48,6 @@ export declare function runStage07(ctx: Awaited<ReturnType<typeof runStage06>>):
48
48
  retentionLaneStore: import("../plugins/retention-lane-store-sql.js").RetentionLaneStore | undefined;
49
49
  runner: import("@sema-agent/core").Runner;
50
50
  runnerDeps: import("@sema-agent/core").RunnerDeps;
51
- runnerTierFrozen: boolean;
52
51
  runtimeCapsPeekResolver: import("../runtime-caps-resolver.js").EntitlementsResolverFn | undefined;
53
52
  sendFileLedger: import("../plugins/send-file-ledger.js").SendFileLedger | undefined;
54
53
  sendUserFileEmitter: import("../capabilities/send-user-file-tool.js").SendUserFileEmitter | undefined;
@@ -81,7 +80,7 @@ export declare function runStage07(ctx: Awaited<ReturnType<typeof runStage06>>):
81
80
  dropSessionScoped: (sid: string) => void;
82
81
  executionEnvFactory: import("@sema-agent/core").ExecutionEnvFactory | undefined;
83
82
  fenceSessionOwner: (sid: string, owner: string | null) => void;
84
- lspManager: import("../lsp/manager.js").E2bLspManager | import("@sema-agent/core").NodeLspManager | undefined;
83
+ lspManager: import("@sema-agent/core").NodeLspManager | import("../lsp/manager.js").E2bLspManager | undefined;
85
84
  perSessionCwd: Map<string, string>;
86
85
  perTaskImage: import("../per-task-image.js").PerTaskImageRegistry;
87
86
  sendUserFileTaskEnvs: import("../capabilities/sandbox-file-send.js").TaskEnvRegistry | undefined;
@@ -6,7 +6,7 @@ import { parseCapEnv, parseNumOrFailNonNegative } from "../config.js";
6
6
  import { ensureChildSessionDurableWithPromotion } from "../plugins/session-store.js";
7
7
  import { webSearchConfigFromEnv, createWebSearchBackend, shouldProbeWebSearchOnBoot } from "../plugins/web-search.js";
8
8
  import { createAuthorizer } from "../security.js";
9
- import { assertGateIntentServiceable, buildForceDurableGateInertNotice, buildNoResponderBootWarns, hasOperatorGateIntent } from "../approval.js";
9
+ import { buildForceDurableGateInertNotice, buildNoResponderBootWarns, gateIntentUnserviceableReason, singleUserAutoAcceptBaselineApplies } from "../approval.js";
10
10
  import { executionLaneOf } from "../execution-lane-caps.js";
11
11
  import { loadDeploymentSkills } from "../capabilities/skills.js";
12
12
  import { createRepoClient } from "../capabilities/repo-tools.js";
@@ -46,9 +46,9 @@ export async function runStage07(ctx) {
46
46
  maxOutputTokens: capEnv("TASK_MAX_OUTPUT_TOKENS_MAX"),
47
47
  maxTurns: capEnv("TASK_MAX_TURNS_MAX"),
48
48
  };
49
- const operatorGateIntent = hasOperatorGateIntent(config);
50
- assertGateIntentServiceable(config, checkpointStore !== undefined);
51
- const singleUserAutoAcceptBaseline = config.requirePrincipal !== true && !operatorGateIntent;
49
+ const gateRefusal = gateIntentUnserviceableReason(config, checkpointStore !== undefined);
50
+ if (gateRefusal !== undefined)
51
+ throw new Error(gateRefusal);
52
52
  await reclaimOrphansAtBoot(runStore, checkpointStore, logger);
53
53
  const imageIndex = backend?.imageIndex ? backend.imageIndex() : undefined;
54
54
  const selectEnvTool = config.selectEnvironmentTool && config.remoteExec?.provider === "k8s" && imageIndex
@@ -79,7 +79,7 @@ export async function runStage07(ctx) {
79
79
  approvalFaceWired: toolApproval !== undefined || durableEnabled,
80
80
  }))
81
81
  logger.warn(w.event, w.fields);
82
- if (singleUserAutoAcceptBaseline) {
82
+ if (singleUserAutoAcceptBaselineApplies(config)) {
83
83
  logger.info("tool_policy_single_user_default", {
84
84
  posture: "adjudicated auto-accept — ALL write/hand/MCP tool surfaces auto-allowed (core per-surface UNGATED enumeration suppressed for single-user turnkey)",
85
85
  tighten: "APPROVAL_REQUIRE / AUTONOMY / commandPolicy (re-surfaces + gates specific surfaces)",
@@ -155,6 +155,6 @@ export async function runStage07(ctx) {
155
155
  const builtinScenarioNames = Object.keys(scenarioDetails);
156
156
  configCenter.applyCenterScenarios({ scenarioDeps, scenarios, scenarioDetails, builtinScenarioNames });
157
157
  logger.info("capabilities", { skills: skills.length, scenarios: Object.keys(scenarios), repoTools: Boolean(repoClient) });
158
- return { ...ctx, approvalExemptionStore, approvalNonceStore, authorize, imageBakes, imageIndex, parkedReviveInheritedGate, parkedReviveTool, rateLimiter, resumeAnchorStore, runStore, scenarioDetails, scenarios, selectEnvTool, singleUserAutoAcceptBaseline, taskLimitCaps, taskTimeoutSec, webSearchProvider: webSearchCfg?.provider };
158
+ return { ...ctx, approvalExemptionStore, approvalNonceStore, authorize, imageBakes, imageIndex, parkedReviveInheritedGate, parkedReviveTool, rateLimiter, resumeAnchorStore, runStore, scenarioDetails, scenarios, selectEnvTool, taskLimitCaps, taskTimeoutSec, webSearchProvider: webSearchCfg?.provider };
159
159
  }
160
160
  //# sourceMappingURL=stage-07-capability-layer.js.map
@@ -2,7 +2,7 @@
2
2
  * 装配第 8 段 —— **后台 reaper**(§3-8)。
3
3
  * 维护 tick(startReapers)+ 托管留存 lane(独立定时器)+ OTLP 导出 + 限额座换代口 + 五只 boot Runner 的
4
4
  * 原子换代闭包 + config-center 刷新环与 key resolver 首建。
5
- * ⚠️ 位置即契约:刷新环必须在 `new Runner` 之后(runnerTierFrozen 与构造函数读同一 config 代际);
5
+ * ⚠️ 位置即契约:刷新环必须在 `new Runner` 之后(它要五只 boot Runner 的换代口 `swapRunnerDeps`);
6
6
  * key resolver 首建读的必须是 boot apply 之后的 config。
7
7
  * 🔴 `runDenySweep` 的 holder 住在 `late-binds.ts`(赋值点在第十段的 server 之后)。
8
8
  */
@@ -30,7 +30,6 @@ export declare function runStage08(ctx: Awaited<ReturnType<typeof runStage07>>):
30
30
  scenarioDetails: Record<string, import("../capabilities/scenarios.js").ScenarioDetail>;
31
31
  scenarios: Record<string, import("../index.js").Scenario>;
32
32
  selectEnvTool: import("@sema-agent/core").ToolSpec<import("@sema-agent/core").TSchema> | undefined;
33
- singleUserAutoAcceptBaseline: boolean;
34
33
  taskLimitCaps: {
35
34
  timeoutSec: number | undefined;
36
35
  maxOutputTokens: number | undefined;
@@ -41,6 +40,7 @@ export declare function runStage08(ctx: Awaited<ReturnType<typeof runStage07>>):
41
40
  approvalAskAudit: import("../approval-ask-audit-store.js").FileApprovalAskAuditStore | undefined;
42
41
  centerRuntimeCapsResolver: ((principal: string | undefined) => Promise<import("@sema-agent/core").RuntimeCaps | undefined>) | undefined;
43
42
  checkpointStore: import("../plugins/store-backend.js").CheckpointStoreFull | undefined;
43
+ consolidationSeatWatch: import("./memory-consolidation.js").ConsolidationSeatWatch | undefined;
44
44
  durableEnabled: boolean;
45
45
  elicitation: import("../elicitation.js").ElicitationCoordinator | undefined;
46
46
  fleetBus: import("../fleet/fleet-bus.js").FleetEventBus;
@@ -61,7 +61,6 @@ export declare function runStage08(ctx: Awaited<ReturnType<typeof runStage07>>):
61
61
  retentionLaneStore: import("../plugins/retention-lane-store-sql.js").RetentionLaneStore | undefined;
62
62
  runner: Runner;
63
63
  runnerDeps: import("@sema-agent/core").RunnerDeps;
64
- runnerTierFrozen: boolean;
65
64
  runtimeCapsPeekResolver: import("../runtime-caps-resolver.js").EntitlementsResolverFn | undefined;
66
65
  sendFileLedger: import("../plugins/send-file-ledger.js").SendFileLedger | undefined;
67
66
  sendUserFileEmitter: import("../capabilities/send-user-file-tool.js").SendUserFileEmitter | undefined;
@@ -94,7 +93,7 @@ export declare function runStage08(ctx: Awaited<ReturnType<typeof runStage07>>):
94
93
  dropSessionScoped: (sid: string) => void;
95
94
  executionEnvFactory: import("@sema-agent/core").ExecutionEnvFactory | undefined;
96
95
  fenceSessionOwner: (sid: string, owner: string | null) => void;
97
- lspManager: import("../lsp/manager.js").E2bLspManager | import("@sema-agent/core").NodeLspManager | undefined;
96
+ lspManager: import("@sema-agent/core").NodeLspManager | import("../lsp/manager.js").E2bLspManager | undefined;
98
97
  perSessionCwd: Map<string, string>;
99
98
  perTaskImage: import("../per-task-image.js").PerTaskImageRegistry;
100
99
  sendUserFileTaskEnvs: import("../capabilities/sandbox-file-send.js").TaskEnvRegistry | undefined;
@@ -5,7 +5,7 @@ import { startRetentionLane, RETENTION_LEASE_TTL_FACTOR } from "./retention-lane
5
5
  import { createLimitSync } from "./limit-sync.js";
6
6
  import { runDenySweep } from "./late-binds.js";
7
7
  export function runStage08(ctx) {
8
- const { approvalNonceStore, backend, backgroundAgentStore, ceilingVerdict, checkpointStore, config, configCenter, costQuota, fileHistoryErasure, handslessRunner, handslessSubRunner, hookAgentRunner, imageBakes, instanceId, localRoot, logger, mailboxStore, metrics, permissionRuleStores, pricing, rateLimiter, retentionExecutor, retentionLaneStore, rosterStore, runStore, runner, runnerTierFrozen, sqlWorkflowRunStore, subRunner, taskAttachmentStore, toolApproval, toolResultStore, workflowJournalStore, workflowNotifyGate, workflowNotifyJournal, workflowRecoverOpts, worktreeReap } = ctx;
8
+ const { approvalNonceStore, backend, backgroundAgentStore, ceilingVerdict, checkpointStore, config, configCenter, consolidationSeatWatch, costQuota, fileHistoryErasure, handslessRunner, handslessSubRunner, hookAgentRunner, imageBakes, instanceId, localRoot, logger, mailboxStore, metrics, permissionRuleStores, pricing, rateLimiter, retentionExecutor, retentionLaneStore, rosterStore, runStore, runner, sqlWorkflowRunStore, subRunner, taskAttachmentStore, toolApproval, toolResultStore, workflowJournalStore, workflowNotifyGate, workflowNotifyJournal, workflowRecoverOpts, worktreeReap } = ctx;
9
9
  const reaper = startReapers({
10
10
  config, logger, metrics, localRoot, backend, subRunner, runStore, checkpointStore,
11
11
  rateLimiter, costQuota, toolResultStore, fileHistoryErasure, taskAttachmentStore, imageBakes, worktreeReap,
@@ -71,7 +71,7 @@ export function runStage08(ctx) {
71
71
  }
72
72
  }
73
73
  };
74
- configCenter.startRefreshLoop({ runnerTierFrozen, pricing, limitSync, swapRunnerDeps, ceilingVerdict });
74
+ configCenter.startRefreshLoop({ pricing, limitSync, swapRunnerDeps, ceilingVerdict, checkpointStorePresent: checkpointStore !== undefined, ...(consolidationSeatWatch !== undefined ? { consolidationSeat: consolidationSeatWatch } : {}) });
75
75
  configCenter.initKeyResolver();
76
76
  return { ...ctx, otelExporter, reaper, retentionLaneStarted };
77
77
  }
@@ -64,7 +64,6 @@ export declare function runStage09(ctx: Awaited<ReturnType<typeof runStage08>>):
64
64
  scenarioDetails: Record<string, import("../capabilities/scenarios.js").ScenarioDetail>;
65
65
  scenarios: Record<string, import("../index.js").Scenario>;
66
66
  selectEnvTool: import("@sema-agent/core").ToolSpec<import("@sema-agent/core").TSchema> | undefined;
67
- singleUserAutoAcceptBaseline: boolean;
68
67
  taskLimitCaps: {
69
68
  timeoutSec: number | undefined;
70
69
  maxOutputTokens: number | undefined;
@@ -75,6 +74,7 @@ export declare function runStage09(ctx: Awaited<ReturnType<typeof runStage08>>):
75
74
  approvalAskAudit: import("../approval-ask-audit-store.js").FileApprovalAskAuditStore | undefined;
76
75
  centerRuntimeCapsResolver: ((principal: string | undefined) => Promise<import("@sema-agent/core").RuntimeCaps | undefined>) | undefined;
77
76
  checkpointStore: import("../plugins/store-backend.js").CheckpointStoreFull | undefined;
77
+ consolidationSeatWatch: import("./memory-consolidation.js").ConsolidationSeatWatch | undefined;
78
78
  durableEnabled: boolean;
79
79
  elicitation: import("../elicitation.js").ElicitationCoordinator | undefined;
80
80
  fleetBus: import("../fleet/fleet-bus.js").FleetEventBus;
@@ -95,7 +95,6 @@ export declare function runStage09(ctx: Awaited<ReturnType<typeof runStage08>>):
95
95
  retentionLaneStore: import("../plugins/retention-lane-store-sql.js").RetentionLaneStore | undefined;
96
96
  runner: import("@sema-agent/core").Runner;
97
97
  runnerDeps: import("@sema-agent/core").RunnerDeps;
98
- runnerTierFrozen: boolean;
99
98
  runtimeCapsPeekResolver: import("../runtime-caps-resolver.js").EntitlementsResolverFn | undefined;
100
99
  sendFileLedger: import("../plugins/send-file-ledger.js").SendFileLedger | undefined;
101
100
  sendUserFileEmitter: import("../capabilities/send-user-file-tool.js").SendUserFileEmitter | undefined;
@@ -128,7 +127,7 @@ export declare function runStage09(ctx: Awaited<ReturnType<typeof runStage08>>):
128
127
  dropSessionScoped: (sid: string) => void;
129
128
  executionEnvFactory: import("@sema-agent/core").ExecutionEnvFactory | undefined;
130
129
  fenceSessionOwner: (sid: string, owner: string | null) => void;
131
- lspManager: import("../lsp/manager.js").E2bLspManager | import("@sema-agent/core").NodeLspManager | undefined;
130
+ lspManager: import("@sema-agent/core").NodeLspManager | import("../lsp/manager.js").E2bLspManager | undefined;
132
131
  perSessionCwd: Map<string, string>;
133
132
  perTaskImage: import("../per-task-image.js").PerTaskImageRegistry;
134
133
  sendUserFileTaskEnvs: import("../capabilities/sandbox-file-send.js").TaskEnvRegistry | undefined;
@@ -64,7 +64,6 @@ export declare function runStage10(ctx: Awaited<ReturnType<typeof runStage09>>):
64
64
  scenarioDetails: Record<string, import("../capabilities/scenarios.js").ScenarioDetail>;
65
65
  scenarios: Record<string, import("../index.js").Scenario>;
66
66
  selectEnvTool: import("@sema-agent/core").ToolSpec<import("@sema-agent/core").TSchema> | undefined;
67
- singleUserAutoAcceptBaseline: boolean;
68
67
  taskLimitCaps: {
69
68
  timeoutSec: number | undefined;
70
69
  maxOutputTokens: number | undefined;
@@ -75,6 +74,7 @@ export declare function runStage10(ctx: Awaited<ReturnType<typeof runStage09>>):
75
74
  approvalAskAudit: import("../approval-ask-audit-store.js").FileApprovalAskAuditStore | undefined;
76
75
  centerRuntimeCapsResolver: ((principal: string | undefined) => Promise<import("@sema-agent/core").RuntimeCaps | undefined>) | undefined;
77
76
  checkpointStore: import("../plugins/store-backend.js").CheckpointStoreFull | undefined;
77
+ consolidationSeatWatch: import("./memory-consolidation.js").ConsolidationSeatWatch | undefined;
78
78
  durableEnabled: boolean;
79
79
  elicitation: import("../elicitation.js").ElicitationCoordinator | undefined;
80
80
  fleetBus: import("../fleet/fleet-bus.js").FleetEventBus;
@@ -95,7 +95,6 @@ export declare function runStage10(ctx: Awaited<ReturnType<typeof runStage09>>):
95
95
  retentionLaneStore: import("../plugins/retention-lane-store-sql.js").RetentionLaneStore | undefined;
96
96
  runner: import("@sema-agent/core").Runner;
97
97
  runnerDeps: import("@sema-agent/core").RunnerDeps;
98
- runnerTierFrozen: boolean;
99
98
  runtimeCapsPeekResolver: import("../runtime-caps-resolver.js").EntitlementsResolverFn | undefined;
100
99
  sendFileLedger: import("../plugins/send-file-ledger.js").SendFileLedger | undefined;
101
100
  sendUserFileEmitter: import("../capabilities/send-user-file-tool.js").SendUserFileEmitter | undefined;
@@ -128,7 +127,7 @@ export declare function runStage10(ctx: Awaited<ReturnType<typeof runStage09>>):
128
127
  dropSessionScoped: (sid: string) => void;
129
128
  executionEnvFactory: import("@sema-agent/core").ExecutionEnvFactory | undefined;
130
129
  fenceSessionOwner: (sid: string, owner: string | null) => void;
131
- lspManager: import("../lsp/manager.js").E2bLspManager | import("@sema-agent/core").NodeLspManager | undefined;
130
+ lspManager: import("@sema-agent/core").NodeLspManager | import("../lsp/manager.js").E2bLspManager | undefined;
132
131
  perSessionCwd: Map<string, string>;
133
132
  perTaskImage: import("../per-task-image.js").PerTaskImageRegistry;
134
133
  sendUserFileTaskEnvs: import("../capabilities/sandbox-file-send.js").TaskEnvRegistry | undefined;
@@ -23,7 +23,7 @@ import { bindServerLateSeats } from "./late-binds.js";
23
23
  import { serviceVersion } from "../service-version.js";
24
24
  import { buildDisplaySafeUrl } from "../trace/redact.js";
25
25
  export async function runStage10(ctx) {
26
- 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, parkedReviveTool, 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, singleUserAutoAcceptBaseline, sqlEngineFacts, sqlWorkflowRunStore, staticWiring, storeBackendDegraded, subRunner, subagentSteerRegistry, taskAttachmentStore, taskLimitCaps, taskTimeoutSec, toolApproval, toolResultStore, usageWindowStore, webSearchProvider, workflowAgentRegistry, workflowAgentSessionIndex, workflowCompletionInbox, workflowJournalStore, workflowModelAllowlistSnapshot, workflowNotifyGate, workflowNotifyJournal, workflowRecoverOpts, workflowRunStore, workflowsCapable } = ctx;
26
+ 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, parkedReviveTool, 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
27
  const stores = {
28
28
  runStore,
29
29
  resumeAnchorStore: resumeAnchorStore ? resumeAnchorStore : undefined,
@@ -171,7 +171,7 @@ export async function runStage10(ctx) {
171
171
  hookLlm, hookAgent, fleetBus, hookWakeBus, resumeAnchorStore, ownerAware, taskLimitCaps, taskTimeoutSec,
172
172
  noteSessionShellGate: sessionShellGateRegistry.note,
173
173
  selectEnvTool, sendUserFileToolSpec, memoryEngine, durableEnabled, approvalExemptionStore,
174
- singleUserAutoAcceptBaseline, checkpointStore, deploymentHooks, imageIndex, perTaskImage,
174
+ checkpointStore, deploymentHooks, imageIndex, perTaskImage,
175
175
  sessionEnvSelection,
176
176
  lockedKeys: governanceSeams.lockedKeys,
177
177
  liveQuestionFace: question,
@@ -78,8 +78,20 @@ export interface ApplyPluginsResult {
78
78
  /** 本轮拉取失败、沿用上次成功物化的插件名。 */
79
79
  lkgUsed: string[];
80
80
  }
81
- /** 总入口(条款 3/7/8):逐条物化 + 装载 + 让位合并。materialize 盘位 = `<cacheRoot>/<name>/live`
82
- * (成功后原子 rename 替换,失败保留旧盘位 = LKG)。 */
81
+ /**
82
+ * 装载器**会物化**的插件(按声明序)—— 每条声明过**同一只**判定 {@link validatePluginDecl},只留 `ok` 的
83
+ * `{name, url, sha?}`。restart 指纹(`config-center/restart-signal.ts` 的 skills 片,S-668 波 0)读的就是它:
84
+ * 装载器不读的字段(notes / version)、整条跳过的声明(`enabled:false` / marketplace 源 / 白名单外 host / 坏形)
85
+ * 在这里一并消失,于是「重启之后什么都不会变」的发布不签重启(codex 对抗复审 r1 [medium]);顺序保留 ——
86
+ * 撞名让位按声明序,对调两条是真变化。`allowHosts` 与装载时同一份(部署事实 `PLUGINS_ALLOW_HOSTS`)。
87
+ */
88
+ export declare function materializablePlugins(eff: {
89
+ plugins?: unknown;
90
+ }, allowHosts: readonly string[]): Array<{
91
+ name: string;
92
+ url: string;
93
+ sha?: string;
94
+ }>;
83
95
  export declare function applyCenterPlugins(baseline: LoadedSkill[], eff: {
84
96
  plugins?: unknown;
85
97
  }, opts: {
@@ -113,12 +113,20 @@ export function resolvePluginSkillsRoot(cloneRoot) {
113
113
  }
114
114
  return { ok: true, dir: candidate };
115
115
  }
116
+ function declaredPlugins(eff) {
117
+ if (Array.isArray(eff.plugins))
118
+ return eff.plugins;
119
+ const wrapped = eff.plugins?.plugins;
120
+ return Array.isArray(wrapped) ? wrapped : [];
121
+ }
122
+ export function materializablePlugins(eff, allowHosts) {
123
+ return declaredPlugins(eff).flatMap((decl) => {
124
+ const v = validatePluginDecl(decl, allowHosts);
125
+ return v.ok ? [{ name: v.name, url: v.url, ...(v.sha !== undefined ? { sha: v.sha } : {}) }] : [];
126
+ });
127
+ }
116
128
  export async function applyCenterPlugins(baseline, eff, opts) {
117
- const declared = Array.isArray(eff.plugins)
118
- ? (eff.plugins)
119
- : Array.isArray(eff.plugins?.plugins)
120
- ? (eff.plugins.plugins)
121
- : [];
129
+ const declared = declaredPlugins(eff);
122
130
  const out = { skills: [...baseline], failures: [], letGo: [], lkgUsed: [] };
123
131
  if (declared.length === 0)
124
132
  return out;
@@ -18,8 +18,9 @@ export const HANDS_BAND_TOOL_NAMES = Object.freeze([
18
18
  "Monitor",
19
19
  "EnterWorktree",
20
20
  "ExitWorktree",
21
+ "ListAgents",
21
22
  ].filter((n, i, all) => !RETIRED_TOOL_NAMES.has(n) && all.indexOf(n) === i));
22
- export const HANDS_DUAL_USE_TOOL_NAMES = Object.freeze(["TaskOutput", "TaskStop", "SendMessage", "AgentTranscript"]);
23
+ export const HANDS_DUAL_USE_TOOL_NAMES = Object.freeze(["TaskOutput", "TaskStop", "SendMessage", "AgentTranscript", "ListAgents"]);
23
24
  export const HANDS_EXCLUSIVE_TOOL_NAMES = Object.freeze(HANDS_BAND_TOOL_NAMES.filter((n) => !HANDS_DUAL_USE_TOOL_NAMES.includes(n)));
24
25
  export function handsDenyPolicy() {
25
26
  return createAllowDenyPolicy({ deny: [...HANDS_EXCLUSIVE_TOOL_NAMES] });
@@ -5,7 +5,7 @@ import { parseRepo, repoToolsFor } from "./repo-tools.js";
5
5
  import { createCouncilTool, clampRounds } from "./code-review-council.js";
6
6
  import { stablePrompt } from "./prompt.js";
7
7
  import { createDiscussionTools, discussionCoordinatorPrompt, getTeam, teamNames } from "./team.js";
8
- import { createWebSearchBackend, webSearchConfigFromSettings } from "../plugins/web-search.js";
8
+ import { createWebSearchBackend, judgeRequestWebSearch } from "../plugins/web-search.js";
9
9
  import { skillsForScenario } from "./skills.js";
10
10
  import { pickHandsRunner } from "./hands-lane.js";
11
11
  export { SEMA_IDENTITY } from "./prompts/identity.js";
@@ -31,12 +31,25 @@ export function codePromptProvider(brand) {
31
31
  function subRunnerFor(deps, hands) {
32
32
  return pickHandsRunner(hands, { full: deps.subRunner, handsless: deps.handslessSubRunner });
33
33
  }
34
+ function requestWebSearchBackend(verdict, deployment, logger) {
35
+ switch (verdict.kind) {
36
+ case "absent":
37
+ return deployment;
38
+ case "honored":
39
+ return createWebSearchBackend(verdict.config);
40
+ case "malformed":
41
+ logger?.warn("web_search_settings_unusable", {
42
+ issue: verdict.issue,
43
+ note: "this request carries a settings.webSearch block that is not well-formed (stored before the submit-time check, or assembled without it); WebSearch is not mounted for this call rather than sending the searches to a different backend",
44
+ });
45
+ return undefined;
46
+ }
47
+ }
34
48
  export function buildScenarios(deps) {
35
49
  const defaultScenario = (req, _principal, ctx) => {
36
- const reqWebSearch = deps.requirePrincipal !== true
37
- ? webSearchConfigFromSettings(req?.settings?.webSearch)
38
- : undefined;
39
- const webSearch = reqWebSearch ? createWebSearchBackend(reqWebSearch) : deps.webSearch;
50
+ const webSearch = requestWebSearchBackend(deps.requirePrincipal !== true
51
+ ? judgeRequestWebSearch(req?.settings?.webSearch)
52
+ : { kind: "absent" }, deps.webSearch, deps.logger);
40
53
  return {
41
54
  tools: [
42
55
  nowTool(),
@@ -18,8 +18,8 @@
18
18
  * 在本仓缺省场景下那会连 Agent 与 Task 四件一起卷进候选(与 CC 姿态相反),所以我们**刻意不开**;
19
19
  * 🔴 该判断在 core 5.40.0 / 7.15.0 上**逐字仍成立**(亲验):两次收窄动的都是「候选集是谁」,而
20
20
  * Agent / Task 四件是 **caller spec 工具**,收窄后照样在候选池里 —— 不开 auto 的理由没有被消解。
21
- * 我方**从不接线**这个座:全树零 `RunnerDeps.deferMode` 赋值(`deferModelPlane` 是 config-center 的
22
- * 同形近名,与本轴无关)。机器钉见 test/tool-defer.test.ts 的 [ref] 件⑤ 格 —— 这条「零接线」正是本仓
21
+ * 我方**从不接线**这个座:全树零 `RunnerDeps.deferMode` 赋值(config-center 曾有同形近名 `deferModelPlane`,
22
+ * 与本轴无关,已于 S-668 波 0 退役)。机器钉见 test/tool-defer.test.ts 的 [ref] 件⑤ 格 —— 这条「零接线」正是本仓
23
23
  * 在两个 core 世代之间行为逐字不变的**全部**依据,所以它值一根钉而不只是一句注。
24
24
  * - per-tool `ToolSpec.defer:true` 是本 helper 施加的那一位。**7.15.0 起它是一次「进候选集」的声明,
25
25
  * 不再是「一定占位」的保证** —— 长尾集单独一只(下面实测 ≈842 字符)远在阈值之下,于是这根旋钮在
@@ -10,14 +10,14 @@ export type CatalogDangerAxis = (typeof CATALOG_DANGER_AXES)[number];
10
10
  /** 域 = 配置九组(类型锁步:组集变了这里跟着红)。 */
11
11
  export type CatalogDomain = keyof ServiceConfigGroups;
12
12
  export declare const CATALOG_RESTART_SLICES: readonly RestartSlice[];
13
- /** center 联接座(spec 层)。`models-plane` 是内部时机词:build 时按 tier 表在场性实算成 hot/restart。 */
13
+ /** center 联接座(spec 层)。 */
14
14
  export interface CatalogCenterSeatSpec {
15
15
  readonly domain: string;
16
16
  readonly path: string;
17
17
  /** `centerManagedKeys`(config-center 在管键集)里查「在管」用的键;models 面统一 "models"。 */
18
18
  readonly centerKey: string;
19
19
  readonly precedence: "center-wins" | "env-wins";
20
- readonly timing: "hot" | "restart" | "inert" | "models-plane";
20
+ readonly timing: "hot" | "restart" | "inert";
21
21
  readonly restartSlice?: RestartSlice;
22
22
  readonly restartReason?: string;
23
23
  }
@@ -1,7 +1,7 @@
1
1
  import { CROSS_SESSION_INBOUND_SETTINGS, CROSS_SESSION_DIALOG_EXPIRY_VALUES } from "@sema-agent/core";
2
2
  import { THINKING_FORMATS } from "./model-compat.js";
3
3
  import { RESILIENCE_DEFAULTS, TASK_WRITE_FACE_ESCALATIONS } from "./config-types.js";
4
- import { webSearchConfigFromEnv, WEB_SEARCH_PROVIDERS } from "./plugins/web-search.js";
4
+ import { webSearchProviderFromEnv, WEB_SEARCH_PROVIDERS } from "./plugins/web-search.js";
5
5
  import { AUTONOMY_MODES } from "./runtime-governance.js";
6
6
  import { REMOTE_EXEC_PROVIDER_WORDS } from "./execution-lane-caps.js";
7
7
  import { CENTER_READ_FACE_LIVE_KEYS } from "./config-center/read-face.js";
@@ -15,10 +15,9 @@ const RESTART_SLICE_SET = {
15
15
  mcp: true,
16
16
  a2a: true,
17
17
  scenarios: true,
18
- "runtime-gates": true,
19
- "models-tiers": true,
20
18
  "degrade-route": true,
21
19
  "read-face": true,
20
+ "memory-consolidation": true,
22
21
  };
23
22
  export const CATALOG_RESTART_SLICES = Object.keys(RESTART_SLICE_SET);
24
23
  const r = (name, domain, type, semantics, opts = {}) => ({ name, domain, type, semantics, ...opts });
@@ -31,7 +30,7 @@ const cLimits = (path, over = {}) => ({
31
30
  ...over,
32
31
  });
33
32
  const cGov = (path, over = {}) => ({ domain: "governance", path, centerKey: path, precedence: "center-wins", timing: "hot", ...over });
34
- const cModels = (path) => ({ domain: "models", path, centerKey: "models", precedence: "center-wins", timing: "models-plane" });
33
+ const cModels = (path) => ({ domain: "models", path, centerKey: "models", precedence: "center-wins", timing: "hot" });
35
34
  const cRead = (path, centerKey, over = {}) => ({
36
35
  domain: "readFace",
37
36
  path,
@@ -65,7 +64,7 @@ export const CONFIG_CATALOG = [
65
64
  r("APPROVAL_DENY", "approval", "csv", "始终拒绝的工具名单(装配期拒不可匹配名)", { danger: SEC, configKey: "approvalDeny" }),
66
65
  r("APPROVAL_HMAC_KEYS", "approval", "secret", "直连门(D-G)HMAC 完整性密钥集(空=直连门不激活)", { danger: SEC }),
67
66
  r("APPROVAL_NEVER_AUTO", "approval", "csv", "永不被 AUTO_BUDGET 自动放行的工具集(不可逆动作恒要人)", { danger: SEC, configKey: "approvalNeverAuto" }),
68
- r("APPROVAL_REQUIRE", "approval", "csv", "必须人审的工具名单(F4 门;center 腿走 runtime-gates 重启片)", { danger: SEC, configKey: "approvalRequire", center: cGov("approvalRequire", { timing: "restart", restartSlice: "runtime-gates" }) }),
67
+ r("APPROVAL_REQUIRE", "approval", "csv", "必须人审的工具名单(F4 门;center 腿热:refresh 拍整表换代,无 checkpoint 店的部署发非空名单 ⇒ 审批组整组拒)", { danger: SEC, configKey: "approvalRequire", center: cGov("approvalRequire") }),
69
68
  r("APPROVAL_TERMINAL_BACKSTOP_MS", "approval", "number", "SQL checkpoint 店 pending 行的 crash-safe 绝对 GC 上限(ms;下限 60s,非有限值落默认)", { staticDefault: String(30 * 86_400_000), danger: DATA }),
70
69
  r("APPROVAL_TIMEOUT_SEC", "approval", "number", "durable HITL 等人上限(秒);0=无限期等,时间型 suspended 回收不跑", { staticDefault: "0", configKey: "approvalTimeoutSec" }),
71
70
  r("ASK_QUESTION_ENABLED", "approval", "boolean", "AskUserQuestion 活流 HITL 装配开关", { derivedDefaultNote: "posture 三态:显式词恒赢,缺席 = 单用户(REQUIRE_PRINCIPAL≠true)⇒ on / 多租户 ⇒ off", configKey: "askQuestionEnabled" }),
@@ -102,7 +101,7 @@ export const CONFIG_CATALOG = [
102
101
  r("CORS_ORIGIN", "auth", "csv", "CORS 白名单(单值 v1 兼容回显;多值按请求 Origin 匹配 + Vary;未设=无 CORS 面)", { danger: SEC, configKey: "corsOrigins" }),
103
102
  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" }) }),
104
103
  r("CROSS_SESSION_DIALOG_EXPIRY", "approval", "enum", "held 跨会话消息等待人审的期限(CC settings 键名 dialogExpiry;未设=不铸键,引擎自落缺省 5m;词表外拒启)", { danger: SEC, enumValues: [...CROSS_SESSION_DIALOG_EXPIRY_VALUES], configKey: "crossSessionDialogExpiry", derivedDefaultNote: "缺席 ⇒ 不铸键,core 自缺省 5m", resolve: (c) => c.crossSessionDialogExpiry ?? null }),
105
- r("CROSS_SESSION_INBOUND", "approval", "enum", "入站跨会话消息的部署/组织层治理(CC settings 键名 crossSessionInbound 的 managed 层;未设=本层不表态≠accept;词表外拒启)", { danger: SEC, enumValues: [...CROSS_SESSION_INBOUND_SETTINGS], configKey: "crossSessionInbound", derivedDefaultNote: "缺席 ⇒ managed 层不设,由其余层与 mode-parity 决定", resolve: (c) => c.crossSessionInbound ?? null }),
104
+ r("CROSS_SESSION_INBOUND", "approval", "enum", "入站跨会话消息的部署/组织层治理(CC settings 键名 crossSessionInbound 的 managed 层;未设=本层不表态≠ next-turn;词表外拒启)", { danger: SEC, enumValues: [...CROSS_SESSION_INBOUND_SETTINGS], configKey: "crossSessionInbound", derivedDefaultNote: "缺席 ⇒ managed 层不设,由其余层与 mode-parity 决定", resolve: (c) => c.crossSessionInbound ?? null }),
106
105
  r("DB_BACKEND", "store", "enum", "store 后端引擎选择(mysql 兼容/pg/本机文件店/纯内存)", { enumValues: ["mysql", "pg", "local", "memory"], danger: DATA, derivedDefaultNote: "裸 boot ⇒ local(多租户裸 boot ⇒ memory + notice);有 SQL 信号 ⇒ mysql", configKey: "dbBackend", resolve: (c) => c.dbBackend }),
107
106
  r("DB_QUERY_TIMEOUT_MS", "store", "number", "per-query DB 超时(ms;坏值回默认姿态+warn,绝不 NaN 进 driver)", { derivedDefaultNote: "缺席 ⇒ 池级不设/计数族 30s(按引擎)", configKey: "dbQueryTimeoutMs" }),
108
107
  r("DEFAULT_SCENARIO", "integrations", "string", "出厂缺省场景(编码产品出厂态;通用中性 persona 用 default 显式选回)", { staticDefault: "code", configKey: "defaultScenario" }),
@@ -454,12 +453,12 @@ export const CONFIG_CATALOG = [
454
453
  r("UNREACHED_ASK_TTL_MS", "approval", "number", "零活流铸造的 ask 悬挂窗(ms;[60s,24h] 拒启带界)", { staticDefault: "3600000" }),
455
454
  r("USAGE_WINDOWS", "orchestration", "opaque", "部署级治理窗(JSON 数组;滑动窗内跨任务 token/$ 预算,key=principal)", { danger: BILL }),
456
455
  r("WEB_SEARCH_API_KEY", "orchestration", "secret", "web search 后端 API 凭据(brave/tavily)", { danger: [...SEC, ...BILL] }),
457
- r("WEB_SEARCH_ENDPOINT", "orchestration", "url", "web search 后端 endpoint 覆写(searxng 自建面)"),
458
- r("WEB_SEARCH_MAX_RESULTS", "orchestration", "number", "web search 单次结果数上限", { derivedDefaultNote: "缺席/非正 ⇒ backend 默认" }),
456
+ r("WEB_SEARCH_ENDPOINT", "orchestration", "url", "web search 后端 endpoint 覆写(searxng 实例地址;brave 下可指向 Brave 兼容网关的完整搜索 URL,如 Nólë;空 = 未设;不是 scheme://host 形的绝对 URL ⇒ 拒启)"),
457
+ r("WEB_SEARCH_MAX_RESULTS", "orchestration", "number", "web search 单次结果数上限(非数 / < 1 ⇒ 拒启;> 20 夹到 20)", { derivedDefaultNote: "缺席/空 ⇒ backend 默认 10" }),
459
458
  r("WEB_SEARCH_PROBE_ON_BOOT", "orchestration", "boolean", "boot 期 web search 连通性探测(只认 true/1;含糊值当没开——猜错方向是凭空出网)", { staticDefault: "false" }),
460
- r("WEB_SEARCH_PROVIDER", "orchestration", "enum", "web search 工具后端(设合法词=装配 WebSearch;其它/缺席=不装配)", { enumValues: WEB_SEARCH_PROVIDERS, danger: SEC, resolve: (_c, env) => webSearchConfigFromEnv(env)?.provider ?? null }),
461
- r("WEB_SEARCH_SEARXNG_PARAMS", "orchestration", "string", "searxng 额外查询参数(k=v 对;解析不出任何一对 ⇒ 键不铸)"),
462
- r("WEB_SEARCH_TIMEOUT_MS", "orchestration", "number", "web search 单次超时(ms)", { derivedDefaultNote: "缺席/非正 ⇒ backend 默认" }),
459
+ r("WEB_SEARCH_PROVIDER", "orchestration", "enum", "web search 工具后端(设合法词=装配 WebSearch;其它/缺席=不装配)", { enumValues: WEB_SEARCH_PROVIDERS, danger: SEC, resolve: (_c, env) => webSearchProviderFromEnv(env) ?? null }),
460
+ r("WEB_SEARCH_SEARXNG_PARAMS", "orchestration", "string", "searxng 额外查询参数(name=value;name=value;空 ⇒ 不铸;任一段不成形 / 名不是参数名 / 同名两次 ⇒ 拒启)"),
461
+ r("WEB_SEARCH_TIMEOUT_MS", "orchestration", "number", "web search 单次超时(ms;非数 / < 1 ⇒ 拒启;< 1000 抬到 1000)", { derivedDefaultNote: "缺席/空 ⇒ backend 默认 10000" }),
463
462
  r("WORKFLOW_AGENTS_READONLY", "orchestration", "boolean", "workflow 子 agent 只读钳(TOB 保守旋钮;默认同权 CC parity)", { staticDefault: "false", danger: SEC, configKey: "workflowAgentsReadOnly" }),
464
463
  r("WORKFLOW_JOURNAL_RETENTION_MS", "orchestration", "number", "workflow_journal GC 保留期(ms)", { staticDefault: String(7 * 24 * 60 * 60 * 1000), danger: DATA, configKey: "workflowJournalRetentionMs" }),
465
464
  r("WORKFLOW_ORPHAN_GRACE_MS", "orchestration", "number", "running workflow 判崩溃孤儿的年龄阈(ms)", { staticDefault: String(24 * 60 * 60 * 1000), configKey: "workflowOrphanGraceMs" }),
@@ -537,8 +536,8 @@ export function buildConfigCatalog(inputs) {
537
536
  const seat = spec.center;
538
537
  const domainExists = seat !== undefined && centerDomains.includes(seat.domain);
539
538
  const managedNow = seat !== undefined && (centerManagedKeys?.has(seat.centerKey) ?? false);
540
- const centerTiming = seat === undefined ? undefined : seat.timing === "models-plane" ? "hot" : seat.timing;
541
- const centerRestartSlice = seat === undefined || seat.timing === "models-plane" ? undefined : seat.restartSlice;
539
+ const centerTiming = seat?.timing;
540
+ const centerRestartSlice = seat?.restartSlice;
542
541
  const centerEffective = seat !== undefined && managedNow && centerTiming === "hot" && (seat.precedence === "center-wins" || !envHeld);
543
542
  const effectiveLane = centerEffective ? "center" : envHeld ? "env" : "default";
544
543
  let effectiveValue;
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * EffectiveConfig application — mutate the boot `ServiceConfig` OVER with a pulled `EffectiveConfig`
3
- * (models/roles/tiers/projects `applyEffective`, the boot-only approval gate + runtime limit residue
3
+ * (models/roles/tiers/projects `applyEffective`, the approval group + runtime limit residue
4
4
  * `applyRuntimeGates`, the per-request-hot governance baton `applyRuntimeHot`) plus their shared helpers
5
5
  * (`toModel`, `mutateInPlace`, `resolveDefaultModelName`, `logEffectiveDiff`). Split out of `facade.ts`
6
6
  * ([ref] A13, internal-lossless — the facade re-exports every symbol below unchanged).
@@ -10,6 +10,7 @@ import type { ServiceConfig } from "../config-types.js";
10
10
  import { type SealedKeyOpener } from "../sealed-key.js";
11
11
  import type { Logger } from "../observability/logger.js";
12
12
  import type { EffectiveConfig } from "./types.js";
13
+ import { type ConfigApplyReport } from "./apply-ledger.js";
13
14
  /** Apply center config OVER the env-derived config (mutates `config`). Empty domains keep env defaults. */
14
15
  /** Replace a record's contents IN PLACE (clear keys, copy source) so holders of the SAME reference see the
15
16
  * change. This is what makes models/roles/pricing HOT-apply: core's Runner reads
@@ -34,41 +35,38 @@ export declare const UNPARSABLE_AT_MODEL_ALLOWLIST_ENTRY = "\0<unparsable-at-mod
34
35
  * commit 之前,活配置零触碰)。caller 收到 false 必须把**同一世代的旁路消费**(prompts 采用/pricing/
35
36
  * keyResolver/etag 推进/LKG 落盘)一并跳过——否则 applyEffective 拒了主面、旁路却半应用同一个被拒
36
37
  * 世代,混合世代从侧门回来。首次 apply(live 未登记)恒 true。 */
37
- /** [ref]:一次 apply 的**世代账载荷**(post-commit 发,[ref]② —— commit 段仍是零回调零发射)。 */
38
- export interface ApplyReport {
39
- readonly version: number;
40
- /** [ref]:本候选的收货序数(= opts.generation 原样带回;世代账按它记拒收,身份标仍是 version)。 */
41
- readonly ordinal?: number;
42
- readonly limits: {
43
- /** 本世代真正换了值的键。 */
44
- readonly applied: readonly string[];
45
- /** 值合法但**不热**的键(逐键 defer:变更只能重启生效)。 */
46
- readonly deferred?: ReadonlyArray<{
47
- readonly key: string;
48
- readonly reason: string;
49
- }>;
50
- /** 整批被拒时的成因(消毒:键名 + 判据文案,绝不带值)。 */
51
- readonly rejected?: {
52
- readonly keys: string[];
53
- readonly reason: string;
54
- readonly restartReason?: string;
55
- };
56
- };
38
+ /** [ref]:一次 apply 的**世代账载荷**(post-commit 发,[ref]② —— commit 段仍是零回调零发射)。形 = 世代账的
39
+ * `ConfigApplyReport`(**每一组都报**,组闭集 `CONFIG_APPLY_GROUPS` 在 apply-ledger.ts)+ 整代判决。 */
40
+ export interface ApplyReport extends ConfigApplyReport {
57
41
  /** 🔴 codex R2-F1:本候选是否**完整**应用 —— 有任何一组被拒 ⇒ false。调用方据此决定要不要把它发布成
58
42
  * LKG / 推进 etag(一个「其余面提交了但某组被拒」的候选覆盖掉好 LKG,重启后就再也回不去了)。 */
59
43
  readonly fullyApplied: boolean;
60
44
  }
61
- export declare function applyEffective(config: ServiceConfig, eff: EffectiveConfig, logger?: Logger, opts?: {
62
- teamsOnly?: boolean;
63
- sealedKeys?: SealedKeyOpener;
64
- deferModelPlane?: boolean;
65
- generation?: number;
66
- report?: (r: ApplyReport) => void;
67
- swapPlane?: (plane: {
45
+ /**
46
+ * S-668 波 2 —— 一次 apply 在**哪条腿**上跑,以及这条腿知道的**部署事实**。必填(没有缺省腿):安全轴上「忘了说是哪条
47
+ * 腿」不许静默落成某一种语义([ref])。
48
+ * · `boot` —— 装配期(stage-02 的 boot 拉取 / LKG 起服;run-local 的一次性装配)。此刻 checkpoint 店**还没建**,
49
+ * 审批组的「门意图可服务」由装配完之后的 stage-07 对**整份** config 判(拒启)—— 与热腿是同一只
50
+ * `gateIntentUnserviceableReason`,不是两份判据。
51
+ * · `hot` —— 进程已起(refresh 拍 / 迟到 boot 到货)。`checkpointStorePresent` = 本进程建没建出 checkpoint 店
52
+ * (stage-06 的产物,**不在 ServiceConfig 里**),审批组 stage 当场对候选判,不可服务 ⇒ 整组拒。
53
+ */
54
+ export type ApplyLeg = {
55
+ readonly leg: "boot";
56
+ } | {
57
+ readonly leg: "hot";
58
+ readonly checkpointStorePresent: boolean;
59
+ };
60
+ export type ApplyEffectiveOpts = ApplyLeg & {
61
+ readonly sealedKeys?: SealedKeyOpener;
62
+ readonly generation?: number;
63
+ readonly report?: (r: ApplyReport) => void;
64
+ readonly swapPlane?: (plane: {
68
65
  models: Record<string, Model>;
69
66
  tiers: Record<string, string>;
70
67
  }) => void;
71
- }): boolean;
68
+ };
69
+ export declare function applyEffective(config: ServiceConfig, eff: EffectiveConfig, logger: Logger | undefined, opts: ApplyEffectiveOpts): boolean;
72
70
  /**
73
71
  * UNIFORM presence semantics across the center gates (center `f95438a` adopted this from our sentinel flag —
74
72
  * SERVICE-INTEGRATION「Runtime 治理/限额闸」): every gate field is center `.optional()`, so
@@ -87,14 +85,9 @@ export declare function applyEffective(config: ServiceConfig, eff: EffectiveConf
87
85
  /** `runtime` 域的限额五闸(= settings-schema `RuntimeConfig` 的全部键,[ref] 件3 对账门钉集合等式),ONE place:
88
86
  * legacy 位的 presence 判据({@link runtimeGatePresent} → stage-limits 的 `legacyGates`)、config-catalog 的
89
87
  * center 在管键、dry-run 现值快照都从这一张表派生。[ref] 批1 起五键**全热**(属主 stage-limits,boot+refresh
90
- * 双腿),所以本表与 restart-to-apply 再无关系——审批闸不在这里:它住 governance 域({@link BOOT_GATE_KEYS})。 */
88
+ * 双腿),所以本表与 restart-to-apply 再无关系——审批闸不在这里:它住 governance 域({@link stageApprovalGate})。 */
91
89
  export declare const RUNTIME_GATE_KEYS: readonly ["rateLimitPerMin", "maxTaskCostUsd", "maxTaskTokens", "maxPrincipalCostUsd", "costQuotaWindowSec"];
92
90
  export type RuntimeGateKey = (typeof RUNTIME_GATE_KEYS)[number];
93
- /** boot-only(restart-to-apply)的治理闸:今天只有 `governance.approvalRequire` 一键——它被 main.ts 在本函数之后
94
- * 用 `config` 构造进 approval gate,refresh 腿(teamsOnly)不写,变更由 restart-signal 的 `runtime-gates` 片通告。
95
- * ([ref] 批2 若把它转热,这张表清空、那个片随之退役——一处改动。) */
96
- export declare const BOOT_GATE_KEYS: readonly ["approvalRequire"];
97
- export type BootGateKey = (typeof BOOT_GATE_KEYS)[number];
98
91
  /** Uniform presence rule (center f95438a) for the `runtime` limit gates: "managed" when the field is present —
99
92
  * any number (incl. 0). Sole guard: costQuotaWindowSec must be > 0 (a 0 window is nonsensical — core divides by
100
93
  * it; center's `.positive()` already enforces, belt-and-suspenders here). The approval gate's presence rule
@@ -106,7 +99,8 @@ export type BootGateKey = (typeof BOOT_GATE_KEYS)[number];
106
99
  * 判据与各消费腿**同源**,不自铸第二份:
107
100
  * · limits 域 = `hasOwnProperty`(stage-limits 的 published 判据逐字——present 含显式 0);
108
101
  * · 六闸 legacy 位 = {@link runtimeGatePresent}(旧位 presence 规则的唯一属主);
109
- * · governance/runtime 热二件 + approvalRequire = 键在场(applyRuntimeHot 的 non-sticky 判据同门);
102
+ * · governance 三件(autonomy / commandPolicy / approvalRequire)= 键在场(applyRuntimeHot 与 stageApprovalGate
103
+ * 的 non-sticky presence 判据同门:`!== undefined`);
110
104
  * · readFace 域成员 = 键在场(read-face.ts 的逐键 env-wins 合成看的就是成员在场性);
111
105
  * · models 面 = 有启用行(markRosterLanded 同判据);atModelAllowlist 单列(整表重算键)。
112
106
  * 纯投影零副作用;`undefined` eff(还没拉到 / 纯 env 形)⇒ 空集。
@@ -114,11 +108,12 @@ export type BootGateKey = (typeof BOOT_GATE_KEYS)[number];
114
108
  export declare function centerManagedConfigKeys(eff: EffectiveConfig | undefined): ReadonlySet<string>;
115
109
  export declare function runtimeGatePresent(rt: NonNullable<EffectiveConfig["runtime"]>, key: RuntimeGateKey): boolean;
116
110
  /** 独立调用面的兼容壳(测试/外部):stage → 就地赋值 → 通知。applyEffective 不走这里——它把
117
- * assignments 并进自己的 commit 段以保住整世代原子性([ref]②)。入参是 eff 的两个域([ref] 件3:审批闸
111
+ * 赋值并进自己的 commit 段以保住整世代原子性([ref]②)。入参是 eff 的两个域([ref] 件3:审批闸
118
112
  * 住 governance,限额五闸住 runtime——两位各自的属主判据,这里不再合成视图)。
119
113
  * [ref]:限额五闸的属主已移交热腿,所以本壳**同样**要过 stage-limits —— 否则一个仍叫 applyRuntimeGates
120
- * 的函数会静默漏掉五个限额键(比改名更坏的那种沉默)。 */
121
- export declare function applyRuntimeGates(config: ServiceConfig, eff: Pick<EffectiveConfig, "runtime" | "governance"> | undefined, logger?: Logger): void;
114
+ * 的函数会静默漏掉五个限额键(比改名更坏的那种沉默)。S-668 波 2:审批组的判据依赖腿(④ 只在热腿判),
115
+ * 所以本壳与 applyEffective 一样**必须**说是哪条腿。 */
116
+ export declare function applyRuntimeGates(config: ServiceConfig, eff: Pick<EffectiveConfig, "runtime" | "governance"> | undefined, leg: ApplyLeg, logger?: Logger): void;
122
117
  export declare function applyRuntimeHot(config: ServiceConfig, gov: EffectiveConfig["governance"], logger?: Logger): void;
123
118
  /**
124
119
  * [ref]① 显式默认解析——applyEffective 与 dry-run(logEffectiveDiff.wouldDefaultModel)共用的单源。优先级