/** * ForkRoutingSessionStore — the subRunner's session backing after core 1.350's fork 终修([ref]①/[ref]①). * * 问题形:core 的 `hostSessionFork` 在 HOST runner 的店里 fork,fork 子任务带 `requireExistingSession:true` * 在 opts.runner(subRunner)上执行——subRunner 拆店时子任务查不到刚 fork 出的会话,响亮失败 * `resume.session_not_found`(cli 真机复现)。 * * 但把 host durable 店**整个**给 subRunner(第一版修)被 codex 判两 high:普通子任务(council 镜头/ * team/子代理)会把每次运行都铸成 committed durable 会话——sweepStagingSessions 只扫**无 session_meta** * 的 staging 弃置行,committed 子会话没有 reaper=无界增长;且进 owner 过滤的会话枚举(零 run 的内部 * 会话污染 /v1/sessions)。 * * 此包装店按**会话的真居所**路由,两边语义都保住: * - `acquire(id, {requireExisting})`(fork 子 + retained-child resume 的形)→ **host 店优先**,miss 落 * transient(retained 瞬态子的 resume 仍走私店);两边都 miss=抛 host 侧错误(fail-loud 原语义)。 * - `acquire()`(无 id=普通子任务铸新会话)→ **transient**(throwaway 姿势保留:council/team 子会话 * 不落 durable、不进枚举、TTL 自清)。 * - `acquire(id)`(带 id 不 requireExisting)→ 已有路由按路由走;未知 id 落 transient(claim-create * 绝不打到 durable 店——那是 authorizer 的职权)。 * - `fork(sourceId)` → 源在哪店在哪店 fork(路由未知时 host 先试,null 再 transient);新 id 记路由。 * - `list`/`size`/`dispose` = transient 侧(host 店归 main runner 所有;不双列、不双清)。 * * 路由表 LRU 上限 {@link ROUTE_MAX}:满额逐最老(refresh-on-use);被逐后下次 `acquire` 按上述规则重derive * (requireExisting 的 host-first 重试仍落对店),中途 touch/noteTaskRun 缺路由=落 transient(no-op 级)。 */ import { type SessionStore, type AcquiredSession, type SessionStoreSummary, type SessionPlacement, type PlacedSessionRow } from "@sema-agent/core"; /** acquire 选项形(core 5.48 SessionStore 契约):[ref] placement=受信 spawner 的分区建会话声明。 */ type AcquireOpts = { requireExisting?: boolean; placement?: SessionPlacement; }; export declare class ForkRoutingSessionStore implements SessionStore { private readonly host; private readonly transient; private readonly route; /** pin 时的店归属——unpin 恒回同店(LRU 逐出不拆对,见 pin/unpin)。 */ private readonly pinnedAt; /** [ref] 晋升名册(探针 Finding A 修):已 promoteToHost 的 id **永久**钉 host——路由表会被 LRU * 逐出,而私店的 forget 只清 entries 缓存行、底层 repo 行不清且 sweep 扫不到(core session-store.js * 亲读),逐出后 transient-first 的 requireExisting 会把陈旧快照端回给 revive。本集不逐出(上界= * parked 子代数,天然小);pick/acquire 首查。 */ private readonly promoted; /** [ref] placement 半场:placed id 名册(与 promoted 同姿势——不随路由 LRU 逐出)。placed 会话的 * 真居所恒=host(分区在 durable 店),任何形的 acquire/touch/release 都不得落 transient—— * 否则 transient 的 claim-create 会铸出同 id 影子会话,requireExisting 的 transient-first 顺序 * 会让影子遮蔽真转录(F-7 顺序对 placed 反向成害)。 */ private readonly placed; /** [ref]:host 店的 placements 声明透传(声明制——包装层不 duck-type,host 声明了才武装 placed * 路由;host 不声明时本店全行为与 5.47 前逐字节同)。 */ readonly placements?: SessionStore["placements"]; /** [ref]:分区枚举透传(host 有才有——恒在的空实现会把「不能枚举」谎报成「没有 placed 会话」)。 */ readonly listPlaced?: (kind: "subagent", opts?: { olderThanMs?: number; scope?: string; }) => Promise; constructor(host: SessionStore, transient: SessionStore); private remember; private pick; /** [ref]:host 声明分区时的冷缓存判位探针。判据=host `requireExisting` acquire 的 `SessionMetadata.placement` * (5.48 additive,File/SQL/Ttl 三形都带——比 `placementOf` 面更普适:Ttl 缓存层不透传 placementOf)。 * placed 命中把 acquired 会话一并交回(调用方正需要它时零二次 acquire);非 placed/absent 的探针 * acquire 一律 **forget** 收尾([ref] 义务①:验存在的 acquire 禁 release——release(placed)=真删)。 */ private hostPlacedProbe; acquire(sessionId?: string, opts?: AcquireOpts): Promise; touch(sessionId: string): void | Promise; release(sessionId: string): Promise; forget(sessionId: string): void | Promise; pin(sessionId: string): void | Promise; unpin(sessionId: string): void | Promise; noteTaskRun(sessionId: string, taskId: string, runId?: string): void | Promise; list(): Promise; /** * [ref] 子代 park 晋升:把住在 transient(TTL)店的子会话**整树拷贝**进 host durable 店,然后 * 让该 id 从此路由到 host——`ensureChildSessionDurable` 的「主动晋升」半场(1.263 的只查不迁形在 * 拆店生产形下恒否决 park:子会话从不在 host 店,core 收 veto 即 expire checkpoint + settle failed, * cli 真机 14ms~2s expired 即此)。 * * 三步缺一不可: * 1. 拷贝:transient 读整树(getEntries)+ 源 leaf → host 店 acquire(id)(durable 店的 acquire * 缺行即建行)→ storage.appendEntry 逐条 verbatim(重复 id 容忍=幂等续迁:上次晋升半途崩的 * 残留行跳过)→ setLeafId(源 leaf)(分支树以源为准,不信 append 推进的 leaf)。 * 2. transient.forget(id):`requireExisting` 的 acquire 是 **transient-first**(F-7 裁定)——私店 * 副本不移除,revive 会续写进 1h TTL 副本,durable park 名存实亡。 * 3. 路由记 host(此后 touch/pin/release 全走 host;pinnedAt 若记着 transient 一并改写, * unpin 不落错店)。 * * 已在 host(路由或真行)⇒ 幂等直返。两店都无此会话 ⇒ 抛(调用方=park veto,诚实 fail-closed)。 */ promoteToHost(sessionId: string): Promise; fork(sourceId: string, owner?: string | null): Promise; get size(): number; dispose(): void | Promise; } export {}; //# sourceMappingURL=fork-routing-session-store.d.ts.map