@sema-agent/server 7.68.0 → 7.70.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.
- package/README.md +3 -0
- package/README.zh-CN.md +3 -0
- package/USAGE.md +122 -9
- package/dist/adoption/plan.js +11 -0
- package/dist/approval-ask-machine.d.ts +1 -1
- package/dist/approval-card.d.ts +27 -0
- package/dist/approval-card.js +11 -0
- package/dist/approval-reconciler.d.ts +0 -54
- package/dist/approval-reconciler.js +2 -2
- package/dist/approval.js +5 -4
- package/dist/boot/config-center.d.ts +6 -7
- package/dist/boot/config-center.js +31 -8
- package/dist/boot/leader.js +1 -1
- package/dist/boot/parked-revive-gate.js +10 -9
- package/dist/boot/runner-deps.d.ts +35 -4
- package/dist/boot/runner-deps.js +2 -6
- package/dist/boot/workflow-orchestration.d.ts +2 -0
- package/dist/boot/workflow-orchestration.js +10 -2
- package/dist/budget.js +3 -2
- package/dist/config-catalog.d.ts +31 -2
- package/dist/config-catalog.js +61 -24
- package/dist/config-center/apply-effective.d.ts +8 -1
- package/dist/config-center/apply-effective.js +23 -9
- package/dist/config-center/facade.d.ts +1 -1
- package/dist/config-center/facade.js +1 -1
- package/dist/config-center/read-face.d.ts +30 -1
- package/dist/config-center/read-face.js +33 -15
- package/dist/config-center/restart-signal.js +2 -1
- package/dist/config-center/types.d.ts +20 -5
- package/dist/config-provider.js +1 -0
- package/dist/config-types.d.ts +17 -2
- package/dist/config.d.ts +0 -21
- package/dist/config.js +19 -6
- package/dist/http/route-ctx.d.ts +39 -1
- package/dist/http/routes/approvals-assistant.js +4 -12
- package/dist/http/routes/capabilities.js +1 -0
- package/dist/http/routes/diagnostics.js +4 -0
- package/dist/http/routes/runs.js +2 -3
- package/dist/http/routes/tasks.js +22 -18
- package/dist/http/routes/workflows.js +15 -7
- package/dist/http/server.d.ts +10 -2
- package/dist/http/server.js +74 -26
- package/dist/leader/wire.d.ts +9 -2
- package/dist/leader/wire.js +6 -6
- package/dist/main.js +8 -7
- package/dist/model-compat.d.ts +81 -0
- package/dist/model-compat.js +52 -0
- package/dist/observability/fail-open.d.ts +4 -0
- package/dist/observability/fail-open.js +4 -0
- package/dist/observability/metrics.d.ts +0 -6
- package/dist/observability/metrics.js +3 -1
- package/dist/observability/permission-rule-events.d.ts +20 -0
- package/dist/observability/permission-rule-events.js +28 -0
- package/dist/observability/run-terminal-log.d.ts +9 -19
- package/dist/observability/run-terminal-log.js +183 -32
- package/dist/observability/tool-trace.d.ts +12 -0
- package/dist/observability/tool-trace.js +5 -1
- package/dist/operator-ask.d.ts +46 -0
- package/dist/operator-ask.js +4 -0
- package/dist/orchestration/workflow-agent-session-index.d.ts +96 -0
- package/dist/orchestration/workflow-agent-session-index.js +146 -0
- package/dist/orchestration/workflow-journal-entry.d.ts +78 -0
- package/dist/orchestration/workflow-journal-entry.js +20 -0
- package/dist/orchestration/workflow-notify-journal.d.ts +43 -1
- package/dist/orchestration/workflow-notify-journal.js +43 -3
- package/dist/parked-decide.d.ts +115 -5
- package/dist/parked-decide.js +94 -26
- package/dist/plugins/pg-pool.js +8 -0
- package/dist/plugins/store-backend.d.ts +7 -1
- package/dist/plugins/store-backend.js +3 -1
- package/dist/plugins/tidb-pool.js +8 -0
- package/dist/plugins/workflow-journal-store-sql.d.ts +1 -1
- package/dist/plugins/workflow-journal-store-sql.js +5 -5
- package/dist/plugins/workflow-run-store-sql.d.ts +30 -0
- package/dist/plugins/workflow-run-store-sql.js +40 -0
- package/dist/posture-source.d.ts +23 -3
- package/dist/posture-source.js +1 -1
- package/dist/read-face-posture.d.ts +8 -1
- package/dist/read-face-posture.js +1 -0
- package/dist/router/route-orchestration.js +2 -1
- package/dist/run-cancel-context.d.ts +50 -1
- package/dist/run-cancel-context.js +22 -0
- package/dist/run-local.d.ts +14 -6
- package/dist/run-local.js +2 -2
- package/dist/runs.d.ts +0 -51
- package/dist/runs.js +40 -23
- package/dist/runtime-governance.js +5 -4
- package/dist/tool-approval.d.ts +31 -5
- package/dist/tool-approval.js +5 -4
- package/dist/trace/core-keyset-guard.d.ts +9 -5
- package/dist/trace/engine-notice-wire.d.ts +1 -1
- package/dist/trace/engine-notice-wire.js +1 -0
- package/dist/trace/ledger-events.d.ts +17 -1
- package/dist/trace/ledger-events.js +6 -1
- package/dist/trace/ledger-sink.d.ts +10 -8
- package/dist/trace/ledger-sink.js +2 -1
- package/dist/trace/project.d.ts +3 -2
- package/dist/trace/redact.d.ts +28 -0
- package/dist/trace/redact.js +50 -27
- package/package.json +4 -4
|
@@ -83,22 +83,23 @@ function autoModePostureOf(deps, row, caps) {
|
|
|
83
83
|
}
|
|
84
84
|
function createUnreachableAncestorClassifier(deps, row) {
|
|
85
85
|
const inner = createAutoModeDecider({
|
|
86
|
-
failureThreshold: 1,
|
|
87
86
|
classify: async () => {
|
|
88
87
|
throw new Error("the ancestor task's auto-mode classifier is a live closure over that task's own transcript/brain — it cannot be " +
|
|
89
|
-
"reconstructed on a fresh replica, so this inherited layer has no classifier verdict to give
|
|
90
|
-
"through to the ancestor's frozen approver chain)");
|
|
88
|
+
"reconstructed on a fresh replica, so this inherited layer has no classifier verdict to give");
|
|
91
89
|
},
|
|
92
|
-
onBreakerOpen: () => deps.logger.warn("parked_revive_ancestor_classifier_unreachable", {
|
|
93
|
-
handle: row.handle,
|
|
94
|
-
sessionId: row.sessionId,
|
|
95
|
-
note: "an inherited ancestor layer was auto-mode armed at park time; its classifier does not survive the process boundary — inherited asks resolve on the approver chain (durable park) instead of a classifier verdict",
|
|
96
|
-
}),
|
|
97
90
|
});
|
|
91
|
+
let warned = false;
|
|
98
92
|
return {
|
|
99
|
-
breakerOpen: () => inner.breakerOpen(),
|
|
100
93
|
decide: async (input, signal) => {
|
|
101
94
|
recordFailOpen("server.parked-revive.ancestor-classifier-unreachable", row.handle);
|
|
95
|
+
if (!warned) {
|
|
96
|
+
warned = true;
|
|
97
|
+
deps.logger.warn("parked_revive_ancestor_classifier_unreachable", {
|
|
98
|
+
handle: row.handle,
|
|
99
|
+
sessionId: row.sessionId,
|
|
100
|
+
note: "an inherited ancestor layer was auto-mode armed at park time; its classifier does not survive the process boundary — since core 7.12.0 (#661) the inherited station DENIES such calls outright (it no longer falls through to the frozen approver chain), so classifier-eligible inherited asks on this redeemed leg are auto-denied, not re-parked for a person",
|
|
101
|
+
});
|
|
102
|
+
}
|
|
102
103
|
return inner.decide(input, signal);
|
|
103
104
|
},
|
|
104
105
|
};
|
|
@@ -79,6 +79,39 @@ export interface RunnerDepsCtx {
|
|
|
79
79
|
question: QuestionCoordinator | undefined;
|
|
80
80
|
toolApproval: ToolApprovalCoordinator | undefined;
|
|
81
81
|
sessionStore: ReturnType<StoreBackend["session"]>;
|
|
82
|
+
/**
|
|
83
|
+
* B-060(test [ref] 核心发现①)—— **一条部署腿上的每一只 Runner 看同一只检查点店**。
|
|
84
|
+
*
|
|
85
|
+
* 本键从「差异键」升进{@link createSharedRunnerDeps 共享基座}。修前它**只**挂在 subRunner 上,主
|
|
86
|
+
* runner 靠 per-task `spec.checkpointStore` 拿店 —— 而 core 的 `run_workflow` 是挂在**主** Runner
|
|
87
|
+
* (`prepare-caps-and-workflow.js:244` 的 `runner: runnerSelf`;core 7.12.0 亲核行号,B-060 票据引的
|
|
88
|
+
* :249 是 7.11.2 的坐标)上的,workflow 子代的 childSpec 不带
|
|
89
|
+
* `checkpointStore`(`orchestration/workflow-primitives.js:80-81` 只透传 `"disabled"` 这一种状态),
|
|
90
|
+
* 于是子代的 `resolveCheckpointStore(spec, deps)` 落到主 runner 这一格 = `undefined`
|
|
91
|
+
* ⇒ `parkLane {capable:false, reasons:["no_checkpoint_store"]}` ⇒ 受门的调用不是 park 而是
|
|
92
|
+
* `delegation.ask_unresolvable{parkLaneExisted:false}` 直接拒(过度拒绝,S-185 的 `/decide`
|
|
93
|
+
* 第三车道在这条路径上结构性不可达)。
|
|
94
|
+
*
|
|
95
|
+
* 🔴 host 腿的读数判据是**core 侧硬的**,不是本仓的但愿:`resolveCheckpointStore`
|
|
96
|
+
* (`checkpoint-store.js:367`)**spec 优先**——`spec.checkpointStore` 在场就用它、`"disabled"` 就是
|
|
97
|
+
* 关、只有缺席才落 deps。而本仓 host 腿恒经 `resolveSpec` 的 durableEnabled 分支盖上**同一个实例**,
|
|
98
|
+
* 且 `durableEnabled ⇔ checkpointStore !== undefined`(`boot/coordinators.ts:70` 与 main.ts 的构造
|
|
99
|
+
* 条件同源)⇒ 每一条经 `resolveSpec` 的腿看到的店逐字同一只。
|
|
100
|
+
*
|
|
101
|
+
* ⚠️ **如实登记一处不经解析器的直读**(codex 交叉复审提名,亲读 core dist 确认):
|
|
102
|
+
* `core/runner/run-terminal-adoption.js:74` 读的是 `runner.deps.checkpointStore` **本身**,不走
|
|
103
|
+
* `resolveCheckpointStore`。它是「宿主任务 park 时,把没排空的 steer / follow-up 迁移到 park token 上」
|
|
104
|
+
* 那一步 —— 修前主 runner 这一格是空的,于是**整段迁移静默不跑**(那些输入随本腿一起丢,只留
|
|
105
|
+
* `task.user_followup_undrained` 一类通告)。接上之后它开始真的跑。方向是修好一条本就该在的腿,
|
|
106
|
+
* 但它**不是**「host 腿字节不变」的一部分,所以写在这里而不是混进上面那句。
|
|
107
|
+
*
|
|
108
|
+
* 🔴 这一席是**部署腿级**的答案,不是「哪只 Runner」的属性:一条腿要么有赎回口(HTTP 面的
|
|
109
|
+
* `/v1/approvals/:id/decide` + resume 家族)、于是它的每只 Runner 都该有店;要么没有(`run-local`
|
|
110
|
+
* 的一次性 CLI)、于是**一只都不该有** —— core 的平台/资源停驻(`prepare-boundary-parks.js` 的
|
|
111
|
+
* `suspendForPlatformLimit`)只看「店在不在」,不看 approver 席,给一条没有赎回口的腿接店 = 用一张
|
|
112
|
+
* 没人能兑付的 checkpoint 换掉一次干净的失败。
|
|
113
|
+
*/
|
|
114
|
+
checkpointStore: RunnerDeps["checkpointStore"];
|
|
82
115
|
memoryEngine: {
|
|
83
116
|
backend: MemoryBackend;
|
|
84
117
|
root: string;
|
|
@@ -154,7 +187,7 @@ export interface RunnerDepsCtx {
|
|
|
154
187
|
}
|
|
155
188
|
/** [ref] A10 留档发现②:main runner `RunnerDeps` 与 main.ts subRunner 字面量之间此前手工重复
|
|
156
189
|
* 的 ~15 个键,类型标注见 {@link createSharedRunnerDeps} 头注。 */
|
|
157
|
-
export type SharedRunnerDeps = Pick<RunnerDeps, "brain" | "readFace" | "readDenyPatterns" | "readDenyBuiltinTiers" | "readDenyBuiltinExclude" | "memoryDelegationEvidence" | "memoryProvenance" | "memoryCapturePolicy" | "delegationEntryCaps" | "crossSessionInbound" | "crossSessionDialogExpiry" | "writeProtectedPaths" | "models" | "roles" | "tiers" | "pricing" | "tracer" | "promptSource" | "executionEnvFactory" | "lspManager" | "backgroundAgentStore" | "mailboxStore" | "rosterStore" | "hooks" | "toolResultStore" | "hands" | "sessionPolicyStore" | "usageWindows" | "usageWindowStore" | "memoryScopeAdmission" | "deploymentMemoryScopes" | "sharedMemoryStores" | "compliancePostureResolver" | "lockedConfig" | "retentionPolicy" | "onNotice" | "mcpRevocations">;
|
|
190
|
+
export type SharedRunnerDeps = Pick<RunnerDeps, "brain" | "readFace" | "readDenyPatterns" | "readDenyBuiltinTiers" | "readDenyBuiltinExclude" | "memoryDelegationEvidence" | "memoryProvenance" | "memoryCapturePolicy" | "delegationEntryCaps" | "crossSessionInbound" | "crossSessionDialogExpiry" | "writeProtectedPaths" | "models" | "roles" | "tiers" | "pricing" | "tracer" | "promptSource" | "executionEnvFactory" | "lspManager" | "backgroundAgentStore" | "mailboxStore" | "rosterStore" | "hooks" | "toolResultStore" | "checkpointStore" | "hands" | "sessionPolicyStore" | "usageWindows" | "usageWindowStore" | "memoryScopeAdmission" | "deploymentMemoryScopes" | "sharedMemoryStores" | "compliancePostureResolver" | "lockedConfig" | "retentionPolicy" | "onNotice" | "mcpRevocations">;
|
|
158
191
|
/**
|
|
159
192
|
* [ref] A10 留档发现②(review 2026-07-29,[ref]§三族A 同源修补的延续):main runner 的
|
|
160
193
|
* `RunnerDeps` 字面量(下方 `createRunnerDeps`)与 `main.ts` 里 subRunner 的 `new Runner({...})`
|
|
@@ -176,14 +209,12 @@ export type SharedRunnerDeps = Pick<RunnerDeps, "brain" | "readFace" | "readDeny
|
|
|
176
209
|
* 差异键(为何不在本共享基座、逐一注明):
|
|
177
210
|
* - `sessionStore`:main runner 用宿主 durable 会话店;subRunner 用私有短 TTL 的
|
|
178
211
|
* `ForkRoutingSessionStore`(main.ts 现场构造,子代转录生命周期与主会话不同)。
|
|
179
|
-
* - `checkpointStore`:main runner 走 `spec.checkpointStore` 分支(不进 `RunnerDeps`,本 ctx 根本
|
|
180
|
-
* 没有这个字段);subRunner 单独携带(子代执行面的 park 设施,[ref])。
|
|
181
212
|
* - `onBackgroundChildEvent` / `loadProjectMemory` / `probeInstructionSources` / `onError`:
|
|
182
213
|
* [ref]§三族A 已用「直引 `runnerDeps.X`」修过(main.ts 两处引用同一个 `runnerDeps` 实例的同一
|
|
183
214
|
* 属性,比再抽一层共享基座更强的同源保证),不重复收纳进这里。
|
|
184
215
|
*/
|
|
185
216
|
/** 基座真实消费的窄面(Pick)——subRunner 调用点(main.ts)只需凑这 13 个字段,不必造全量 ctx。 */
|
|
186
|
-
export type SharedRunnerDepsCtx = Pick<RunnerDepsCtx, "config" | "brain" | "pricing" | "tracer" | "promptSource" | "executionEnvFactory" | "lspManager" | "backgroundAgentStore" | "mailboxStore" | "rosterStore" | "deploymentHooks" | "toolResultStore" | "sessionPolicyStore" | "hands" | "usageWindowStore" | "orgMemoryAdmission" | "sharedMemoryStores" | "logger" | "governanceSeams" | "mcpRevocations">;
|
|
217
|
+
export type SharedRunnerDepsCtx = Pick<RunnerDepsCtx, "config" | "brain" | "pricing" | "tracer" | "promptSource" | "executionEnvFactory" | "lspManager" | "backgroundAgentStore" | "mailboxStore" | "rosterStore" | "deploymentHooks" | "toolResultStore" | "checkpointStore" | "sessionPolicyStore" | "hands" | "usageWindowStore" | "orgMemoryAdmission" | "sharedMemoryStores" | "logger" | "governanceSeams" | "mcpRevocations">;
|
|
187
218
|
/**
|
|
188
219
|
* core `EngineNotice` → 结构化 `engine_notice` 行的**唯一**转发体([ref]②)。
|
|
189
220
|
*
|
package/dist/boot/runner-deps.js
CHANGED
|
@@ -69,6 +69,7 @@ export function createSharedRunnerDeps(ctx) {
|
|
|
69
69
|
hooks: ctx.deploymentHooks,
|
|
70
70
|
hands: ctx.hands,
|
|
71
71
|
toolResultStore: ctx.toolResultStore,
|
|
72
|
+
checkpointStore: ctx.checkpointStore ? ctx.checkpointStore : undefined,
|
|
72
73
|
sessionPolicyStore: ctx.sessionPolicyStore ? ctx.sessionPolicyStore : undefined,
|
|
73
74
|
memoryScopeAdmission: ctx.orgMemoryAdmission.memoryScopeAdmission ? ctx.orgMemoryAdmission.memoryScopeAdmission : undefined,
|
|
74
75
|
deploymentMemoryScopes: ctx.orgMemoryAdmission.deploymentMemoryScopes && ctx.orgMemoryAdmission.deploymentMemoryScopes.length > 0 ? ctx.orgMemoryAdmission.deploymentMemoryScopes : undefined,
|
|
@@ -147,12 +148,7 @@ export function createRunnerDeps(ctx) {
|
|
|
147
148
|
onElicit: elicitation ? elicitation.elicit : undefined,
|
|
148
149
|
onQuestion: question ? question.question : undefined,
|
|
149
150
|
onAsk: createRunnerDepsOnAsk(toolApproval),
|
|
150
|
-
autoMode: {
|
|
151
|
-
onBreakerOpen: (info) => {
|
|
152
|
-
metrics.inc("auto_mode_breaker_open_total");
|
|
153
|
-
logger.warn("auto_mode_breaker_open", { consecutiveFailures: info.consecutiveFailures, lastCause: info.lastCause });
|
|
154
|
-
},
|
|
155
|
-
},
|
|
151
|
+
autoMode: {},
|
|
156
152
|
sessionStore,
|
|
157
153
|
memoryBackend: memoryEngine ? memoryEngine.backend : undefined,
|
|
158
154
|
memoryEngineDir: memoryEngine ? memoryEngine.root : undefined,
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
import { type WorkflowRun, type WorkflowRunStore } from "@sema-agent/core";
|
|
15
15
|
import type { ServiceConfig } from "../config.js";
|
|
16
16
|
import { FleetEventBus } from "../fleet/fleet-bus.js";
|
|
17
|
+
import { type WorkflowAgentSessionIndex } from "../orchestration/workflow-agent-session-index.js";
|
|
17
18
|
import type { Logger } from "../observability/logger.js";
|
|
18
19
|
import type { Metrics } from "../observability/metrics.js";
|
|
19
20
|
import { type WorkflowCompletionInbox } from "../orchestration/workflow-completion-inbox.js";
|
|
@@ -47,5 +48,6 @@ export declare function createWorkflowOrchestration(ctx: WorkflowOrchestrationCt
|
|
|
47
48
|
finalizeStartedBeforeMs?: number;
|
|
48
49
|
publishTerminalFleetRow?: (id: string, run: WorkflowRun) => void;
|
|
49
50
|
};
|
|
51
|
+
workflowAgentSessionIndex: WorkflowAgentSessionIndex | undefined;
|
|
50
52
|
};
|
|
51
53
|
//# sourceMappingURL=workflow-orchestration.d.ts.map
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { FileWorkflowRunStore, InMemoryWorkflowRunStore } from "@sema-agent/core";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { FleetEventBus } from "../fleet/fleet-bus.js";
|
|
4
|
+
import { LocalWorkflowAgentSessionIndex } from "../orchestration/workflow-agent-session-index.js";
|
|
4
5
|
import { FileWorkflowCompletionInbox, InMemoryWorkflowCompletionInbox, resolveCompletionRoute } from "../orchestration/workflow-completion-inbox.js";
|
|
5
6
|
import { FileWorkflowNotifyJournalStore, JournalingWorkflowRunStore, WorkflowNotifyGate } from "../orchestration/workflow-notify-journal.js";
|
|
6
7
|
import { SubagentSteerRegistry } from "../orchestration/subagent-steer.js";
|
|
@@ -54,8 +55,15 @@ export function createWorkflowOrchestration(ctx) {
|
|
|
54
55
|
})
|
|
55
56
|
: undefined;
|
|
56
57
|
const fleetBus = new FleetEventBus();
|
|
58
|
+
const workflowAgentSessionIndex = config.selfOrchestrationEnabled
|
|
59
|
+
? config.workflowRunStoreBackend === "memory"
|
|
60
|
+
? new LocalWorkflowAgentSessionIndex()
|
|
61
|
+
: (sqlWorkflowRunStore
|
|
62
|
+
? backend.workflowAgentSessionIndex()
|
|
63
|
+
: new LocalWorkflowAgentSessionIndex(join(config.localDataRoot ?? localRoot, "workflows"), undefined, (msg, meta) => logger.warn(msg, meta)))
|
|
64
|
+
: undefined;
|
|
57
65
|
const workflowRunStore = baseWorkflowRunStore
|
|
58
|
-
? new JournalingWorkflowRunStore(baseWorkflowRunStore, workflowNotifyGate, fleetBus)
|
|
66
|
+
? new JournalingWorkflowRunStore(baseWorkflowRunStore, workflowNotifyGate, fleetBus, workflowAgentSessionIndex)
|
|
59
67
|
: undefined;
|
|
60
68
|
const replicaLocalNotifyJournal = workflowNotifyJournal instanceof FileWorkflowNotifyJournalStore;
|
|
61
69
|
const workflowRecoverOpts = {
|
|
@@ -70,7 +78,7 @@ export function createWorkflowOrchestration(ctx) {
|
|
|
70
78
|
return {
|
|
71
79
|
sqlWorkflowRunStore, workflowNotifyJournal, workflowCompletionInbox, deliverWorkflowCompletion,
|
|
72
80
|
workflowNotifyGate, fleetBus, workflowRunStore, workflowJournalStore, outcomeSink,
|
|
73
|
-
workflowAgentRegistry, subagentSteerRegistry, workflowRecoverOpts,
|
|
81
|
+
workflowAgentRegistry, subagentSteerRegistry, workflowRecoverOpts, workflowAgentSessionIndex,
|
|
74
82
|
};
|
|
75
83
|
}
|
|
76
84
|
//# sourceMappingURL=workflow-orchestration.js.map
|
package/dist/budget.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { modelCostToPricing } from "@sema-agent/core";
|
|
2
2
|
import { currentPrincipal } from "./observability/principal-context.js";
|
|
3
|
+
import { isPermissionRuleEventKind, permissionRuleEventWordOf } from "./observability/permission-rule-events.js";
|
|
3
4
|
import { promptManifestRecordOf, configAssembledRecordOf } from "./observability/prompt-manifest.js";
|
|
4
5
|
export function buildPricing(models) {
|
|
5
6
|
const pricing = {};
|
|
@@ -119,8 +120,8 @@ export function createTracer(metrics, costQuota, modelUsage, fleetUsage, fleetLe
|
|
|
119
120
|
else if (e.kind.startsWith("compaction.")) {
|
|
120
121
|
metrics.inc("compaction_events_total", { outcome: e.kind.slice("compaction.".length), trigger: e.trigger ?? "none" });
|
|
121
122
|
}
|
|
122
|
-
else if (e.kind
|
|
123
|
-
metrics.inc("permission_rule_events_total", { event: e.kind
|
|
123
|
+
else if (isPermissionRuleEventKind(e.kind)) {
|
|
124
|
+
metrics.inc("permission_rule_events_total", { event: permissionRuleEventWordOf(e.kind) });
|
|
124
125
|
}
|
|
125
126
|
};
|
|
126
127
|
}
|
package/dist/config-catalog.d.ts
CHANGED
|
@@ -34,10 +34,30 @@ export interface ConfigCatalogSpec {
|
|
|
34
34
|
readonly derivedDefaultNote?: string;
|
|
35
35
|
readonly danger?: readonly CatalogDangerAxis[];
|
|
36
36
|
readonly enumValues?: readonly string[];
|
|
37
|
+
/**
|
|
38
|
+
* S-100 —— **底层布尔 ↔ 闭集词**的行内映射,只给「实算腿回布尔、而行对外说词」的 enum 行。
|
|
39
|
+
*
|
|
40
|
+
* 为什么写在行上而不是建第二张全局极性表:`config.ts` 的极性表(`ConfigKnobRecord`)记的是
|
|
41
|
+
* **布尔旋钮**的来源与默认方向,它压根不带词 —— 拿它当 `true→enumValues[0]` 的位置约定用,等于
|
|
42
|
+
* 在两个文件之间约定一个不写下来的下标。行自己声明,是同一处声明它 `enumValues` 的地方。
|
|
43
|
+
*
|
|
44
|
+
* 某一极**没有词**(该极的真实投影是「键不在场」)⇒ 显式写 `null`,不是省略 —— 省略与「这一行忘了
|
|
45
|
+
* 声明映射」在形上同形,而后者必须落到响亮的 `effectiveValueInvalid` 臂。
|
|
46
|
+
*/
|
|
47
|
+
readonly booleanEnum?: {
|
|
48
|
+
readonly true: string | null;
|
|
49
|
+
readonly false: string | null;
|
|
50
|
+
};
|
|
37
51
|
/** `ServiceConfig` 的平铺键(实算兜底 + center managedNow 判据对表用;嵌套块不填,走 resolve)。 */
|
|
38
52
|
readonly configKey?: string;
|
|
39
|
-
/**
|
|
40
|
-
|
|
53
|
+
/**
|
|
54
|
+
* 本进程实算(非 secret/opaque 行才可用;返回 undefined = 本行没有实算腿,落 env 原文/staticDefault)。
|
|
55
|
+
*
|
|
56
|
+
* 🔴 第二参是**部署 env**,不是第五条供值腿:有几根旋钮的属主不在 `ServiceConfig` 上(它在装配点按
|
|
57
|
+
* env 现算,如 WebSearch 后端),而目录的纪律是「回显属主算出来的值」—— 只能把同一条实算腿的入参放宽,
|
|
58
|
+
* 不能在目录里再抄一遍属主的读法(S-100 / codex r1 F3 的成案:抄一遍就是第二个写者,当场分歧)。
|
|
59
|
+
*/
|
|
60
|
+
readonly resolve?: (config: ServiceConfig, env: NodeJS.ProcessEnv) => CatalogValue | undefined;
|
|
41
61
|
readonly center?: CatalogCenterSeatSpec;
|
|
42
62
|
/** 缺省 "deploy";retired/零消费键标 "inert"(设计稿 ⚪ 的 env 腿投影)。 */
|
|
43
63
|
readonly envTiming?: "inert";
|
|
@@ -69,6 +89,15 @@ export interface ConfigCatalogRow {
|
|
|
69
89
|
readonly staticDefault?: string;
|
|
70
90
|
readonly derivedDefaultNote?: string;
|
|
71
91
|
readonly effectiveValue: CatalogValue;
|
|
92
|
+
/**
|
|
93
|
+
* S-100(additive;**只在真非法时铸,恒不铸 `false`**)—— 本行的实算值**落在它自己宣告的
|
|
94
|
+
* `enumValues` 之外**,或它的实算腿回了一个本行没有声明映射的布尔。
|
|
95
|
+
*
|
|
96
|
+
* 在场时 `effectiveValue` 恒 `null`:回显一个集外值等于目录在教消费方写一个写回去就非法的词,
|
|
97
|
+
* 而**静默丢**会让「配错了」与「真的没设」在 wire 上同形([ref] 响亮臂)。
|
|
98
|
+
* 缺席 = 这一格没问题(不是「不知道」)。
|
|
99
|
+
*/
|
|
100
|
+
readonly effectiveValueInvalid?: true;
|
|
72
101
|
readonly valueClass: "plain" | "secret" | "opaque";
|
|
73
102
|
readonly danger: readonly CatalogDangerAxis[];
|
|
74
103
|
readonly center?: ConfigCatalogRowCenter;
|
package/dist/config-catalog.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { CROSS_SESSION_INBOUND_SETTINGS, CROSS_SESSION_DIALOG_EXPIRY_VALUES } from "@sema-agent/core";
|
|
2
|
+
import { THINKING_FORMATS } from "./model-compat.js";
|
|
3
|
+
import { webSearchConfigFromEnv } from "./plugins/web-search.js";
|
|
4
|
+
import { CENTER_READ_FACE_LIVE_KEYS } from "./config-center/read-face.js";
|
|
2
5
|
export const CATALOG_VALUE_TYPES = ["boolean", "number", "string", "enum", "csv", "json", "path", "url", "secret", "opaque"];
|
|
3
6
|
export const CATALOG_TIMINGS = ["hot", "restart", "deploy", "inert"];
|
|
4
7
|
export const CATALOG_DANGER_AXES = ["security", "billing", "data"];
|
|
@@ -24,7 +27,14 @@ const cLimits = (path, over = {}) => ({
|
|
|
24
27
|
});
|
|
25
28
|
const cGov = (path, over = {}) => ({ domain: "governance", path, centerKey: path, precedence: "center-wins", timing: "hot", ...over });
|
|
26
29
|
const cModels = (path) => ({ domain: "models", path, centerKey: "models", precedence: "center-wins", timing: "models-plane" });
|
|
27
|
-
const cRead = (path, centerKey) => ({
|
|
30
|
+
const cRead = (path, centerKey, over = {}) => ({
|
|
31
|
+
domain: "readFace",
|
|
32
|
+
path,
|
|
33
|
+
centerKey,
|
|
34
|
+
precedence: "env-wins",
|
|
35
|
+
...(CENTER_READ_FACE_LIVE_KEYS.has(path) ? { timing: "hot" } : { timing: "restart", restartSlice: "read-face" }),
|
|
36
|
+
...over,
|
|
37
|
+
});
|
|
28
38
|
const SEC = ["security"];
|
|
29
39
|
const BILL = ["billing"];
|
|
30
40
|
const DATA = ["data"];
|
|
@@ -74,7 +84,7 @@ export const CONFIG_CATALOG = [
|
|
|
74
84
|
r("BG_AGENT_STALE_RUNNING_MS", "orchestration", "number", "running 后台 agent 行写者心跳断更超此值翻 failed(ms;下限 10min 硬钳)", { staticDefault: String(15 * 60_000), configKey: "backgroundAgentStaleRunningMs" }),
|
|
75
85
|
r("BIND_HOST", "auth", "string", "监听绑址(显式值恒生效)", { danger: SEC, derivedDefaultNote: "缺席:无鉴权写面(allowUnauthedWrites ∧ 零凭证)自动收窄 127.0.0.1,其余全接口", resolve: (c) => c.bindHost ?? null }),
|
|
76
86
|
r("COMPLIANCE_ADDITIONAL_DENIES", "auth", "csv", "合规档位之上额外拒绝的能力名(只能收紧;词表=core 闭集,拼错拒启)", { danger: SEC }),
|
|
77
|
-
r("COMPLIANCE_PROFILE", "auth", "enum", "本部署合规档位(core compliancePostureResolver 供数;拼错拒启)", { danger: SEC, enumValues: ["hipaa", "zdr"] }),
|
|
87
|
+
r("COMPLIANCE_PROFILE", "auth", "enum", "本部署合规档位(core compliancePostureResolver 供数;拼错拒启)", { danger: SEC, enumValues: ["hipaa", "zdr"], resolve: (c) => c.compliancePosture?.profile ?? null }),
|
|
78
88
|
r("CONFIG_BOOT_FETCH_BUDGET_MS", "integrations", "number", "boot 首拉配置的抢答窗(ms;过窗以 env/兜底起服,拉取转后台热应用)", { staticDefault: "1500", configKey: "configBootFetchBudgetMs" }),
|
|
79
89
|
r("CONFIG_CATALOG_ENABLED", "auth", "boolean", "本目录端点 GET /v1/config/catalog 的旋钮(operator-only 面;合规部署可关 ⇒ 路径 404 + 能力位翻假)", { staticDefault: "true", danger: SEC, configKey: "configCatalogEnabled" }),
|
|
80
90
|
r("CONFIG_LKG_DURABLE", "integrations", "boolean", "LKG 跨重启 handoff(严格 'true' 才开:重启先用 LKG 起服,tier 候选借此落地)", { staticDefault: "false" }),
|
|
@@ -191,7 +201,7 @@ export const CONFIG_CATALOG = [
|
|
|
191
201
|
r("LOG_LEVEL", "observability", "string", "日志档位", { staticDefault: "info", configKey: "logLevel" }),
|
|
192
202
|
r("LSP_ENABLED", "orchestration", "boolean", "LSP 沙箱腿 opt-in(需烤好的 LSP 模板;host 腿另有 LSP_HOST_ENABLED)", { staticDefault: "false", configKey: "lspEnabled" }),
|
|
193
203
|
r("LSP_HOST_ENABLED", "orchestration", "boolean", "LSP host 腿开关(本机 language server,优雅降级)", { staticDefault: "true", configKey: "lspHostEnabled" }),
|
|
194
|
-
r("MANUAL_MODE_SHELL_GATE", "approval", "enum", "手动模式 shell 门收紧腿(off=no-op 非放松 —— 本键只有收紧半场;拼错拒启)", { danger: SEC, enumValues: ["always", "classify", "off"] }),
|
|
204
|
+
r("MANUAL_MODE_SHELL_GATE", "approval", "enum", "手动模式 shell 门收紧腿(off=no-op 非放松 —— 本键只有收紧半场;拼错拒启)", { danger: SEC, enumValues: ["always", "classify", "off"], resolve: (c) => c.manualModeShellGate ?? null, derivedDefaultNote: "off/未设 ⇒ 本键不贡献任何东西(归一成键缺席)⇒ 回显 null;真姿态只有 always/classify 两个" }),
|
|
195
205
|
r("MAX_PRINCIPAL_COST_USD", "limitsHttp", "number", "per-principal 窗内成本天花板(0=关)", { staticDefault: "0", danger: BILL, configKey: "maxPrincipalCostUsd", center: cLimits("maxPrincipalCostUsd") }),
|
|
196
206
|
r("MAX_TASK_COST_USD", "limitsHttp", "number", "单任务成本天花板(0=关)", { staticDefault: "0", danger: BILL, configKey: "maxTaskCostUsd", center: cLimits("maxTaskCostUsd") }),
|
|
197
207
|
r("MAX_TASK_TOKENS", "limitsHttp", "number", "单任务 token 天花板(per-slice 窗;0=关)", { staticDefault: "0", danger: BILL, configKey: "maxTaskTokens", center: cLimits("maxTaskTokens") }),
|
|
@@ -201,7 +211,7 @@ export const CONFIG_CATALOG = [
|
|
|
201
211
|
r("MCP_ELICITATION_MIN_INTERVAL_MS", "approval", "number", "elicitation per-server 最小间隔(ms)", { derivedDefaultNote: "缺席 ⇒ core 出厂窗" }),
|
|
202
212
|
r("MCP_ELICITATION_TTL_MS", "approval", "number", "elicitation 无人应答释放窗(ms)", { derivedDefaultNote: "缺席 ⇒ core 出厂窗" }),
|
|
203
213
|
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" }),
|
|
204
|
-
r("MEMORY_CONSOLIDATION_DRIVER", "memory", "enum", "记忆折叠阀门(off=零装配零探针,两 admin 口 404 族)", { enumValues: ["on", "off"], staticDefault: "off", danger: BILL }),
|
|
214
|
+
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" } }),
|
|
205
215
|
r("MEMORY_CONSOLIDATION_INTERVAL_SEC", "memory", "number", "折叠周期腿节律(秒;本 build 任何正值拒启 —— 周期腿未落地,恒 0)", { staticDefault: "0" }),
|
|
206
216
|
r("MEMORY_CONSOLIDATION_SCOPES", "memory", "csv", "折叠阀门 scope 表(恰一条时=admin 口缺省;不是授权边界)"),
|
|
207
217
|
r("MEMORY_DELEGATION_EVIDENCE", "memory", "enum", "委派臂污染标记的证据标准", { enumValues: ["static-face", "attested-only"], staticDefault: "static-face", danger: SEC, configKey: "memoryDelegationEvidence" }),
|
|
@@ -210,7 +220,7 @@ export const CONFIG_CATALOG = [
|
|
|
210
220
|
r("MEMORY_EMBEDDER_ENDPOINT", "memory", "url", "OpenAI 兼容 /embeddings 端点(pg 记忆后端的向量面)"),
|
|
211
221
|
r("MEMORY_EMBEDDER_MODEL", "memory", "string", "embeddings 模型名"),
|
|
212
222
|
r("MEMORY_EMBEDDER_TIMEOUT_MS", "memory", "number", "单次 embed 活性上限(ms)", { staticDefault: "30000" }),
|
|
213
|
-
r("MEMORY_ENGINE", "memory", "enum", "记忆引擎总开关(注入式文件记忆面;双族词表,未知词拒启)", { enumValues: ["on", "off"], staticDefault: "on" }),
|
|
223
|
+
r("MEMORY_ENGINE", "memory", "enum", "记忆引擎总开关(注入式文件记忆面;双族词表,未知词拒启)", { enumValues: ["on", "off"], staticDefault: "on", configKey: "memoryEngineEnabled", booleanEnum: { true: "on", false: "off" } }),
|
|
214
224
|
r("MEMORY_ENGINE_BACKEND", "memory", "enum", "记忆持久面方言(file=现状;pg/tidb=DB 真身,多租户点亮的唯一门)", { enumValues: ["file", "pg", "tidb"], staticDefault: "file", danger: DATA, configKey: "memoryEngineBackend" }),
|
|
215
225
|
r("MEMORY_ENGINE_DIR", "memory", "path", "记忆引擎根覆写", { derivedDefaultNote: "缺席 ⇒ core 默认链(AGENT_DATA_DIR → ~/.ai-agent)", configKey: "memoryEngineDir" }),
|
|
216
226
|
r("MEMORY_ENGINE_REMOTE_LANE", "memory", "enum", "远端执行车道上放行 file 记忆引擎的 plane-split 覆写", { enumValues: ["allow"] }),
|
|
@@ -218,7 +228,7 @@ export const CONFIG_CATALOG = [
|
|
|
218
228
|
r("MEMORY_ORG_DIRECTORY_JSON", "memory", "opaque", "单机形 org 目录原文(principal→org:scope 授权表;解析期整段校验)", { danger: SEC }),
|
|
219
229
|
r("MEMORY_ORG_GRANT_TTL_MS", "memory", "number", "org 目录判决缓存 TTL(ms;准入判决滞后上限)", { staticDefault: "60000", configKey: "memoryOrgGrantTtlMs" }),
|
|
220
230
|
r("MEMORY_ORG_UNAVAILABLE_BACKOFF_MS", "memory", "number", "org 目录取数失败退避窗(ms)", { staticDefault: "10000", configKey: "memoryOrgUnavailableBackoffMs" }),
|
|
221
|
-
r("MEMORY_PERSISTENCE_CAPABLE", "memory", "enum", "本部署能否把「记住 X」落到持久处的自我声明(三态;未知词拒启)", { enumValues: ["on", "off"], derivedDefaultNote: "缺席 ⇒ 键不上 TaskSpec,core 按 roster 推断" }),
|
|
231
|
+
r("MEMORY_PERSISTENCE_CAPABLE", "memory", "enum", "本部署能否把「记住 X」落到持久处的自我声明(三态;未知词拒启)", { enumValues: ["on", "off"], derivedDefaultNote: "缺席 ⇒ 键不上 TaskSpec,core 按 roster 推断", configKey: "memoryPersistenceCapable", booleanEnum: { true: "on", false: "off" } }),
|
|
222
232
|
r("MEMORY_PROVENANCE", "memory", "enum", "已暴露会话的记忆写处置(carry=带 origin 标记照写;off=关)", { enumValues: ["off", "carry"], staticDefault: "carry", danger: SEC, configKey: "memoryProvenance" }),
|
|
223
233
|
r("MEMORY_SCOPE", "memory", "string", "单用户部署的记忆 scope 名", { derivedDefaultNote: "缺席:引擎开 ∧ 单用户 ⇒ local;其余 ⇒ 缺席(记忆暗)", configKey: "memoryScope" }),
|
|
224
234
|
r("MEMORY_SYNC_MAX_PULL_ENTRIES", "memory", "number", "TOC 同步单轮 pull 条数帽(设了必须 ≥1 整数)", { derivedDefaultNote: "缺席 ⇒ 键缺席,core 默认" }),
|
|
@@ -284,6 +294,16 @@ export const CONFIG_CATALOG = [
|
|
|
284
294
|
},
|
|
285
295
|
}),
|
|
286
296
|
r("MODEL_ROUTER_ID", "modelPlane", "string", "leader 路由分类模型覆写", { derivedDefaultNote: "缺席 ⇒ MODEL_CHEAP_ID,再退 default", danger: BILL }),
|
|
297
|
+
r("MODEL_THINKING_FORMAT", "modelPlane", "enum", "本网关收「开/关思考」的拼法(上 core OpenAICompletionsCompat.thinkingFormat;缺省开思考的 vLLM/Qwen 网关只有 qwen-chat-template 关得掉;anthropic 腿不铸此键;未知词拒启)", {
|
|
298
|
+
enumValues: [...THINKING_FORMATS],
|
|
299
|
+
derivedDefaultNote: "缺席 ⇒ 不铸此键,core 按 baseUrl/模型 id 自行推断(默认 openai 的 reasoning_effort)",
|
|
300
|
+
resolve: (c) => {
|
|
301
|
+
const compat = c.model.compat;
|
|
302
|
+
if (compat === undefined || !("thinkingFormat" in compat))
|
|
303
|
+
return null;
|
|
304
|
+
return compat.thinkingFormat ?? null;
|
|
305
|
+
},
|
|
306
|
+
}),
|
|
287
307
|
r("MODEL_VERIFIER", "modelPlane", "string", "verify 门的异构 verifier(须命中目录;typo 回 default 并 boot warn=自评自批)", { derivedDefaultNote: "缺席/不在目录 ⇒ default(=implementer,去相关性失效警告)" }),
|
|
288
308
|
r("MODEL_VISION", "modelPlane", "boolean", "主模型 vision 声明(false 让 model_no_vision 预检真拦)", { staticDefault: "true", resolve: (c) => (Array.isArray(c.model.input) ? c.model.input.includes("image") : true), center: cModels("models[].vision") }),
|
|
289
309
|
r("MYSQL_DATABASE", "store", "string", "MySQL 协议库名(选中该引擎时必填)", { danger: DATA }),
|
|
@@ -332,12 +352,12 @@ export const CONFIG_CATALOG = [
|
|
|
332
352
|
r("RESOURCE_SUSPEND_TOTAL_TOKENS", "approval", "number", "跨片 token 总额(0=不设;与 MAX_TASK_TOKENS 的 per-slice 窗是两根轴)", { staticDefault: "0", danger: BILL, configKey: "resourceSuspendTotalTokens" }),
|
|
333
353
|
r("RESOURCE_SUSPEND_TTL_SEC", "approval", "number", "resource/preempt checkpoint 的弃置 GC TTL(秒;0 ⇒ core 默认 30d)", { staticDefault: "0", danger: DATA, configKey: "resourceSuspendTtlSec" }),
|
|
334
354
|
r("RETENTION_MAX_AGE_DAYS", "auth", "number", "托管留存期声明(core 启动期能力校验;真删除归 sweep lane)", { danger: DATA }),
|
|
335
|
-
r("RETENTION_MODE", "auth", "enum", "留存 sweep 灰度档(拼错拒启 —— audit-only 谎成 enforce 两个方向都致命)", { enumValues: ["audit-only", "enforce"], staticDefault: "audit-only", danger: DATA }),
|
|
355
|
+
r("RETENTION_MODE", "auth", "enum", "留存 sweep 灰度档(拼错拒启 —— audit-only 谎成 enforce 两个方向都致命)", { enumValues: ["audit-only", "enforce"], staticDefault: "audit-only", danger: DATA, resolve: (c) => c.retentionSweep.mode }),
|
|
336
356
|
r("RETENTION_SWEEP_INTERVAL_SEC", "auth", "number", "留存 sweep 节律(秒;0=lane 关)", { staticDefault: "0", danger: DATA }),
|
|
337
357
|
r("REWIND_SNAPSHOT_MAX_MB", "store", "number", "已退役(S-15 整树快照纪元退役):设了只 boot warn 一次,零行为", { envTiming: "inert" }),
|
|
338
358
|
r("ROSTER_RETENTION_MS", "orchestration", "number", "agent_roster 行 TTL(ms;SQL twins 扩展面)", { staticDefault: String(30 * 24 * 60 * 60 * 1000), danger: DATA, configKey: "rosterRetentionMs" }),
|
|
339
359
|
r("ROUTER_ENABLED", "orchestration", "boolean", "A 价值路由(per-task 编排自动决策;ship-dark)", { staticDefault: "false", configKey: "routerEnabled" }),
|
|
340
|
-
r("RUN_STORE_STRICT_HYDRATE", "store", "enum", "file run-store 列不出 runs/ 时拒启的 fail-closed 门(缺省带病起动+响亮披露)", { enumValues: ["on", "off"], staticDefault: "off", danger: DATA, configKey: "runStoreStrictHydrate" }),
|
|
360
|
+
r("RUN_STORE_STRICT_HYDRATE", "store", "enum", "file run-store 列不出 runs/ 时拒启的 fail-closed 门(缺省带病起动+响亮披露)", { enumValues: ["on", "off"], staticDefault: "off", danger: DATA, configKey: "runStoreStrictHydrate", booleanEnum: { true: "on", false: "off" } }),
|
|
341
361
|
r("S3_ENDPOINT", "store", "url", "外接 S3 兼容位(单独在场即开公链签发,兼作公网面)", { danger: DATA }),
|
|
342
362
|
r("S3_PUBLIC_BUCKET", "store", "string", "SendUserFile 公链桶", { derivedDefaultNote: "缺席 ⇒ sema-public" }),
|
|
343
363
|
r("S3_PUBLIC_ENDPOINT", "store", "url", "SendUserFile 公网面 endpoint(跨仓契约名,禁改)"),
|
|
@@ -422,7 +442,7 @@ export const CONFIG_CATALOG = [
|
|
|
422
442
|
r("WEB_SEARCH_ENDPOINT", "orchestration", "url", "web search 后端 endpoint 覆写(searxng 自建面)"),
|
|
423
443
|
r("WEB_SEARCH_MAX_RESULTS", "orchestration", "number", "web search 单次结果数上限", { derivedDefaultNote: "缺席/非正 ⇒ backend 默认" }),
|
|
424
444
|
r("WEB_SEARCH_PROBE_ON_BOOT", "orchestration", "boolean", "boot 期 web search 连通性探测(只认 true/1;含糊值当没开——猜错方向是凭空出网)", { staticDefault: "false" }),
|
|
425
|
-
r("WEB_SEARCH_PROVIDER", "orchestration", "enum", "web search 工具后端(设合法词=装配 WebSearch;其它/缺席=不装配)", { enumValues: ["brave", "tavily", "searxng"], danger: SEC }),
|
|
445
|
+
r("WEB_SEARCH_PROVIDER", "orchestration", "enum", "web search 工具后端(设合法词=装配 WebSearch;其它/缺席=不装配)", { enumValues: ["brave", "tavily", "searxng"], danger: SEC, resolve: (_c, env) => webSearchConfigFromEnv(env)?.provider ?? null }),
|
|
426
446
|
r("WEB_SEARCH_SEARXNG_PARAMS", "orchestration", "string", "searxng 额外查询参数(k=v 对;解析不出任何一对 ⇒ 键不铸)"),
|
|
427
447
|
r("WEB_SEARCH_TIMEOUT_MS", "orchestration", "number", "web search 单次超时(ms)", { derivedDefaultNote: "缺席/非正 ⇒ backend 默认" }),
|
|
428
448
|
r("WORKFLOW_AGENTS_READONLY", "orchestration", "boolean", "workflow 子 agent 只读钳(TOB 保守旋钮;默认同权 CC parity)", { staticDefault: "false", danger: SEC, configKey: "workflowAgentsReadOnly" }),
|
|
@@ -464,17 +484,36 @@ function stripUrlUserinfo(v) {
|
|
|
464
484
|
u.hash = "";
|
|
465
485
|
return u.href;
|
|
466
486
|
}
|
|
467
|
-
function
|
|
487
|
+
function sanitizeEnumValue(spec, v) {
|
|
488
|
+
const words = spec.enumValues;
|
|
489
|
+
if (v === null)
|
|
490
|
+
return { value: null };
|
|
491
|
+
if (words === undefined)
|
|
492
|
+
return { value: null, invalid: true };
|
|
493
|
+
if (typeof v === "boolean") {
|
|
494
|
+
const word = spec.booleanEnum?.[v ? "true" : "false"];
|
|
495
|
+
if (word === null)
|
|
496
|
+
return { value: null };
|
|
497
|
+
return word !== undefined && words.includes(word) ? { value: word } : { value: null, invalid: true };
|
|
498
|
+
}
|
|
499
|
+
if (typeof v === "string")
|
|
500
|
+
return words.includes(v) ? { value: v } : { value: null, invalid: true };
|
|
501
|
+
return { value: null, invalid: true };
|
|
502
|
+
}
|
|
503
|
+
function sanitizeValue(spec, v) {
|
|
504
|
+
const type = spec.type;
|
|
468
505
|
if (type === "url") {
|
|
469
506
|
if (typeof v === "string")
|
|
470
|
-
return v.includes(",") ? v.split(",").map((s) => stripUrlUserinfo(s.trim())).filter((s) => s.length > 0) : stripUrlUserinfo(v);
|
|
507
|
+
return { value: v.includes(",") ? v.split(",").map((s) => stripUrlUserinfo(s.trim())).filter((s) => s.length > 0) : stripUrlUserinfo(v) };
|
|
471
508
|
if (Array.isArray(v))
|
|
472
|
-
return v.map((s) => stripUrlUserinfo(s));
|
|
473
|
-
return v;
|
|
509
|
+
return { value: v.map((s) => stripUrlUserinfo(s)) };
|
|
510
|
+
return { value: v };
|
|
474
511
|
}
|
|
475
512
|
if (type === "csv" && typeof v === "string")
|
|
476
|
-
return v.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
477
|
-
|
|
513
|
+
return { value: v.split(",").map((s) => s.trim()).filter((s) => s.length > 0) };
|
|
514
|
+
if (type === "enum")
|
|
515
|
+
return sanitizeEnumValue(spec, v);
|
|
516
|
+
return { value: v };
|
|
478
517
|
}
|
|
479
518
|
function valueClassOf(type) {
|
|
480
519
|
if (type === "secret")
|
|
@@ -501,6 +540,7 @@ export function buildConfigCatalog(inputs) {
|
|
|
501
540
|
const centerEffective = seat !== undefined && managedNow && centerTiming === "hot" && (seat.precedence === "center-wins" || !envHeld);
|
|
502
541
|
const effectiveLane = centerEffective ? "center" : envHeld ? "env" : "default";
|
|
503
542
|
let effectiveValue;
|
|
543
|
+
let effectiveValueInvalid;
|
|
504
544
|
if (valueClass === "secret") {
|
|
505
545
|
effectiveValue = { present: envHeld };
|
|
506
546
|
}
|
|
@@ -508,15 +548,11 @@ export function buildConfigCatalog(inputs) {
|
|
|
508
548
|
effectiveValue = envHeld ? { present: true, length: raw.length } : { present: false };
|
|
509
549
|
}
|
|
510
550
|
else {
|
|
511
|
-
const resolved = spec.resolve !== undefined ? spec.resolve(config) : spec.configKey !== undefined ? primitiveConfigValue(config, spec.configKey) : undefined;
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
? knob.value
|
|
517
|
-
: envHeld
|
|
518
|
-
? sanitizeValue(spec.type, raw)
|
|
519
|
-
: spec.staticDefault ?? null;
|
|
551
|
+
const resolved = spec.resolve !== undefined ? spec.resolve(config, env) : spec.configKey !== undefined ? primitiveConfigValue(config, spec.configKey) : undefined;
|
|
552
|
+
const picked = resolved !== undefined ? resolved : knob !== undefined ? knob.value : envHeld ? raw : spec.staticDefault ?? null;
|
|
553
|
+
const sanitized = sanitizeValue(spec, picked);
|
|
554
|
+
effectiveValue = sanitized.value;
|
|
555
|
+
effectiveValueInvalid = sanitized.invalid;
|
|
520
556
|
}
|
|
521
557
|
return {
|
|
522
558
|
name: spec.name,
|
|
@@ -530,6 +566,7 @@ export function buildConfigCatalog(inputs) {
|
|
|
530
566
|
...(spec.staticDefault !== undefined ? { staticDefault: spec.staticDefault } : {}),
|
|
531
567
|
...(spec.derivedDefaultNote !== undefined ? { derivedDefaultNote: spec.derivedDefaultNote } : {}),
|
|
532
568
|
effectiveValue,
|
|
569
|
+
...(effectiveValueInvalid === true ? { effectiveValueInvalid } : {}),
|
|
533
570
|
valueClass,
|
|
534
571
|
danger: spec.danger ?? [],
|
|
535
572
|
...(seat !== undefined && centerTiming !== undefined
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
/**
|
|
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
|
|
4
|
+
* `applyRuntimeGates`, the per-request-hot governance baton `applyRuntimeHot`) plus their shared helpers
|
|
5
|
+
* (`toModel`, `mutateInPlace`, `resolveDefaultModelName`, `logEffectiveDiff`). Split out of `facade.ts`
|
|
6
|
+
* ([ref] A13, internal-lossless — the facade re-exports every symbol below unchanged).
|
|
7
|
+
*/
|
|
1
8
|
import type { Model } from "@sema-agent/core";
|
|
2
9
|
import type { ServiceConfig } from "../config-types.js";
|
|
3
10
|
import { type SealedKeyOpener } from "../sealed-key.js";
|
|
@@ -23,7 +30,7 @@ export declare function mutateInPlace<V>(target: Record<string, V>, source: Reco
|
|
|
23
30
|
* 🔴 只活在内存判定面:不进 wire(`/v1/models` 只发布尔)、不进日志(warn 打的是原始坏值的类型标记)。
|
|
24
31
|
*/
|
|
25
32
|
export declare const UNPARSABLE_AT_MODEL_ALLOWLIST_ENTRY = "\0<unparsable-at-model-allowlist-entry>";
|
|
26
|
-
/** 返回值=是否 COMMIT(false 两臂:[ref]③ 世代序 CAS 拒绝 /
|
|
33
|
+
/** 返回值=是否 COMMIT(false 两臂:[ref]③ 世代序 CAS 拒绝 / 7.10.0 swapPlane 换装拒绝——两臂都在
|
|
27
34
|
* commit 之前,活配置零触碰)。caller 收到 false 必须把**同一世代的旁路消费**(prompts 采用/pricing/
|
|
28
35
|
* keyResolver/etag 推进/LKG 落盘)一并跳过——否则 applyEffective 拒了主面、旁路却半应用同一个被拒
|
|
29
36
|
* 世代,混合世代从侧门回来。首次 apply(live 未登记)恒 true。 */
|
|
@@ -1,16 +1,29 @@
|
|
|
1
|
-
import { isThinkingLevel } from "@sema-agent/core";
|
|
2
1
|
import { resolveActiveTiers, SEALED_BOX_ALG } from "@sema-agent/settings-schema";
|
|
3
2
|
import { sealedKeyPoison } from "../sealed-key.js";
|
|
4
3
|
import { applyAutoCompactWindow, findUnmatchableToolNames, formatUnmatchableToolNames, parseAutonomy } from "../config.js";
|
|
4
|
+
import { buildModelCompat, readModelCompat } from "../model-compat.js";
|
|
5
5
|
import { validateCommandRules } from "../runtime-governance.js";
|
|
6
6
|
import { projectCollabToWorkflows, registerCollabWorkflows } from "../capabilities/collab-workflows.js";
|
|
7
7
|
import { stageLimits } from "./stage-limits.js";
|
|
8
8
|
import { BATCH1_LIMIT_KEYS } from "./hot-keys-registry.js";
|
|
9
|
-
const asModelThinking = (v) => (v && isThinkingLevel(v) && v !== "off" ? v : undefined);
|
|
10
9
|
const charsPerTokenOf = (v) => (typeof v === "number" && Number.isFinite(v) && v > 0 ? v : undefined);
|
|
11
|
-
function
|
|
12
|
-
const
|
|
13
|
-
|
|
10
|
+
function envDerivedModel(config) {
|
|
11
|
+
const m = config.envModel;
|
|
12
|
+
if (m === undefined) {
|
|
13
|
+
throw new Error("config.envModel is missing — it is the BL-8 inheritance baseline (the boot env-derived model) and is REQUIRED on ServiceConfig. " +
|
|
14
|
+
"A hand-rolled `as ServiceConfig` fixture must set it to the same object as `model`; there is deliberately no fallback to `config.model` (that fallback IS the bug this seat removes).");
|
|
15
|
+
}
|
|
16
|
+
return m;
|
|
17
|
+
}
|
|
18
|
+
function toModel(m, envDefaults = {}, logger) {
|
|
19
|
+
const thinkingDefault = envDefaults.defaultThinking;
|
|
20
|
+
const declared = readModelCompat(m.compat);
|
|
21
|
+
if (!declared.ok)
|
|
22
|
+
logger?.warn("model_compat_dropped", { model: m.name, issue: declared.issue, hint: "the per-model wire-compat declaration was dropped whole — the engine falls back to its own inference (today's behaviour); fix the entry to make it live" });
|
|
23
|
+
else if (declared.compat !== undefined && m.provider === "anthropic") {
|
|
24
|
+
logger?.warn("model_compat_dropped", { model: m.name, issue: "declared on an anthropic model", hint: "`compat` mirrors OpenAICompletionsCompat — the Anthropic brain reads AnthropicMessagesCompat and never sees these keys; the declaration is dropped" });
|
|
25
|
+
}
|
|
26
|
+
const compat = m.provider === "anthropic" ? undefined : buildModelCompat(declared.ok ? declared.compat : undefined, envDefaults.compat);
|
|
14
27
|
const model = {
|
|
15
28
|
id: m.id,
|
|
16
29
|
name: m.name,
|
|
@@ -29,7 +42,7 @@ function toModel(m, envDefaults = {}) {
|
|
|
29
42
|
: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
30
43
|
params: { ...(m.tier ? { tier: m.tier } : {}), promptCacheFamily: m.provider === "anthropic" ? "input-excludes-cached" : "input-includes-cached" },
|
|
31
44
|
...(thinkingDefault ? { defaultThinking: thinkingDefault } : {}),
|
|
32
|
-
...(
|
|
45
|
+
...(compat ? { compat } : {}),
|
|
33
46
|
...(m.extraBody ? { extraBody: m.extraBody } : {}),
|
|
34
47
|
...(Array.isArray(m.promptGuidance) && m.promptGuidance.length > 0 ? { promptGuidance: m.promptGuidance.map(String) } : {}),
|
|
35
48
|
};
|
|
@@ -97,7 +110,7 @@ export function applyEffective(config, eff, logger, opts = {}) {
|
|
|
97
110
|
logger?.warn("sema_registry_model_plane_swap_refused", {
|
|
98
111
|
version: staged.version,
|
|
99
112
|
error: err instanceof Error ? err.message : String(err),
|
|
100
|
-
note: "the Runner refused the candidate model plane (
|
|
113
|
+
note: "the Runner refused the candidate model plane (swapDeps screens the whole candidate atomically — the current generation stays in force untouched); candidate refused whole, re-judged next poll (/health shows target≠applied while it persists)",
|
|
101
114
|
});
|
|
102
115
|
return false;
|
|
103
116
|
}
|
|
@@ -174,7 +187,8 @@ function stageEffective(config, eff, logger, opts) {
|
|
|
174
187
|
const models = {};
|
|
175
188
|
const modelApiKeyEnv = {};
|
|
176
189
|
const modelApiKeys = {};
|
|
177
|
-
const
|
|
190
|
+
const envModel = envDerivedModel(config);
|
|
191
|
+
const envDefaults = { maxTokens: envModel.maxTokens, contextWindow: envModel.contextWindow, reasoning: envModel.reasoning, charsPerToken: envModel.charsPerToken, defaultThinking: envModel.defaultThinking, compat: envModel.compat };
|
|
178
192
|
for (const m of enabled) {
|
|
179
193
|
const rawBaseUrl = m.baseUrl;
|
|
180
194
|
let safeUrl = rawBaseUrl;
|
|
@@ -194,7 +208,7 @@ function stageEffective(config, eff, logger, opts) {
|
|
|
194
208
|
safeUrl = undefined;
|
|
195
209
|
}
|
|
196
210
|
}
|
|
197
|
-
models[m.name] = toModel(safeUrl === rawBaseUrl ? m : { ...m, baseUrl: safeUrl }, envDefaults);
|
|
211
|
+
models[m.name] = toModel(safeUrl === rawBaseUrl ? m : { ...m, baseUrl: safeUrl }, envDefaults, logger);
|
|
198
212
|
if (m.sealedApiKey) {
|
|
199
213
|
const s = m.sealedApiKey;
|
|
200
214
|
if (s.alg !== SEALED_BOX_ALG) {
|
|
@@ -44,6 +44,6 @@ export { fetchEffective, fetchPrincipalCaps, fetchPrincipalOrgMemory, ConfigCent
|
|
|
44
44
|
export { mutateInPlace, applyEffective, type ApplyReport, applyRuntimeGates, applyRuntimeHot, resolveDefaultModelName, logEffectiveDiff, } from "./apply-effective.js";
|
|
45
45
|
export { restartReasons, planeHasActiveTiers, modelPlaneChanged, type RestartSlice, type RestartSliceCtx, type RestartSignal, } from "./restart-signal.js";
|
|
46
46
|
export { applyCenterSkills, resolveMcpServers, mcpForScenario, resolveA2aPeers, a2aForScenario } from "./skills-mcp.js";
|
|
47
|
-
export { applyCenterReadFace } from "./read-face.js";
|
|
47
|
+
export { applyCenterReadFace, CENTER_READ_FACE_LIVE_KEYS, type CenterReadFaceApply, type CenterReadFaceReport, type ReadFaceBaseline } from "./read-face.js";
|
|
48
48
|
export type { CenterSkillManifest, CenterMcpServer, CenterA2aPeer, CenterReadFace, EffectiveConfig, ExecutionRuling, SessionMirrorRuling, } from "./types.js";
|
|
49
49
|
//# sourceMappingURL=facade.d.ts.map
|
|
@@ -2,5 +2,5 @@ export { fetchEffective, fetchPrincipalCaps, fetchPrincipalOrgMemory, ConfigCent
|
|
|
2
2
|
export { mutateInPlace, applyEffective, applyRuntimeGates, applyRuntimeHot, resolveDefaultModelName, logEffectiveDiff, } from "./apply-effective.js";
|
|
3
3
|
export { restartReasons, planeHasActiveTiers, modelPlaneChanged, } from "./restart-signal.js";
|
|
4
4
|
export { applyCenterSkills, resolveMcpServers, mcpForScenario, resolveA2aPeers, a2aForScenario } from "./skills-mcp.js";
|
|
5
|
-
export { applyCenterReadFace } from "./read-face.js";
|
|
5
|
+
export { applyCenterReadFace, CENTER_READ_FACE_LIVE_KEYS } from "./read-face.js";
|
|
6
6
|
//# sourceMappingURL=facade.js.map
|
|
@@ -1,6 +1,35 @@
|
|
|
1
1
|
import type { ServiceConfig } from "../config-types.js";
|
|
2
|
+
import type { ReadFaceSource } from "../read-face-posture.js";
|
|
2
3
|
import type { Logger } from "../observability/logger.js";
|
|
3
4
|
import type { CenterReadFace, EffectiveConfig } from "./types.js";
|
|
5
|
+
/**
|
|
6
|
+
* S-174 —— 哪些 center 键是**热换席**。判据**不是本仓的意见**:`KEY_MAP` 的 `cfgKey` 落在 core 的闭集
|
|
7
|
+
* `SWAPPABLE_DEP_SEATS` 里(经 `isSwappableDepSeat` 亲问)⇒ 这个键 refresh 期可换;否则它是 boot-baked
|
|
8
|
+
* 座,只有重启能换。core 加/删可换席 ⇒ 本集合与 `read-face` restart 片的指纹**同时**跟着走,不留第二份
|
|
9
|
+
* 手抄表(restart-signal.ts 读的就是这一只)。
|
|
10
|
+
*/
|
|
11
|
+
export declare const CENTER_READ_FACE_LIVE_KEYS: ReadonlySet<keyof CenterReadFace>;
|
|
12
|
+
/** 本进程的**前 center** READ face 姿态(env / posture / 引擎默认)。`live` 相必带 —— 一次发布**撤回**
|
|
13
|
+
* `face` 键时,活席要还原到它,而不是把上一份发布的档位无限期latch 住(那是这条轴上的 fail-OPEN 方向:
|
|
14
|
+
* 组织把 `open` 撤回之后,worker 还在 open)。boot 相不需要:那一刻 `config` 上的值就是基线。 */
|
|
15
|
+
export interface ReadFaceBaseline {
|
|
16
|
+
readonly readFace?: "open" | "roots";
|
|
17
|
+
readonly readFaceSource: ReadFaceSource;
|
|
18
|
+
}
|
|
19
|
+
/** 本次调用落在哪一相(见文件头注「一席热、三席重启」)。判别联合而不是可选字段:`baseline` 只在
|
|
20
|
+
* `live` 相有意义,写成可选就等于允许一次「没有基线的 live 调用」——那正是撤回臂会静默失效的形。 */
|
|
21
|
+
export type CenterReadFaceApply = {
|
|
22
|
+
readonly phase: "boot";
|
|
23
|
+
} | {
|
|
24
|
+
readonly phase: "live";
|
|
25
|
+
readonly baseline: ReadFaceBaseline;
|
|
26
|
+
};
|
|
27
|
+
/** 调用回执。`envHeld` 同旧返回值(喂 `RestartSliceCtx.readFaceEnvHeld`);`faceChanged` 是 live 相的
|
|
28
|
+
* 换席信号 —— 真 ⇒ 调用方必须 `swapDeps({ readFace: config.readFace })` 一次,否则 config 与 Runner 分家。 */
|
|
29
|
+
export interface CenterReadFaceReport {
|
|
30
|
+
readonly envHeld: readonly (keyof CenterReadFace)[];
|
|
31
|
+
readonly faceChanged: boolean;
|
|
32
|
+
}
|
|
4
33
|
/**
|
|
5
34
|
* 应用 center 的 readFace 域(**boot-only**:调用点在 boot 拉取之后、`createRunnerDeps` 之前)。
|
|
6
35
|
* 缺席 ⇒ 零触碰。规则见文件头注 ①②③。
|
|
@@ -10,5 +39,5 @@ import type { CenterReadFace, EffectiveConfig } from "./types.js";
|
|
|
10
39
|
* 否则 center 改一个本机永远用不上的键,整片 fleet 会为一次对自己毫无影响的发布滚动重启一遍
|
|
11
40
|
* (codex 交叉复审 finding 2,验真后采纳的那一半)。
|
|
12
41
|
*/
|
|
13
|
-
export declare function applyCenterReadFace(config: ServiceConfig, eff: EffectiveConfig, logger?: Logger):
|
|
42
|
+
export declare function applyCenterReadFace(config: ServiceConfig, eff: EffectiveConfig, logger?: Logger, apply?: CenterReadFaceApply): CenterReadFaceReport;
|
|
14
43
|
//# sourceMappingURL=read-face.d.ts.map
|