/** * [ref] A10:composition root 分段 —— `RunnerDeps` 装配(含 self-orchestration 成组段)。 * * 纯搬运:两个字面量逐字来自 `main.ts`(原 1579-1881 行),缩进不变(顶层键仍在 4 空格, * `deps-literal-shape-gate` 的顶层条件展开门覆盖面不变);新增的只有 import 与包壳。 * * ⚠️ **位置即契约**:必须晚于 stores/exec-env/coordinators/workflow 段(所有键都是它们的产物), * 且早于 `new Runner(runnerDeps)` —— Runner 构造会冻结 tier 展开后的私有目录副本 * (`runnerTierFrozen` 判据就取自构造那一刻的 `config.tiers`)。 * `getRunStore()` 是唯一的晚绑取值(runStore 在本段之后才构造),原文靠同作用域前向引用。 */ import { type CacheBreakFinding, type RunnerDeps, type WorkflowRunStore, type AskOutcome } from "@sema-agent/core"; import type { createBrain } from "../brain.js"; import type { buildPricing, createTracer } from "../budget.js"; import type { ServiceConfig } from "../config.js"; import type { ElicitationCoordinator } from "../elicitation.js"; import { FleetEventBus } from "../fleet/fleet-bus.js"; import type { Logger } from "../observability/logger.js"; import type { Metrics } from "../observability/metrics.js"; import { type ConsolidationDriverSeat } from "./memory-consolidation.js"; import { WorkflowAgentRegistry } from "../orchestration/workflow-agent-steer.js"; import { type WorkflowCompletionInbox } from "../orchestration/workflow-completion-inbox.js"; import { type EngineNoticeRouter } from "../trace/engine-notice-wire.js"; import { WorkflowNotifyGate, type WorkflowCompletionPayload } from "../orchestration/workflow-notify-journal.js"; import type { ServiceWorkflowJournalStore, SessionCaptureRecordSource, StoreBackend, ToolResultStoreFull } from "../plugins/store-backend.js"; import type { QuestionCoordinator } from "../question.js"; import type { ToolApprovalCoordinator } from "../tool-approval.js"; import type { MemoryBackend } from "@sema-agent/core"; import type { MemorySyncRunner } from "../memory-sync-client.js"; import type { MailboxRecipientLifecycle } from "../plugins/mailbox-recipient-lifecycle.js"; /** * `RunnerDeps.onAsk` 的装配([ref] 件4:从内联闭包提为具名导出,**纯 testability 重构、零行为变化**)。 * * 为什么值得有名字(历史理由,[ref] 当时):bg / resume 两条腿彼时没有 per-task `spec.onAsk` 装配点, * 它们唯一的审批接缝就是这一格。端到端测试此前只能把这一行**照抄**一份,再靠一条读源码 * 的正则当漂移告警 —— 那种锚只抓得到「这一行没了/改名了」,抓不到任何语义变化,而且抄件与真件从此是两 * 份实现。提成导出之后,测试直接取本函数,接缝与 production 是**同一个符号**。 * ⚠️ [ref] 起**三腿都装** per-task `spec.onAsk = boundAsk`(sync=routes/tasks.ts、bg=runs.ts、 * resume=server.ts driveResumeLeg;core `resolveAsk` 取 `spec.onAsk ?? deps.onAsk` ⇒ 这三条腿及其继承链 * 子代不再落到本格)—— 本格现役的到达面 = 没有 per-task 装配的腿(headless/durable-submit、协调器在场 * 但 taskId 缺席的退化 resume 腿)与任何未来新腿的缺省兜底,fail-direction 不变(零 ctx ⇒ 政策口)。 * * 命名(CLAUDE.md 工厂命名律):返回的是一个闭包(活对象)⇒ `create*`。 * * 返回型**刻意窄于** `RunnerDeps["onAsk"]`(它还允许 `"allow"`/`"deny"` 两个字符串常量):本仓的接缝 * 恒是回调形,而 core 的 sync-first 判据只看 `onAsk !== undefined` ⇒ 字符串模式会顶掉 durable park * (core backlog #73,黑板 [ref]④ / [ref] 已知悉)。窄返回型把「本仓零受迫于那一形」变成编译期事实。 * * 语义逐字不变:[ref]/[ref]② 的 live tool-approval 接缝(core `resolveAsk` 读 `spec.onAsk ?? deps.onAsk`), * ALS 路由同 `onQuestion` —— 被协调器 `runWithContext` 包住的腿才够得着人。[ref] G1 终态(core 1.295 * OnAsk 三值化)下**恒**接线:回调逐 ask 时刻判活人,无附着/卡送达失败 ⇒ 返 `"unavailable"`,core 以 * `approverUnavailable` 回路把该 ask 交回 `suspendAsk` 走 durable park;无 park 设施的部署 core 自己 * fail-closed deny。协调器缺席(协议未上场)⇒ `undefined`,即「这条部署没有活人接缝」。 */ export type RunnerDepsOnAsk = (req: Parameters[0], signal?: AbortSignal) => Promise; export declare function createRunnerDepsOnAsk(toolApproval: ToolApprovalCoordinator | undefined): RunnerDepsOnAsk | undefined; /** [ref] 件⑤:core `onError(phase:"config")` 的 `classification` 已知词表。⚠️ core d.ts(`RunnerDeps.onError` 注)只登记了 * prompt-cache 相的五词,config 相的词**只在 dist js 出现**(7.2.0 亲扫 dist 下全部 js 的 `phase: "config"` 发射点:五词—— * `interaction-posture-refused`(prepare-config-doors)/ `memory-tools-not-mounted` / `sandbox-admission-unavailable` / * `shared-memory-not-mounted` / `shell-gate-off`(prepare-hands-readface));[ref] 去幻觉轮真抓:首版只写了 shell-gate-off * 一词,其余四词会被误标 unknown。所以这是「按实现亲读」的开集快照,不是 core 类型闭集——词表外的值原样进日志并标 * `classificationKnown:false`(指标标签折 `unknown`),不吞不编;与安装包的锁步由 test/boot-runner-deps-shared-base.test.ts * 的 dist 扫描格钉住(core 加词/收进 d.ts 那天先红)。 */ export declare const CONFIG_ADVISORY_CLASSIFICATIONS: ReadonlySet; /** S-386:core 的 [ref] 前缀缓存**断裂**探测器交给宿主的根因**闭集**,以及每个词的日志级别。 * 闭集**不抄词**:词源直接取 core 公开导出的 `CacheBreakFinding["cause"]`(`@sema-agent/core` 根导出, * `dist/index.d.ts`;实现在 `dist/core/cache-break-detector.d.ts`),于是 core 加词/删词 ⇒ * `Record` 少键/多键 = **编译错**(CLAUDE.md [ref]「词表是闭集 + 穷举」)。 * ⚠️ 首版在这里**手抄**了五个字面量,并在本注与 CHANGELOG 里声称「core 加词 = 编译错」—— 复审实测 * 证伪(给 core d.ts 加词后 tsc 照绿,只有一格正则扫描会红):抄来的联合与 core 无编译期联系。 * 运行期收到词表外的值(`ctx.classification` 在 core 的 onError 契约上只是 `string`)走最响的那一档 * (error)并标 `classificationKnown:false`,退化方向永远是**多一条告警**。 * * 🔴 定级判据是**事实**,不是 provider 名单:`dist/core/cache-break-detector.js` 的 `observe()` 里 * `const before = prev.cacheRead; if (before <= 0) return undefined` —— 探测器只有在**上一轮真的从 * 前缀缓存读到过 token** 时才会给出 finding。所以「带 classification」这件事本身就证明了「这条路由 * 在服务前缀缓存」,前缀 bug 四词(model-switch / tool-schema / tool-set / system-prefix)落 error * 是有据的;`server-or-ttl` 在 agentic 任务里是常态(慢工具 ⇒ 5min+ 间隔,provider 缓存到期)⇒ warn。 */ export type PromptCacheBreakCause = CacheBreakFinding["cause"]; export declare const PROMPT_CACHE_BREAK_CAUSE_LEVEL: Readonly>; export interface RunnerDepsCtx { config: ServiceConfig; logger: Logger; metrics: Metrics; localRoot: string; promptSource: RunnerDeps["promptSource"]; rosterStore: RunnerDeps["rosterStore"]; backgroundAgentStore: RunnerDeps["backgroundAgentStore"]; mailboxStore: RunnerDeps["mailboxStore"]; /** * [ref]:mailbox 的**收件人生命周期面**(预删除态)。与 `mailboxStore` **成对**(`boot/stores.ts` 同段铸, * 两键同在场/同缺席);是 peer 目录席三个合取项之一,也是会话删除级联落墓碑的那一口。 */ mailboxRecipientLifecycle: MailboxRecipientLifecycle | undefined; /** [ref]-T1:治理窗账本店(USAGE_WINDOWS 配置时才在场;与 config.usageWindows 成对进 deps)。 */ usageWindowStore: RunnerDeps["usageWindowStore"]; brain: ReturnType; pricing: ReturnType; tracer: ReturnType; outcomeSink: ReturnType | undefined; elicitation: ElicitationCoordinator | undefined; question: QuestionCoordinator | undefined; toolApproval: ToolApprovalCoordinator | undefined; sessionStore: ReturnType; /** * B-060(test [ref] 核心发现①)—— **一条部署腿上的每一只 Runner 看同一只检查点店**。 * * 本键从「差异键」升进{@link createSharedRunnerDeps 共享基座}。修前它**只**挂在 subRunner 上,主 * runner 靠 per-task `spec.checkpointStore` 拿店 —— 而 core 的 `run_workflow` 是挂在**主** Runner * (`prepare-caps-and-workflow.js` 的 `runner: runnerSelf`;⚠️ S-516:这里**不写 dist 行号** —— * B-060 票据当年引的那组坐标每版都在漂,复核一律按符号名搜)上的。 * * ⚠️ **B-060 当初写下的病理有一半已被 core 7.14.0 消解,如实订正**([ref] 的 `CheckpointSeat` 重构): * · **7.13.0 及以前**:`orchestration/workflow-primitives.js` 只在父侧 * `parentCheckpointStoreDisabled === true` 时给子代盖 `"disabled"`,**不把父侧真店透传给子代** * ⇒ 子代的 `resolveCheckpointStore(spec, deps)` 落到主 runner 这一格 = `undefined` * ⇒ `parkLane {capable:false, reasons:["no_checkpoint_store"]}` ⇒ 受门的调用不是 park 而是 * `delegation.ask_unresolvable{parkLaneExisted:false}` 直接拒(过度拒绝)。 * · **7.14.0 起**:core 在 prepare 一次铸出 `checkpointSeat = checkpointSeatOf(spec, resolved)` * (`prepare-task.js`),两条委派车道**逐字复制**它到子代 —— workflow 腿经 * `parentCheckpointStore: checkpointSeat`(`prepare-caps-and-workflow.js`)落到 * `workflow-primitives.js`(现在的条件是「父侧有座 ∧(座是 disabled ∨ 子 spec 没自带店)」⇒ * **真店也透传**),Task 腿经 `ToolExecuteContext.checkpointStoreForChildren`。 * ⇒ 「子代拿不到店」这条病路 core 侧已经堵死。**但本席仍然承重**,理由是那只座**本身**就从 * `resolveCheckpointStore(spec, deps)` 来:主 runner 这一格空着、而 spec 又没带店时,座就是 * `undefined`,子代照样什么都拿不到。本席不是 core 单源的第二个写者,它就是 core 读的那个 deps 格。 * * 🔴 host 腿的读数判据是**core 侧硬的**,不是本仓的但愿:`resolveCheckpointStore` * (`checkpoint-store.js`,7.14.0 亲核)**spec 优先**——`spec.checkpointStore` 在场就用它、 * `"disabled"` 就是关、只有缺席才落 deps。而本仓 host 腿恒经 `resolveSpec` 的 durableEnabled 分支盖上 * **同一个实例**,且 `durableEnabled ⇔ checkpointStore !== undefined`(`boot/coordinators.ts:70` 与 * main.ts 的构造条件同源)⇒ 每一条经 `resolveSpec` 的腿看到的店逐字同一只。 * * 🔴 **core 7.14.0 新加的响亮拒(装配纪律,本仓已合规但必须知道)**:`agents/subagent.js` 在 * `wantsBackground` 臂上会检查「`ScenarioDeps.checkpointStore`(→ `bg.checkpointStore`)与本 run 的座 * **不是同一只**」⇒ `configError("config.invalid_checkpoint_store")` **抛**。本仓 main.ts 的 * `scenarioDeps.checkpointStore` 与本席是同一个 `const`,run-local 两边都不接 ⇒ 两条腿都不触发; * 哪天有人给这两处接了不同的店,失败是响亮的而不是静默半场。 * * ⚠️ **如实登记一处不经解析器的直读**(codex 交叉复审提名,亲读 core dist 确认): * `core/runner/run-terminal-adoption.js`(7.14.0 亲核)读的是 * `runner.deps.checkpointStore` **本身**,不走 `resolveCheckpointStore`,**也不走座** —— 所以 7.14.0 * 的座重构对它一个字节都没改,本席对它仍是唯一来源。它是「宿主任务 park 时,把没排空的 steer / * follow-up 迁移到 park token 上」那一步 —— 修前主 runner 这一格是空的,于是**整段迁移静默不跑** * (那些输入随本腿一起丢,只留 `task.user_followup_undrained` 一类通告)。接上之后它开始真的跑。 * * 🔴 这一席是**部署腿级**的答案,不是「哪只 Runner」的属性:一条腿要么有赎回口(HTTP 面的 * `/v1/approvals/:id/decide` + resume 家族)、于是它的每只 Runner 都该有店;要么没有(`run-local` * 的一次性 CLI)、于是**一只都不该有** —— core 的平台/资源停驻(`prepare-boundary-parks.js` 的 * `suspendForPlatformLimit`)只看「店在不在」,不看 approver 席,给一条没有赎回口的腿接店 = 用一张 * 没人能兑付的 checkpoint 换掉一次干净的失败。 */ checkpointStore: RunnerDeps["checkpointStore"]; memoryEngine: { backend: MemoryBackend; root: string; } | undefined; /** * S-399(core 7.21.1)—— 记忆整理的**已解析座**(`boot/memory-consolidation.ts` 的 * `resolveConsolidationDriverSeat` 产物),在场 **iff** `MEMORY_CONSOLIDATION_DRIVER=on` 且记忆引擎接了线。 * * 🔴 **一座两用,不是两次装配**:同一只座同时喂 admin 阀门的操作面引擎与这里的引擎面两席 * (`RunnerDeps.memoryConsolidation` + `memoryConsolidationDriver`)。第二次解析会让「谁在花钱」 * 在两条腿上各答一遍,而座是 boot 期定的(restart-to-apply,理由在该模块顶注)。 * 缺席 ⇒ 两席都**不写键** ⇒ 引擎那半场对 7.20.x 逐字节不变。 */ consolidationSeat: ConsolidationDriverSeat | undefined; /** [ref]②([ref] §2.1b,core 7.0.2 [ref] 件2)/ S-215:会话 capture opt-out 记录的**按平面取用源** * (`backend.sessionCaptureRecords()`,main.ts 取**一次**、与四只 operator 面引擎同一只源 —— 全站同批 * 注入律,机器钉 = test/memory-capture-optout-lane.test.ts ②)。在场 ⇒ `capture:"off"` 声明与中途翻转 * verb 真持久;缺席 ⇒ core 读作「无店」,而 core 的 `captureCarrierUnsupported` 判据是**席位在不在** * (不是「文件形能不能用」),叠上 `isRemoteExecutionEnv` 的接口形鸭子判 ⇒ 两条 ingress 都拒 * `config.memory_capture_unsupported`。S-215 起三条车道都供(SQL 双方言 twin / local 的控制面文件三腿), * 所以缺席今天只剩一个成因:**记忆面暗**的 local 部署(那时 core 的 capture 腿本就不参与)。 */ sessionCaptureRecords: SessionCaptureRecordSource | undefined; memorySyncRunner: MemorySyncRunner | undefined; /** [ref] 件A([ref] 件3③):org 记忆准入两 seam 的装配产物(boot/org-memory.ts)。进**共享 * 基座**——主/sub 两 Runner 必须同源:委托子代的 prepare 同样跑准入(委托冻结的重判腿),漏挂 * subRunner=子代平面跳过目录判决。 */ orgMemoryAdmission: import("./org-memory.js").OrgMemoryAdmissionWiring; /** [ref] 件B/C/D([ref] 件1):三个部署治理座席的装配产物(boot/governance-seams.ts)。进**共享 * 基座** —— 合规否决与锁定层必须同样管住委托子代那条腿(子代 prepare 走同一套 preflight;漏挂 * subRunner = 子代平面跳过档位与锁,与件A 同一个理由)。`retentionPolicy` 是纯声明,同源携带。 */ governanceSeams: import("./governance-seams.js").GovernanceSeams; /** [ref] —— org 共享记忆库的供给面(core `RunnerDeps.sharedMemoryStores`)。在场即挂载 * `memory_list`/`memory_read` 两只工具(core 没有 TaskSpec 伴生旋钮:dep 的在场**就是**部署意图); * 缺席 ⇒ 两只都不挂,连占位都没有。装配点按「SQL 供给面 ∧ org 折叠面」双在场才铸它,与 HTTP 面 * 同一个合取式(main.ts)。 */ sharedMemoryStores: RunnerDeps["sharedMemoryStores"]; toolResultStore: ToolResultStoreFull | undefined; sessionPolicyStore: ReturnType | undefined; runtimeCapsResolver: RunnerDeps["runtimeCapsResolver"]; /** [ref] per-edited-file rewind history (core `RunnerDeps.fileHistoryStore`; replaces the retired E19 * whole-tree snapshot seat — core 94632197 renamed the seat, not a coexistence). WIRING IS THE OPT-IN. */ fileHistoryStore: ReturnType | undefined; /** [ref] 车二:持久化权限规则店 provider(core `RunnerDeps.permissionRuleStore`)。缺席 ⇒ 引擎的 * `permissionRules.storeWired` 如实报 false、`AskRequest.ruleOffers` 不铸(诚实缺席)。 */ permissionRuleStore: RunnerDeps["permissionRuleStore"]; /** [ref](core 5.50 [ref]):mid-turn MCP 撤销台账席 —— core 每次 MCP dispatch 前同步探 * `isRevoked(serverName)`,被撤服务器的调用结算为 coded 拒绝 `mcp.server_revoked`(known-not-executed)。 * 来源=configCenter.takeMcpRevocations()(取走即声明接线,restart 豁免与承接绑同一动作)。 * 缺席=undefined=pre-338 语义(纯 env 部署无中心撤销面)。 */ mcpRevocations: RunnerDeps["mcpRevocations"]; /** [ref] 件⑤([ref] server ③):per-session「显式 bypassPermissions」登记簿的读腿 —— `onError(phase:"config", * classification:"shell-gate-off")` 在本 session 显式声明 bypass 时降级 info(正常态不打 warn)。缺席(桩 / * run-local)= 照 warn(退化方向=多一条告警)。 */ sessionShellGateExplicitlyOff?: ((sessionId: string) => boolean) | undefined; /** * 交接件⑤ —— commit 尾注的署名座(`RunnerDeps.hands.commitCoAuthor`)。 * * 🔴 为什么它属于**共享基座**而不是主 runner 的差异键:署名是**部署身份**([ref]① clay 拍: * 「署名 = 产品身份资产,归部署」),不是「哪一只 Runner 在跑」的属性。一个被委派出去的子代 * 提交进的是**同一个仓**、代表的是**同一个部署** —— 它的 commit 少一行 trailer 没有任何理由。 * 修前两条腿都漏:`main.ts` 与 `run-local.ts` 的 subRunner 都只在主 runner 上写了这一键,而两处 * 的「差异键」注释块逐条列了 sessionStore / checkpointStore / 四个座位,**都没提 hands** * —— 也就是说它不是一次有理由的分歧,是漏配(与本函数头注记的 [ref]§三族A 同一个病族)。 * 由**调用方**给值(而不是在基座里按 `config` 现算):两条腿的判据本就不同(HTTP 腿按 * `configProvider === "local"` 分 branded/非 branded,run-local 恒是 Sema 本地形), * 各自算一次、传进来一次,两只 Runner 自动同源。 */ hands: RunnerDeps["hands"]; executionEnvFactory: RunnerDeps["executionEnvFactory"]; lspManager: RunnerDeps["lspManager"]; fleetBus: FleetEventBus; deploymentHooks: NonNullable; workflowRunStore: WorkflowRunStore | undefined; workflowJournalStore: ServiceWorkflowJournalStore | undefined; workflowAgentRegistry: WorkflowAgentRegistry | undefined; workflowNotifyGate: WorkflowNotifyGate | undefined; workflowCompletionInbox: WorkflowCompletionInbox | undefined; deliverWorkflowCompletion: (p: WorkflowCompletionPayload) => Promise; /** 晚绑(runStore 在本段之后构造)——见文件头「位置即契约」。 */ getRunStore: () => ReturnType | undefined; } /** [ref] A10 留档发现②:main runner `RunnerDeps` 与 main.ts subRunner 字面量之间此前手工重复 * 的 ~15 个键,类型标注见 {@link createSharedRunnerDeps} 头注。 */ export type SharedRunnerDeps = Pick; /** * [ref] A10 留档发现②(review 2026-07-29,[ref]§三族A 同源修补的延续):main runner 的 * `RunnerDeps` 字面量(下方 `createRunnerDeps`)与 `main.ts` 里 subRunner 的 `new Runner({...})` * 字面量之间,以下 ~15 个键此前是**各自手写、独立展开**的同源值(同一 ctx 变量,两处分别拼字面量, * 不是同一引用)——正是 [ref]§三族A 那次「主 runner 挂了、subRunner 漏挂」漏配事故的键族(当时的 * 修法是把 onBackgroundChildEvent/loadProjectMemory/probeInstructionSources/onError 四键改成直引 * `runnerDeps.X`「同一实例」;这里补的是**剩下未被那次改法覆盖**的 15 个键——两处字面量各自独立 * 取值,漏配一个不会有任何编译期或运行期信号)。 * * 抽成本函数 + `Pick` 类型标注(`SharedRunnerDeps`),把「新增/改名一个键、两处忘 * 同步」从「肉眼比对两份手写字面量」变成**编译期护栏**:调用方漏写一个必需键 ⇒ 返回值类型当场 * 红;`RunnerDeps` 改名/删键 ⇒ 这里的联合类型当场红。 * * 消费点(复审 2026-07-30 F1 更新,过期「尚未接上」段已删):**两处都已展开**——下方 * `createRunnerDeps()`(全量 ctx)与 main.ts 的 subRunner `new Runner({...createSharedRunnerDeps(...)})` * (窄面 {@link SharedRunnerDepsCtx})。新增共享键只需加进本函数返回体,两 Runner 自动同源; * **不要**再在任一调用点手写同名键(那会 override 展开,回到 [ref] 漏配病)。 * * 差异键(为何不在本共享基座、逐一注明): * - `sessionStore`:main runner 用宿主 durable 会话店;subRunner 用私有短 TTL 的 * `ForkRoutingSessionStore`(main.ts 现场构造,子代转录生命周期与主会话不同)。 * - `onBackgroundChildEvent` / `loadProjectMemory` / `probeInstructionSources` / `onError`: * [ref]§三族A 已用「直引 `runnerDeps.X`」修过(main.ts 两处引用同一个 `runnerDeps` 实例的同一 * 属性,比再抽一层共享基座更强的同源保证),不重复收纳进这里。 */ /** 基座真实消费的窄面(Pick)——subRunner 调用点(main.ts)只需凑这 13 个字段,不必造全量 ctx。 */ export type SharedRunnerDepsCtx = Pick; /** 转发席只需要 `inc` —— 刻意窄到这一个动词,免得把整只 Metrics 拖进这条纯转发腿。 */ export interface EngineNoticeMetricsSeat { inc: (name: string, labels?: Record) => void; } export declare function createEngineNoticeForwarder(logger: { warn: (msg: string, fields?: Record) => void; }, router?: EngineNoticeRouter, metrics?: EngineNoticeMetricsSeat): NonNullable; /** * `onNotice` **席**的装配判据本身 —— 「`warn` 真在场才铸席」这一条从此是**一个可直接调用的函数**, * 不是散在装配点、只能靠扫源码文本去猜的一行三元(codex 交叉复审 R1-[medium],验真后修)。 * * 病(实测):把 leader 的门写成 `cfg.logger ? ((m, x) => cfg.logger?.warn?.(m, x)) : undefined` 时, * 「装配点切片里出现过 `warn` 这个词」这类**源码文本**判据一律照绿 —— 而只装了 `info` 的部署会拿到一个 * truthy 闭包、照样铸席,通告随后被可选链**静默吞掉**,比不铸席更坏(不铸时 core 至少还打它自己的 * `console.warn`,响亮权留在原处)。任何「在这段文本里找关键字」的门都挡不住这一族变异,因为病灶在 * **值**(闭包 truthy)而不在字面。⇒ 把判据搬进函数,让它可以被**行为**测:info-only ⇒ 空席;带 warn ⇒ * 具名转发体。 * * 命名(CLAUDE.md 工厂律):返回的袋子里装的是**活闭包**(有行为)⇒ `create*`,不是 `build*`。 * 返回**展开形**(`{}` 或 `{ onNotice }`)而不是 `onNotice: undefined`。理由是**键卫生**,不是 core 行为 * (R3 复扫更正:core 5.28 的 `deliverEngineNotice` 只判 `typeof onNotice === "function"`,显式 `undefined` * 与键缺席在 core 那侧行为逐字相同、响亮权都留在 core——旧句「显式 undefined 也算 host 接管」与真码相反): * 展开形让 `new Runner({...seat})` 的键集只随真装配变化,EXPECTED_SHARED_KEYS 类的键集绊线才数得准。 */ export declare function createEngineNoticeSeat(logger: { warn?: (msg: string, fields?: Record) => void; } | undefined, router?: EngineNoticeRouter): { onNotice?: NonNullable; }; /** * [ref](黑板 [ref],[ref] 登记)—— **部署姿态座席族**:运维在配置面写下的姿态(READ 面四席 + 记忆三席 + * 委派入口 caps)。core 明写「每个 run 读**准备它的那只 Runner** 的 deps」⇒ 一台部署里跑任务的**每一只** * Runner 都必须带同一份:主 runner / subRunner / run-local(经 {@link createSharedRunnerDeps} 展开)+ * **leader lane 五只**(`boot/leader.ts` → `LeaderWireConfig.deploymentPosture` → `src/leader/wire.ts` * 五处 `...deploymentPostureSeat`)。 * * 🔴 本函数是这一族**唯一**的取值点。两处各读一遍 config 就是 [ref]§三族A「主挂了、副漏挂」的病族形—— * [ref] 正是 leader 那条腿漏了**全族**(修前 wire.ts 五处一席都没有;边界曾靠 * `test/memory-delegation-evidence-lane.test.ts` ⑤ 组的机器门如实登记,现已翻成正向门)。leader 不许自己 * 再读 config 铸一份,只吃本函数经 cfg 递过去的产物(`test/leader-deployment-posture-seats.test.ts` ③ 钉)。 * * 纯数据(键值全是 config 透传,无闭包)⇒ `build*`(CLAUDE.md 工厂律)。键**恒在场**、缺席 = `undefined` * ([ref] S4 house style;core 消费点全按 `!== undefined` / `??` 判 —— dist * `prepare-hands-readface.js`、`prepare-config-doors.js`、`prepare-task.js` 亲读, * 「键为 undefined」与「键缺席」对引擎逐字等价)。**不复制引擎缺省**(`roots`/`static-face`/`carry`/`open`/ * CC parity 20/200 一个都不抄):上游改缺省之日起本仓不会成为第二份真源。 */ export type DeploymentPostureSeats = Pick; export declare function buildDeploymentPostureSeats(config: Pick): DeploymentPostureSeats; export declare function createSharedRunnerDeps(ctx: SharedRunnerDepsCtx): SharedRunnerDeps; export declare function createRunnerDeps(ctx: RunnerDepsCtx): RunnerDeps; //# sourceMappingURL=runner-deps.d.ts.map