/** * [ref] A10:composition root 分段 —— v2 leader endpoint([ref] + [ref])。 * * 纯搬运:函数体逐字来自 `main.ts`(原 3094-3171 行),缩进不变;新增的只有 import 与包壳。 * * ⚠️ **位置即契约**:必须在 `executionEnvFactory` / `checkpointStore` / `sessionStore` 都装配完之后调用 * (工厂模式的 k8s/host 腿以 `executionEnvFactory` 在场为门;durable 子 worker 挂 `checkpointStore`)。 */ import type { ServiceConfig } from "../config.js"; import type { createBrain } from "../brain.js"; import type { buildPricing } from "../budget.js"; import { createLeaderEndpoint } from "../leader/endpoint.js"; import type { Logger } from "../observability/logger.js"; import type { CheckpointStoreFull, StoreBackend, ToolResultStoreFull } from "../plugins/store-backend.js"; import type { RunnerDeps } from "@sema-agent/core"; export interface LeaderCtx { config: ServiceConfig; logger: Logger; brain: ReturnType; pricing: ReturnType; executionEnvFactory: RunnerDeps["executionEnvFactory"]; toolResultStore: ToolResultStoreFull | undefined; sessionStore: ReturnType; checkpointStore: CheckpointStoreFull | undefined; /** [ref] 件B/C/D([ref],codex R2-F1):部署治理三座席 —— leader 车道的每一只 Runner 都要带上它, * 否则开了 leader 端点的部署就有一条在部署治理**之外**的执行面(合规否决/锁都够不着)。 */ governanceSeams: import("./governance-seams.js").GovernanceSeams; /** * [ref]([ref])—— 主车道 `boot/budget-tracing.ts` 造的**同一只** tracer。leader 车道的每一只 Runner * 都要带:tracer 是 `brain.call` 的唯一记账点(costQuota / fleetUsage / fleetLease / 花费计数四个写口), * 而 `POST /v1/leader` 的准入处**读**的正是它填的那两个桶 —— 缺席 = 门查的桶与花钱的手不是同一只。 * * 🔴 **必填键**(值可为 undefined):这三席都刻意写成非可选,让「main.ts 铸了却忘了递」变成**编译红** * 而不是又一次静默漏配([ref]§三族A 病族的机器护栏)。 */ tracer: RunnerDeps["tracer"]; /** [ref]([ref],[ref]-T1)—— 治理窗**账本店**(与 `config.usageWindows` 声明成对;声明直接从 * 本 ctx 的 config 取,leader 不另读)。缺席 = 未配治理窗的部署。必填键,理由同 `tracer`。 */ usageWindowStore: RunnerDeps["usageWindowStore"]; /** [ref]([ref])—— center 发布的 prompt catalog(`configCenter.promptSource`,与主 runner/subRunner * 同一只)。缺席 ⇒ leader 的四类顶层任务跑引擎内置提示词,而同部署其余三条腿跑运维发布的那一份, * 且 core 顶层新任务是**静默**落回(无 notice)。必填键,理由同 `tracer`。 */ promptSource: RunnerDeps["promptSource"]; /** [ref] 披露半件([ref],codex r2-[high])—— 主车道 `createRunnerDeps` 铸的**同一只**错误汇 * (main.ts 直引 `runnerDeps.onError`,与 subRunner 的先例同姿势)。core 对治理窗**最终扣账失败**的 * 唯一出口就是它(`runtask.js` 的 `catch (flushErr) => deps.onError`),所以它与 `usageWindowStore` * 席同生共死:挂窗不挂汇 = 治理轴上的静默失败面([ref])。必填键,理由同 `tracer`。 */ onError: RunnerDeps["onError"]; /** [ref] 车4 件1:durable store 后端(leader-run 登记表从这里取)。缺席 = 无 DB 的 env-only worker。 */ backend?: StoreBackend | undefined; } /** * 鲁棒性批5 A6(2026-08-05):k8s 腿要求 MinIO 三件套(ENDPOINT/ACCESS_KEY/SECRET_KEY)全在——半开(一件缺) * 就让 `leaderEndpoint` 的三元式落到 undefined,此前**零披露**:运维显式开了 LEADER_ENABLED + * REMOTE_EXEC=k8s,却在日志里既看不到 `leader_endpoint_enabled` 也看不到任何"为什么没启用"的信号——只能 * 靠读源码才知道要去检查 MinIO 三件套。 * * 纯谓词,抽出独立函数:只吃 leaderEnabled/leaderProvider/hasS3 三个原语 + 直接读 process.env 取缺失的 * 具体变量名,不依赖完整 `LeaderCtx`(构造一整套 brain/pricing/sessionStore 只为测一个 warn 分支不值当)。 * 返回 null = 不适用该警告(未开启/非 k8s 腿/三件套齐全);非 null = 该报警,携带具体缺的变量名列表。 */ export declare function leaderK8sMinioGap(leaderEnabled: boolean, leaderProvider: string | undefined, hasS3: boolean): string[] | null; /** * [ref] 车4 件2 的**能力面**门(codex 交叉复审 R1-high①,验真后修)。 * * config 层的门②判的是 `DB_BACKEND` 的字面值 —— 那句话回答的是「运维要没要 durable」。它回答不了 * 「这次启动**真的拿到**了 durable 吗」:`openStoreBackendWithFallback` 在 `SESSION_BACKEND=auto` * (默认)下,SQL 连不上 / 建表失败 ⇒ 返回 `backend=undefined` + 一条 warn 就继续启动。那次启动里 * `leaderRun()` 缺席,leader 端点会静默落进单副本内存臂:多副本各自受理同一个 Idempotency-Key、 * 跨副本 GET 404、重启失忆 —— 而这条腿的产物是**真 git push**。 * * ⇒ 判据锚**真拿到了没有**。纯谓词、只吃两个原语(与 {@link leaderK8sMinioGap} 同姿势),便于单测。 * 抛而不是 warn:这正是 [ref]「安全/执行车道禁静默 fail-open」说的那一类 —— 降级的代价由用户承担, * 而承担的方式是重复的真实推送。 */ export declare function assertLeaderDurableStore(leaderEnabled: boolean, hasLeaderRunStore: boolean): void; export declare function createLeaderFace(ctx: LeaderCtx): ReturnType | undefined; //# sourceMappingURL=leader.d.ts.map