/** * Task Trace projection (S1): the durable run event log → sema-registry's `TraceTurn[]` contract. * * The event log (src/runs.ts) is a flat per-task seq stream: reasoning / text / tool_start / tool_end / * turn_end / compacted / done / suspended / failed. sema-registry renders an ORDERED `blocks[]` transcript * (CC-style thinking↔tool interleave), grouped into turns. This is pure presentation/projection = the * SERVICE layer (core [R57] cross-review: do NOT push this shape into core — different consumer, different shape). * * Per core [R57] corrections: there is no `suspended` event (suspend = the run row's status); per-turn tokens ride * on `turn_end.usage` (core additive). As of core 1.121.0 ([ref] §E1) `tool_end` ALSO carries the model-facing * `output` (+ `truncated`) — redacted + size-bounded at append (runs.ts) and read straight off the event here (the * old session-join `toolOutput` hook is gone, there's a real source now); `eventId`/`parentToolCallId` (§E2) ride * each content event, additive / tolerate-absent. * * 🔴 **本族 `*EventData` 构造器的入参型不是白名单**(S-545 起,别再照旧读法维护它)。 * 每只构造器的形参型是**这条投影的本地入参形**,故意比 core 的分支型松:三条腿里有两条(durable 账本 / * resume 回放)手里只有磁盘上的 `Record`,窄值型在那里结构上不成立,所以 `unknown` + * 内部逐层窄化是**有意**的,不是欠账;核过的 core 键在场性由投影体自己的条件展开臂决定。 * 「这一版 core 的键有没有真的上 wire」这件事的判据**不在形参型上** —— 它在 * `trace/wire-projection-faces.ts` 的登记表 + `test/wire-whitelist-exhaustiveness.test.ts`:门把 core 型的 * 键集从**安装态 `.d.ts`** 机器派生出来,喂一枚全键 fixture 真跑一遍本族每只构造器,再拿**投影体**的键集 * 双向对账(多投一枚 / 漏投一枚各自一条变异负控)。旧形只比形参签名,于是「入参型里写着但投影体里从来 * 没有」这一类(`human_input.principal` 就是一例)与「忘了投」逐字同形 —— 那正是被替掉的盲区。 * 新加一只构造器 ⇒ 同批往登记表加一行,否则普查格红。 */ import type { GateOutcome, McpDelivered, TaskResult, SystemInjectionPriority } from "@sema-agent/core"; import type { RunEvent, RunRecord } from "../plugins/store-contracts.js"; import type { ModelUsageDelta, ModelUsageTracker, PromptManifestRecord } from "../budget.js"; import { type SemaProvenanceInput } from "./sema-provenance.js"; import type { LedgerEventType } from "./ledger-events.js"; /** 账本写口的统一签名 —— `type` 是**闭集**(`ledger-events.ts`):新型必须先登记,否则调用点编译红 * (codex 交叉复审 F2:别名门 `ap("x")` 与动态首参让纯词法门结构性抓不全)。 */ type AppendFn = (type: LedgerEventType, data: unknown) => Promise; type GetEventsFn = (taskId: string, afterSeq: number) => Promise; /** E8 (shell-host): drain the per-task model-usage accumulator into an append-only `model_usage` DELTA event. * Shared by BOTH durable-write legs (runInBackground + the resume leg) so the persisted shape never drifts; the * append-only deltas make a suspended/resumed run's total resume-safe (each leg appends its own, the echo sums). * * core 3.0.0([ref] 装车单 S10,§DESIGN-V2 V2-⑤): every drained row is tagged with `usageBasis: * "uncached-components-v1"` — the write point for this label (the tracker itself, {@link ModelUsageTracker}, * never sets it; it stays a pure token/cost accumulator). This is what lets {@link aggregateModelUsage} tell * apart rows written under the current component semantics from older rows (pre-dating this label) when it * sums across a run's whole event log. */ export declare function appendModelUsageDelta(append: AppendFn, modelUsage: ModelUsageTracker | undefined, taskId: string): Promise; /** [ref]② drain the pending prompt-manifest records (whitelisted at RECORD time, budget.ts — ids/digests/ * counts only, no prompt bodies by core's manifest contract) into durable `prompt_assembled` events, one per * prepare IN EMISSION ORDER (a cascade/verify leg re-prepares under one taskId — every rung's manifest * persists; release-review finding). Shared by both durable-write legs; a no-op when nothing is pending * (one Map.get), so callers fire it liberally. */ export declare function appendPromptManifest(append: AppendFn, tracker: { drain(taskId: string): PromptManifestRecord[] | undefined; drainConfig?(taskId: string): Array> | undefined; } | undefined, taskId: string): Promise; /** E8: at a TERMINAL done — flush the final delta, then SUM all `model_usage` events (every process leg) into the * result's `stats.modelUsage` echo. Returns the result unchanged when no usage was tracked (shell soft-degrades). */ export declare function attachModelUsage(result: T, ctx: { append: AppendFn; getEvents: GetEventsFn; modelUsage: ModelUsageTracker | undefined; taskId: string; }): Promise; /** E8 (shell-host contract): SUM the per-turn `model_usage` delta events in a run's durable event log into the * cumulative per-model total for `TaskStats.modelUsage`. The tracer drains deltas at each turn boundary * (append-only) → this is resume-safe: it sums across ALL process legs of a suspended/resumed run. Returns * undefined when the run logged no model usage (→ the shell soft-degrades to aggregate cost only). * * #59([ref]①):事件体是 **DB 里的 JSON**(旧版本 / 别的 producer 都可能写),此前的 `?? 0` 只接住 * null/undefined——NaN 与字符串会原样进 `+`,一个坏键就把该模型的整 run 总量变成 NaN 或字符串拼接, * 再顺着 `TaskStats.modelUsage` 回声流进 usage-analytics 的整窗聚合。守卫改「非有限数 = 缺席、按 0 计」, * 与 usage-analytics 读侧同口径。 * * 🔴 [ref](core 2.3.0,[ref] 提货单①)——**缺席必须活着走完整条链**。此前本折叠对「costMicroUsd * 缺席」与「=0」输出相同(都是 0),那时无所谓:core 恒发数字,缺席没有 producer。现在 unpriced 部署 * 下 core 行级缺席、budget.ts 如实透传落库,若在这里折 0,诚实就只活到落库为止——回声上又变回 * 「花了 0」,行级消歧白做。故改**未知传染**:该模型任一贡献行缺 costMicroUsd 键 ⇒ 总量的该键**整个不在** * (和里含一个不知道的项,这个和就不知道)。传染按**模型隔离**,不毒别的模型,更不毒整窗。 * * 判别轴(两种"没数"不同罪): * · **键缺席** = 契约允许的「未知」(SDK ModelUsageDelta 五键全可选)⇒ 传染,输出也缺席。 * · **键在场但值坏**(NaN / 字符串 / null)= 数据损坏,写了键的 producer 是在声称自己知道 ⇒ 维持既有 * 折叠(按 0 计,不传染,已知量不受损),否则一条陈年坏行就能把整个模型的成本抹成"未知"。 * 下游补偿件早已就位:usage-analytics 的 numOf + `estimated` 标记(缺数值键 ⇒ 总量偏低并诚实标注)。 */ export declare function aggregateModelUsage(events: RunEvent[]): Record | undefined; export type TraceBlock = { type: "thinking"; text: string; } | { type: "tool-call"; id: string; name: string; label?: string; input: unknown; eventId?: string; parentToolCallId?: string; } | { type: "content"; text: string; } | { type: "tool-result"; callId: string; label?: string; output: unknown; isError?: boolean; truncated?: boolean; totalChars?: number; structured?: unknown; errorCode?: string; delivered?: McpDelivered; gate?: GateOutcome; gatedCallId?: string; eventId?: string; parentToolCallId?: string; } | { type: "prompt-assembled"; constitution: string; totalChars: number; blocks: unknown; sections?: unknown; tools?: unknown; }; /** * 🔴 **模型自由文本账本行(`text` / `reasoning`)的唯一载荷铸点**(B-106 / S-268,7.75.1)。 * * ## 为什么是一只函数,而不是两处各写一句 * 这两行的载荷此前由**两条腿各自手拼**:`trace/ledger-sink.ts` 的 turn flush 与 `http/server.ts` 的 * durable resume 腿 flush。手拼的代价本文件已经付过一次:{@link toolEndEventData} 顶注逐字记着 `label` * 那一案 —— 七个调用点只有三个记得拼,**漏的正是 resume 腿与 ledger-sink 落盘这两处**。 * * ## 🔴 脱敏**不在这里**(写口不是脱敏属主) * 两行的字节由模型写,必须脱 —— 但脱敏口是 `observability/run-terminal.ts` 的 * `redactLedgerEventData`:三孪生 store 的 `appendEvent` 落库前 + `GET /v1/runs/:id/events` 读面各过一次 * (幂等)。理由与 park 行逐字同源(`trace/ledger-sink.ts` 的 `appendParked` 顶注是那条规矩的属主): * 写口脱 = 一个语义面上两个写者,且**盖不住存量行**(升级前落下的明文 `text` 行每次重连照样回放)。 * 所以本函数只管**形**:哪些键、可选键什么时候在场。 * * `eventId` = E18 的 per-message resume 句柄,只有 `text` 行带(`reasoning` 行的调用点不传)—— * 条件展开写成直写字面量([ref] 门① 的硬形:键名不许对扫描器隐形)。 */ export declare function modelTextEventData(ev: { text: string; eventId?: string | undefined; }): Record; /** The durable `tool_start` event payload, shared by BOTH append sites (runInBackground + the resume leg) so the * persisted shape never drifts: redacted args (UNTRUSTED) + E2 identity. `label`([ref]§三族B:durable 腿 * 此前剥掉——同一 run live 观看有展示名、replay/resume 退化成 wire 名):core 展示名 verbatim(tail 面 * 1.251 复审同裁,两腿同处理)。 */ export declare function toolStartEventData(ev: { toolCallId: string; toolName: string; label?: string; args: unknown; eventId?: string; parentToolCallId?: string; }): Record; /** * 🔴 **筛子是一只总函数**(S-239,codex 对抗复审 r1 [medium] 亲核复现后修)—— 上面纪律②许诺的 * 「坏形 ⇒ report + withhold,**不抛**」此前是靠**逐条枚举**坏形兑现的,枚举不到的那些照样抛: * `projectSettlement` 对 `note` / `who.approver` 先 `redactSecrets` 再截断,而 `redactSecrets` 会在 * 病态输入上爆回溯栈(`RangeError`:≥2^23 单段那一形已由 S-219 的 `atLeast` 铸点闭掉,仍能抛的是 JSON 秘密字段臂的 * 转义对循环,S-102/S-106 登记的既有上界,见 `redact.ts` 该行顶注),两条叶各能抛一次。 * * 抛出去的代价按腿算,而且一条比一条重:写腿落账那处的 `.catch` 看不到**同步**投影期的异常;读腿上它 * 直接逃出 `formatEvent` / 投影函数 —— 账本裸回放腿的那一次会当场断流(那一行不上帧、游标不前进、后面的 * `done` 永远到不了、重连恒复现同一处),而 `gate-outcome-defective` 那一格还不涨。 * * ⇒ 兑现方式从「枚举」换成**结构**:任何逃出投影的异常同走既有的那一口 report + withhold。规则因此变少 * (不是「守住已知的 N 种坏形」而是「坏字节一律 withhold」),上面那几道具名形门留着只为**诊断串更准**。 * ⛔ **刻意不动**「先脱敏后截断」的次序:截断在前会把跨界的密钥切成半截明文留在输出里,而且它只治 * `note` 一条叶(`approver` 同病)—— 那是给一个类打两个特判,正是本仓要消掉的形。 */ export declare function screenedGate(v: unknown): GateOutcome | undefined; /** * 🔴 **账本裸回放面的读腿一步**(S-239)—— `GET /v1/runs/:id/events`(`http/routes/runs.ts` 的 * `formatEvent`)对每一行走这一口,把行上的 `GateOutcome` 交给**同一只** {@link screenedGate}。 * * ## 它补的是哪一格 * 7.72.0 把耐久读边界收成一只筛子,但点名的读腿只有两条({@link toolResultFieldsOf} 的投影回放、 * `/decide` 幂等回放体)。**账本裸回放**是第三条,而它此前只过 `redactLedgerEventData` —— 那只函数 * 对 `tool_end` 型**逐字不动**(它管的是 `done.result` / `failed.errorMessage` / park 行三族)。 * 于是第三方 producer / 坏世代写下的集外词与破不变量的记录在投影面整条 withhold + 计数,裸回放面却 * 直接上 wire,而 `gate-outcome-defective` 那一格不涨(运维看不见有 producer 在发坏记录)。 * * ## 为什么是这一形(而不是在路由里加一只筛) * 筛子只有一只 —— 本函数只是把**第三条读腿**接到它上面,规则因此是「凡耐久读腿上的 `GateOutcome` * 一律过 {@link screenedGate},零例外」。它按**型**分派而不是按调用点,所以将来第二个携带 * `GateOutcome` 的账本型只需把 {@link GATE_BEARING_LEDGER_EVENT_TYPE} 扩成词表,读腿一行都不用改。 * * ## 读腿与写腿现在是**同一只**筛(7.74.0 起,零双读) * 7.72.0–7.73.0 期间读腿多一步「认本服务上一版写下的退役词旧拼法」(`unresolvable` → `ancestor_marked`), * 那是一条**长期双读臂**:同一份字节在写腿与读腿上有两种理解,而且没有到期日。7.74.0 把它换成**一次性 * 迁移**(启动期把存量字节原地改名,`plugins/one-time-migrations.ts`,随 7.75.0 删)⇒ 读腿退回写腿那只 * 严格筛子,集外词一律整条 withhold + 计数,不再有第二种读法。 * * ## 与写腿脱敏汇点的关系(别再走回头路) * 写腿的对偶是 `redactLedgerEventData`(三孪生 store 的 `appendEvent` 落库前),它管的是脱敏、不是判形; * 两只函数因此是「共用的脱敏汇点 + 读腿独有的筛」这一层关系,不是同一只函数的两种模式。 * * 不改原对象(调用方与账本共享引用):有变更时换一只信封,原记录一个字节不动。 */ export declare function screenedStoredEventData(type: string, data: unknown): unknown; /** `text_end` 段末帧的**唯一**铸点(三腿共用:top stream / 转发子代腿 / 子代 tail 面)。content = 段权威全文 ⇒ 与 * durable 面(`result` / 账本 `text` 行)同一只 {@link redactModelText}(认出才改写、抛则占位、不设上限)。 * 此前三处各自逐字透传 —— 同一份文本毫秒后在 `done.result.result` 上已脱,live 面却在凭据生成瞬间明文(B-114)。 * `text_delta` 仍逐字:跨 chunk 的凭据无法就地判;clay [ref] ②(2026-09-18)裁定**维持逐字成文**(S-270 U-3 关票), * 权威段 `text_end.content` 已脱敏、消费端段末整段替换即得脱敏读法。 * 坏形(content 非串/空串)⇒ null(丢帧,不铸「权威空段」:core 契约明言空段不发本帧,消费端收到空段会清掉已攒 delta)。 * 身份**四键**随行(live 现状带全四键,消费端可能已依赖);值经 {@link identityKey} —— 非空 string 才上 wire * (S-334 起不再逐字透传:坏形身份键会在 live 写口上铸出假游标)。 */ export declare function textEndEventData(ev: Record): SegmentEndFields | null; /** * live 面的**身份四键**(core `TaskEventIdentity` 全集)—— 一个属主,两处共用({@link segmentEndFields} * 与 sync 腿白名单 catch-all 的 `ident`;此前是两份手抄)。与 durable 面的 {@link identityFields} 的差别 * **只剩键集**:那边两键(durable builder 对另两键的既定排除),这边四键(live 现状,消费端已依赖); * **值判据是同一条** —— 两只都问 {@link identityKey}(非空 `string` 才上 wire,绝不强转)。 * * 🔴 **型必须说得出口**(S-334):`eventId` 是 live 腿 SSE `id:` 行的**唯一**来源,而 `id:` 行是续流游标 * ——一个连值的型都说不出的键不许铸成游标。非串/空串 ⇒ 键缺席(与「无锚不发 `id:` 行」同一条纪律: * 不可锚的位置绝不伪装成可锚)。转发子代腿的帧是 UNTRUSTED RAW(§E1),这一道同时是它的形判门。 * * 🔴 **`type` 而不是 `interface`**(它与 {@link SegmentEndFields} 两处都是,别改回去):TS 只给**类型别名** * 隐式索引签名,interface 没有 ⇒ 写成 interface 的话,每一个把本形交给 `Record` 形参 * (tail 总线帧、live 帧写口)的调用点都得手抄一次 `{ ...frame }` 展开。别名让「声明出来的形」原样流经 * 既有写口,零调用点噪声。 */ export type LiveEventIdentity = { eventId?: string; parentToolCallId?: string; sourceTaskId?: string; bgAgentId?: string; }; /** 段末帧(`text_end` / `reasoning_end`)的载荷**形**(两只帧一个字节不差 —— 一个 wire 概念一份形, * 消费端的读法只学一次;差别只有各自铸点的空段判据)。 */ export type SegmentEndFields = LiveEventIdentity & { content: string; }; /** {@link LiveEventIdentity} 的铸点。[ref] 门①:条件展开的臂逐条是直写字面量(键名对扫描器可见)。 */ export declare function liveIdentityFields(ev: { eventId?: unknown; parentToolCallId?: unknown; sourceTaskId?: unknown; bgAgentId?: unknown; }): LiveEventIdentity; /** * `reasoning_end` 段末帧的**唯一**铸点(三条 live 腿共用:top stream / 转发子代腿 / 子代 tail 面)—— * `text_end` 的推理面孪生({@link textEndEventData}),形逐字同(共用 {@link segmentEndFields})。 * * 🔴 **本帧由 server 自铸,core 不发**(`TaskEvent` 上只有 `reasoning_delta`)。`content` = 该段 * `reasoning_delta` 的累积原文,经与 durable 面**同一只** `redactModelText` —— 这正是本帧存在的理由: * 逐 chunk 的 `reasoning_delta` 对**跨 chunk** 的凭据形无能为力(半截 `sk-` 在任何一块里都不成形), * 而账本腿攒到 flush 边界的那一整段早就脱得干净;live 面此前没有这份权威副本,壳无可替换。 * 段界与账本腿**同一只谓词**(`trace/model-segment.ts` 的 `isModelSegmentBoundary`),所以同一次运行里 * 「账本 `reasoning` 行」与本帧的正文逐字节相等。 * * **空段不发**,判据比 `text_end` 宽一格(那边是 core 契约的「空段不发」,只需判空串):本帧的正文由 * 本仓攒出,一段**全空白**的思考(模型吐了几个换行就去调工具)没有可替换的权威正文,而消费律是 * 「整段替换已攒的 delta」—— 发一枚空白权威段 = 让消费端把已渲染的思考清成空白。坏形(`content` 非串) * 同样丢帧,理由与 `text_end` 逐字同源。 */ export declare function reasoningEndEventData(ev: Record): SegmentEndFields | null; /** The durable `tool_end` payload (shared by both append sites). E1: the model-facing `output` is UNTRUSTED RAW * from core (file/shell/API bodies) — core only SIZE-bounds it (`truncated`), REDACTION is ours (same contract as * `tool_start.args`); never re-fed to a model. E2: identity. Absent `output` ⇒ the tool produced no content body. * core 1.203: `structured` = the tool's CC-shaped `details` card (edit/create/bash/… — core * allowlists the card `type` and size-caps the whole card, omit-whole on overflow, so a subagent's child-TaskResult * can never appear here) — SAME UNTRUSTED RAW contract as `output` (a card carries `originalFile`/stdout bodies), * so it gets the same deep redaction before it is persisted or echoed. `label`(core 展示名,同 tool_start 的 * label 待遇 verbatim)白名单排查(2026-07-24)补齐进函数本体——此前外包给调用点各自手动拼接,7 处只有 * 3 处记得拼,resume leg + ledger-sink 落盘两处漏了,持久化记录本身缺 label 一写盘就永久追不回来。 */ export declare function toolEndEventData(ev: { toolCallId: string; toolName: string; label?: string; isError: boolean; output?: unknown; truncated?: boolean; totalChars?: number; structured?: unknown; errorCode?: string; delivered?: McpDelivered; gate?: GateOutcome; gatedCallId?: string; eventId?: string; parentToolCallId?: string; }): Record; /** * `tool_progress` 的白名单投影(core 7.18.0 [ref])—— 一只**仍在跑**的工具调用的存活 tick。 * * ## 这一帧只上 live,不落账(判据是 core 的契约,不是本仓的偏好) * core `@contract tool_progress.ephemeral` 逐字:「LIVE ONLY, like `task_progress`/`status`: core never * persists this frame (no session-tree entry, no checkpoint state, no resume replay)」。本仓对 * `task_progress` / `status` 正是同一处置(两条 durable 腿零 case),本帧照抄。 * 另一条 core 契约把「不落账」从偏好抬成正确性:`@contract tool_progress.cadence` 逐字「A call that * finishes inside one interval emits NO frame at all」⇒ **帧的缺席从来不是证据**;落账会让回放面凭空 * 多出一份「这只调用跑了多久」的账,而那件事的真源是 `tool_end`(core 明写 `elapsedTimeSeconds` * **不等于** `tool_end` 的 duration)。 * * ## 信任划线 * · `toolCallId` / `toolName` —— 引擎铸的 id 与名册里的工具名(与 `tool_start` 逐字同源)⇒ verbatim。 * · `elapsedTimeSeconds` —— 引擎铸的整秒,单调、一 call 一个原点 ⇒ 有限数才铸键。 * · `output` —— 🔴 **UNTRUSTED RAW**,core 逐字「exactly as `tool_end.output`: process output that may * carry secrets, paths or PII; core bounds only its SIZE, and a consumer MUST redact before persisting * or displaying it」⇒ 与 `tool_end.output` **同一只** `redactDeep`,一个字节的例外都不开。 * · `totalLines` / `totalBytes` —— 引擎计数;core 的缺席律逐字:三者**同进同退**(在场=「观察到了输出」, * 缺席=「一个字节都没观察到」,**从不是**「与上一帧相比没变」)。所以本投影按 `output` 的在场判三者: * `output` 读不出 ⇒ 三键一起缺席,不单独留下两个计数(那会把「没输出」谎报成「有输出但读不出」)。 */ export declare function toolProgressEventData(ev: { toolCallId?: unknown; toolName?: unknown; elapsedTimeSeconds?: unknown; output?: unknown; totalLines?: unknown; totalBytes?: unknown; eventId?: unknown; parentToolCallId?: unknown; }): Record | undefined; /** * `tool_disclosure` 的白名单投影(core 7.18.0 [ref])—— 本条腿的**延迟工具普查**:哪些挂载的工具今天只是 * 名字占位、模型已经激活了哪些、以及是哪条策略判的。 * * ## 三腿对称(与 `tool_roster_delta` 同判、同理由) * 开腿那一刻的快照住在 `wiring_manifest` 之后的**第一帧**,而**之后每一次激活**只活在本帧上。不落账 ⇒ * 断线重连 / 后台跑完再回来的耐久面永远停在开腿版本,而壳正是据它判「这只工具现在还是不是占位名」。 * 本帧是**整只快照**语义(`deferred` / `activated` 全量,消费端整只替换),所以回放不需要合并。 * * ## 缺席语义(core 逐字,别读错) * · **整帧缺席** = 「这条腿什么都没延迟」,**不是**「这个 build 没有这个面」、也不是「未知」; * · `thresholdPercent` 缺席 = `always` / `never` 两档下**根本没查过阈值**,不许读成「取了默认百分比」; * · 一条 resume 腿的**首帧** `activated` 可能非空(引擎重播的),**不许**读成「这条腿激活了它们」。 * * 成员全是闭形值(策略闭词 + 数字 + 名册里的工具名)⇒ 零脱敏,与 `tool_roster_delta` 的名册行同待遇。 * `policy` **不在本仓枚举**(闭集词表属主在 core;抄一份会把 core 加的新词吞成缺席)。 */ export declare function toolDisclosureEventData(ev: { policy?: unknown; thresholdPercent?: unknown; deferred?: unknown; activated?: unknown; eventId?: unknown; parentToolCallId?: unknown; }): Record | undefined; /** The `task_progress` payload (a subagent's live progress tick) — EXPLICIT WHITELIST shared by all * three legs (bg runs.ts + resume append + the sync live SSE frame): taskId/parent linkage/token rollup verbatim, * `name` UNTRUSTED (core sanitizes for rendering, does NOT secret-redact — a taskName can be secret-shaped) → * `redactSecrets`; `spawnName` is the TRUSTED-chain teammate KEY beside it (core 7.25.0 [ref] S0 — validated at the * spawn, so it is an identity, not a label) and rides VERBATIM: redacting an identity key does not make it safe, * it makes it WRONG (two legal names collapsing onto one value — see the key's own note). Never the delegated objective/args. E2 identity rides (eventId/ * parentToolCallId are core-mint uuidv7 identifiers, not content — consumers bind concurrent subagent ticks * exactly by them; the 1.72 whitelist dropped them, leaving a FIFO heuristic). */ export declare function taskProgressEventData(ev: { taskId: string; taskType?: string; parentTaskId?: string; name?: string; spawnName?: string; status?: string; usage?: unknown; currentAction?: string; eventId?: string; parentToolCallId?: string; workflowRunId?: string; workflowAgentLabel?: string; seq?: number; model?: string; }): Record; /** The `task_notification` payload (core 1.202 [ref] P2: a background task finished — this event is the * LEDGER/display projection of the OBSERVED terminal state, emitted the moment core observes it. It does NOT * attest that the `` XML has entered the model context: core's steer targets the NEXT TURN * BOUNDARY ([ref] §7 — turn 在飞时不注入), so the frame can precede actual injection by minutes ([ref] * clay live: 3m54s; the previous wording here — "core already injected in-process" — was the same wrong * assumption that broke the shell's dedup). `injected: false` says exactly that on the wire: a consumer must * NOT treat this frame as an echo of model-context injection. EXPLICIT WHITELIST (never `{...ev}` — the 1.72 task_progress lesson: a * future core field must not ride raw): ids/status/usage verbatim; `summary`/`result`/`output_file` are UNTRUSTED * free text (core bounds+sanitizes for XML, does NOT secret-redact) → `redactSecrets` at OUR wire boundary. Shared * by all three legs (bg runs.ts + resume append + the sync live SSE frame) so the shape never drifts. Takes the * EVENT (not just the payload) so the E2 identity fields core stamps on it (eventId/parentToolCallId) ride the * frame like every other content event (review round 2: the payload-only version silently dropped them). */ export declare function taskNotificationEventData(ev: { notification: { task_id: string; task_type: string; toolUseId?: string; status: string; summary: string; result?: string; output_file?: string; usage?: unknown; sessionId?: string; seq?: number; source?: string; lines?: string[]; stoppedBy?: string; exitCode?: number; partial?: boolean; diagnostics?: string; recentSteps?: Array<{ tool: string; target: string; outcome: string; }>; editedFiles?: Array<{ path: string; edits: number; }>; resumable?: boolean; completionId?: string; error?: string; errorCode?: string; apiFailure?: { status?: number; requestId?: string; }; /** [ref](a) [ref] 片6 d1(core 7.2.0):子代→主代 uplink 帧的**渲染判别**成员。在场 = 这一帧是 * `SendMessage("main")` 的 agent-message(cli 渲 agent-message 卡;父模型面读的是顶层 `` * 帧而非 task-notification 外壳);缺席 = 老形卡。`body` 是子代文本 = UNTRUSTED(与 `result` 同姿势 redact); * `from` 是 core 铸的发送方标签(引擎值,verbatim)。铸造点唯一:core `agents/send-message-tool.ts` uplink 腿。 */ agentMessage?: { from: string; body: string; }; /** 同批:typed 归因侧记(wire 键;core 契约「a host projects it, the model never sees it」)。成员逐个挑取, * `kind` 过闭集(`trace/sema-provenance.ts` 单点,park 半场同一判据);`agentType` 本 mount 恒缺席(工具无 * agent-type 席),在场即透传(fleet spawn 帧按 taskId 可 join 时上游给)。 */ _sema_provenance?: SemaProvenanceInput; }; eventId?: string; parentToolCallId?: string; /** [ref] §3.6(core 5.61.0,additive):本帧**投出去时的注入档位**。见下方投影处的键注 —— 它是 * **事件顶层**键,不是 {@link TaskNotificationPayload} 的键,所以只有把整只 core 事件交给本投影的腿 * 拿得到它;只拿到载荷的 park-drain 腿结构上没有这一位。 */ priority?: SystemInjectionPriority; }): Record; /** The `compacted` payload — EXPLICIT WHITELIST shared by all three legs (bg runs.ts + resume append + the sync * live SSE frame; the 1.72 task_progress lesson: never `{...ev}`). trigger/tokensBefore (MF-18) verbatim; * `preserved_segment.firstKeptEntryId` verbatim (a core-mint entryId, the shell's compaction-divider anchor); * core 1.219 `attachedFiles` (attachWorkingFiles default-ON — the shell renders CC's post-compact `Read {path}` * cards from it): `path` is the engine's own working-file path (env-derived, not free text) but still crosses our * wire → `redactSecrets` (a project path can embed a secret-shaped dir name); chars/truncated are numeric/bool * verbatim. Absent fields stay absent (feature off / nothing attached / no kept-tail floor). */ /** The `diagnostics` payload (core 1.220 [ref] — NEW LSP diagnostics drained at a turn boundary; the model * already got the `` context message in-process, this frame is the shell's render source). * EXPLICIT WHITELIST shared by all three legs. UNTRUSTED RAW (§E1: language-server output over workspace files — * a diagnostic message can quote file content, a secret-shaped uri/source is possible) → `redactSecrets` on the * string fields; severity/range/code numbers verbatim. E2 identity rides. */ export declare function diagnosticsEventData(ev: { files: Array<{ uri: string; diagnostics: Array<{ message: string; severity?: number; range?: unknown; code?: string | number; source?: string; }>; }>; isNew: boolean; eventId?: string; parentToolCallId?: string; }): Record; /** The `steering_injected` payload([ref]/[ref] 裁定透传;core `TaskEvent` 联合臂 `type: "steering_injected"`, * shell 渲染源 = CC 的系统提醒条:core 在 turn 之间给模型注入一条系统提示[todo 提醒/文件变化/日期跨天/ * 技能列表变化…]时随手抛的展示投影,一附件一帧)。EXPLICIT WHITELIST(source=封闭枚举 verbatim; * preview=注入文本的展示摘录,UNTRUSTED[core 未做秘密脱敏]→ `redactSecrets`)。共享三腿(sync 实时 SSE + * bg/resume 落账,ledger-sink 同门)。 */ export declare function steeringInjectedEventData(ev: { source: string; preview: string; eventId?: string; parentToolCallId?: string; }): Record; /** `compaction_outcome`(core 2.5.0 [ref]②:压缩非 compacted 结局——manual /compact 的 mooted/failed * 两条真路)。[ref]A-2/C-2:此前它是**唯一**不走共享 builder、只在 live 腿 res.write 内联挑键的 * 内容帧 ⇒ 两条 durable 腿零 case、回放即消失、三方普查各自失明。收进共享白名单 builder,三腿同源。 * outcome/trigger=闭枚举 verbatim;reason=引擎说明文本(可能引用配置值)→ redactSecrets;identity 同款。 */ export declare function compactionOutcomeEventData(ev: { outcome?: unknown; trigger?: unknown; reason?: unknown; eventId?: unknown; parentToolCallId?: unknown; }): Record; /** The `workspace_changed` payload([ref]二 core 裁定:[ref] §E13 旧臂,L1 门本车首次撞见——非新增, * 是本门扩 TaskEvent 臂集时第一次显式处置。语义 = bash `cd`/EnterWorktree 移动任务逻辑 cwd 后,在移动 * 它的工具结算后发一帧,给 shell/宿主 UI 一个路径提示行。cwd 与已展示的工具 args 同信任级[UNTRUSTED, * RAW 逻辑路径,symlink 不解析]→ `redactSecrets` + 长度 bound[300,与 brainStatusEventData.detail 同 * 惯例]。core 只在真变化时才发(不播初始 cwd);渲染面(cli)不得当持久态——跨 suspend/resume 重置为 * 根,这是消费端纪律,服务端只如实转发单帧不做状态缓存。 */ export declare function workspaceChangedEventData(ev: { cwd: string; eventId?: string; parentToolCallId?: string; }): Record; /** * `wiring_manifest`(core 5.14.0 [ref] 新 `TaskEvent` 臂)—— 每条腿(root/child/resume)在 prepare * 时自证「我到底接了什么线」的清单帧。 * * 🔴 **本构造器存在的唯一理由是剥掉 `governance` 段**。core 给该段盖了 `audience: "operator"` 并成文 * 了服务层义务:*a serving layer forwarding this event to a multi-tenant stream MUST project the governance * section for operators only; **no projection ⇒ do not disclose***。我方的三条腿(sync SSE / durable 账本 / * resume 账本)都是租户可读面,而 operator 投影是 [ref] 的独立大件——按 core 写死的失败方向,**没有投影 * 就不得披露**,所以本批先把整段剥掉止泄(此前 sync 腿的裸 catch-all 会把它连同 `lockedConfig` / * `compliance` / `memoryAdmission` / `retention` 四个治理面在场位原样透传给任意订阅者)。 * operator 投影是**另一个**构造器({@link wiringManifestOperatorEventData},live 腿按调用方身份挑), * 不是往这里加键 —— 两条 durable 腿没有调用方身份可挑,所以它们恒用本构造器。 * ⚠️ 剥段还必须连**对该段取的哈希**一起剥:`configFingerprint` 同样不投影,理由见下方它那一行的旁注 * (无盐哈希 + 4 布尔 = 16 组合的谕示器,把「结构上藏起来」的段原样还原出来)。 * * 其余段逐键挑(禁 `{...manifest}`:1.72 task_progress 教训——未来 core 加段必须由人显式处置,而不是 * 顺着 spread 裸上线,尤其当新段可能又是一个 audience 受限面时)。所有值都是引擎派生的闭枚举/布尔/ * 有限数/不透明摘要串,**非用户内容 ⇒ 不脱敏**;畸形值一律按缺席处理(不铸假值,与 compacted 同规)。 * * 🔴 **未标签面的准入裁定逐面记录在此,缺省不是「放行」**([ref] 裁定 C)。core 只给 `governance` 标了 * audience,其余段没有标签 —— 没标签**不等于**可以默认投给租户,每一段都要有一次人的裁定,写在这里: * · `interaction.posture`(三值:interactive / headless / absent)—— **裁:租户可见**。它是部署形的 * 自述(这条腿有没有人能答问题),消费端据此决定渲不渲交互面;不是治理秘密,也不构成任何治理位的 * 侧信道(它不参与 governance 那 4 个布尔的还原)。 * · `permissionRules.storeWired`(布尔;core 5.18.0 [ref] 新段)—— **裁:租户可见**。同族判据: * 它与 `parkLane.capable` / `elicit.seamWired` / `fleet.backgroundAgentStore` 是同一类「这条腿接没接 * 某个可选设施」的部署形自述,而那三个早已租户可见。消费端的真需求也在租户侧:审批卡要不要渲染 * 「不再询问」这一格,取决于这台 worker 有没有规则店 —— 剥掉它,客户端只能靠试(提交后被拒)才知道。 * 它**不在** `governance` 段里(core 自己把它放在段外),也不构成那 4 个治理布尔的侧信道。 * ⚠️ 恒在段(core 侧 `permissionRules` 无 `?`):两个真值都可能出现,**别把 false 读成缺席**。 * 本仓自 [ref] 车二 / [ref] 起真接了规则店(`main.ts` 在 `PERMISSION_RULES_ENABLED` 为真 ∧ * backend 在场时把 `permissionRuleStore` 装进 `RunnerDeps`,SQL 与 File 两形)⇒ 那样的部署报 * `storeWired: true`;旋钮关掉或后端缺席的部署仍报 `false`。消费端读到 false 就是「这台 worker * 今天没有这条车道」,不许当成「这版引擎还没这个概念」。 * · `permissionRules.syncWired` / `permissionRules.orgGoverned`(布尔两位;core 5.23.0 [ref] §9/§7 * 给**同一段**加的员,[ref] 提货批 codex 复审 high 抓获——首版把它们静默剥了,正是本仓建这份台账要防 * 的「投影白名单剥键」病)—— **裁:租户可见,与同段的 `storeWired` 同待遇**。判据三条: * ① **同段同类**:core 把它们放进 `permissionRules` 而不是 `governance`(唯一带 audience 标签的段), * 且形状与 `storeWired` 逐字同构(恒在的布尔,present-and-false 表示「有这个概念,这里没开」); * ② **消费端真需求在租户侧**:这两位说的是「按下『不再询问』之后,这条同意到底落到多大的信任域里」—— * `syncWired` = 这台 worker 会把规则并进跨设备/传输/服务端的**同一个同意信任域**;`orgGoverned` = * 组织治理层武装着(且它的 fail-closed 契约意味着读不到 org 快照时**每一次**放行都会被抬成真人审批)。 * 同一张审批卡上的同一个动作,后果因这两位而不同,剥掉它们等于让客户端渲一格语义不明的开关; * ③ **不是治理侧信道**:两位都不参与 `governance` 那 4 个布尔的还原(它们描述规则店这条车道自身的 * 形状,与 lockedConfig/compliance/memoryAdmission/retention 无函数关系)。 * ⚠️ 与 `storeWired` **不同**:本仓这两条至今未接(`RunnerDeps.permissionRuleSyncWired` / * `permissionRuleOrg` 全树零声明点,2026-08-10 [ref] 复扫亲验)⇒ 今天恒 `false`,这是**真值**不是 * 缺席。哪天接上其中任一条,连本句一起改 —— 这一行是它的登记处。 * · `modelGate`(可选段 `{ class, removed[], restore }`;core fc52b4d4 [ref] / [ref] 片2b,[ref] 提货件, * 黑板 [ref] @server①)—— **裁:租户可见**。它是 per-session 的**读面**:「我这条 session 因为工具-模型门卸掉了 * 哪些工具」——与 operator 通告 `config.tool_model_gate_removed` **同决策同源**(core 从同一张 trim 表铸两面), * 但通告归 operator 车道(engine_notice 白名单,附录 D),本段归租户:消费端(cli L-70 [ref])要在**首帧一次读** * 里知道自己少了哪些工具、怎么恢复,否则只能等到调用被拒才发现。三键都是引擎派生值:`class` = 合并 gate 表的 class 行键 * (prepare-task 对表外 class 响亮拒),`removed` = 工具 wire 名(core 表键,非用户内容),`restore` = core 铸的一句恢复指引(引擎文案) * ⇒ 不脱敏、逐字。多 class 同卸 ⇒ core 报首 class + removed **并集**(core 合并律;本仓不按 class 过滤)。 * 仅本 run 有门卸时在场:**缺席 = 本 run 没卸**,不铸空段、不铸 `removed: []`。畸形(三键任一缺席/形不合) * ⇒ 整段按缺席 —— 不铸半段,半段会被读成「只卸了这些」。它不构成 governance 四布尔的侧信道(门卸由 * 模型/门 class 决定,与 lockedConfig/compliance/memoryAdmission/retention 无函数关系)。 * · `autoMode`(`{ armed: boolean, reason: AutoModeArmReason }`;core 7.3.1 [ref],147③ 提货件,黑板 [ref]) * —— **裁:租户可见**。它是 per-leg 的**读面**:「我这条腿的 auto 模式武装了吗?没武装是卡在哪一臂?」 * ① **同主体同受众**:本仓早已把同一件事投在租户面上 —— `/v1/capabilities.permissionModeAuto.{armed,reason}` * ([ref],7.57.0 已发)答的是「**若此刻提交**会不会武装」,本段答的是「**这条真跑起来的腿**武装了吗」; * 同一个主体(调用方自己的 run)、同一条轴,只差事前/事后。剥掉它,壳就只能拿事前预测冒充事后事实; * ② **两个值都是引擎派生的闭形**:`armed` 布尔,`reason` 是 core 的闭集词(`AUTO_MODE_ARM_REASONS`: * armed / no_intent / no_face / denied / resolver_fault;core 7.12.0 起 `latch_open` 随熔断/单向闩 * 整族退役)—— 非用户内容,不脱敏、逐字; * ③ **不是 governance 侧信道**:它描述的是**本腿**的武装结论,与 lockedConfig/compliance/memoryAdmission/ * retention 四位无函数关系;而 `denied` 这一词所披露的「组织把这一位关了」在 [ref] 读面上**本就已经** * 对同一租户可见(`org_denied`),本段没有扩大任何受众。 * 🔴 **逐字透传,不映射**:core 的五词与本仓 [ref] 读面的六词(`AUTO_MODE_UNARMED_REASONS`)**刻意不归一** * —— 两张表回答的是两个问题、粒度也不同(core 的 `denied` 一词对应本仓的 `org_denied` / `local_denied` * 两词:core 只看合成后的 caps deny 位,本仓分得出这个 false 是 center 下发的还是本地 env 写的)。 * ⚠️ **`settings_denied` 不折进 `denied`,它折进 `no_intent`**(codex 车BO r1 [medium] 验真后纠): * settings 层的 kill-switch 折的是**模式**(`effectivePermissionMode`:auto → default),于是 resolve-spec * 阶段④ 压根不写意图座 `TaskSpec.autoModeRequested` ⇒ core 的武装谓词第一项就落空;而本地 env 腿折的是 * **caps**(`applyLocalAutoModeDeny`),模式不动、座照写 ⇒ core 才报 `denied`。两条腿在 manifest 上 * **分不开**,要分辨得读 `/v1/capabilities`。对照表与保鲜钉在 `test/wiring-manifest-projection.test.ts` * 的 147③ 节,分岔的行为面格在 `test/permission-mode-shell-gate-translation.test.ts`; * 本函数**只透传**,一个字都不换。 * ⚠️ 缺席 ≠ 「不适用」:core 顶注逐字「absence here means an older mint or an external derivation, never * "not applicable"」⇒ 缺席时**不铸** `armed:false`(那会把「老 mint 写的行」谎报成「这条腿确实没武装」)。 * ⚠️ **自相矛盾按缺席**:core 把 `armed ⇔ reason === "armed"` 写成段内不变量并在 derive 点拒(`readAutoModeFact` * 抛)。账本回放/第三方 producer 送来的矛盾对若原样上 wire,按 `armed` 读与按 `reason` 读的消费端会得到 * **相反**的答案 —— 这条轴上(@cli `sema doctor permissions` 正读它)两个答案比没答案更坏 ⇒ 整段不铸。 * 这不是改语义:它与本函数「畸形值一律按缺席」是同一条规则,而这条不变量是 core 自己写的段内契约。 * · `mcp[]`(`WiringManifestMcpEntry[]`;core 7.5.0 [ref],S-124 提货件)—— **裁:租户可见,逐条目挑键, * `error` 一格不投**。 * ① **同主体同受众**:它是 per-leg 的读面「我这条腿申报的每台 MCP 服务器,连上了没有、挂了是哪一类、 * 挂上来几个工具」。同一主体(调用方自己的 run)、同一条轴上,本仓早已把**更粗**的同类事实投给 * 租户:`GET /v1/sessions/:id/mcp` 的 `connected|failed` 二态。⚠️ 两者**不合并**、也不互相校验 —— * 那条端点的真源是 server 自己的部署面台账(server 自铸的二态),本段的真源是 core 这条腿 * materialize 的连接时快照;同名不同源,合并只会造出一个「哪个才算数」的新问题。 * ② **值的形**:`name`(spec 声明名)/ `source`(声明方自述的层级标签,core 已 single-line + 长度封顶 * 逐字回显)/ `status`(三词闭集)/ `errorCode`(core 7.6.0 起 `McpFailureKind` 十词闭集)/ * `delivered`(投递判词三词闭集)/ `httpStatus`(有限数)/ `toolCount`(有限数)—— 全是引擎或 * **声明方**产的闭形值,非用户内容 ⇒ 不脱敏、逐字。 * ⚠️ `errorCode` **不是纯闭集**:十个具名词之外还有 `http_` 形(core 自陈)⇒ 本仓既不枚举 * 那十个词、也不给它套形正则(词的单一属主在 core;抄一份会把 core 加的新词与新形一起吞成缺席), * 只判非空串,消费端按具名词写的 switch 必须带 default 臂。 * ③ 🔴 **`error` 有意不投**:它是本投影面上唯一的**远端作者自由文本**。判据与代价逐字在 * `core-keyset-guard.ts` ⑥b 的 `WiringMcpExcluded` 一行(要点:进来会同时破本段「无自由文本 ⇒ * 不脱敏」的段内不变量,和 core 7.5.0 [ref] 把远端 MCP 错误文本秘密脱敏收敛成**一个**铸点的单铸律 * —— server 再脱一遍就是同一语义面的第二个写者)。可操作的因由在 `errorCode`,那正是 core 为这条 * 消费面造闭集的理由。**带触发条件的裁定**,消费端提真需求时连同 bound/中和归属一起裁一次再翻。 * ④ **不是 governance 侧信道**:五键描述的是本腿的 MCP 连接尝试,与 lockedConfig/compliance/ * memoryAdmission/retention 四位无函数关系;core 自己也把它排除在 `configFingerprint` 之外 * (「connection state is a fact about this leg's attempt, not about the assembly」)。 * ⚠️ 空数组 ≠ 缺席:core 顶注逐字「an empty array is "this leg declared no servers", absence is an older * mint or an external derivation」⇒ 空数组照铸,别折成缺席。 * 🔴 **⑤ `liveness`(core 7.24.3 [ref] / S-402 提货件)—— 裁:租户可见,整条记录投,逐键挑**。 * `{state, observedAt, errorCode?}` = 引擎**最后一次观察**到的「这台还够得着吗」。受众判据与同行 * 其余键**同一条**(同主体同受众、引擎派生的闭形值、非 governance 侧信道 —— 它同样不进 * `configFingerprint`),所以这一格不需要新的裁定,只需要新的**读法**。 * 🔴 **契约一行(client-core CC-36 的那句话):活性位独立于名册申报与 re-dial 结果。** * `status` 是拨号那一刻的判决且 core **有意冻结**(服务器中途死了仍读 `connected`);re-dial 回执的 * `outcome` 是一次动作的判词;这一格是连接层最后学到的事,自带时戳。三者**合法地可以不一致** —— * `status:"failed"` 与 `liveness.state:"reachable"` 同行是真行(服务器答了握手、答的是协议错: * 活着,且配错了)。⇒ 本仓**不合并、不互相校验、不据一个推另一个**(与 ① 里「两条 MCP 读面同名 * 不同源、合并只会造出『哪个才算数』」逐字同源)。 * ⚠️ **缺席是第四读数,不是一个词**:core 顶注逐字「a consumer must not fold absence into * `unknown`」——`unknown` 是**更强**的断言(引擎看了、看不出来)。 * 🔴 **但本仓出口上的「缺席」比 core 源端的更宽**(codex r1 [medium] 采纳):core 那边是 * 压根没有观察(`skipped` 的行没走到 transport / `invalid_config` 的声明从没被拨过 / 老 mint), * 而经过本投影后**判形没过也落缺席**(词出闭集 / `observedAt` 非有限数 / 整只不是对象)。 * ⇒ 对消费端,缺席只能读成「**没有可用的观察记录**」,**不许**据它反推这条腿拨没拨过号。 * · `writeProtection`(`{ targetView: "spelling-only" | "target" }`;core 7.20.1 [ref] / clay [ref] 丙, * S-361 提货件)—— **裁:租户可见,逐字透传**。它是 per-leg 的读面:「这条腿的写保护判官,拿到的是 * 哪一种目标读法」——`"target"` = 判官握着执行环境,拼法没命中时会在**解析后的真目标**上再判一次; * `"spelling-only"` = 没有环境,拼法就是全部判决,只经别名/软链够到受保护行的写被清掉。 * ① **同主体同受众**:同一条腿、调用方自己的 run。而且这件事对该调用方**本就行为可观测** —— 在无手 * 车道上用一只声明了 `pathTarget` 写面的 MCP 工具对别名写一次就看得见(S-361 亲核可达)。剥掉它 * 并不收走任何能力,只收走那句诚实的标签,等于把 [ref] 刚刚消灭的**静默**在租户半场原样重建。 * ② **值的形**:core 的**两词闭集**,判据走 core 导出的 `WRITE_PROTECTION_TARGET_VIEWS` 常量(不抄 * 词表 —— 与 `delivered` 的 `MCP_DELIVERY_VERDICTS` 同一条);引擎派生、非用户内容 ⇒ 不脱敏、逐字。 * ③ 🔴 **最小披露仍然成立**:本键只答「判官有哪一种读法」,**从不**答「判官握着哪些路径」—— * 名表逐行内容(`rows` / `source` / `droppedDefaultRows`)照旧只在 operator 面 * (`GET /v1/diagnostics/wiring` 的 `writeProtection` 键,S-138),租户面照旧只有 * `/v1/capabilities.writeProtection.{armed, rows, replaced}` 三项计数。两者语义不同、同名不同物: * 诊断面那一格是**本 server 装配的名表**(boot 产物),本段是**core 判官的自报读法**(per-leg)。 * ④ **不是 governance 侧信道**:它是「这条腿接没接手」的函数,与 lockedConfig/compliance/ * memoryAdmission/retention 四位无函数关系。⚠️ 但它**进** core 的 `configFingerprint`(core 顶注 * 逐字:which view the judge got is an ASSEMBLY fact)—— 指纹本就只发 operator 面,不受影响。 * ⚠️ **缺席 = 第三态**:core 的段是 **present-iff 编译出了判官**,`writeProtectedPaths: []` 的部署 * 整段缺席。缺席**绝不**折成 `"spelling-only"`(那是「判官在、但只认拼法」,完全不同的一件事), * 也不铸 `writeProtection: {}`。词出闭集同样**不**折进缺席,走 `dropProjectedFace` 留痕(见那里)。 * · `autoConsolidation`(`{ onRecommendation: true }`;core 7.21.0 [ref],S-362 提货件)—— **裁:operator * 受众,租户面剥**,所以它**不在本构造器里**,在 {@link wiringManifestOperatorEventData}。四条判据逐字在 * `core-keyset-guard.ts` ⑥b 的 `WiringSectionExcluded` 那一行(与 `governance` / `configFingerprint` * 同表同处,免得同一条裁定有两个写者)。一句话:它是**出口事实**(武装 = 每个建议任务之后整库记忆自动 * 外流到整理模型),与 governance 四布尔同族,而不是 `writeProtection` 那种「这条腿手里有什么」的自述; * 并且它对调用方**本就不可观测**(整理跑在终端 harvest 之后、fire-and-forget、宿主侧),剥掉它不收走 * 任何能力 —— 这正是 `writeProtection` 判据①**不成立**的那一格。 * ⚠️ **缺席 = 未武装**(core present-iff),不折 `false`、不读「未知」。 * 🔴 **S-403(7.85.0)起本条要连着 `core-keyset-guard.ts` ⑥b 判据②的补句读**:同一个事实在会话面 * (`GET /v1/sessions/:id/memory-status` 的 `autoConsolidationArmed`)是可读的,而这一段仍然剥 —— * 「一个事实一张脸」,两处同源、唯一读点。裁定正文在那一处,这里不抄第二份。 * · `readDeny`(`{ builtinTiers: readonly string[] }`;core 7.22.0 [ref],S-417 提货件)—— **裁:operator * 受众,租户面剥**,所以它**不在本构造器里**,在 {@link wiringManifestOperatorEventData}。四条判据逐字在 * `core-keyset-guard.ts` ⑥b 的 `WiringSectionExcluded` 那一行(与 `governance` / `configFingerprint` / * `autoConsolidation` 同表同处,免得同一条裁定有两个写者)。一句话:它答的是**这台部署选了哪几档内置 * 敏感路径 deny 表**——一条部署治理面的**选择**,与 `writeProtection` 那种「这条腿手里有什么设施」的 * 自述不同族;而且它是本仓 env `READ_DENY_BUILTIN_TIERS` 的生效读出口,名表面归 operator(S-138)。 * ⚠️ **缺席 = 没有内置行判这条腿**(core present-iff:实质激活至少一行才铸),**不是**「没有读被 deny * 判」—— 部署自己的 `readDenyPatterns` 刻意不上报(模式文本是部署材料,不是枚举)。**空数组永不发出** * (「关」与「开到什么都不判」是同一个事实)⇒ 收到空数组按**坏 producer** 处置并留痕,见那里。 * 未来 core 加一段而它没有 audience 标签时,**先在这里补一条裁定再决定挑不挑键** —— 段级完备性钉 * (test/wiring-manifest-projection.test.ts)会在那一刻先把人拦下来 —— ⚠️ **但那条钉是夹具驱动的**:它只看 * `deriveWiringManifest(EFFECTIVE_FACTS)` 真铸出来的段,facts 不带的可选段(147③ 的 `autoMode` 就是)它一格 * 都红不了(2026-09-04 实测:该钉 26/26 全绿,红出 `autoMode` 的是编译期门)。段级 + 成员级的**编译期**门在 * `core-keyset-guard.ts` ⑥b —— 那才是「core 加了段/加了成员」的可靠红点,两道并用缺一不可。 */ /** * 🔴 **`wiring_manifest.mcp[]` 一行上 wire 的形 —— 全仓唯一属主**(S-481 从 {@link wiringManifestEventData} * 的内联 IIFE **逐字**提出来,零行为变化;既有钉在 `test/wiring-manifest-projection.test.ts`)。 * * 为什么必须是一只具名函数:S-481 又开了两条读面(`GET /v1/capabilities/mcp` / * `POST /v1/capabilities/mcp/probe`),它们发的是**同一种**行。留在内联里,那两条面只能各自再挑一遍键 * —— 而契约 §G.7 第 3 条(`error` 远端原话**有意不投**,理由是「再脱一遍就是同一语义面的第二个写者」) * 这种**裁定**一旦有两个实现,下一个人只会改到其中一个。 * * 逐条目挑键(不是整只判形):`name` / `status` 缺一 ⇒ 丢**这一条**;数组本身非数组 ⇒ 整段缺席。 * 逐键判据与失败方向的理由全在下面各行的注里,`liveness` 那一格的裁定在 §G.7 第 2bis 条。 */ export declare function projectWiringManifestMcpRows(mcpIn: unknown): Array> | undefined; /** * `tool_roster_delta`(core 7.8.0 [ref] ⑪ 新 `TaskEvent` 臂)—— **运行期**名册变更:整只变更后名册 * (`delta.roster`)+ 一段只有持有 `fromDigest` 的消费者才用得上的摘要。 * * 契约里唯一的硬话是失败方向:`fromDigest` 对不上**不是拒绝** —— 携带的整只名册无论如何都是新状态, * 消费端记一次 skew 后按快照重同步。所以本构造器**不**替消费端判 digest,只判「这一帧的载荷是不是 * core 说的那个形」。 * * 判形与 {@link wiringManifestEventData} 的 `tools` 段同一条姿势(理由逐字见那里):属主是 core 的 * typebox schema `ToolRosterDelta`,过了整只透传,没过 ⇒ **整帧不发** + F 类留痕。半张名册变更帧比 * 不发更坏:消费端会据它把没读到的行当成「这只工具被卸了」。 * * ✅ **已转正(core 7.9.0 / [ref] S3)**:引擎自 7.9.0 起**真推**这条帧 —— 驱动在推 `wiring_manifest` * 的那一点订阅腿的名册增量座,一次改变名册的 refresh ⇒ **恰一帧**(序在 manifest 之后、那次 refresh 自己的 * `tool_end` 之前),`delta.fromDigest` = 消费端手上那份名册的 digest。7.66.0 写下的「前瞻接线」披露随本版 * 退役:本臂现在会真的触发,消费端可以依赖它了。 * * 🔴 **三腿共用本构造器(S-212,7.71.0)**:live 腿(`routes/tasks.ts`)+ 两条 durable 腿 * (`trace/ledger-sink.ts` 的 `onEvent` / `http/server.ts` 的 resume switch)。返回 `undefined` 的那一格在 * 三腿上是同一件事 ——**整帧不发 / 整帧不落账**,绝不半张名册上 wire 或落库。落账的正当性(旧裁定 * 「不落账」为何在 7.71.0 撤销)逐字记在 `core-keyset-guard.ts` ⑪ 段。 */ export declare function toolRosterDeltaEventData(ev: { delta?: unknown; eventId?: unknown; parentToolCallId?: unknown; }): Record | undefined; export declare function wiringManifestEventData(ev: { manifest?: unknown; eventId?: unknown; parentToolCallId?: unknown; }): Record; /** * `wiring_manifest` 的 **operator 投影**([ref] 件一)—— {@link wiringManifestEventData} 的租户形加上 * `governance` 段与 core 的真 `configFingerprint`。 * * 🔴 分叉发生在**读口**,不在账本:两条 durable 腿(ledger-sink / resume)恒存剥后形,因为账本写的那一刻 * 不知道未来的读者是谁(`GET /v1/runs/:id/events` 任何 run owner 都读得到),往账本存全量 = 把 operator-only * 面写进租户可读介质;存两份则是双写漂移源。所以只有 live 腿在**知道调用方身份**的那一刻挑构造器。 * 代价是 operator 看不到历史腿的 governance 段 —— 治理四布尔是部署形事实(不随 leg 漂),静态自检面 * (`GET /v1/diagnostics/wiring`)随时给出当前真值。 * * 准入判据是 `explicitOperatorOk`(**不是** `isOperator`):空 `OPERATOR_PRINCIPALS` 必须是「没有人是 * operator」,不能是「所有人都是」—— 后者会让一个没配 operator 名单的部署把治理面发给每一个订阅者。 * * `configFingerprint` 只在本构造器里透传:它是 `sha256(canonicalize(manifest 去 leg/去两个 provenance))` * 前 16 hex,**无盐**且输入含整个 governance 段,而 governance 只有 4 个布尔 ⇒ 16 种组合。租户手里已有其余 * 全部哈希输入,发给租户等于把剥掉的四位原样还原(谕示器);发给 operator 无损 —— 同一份产物里四布尔本就 * 在场。 * * governance **逐键挑**(禁 `{...governance}`):core 往这个段里加第五键时必须由人显式处置,而不是顺着 * spread 裸上线 —— 这个段的每一键都是部署治理面,新键的默认待遇必须是「不发」。 * * `autoConsolidation`(core 7.21.0 [ref],S-362)与 governance 同规:租户面剥、本面发(裁定四条判据在 * `core-keyset-guard.ts` ⑥b `WiringSectionExcluded`)。**present-iff 武装**: * · 整段缺席 ⇒ **未武装**,不铸键、不计数(正面事实,不是「未知」,更不是 `{onRecommendation:false}`); * · 在场且 `onRecommendation === true` ⇒ 逐字铸出(core 的在场形只有这一个,构造期门已拒掉「武装但跑不起来」); * · 在场但读不出那个形 ⇒ `dropProjectedFace` 留痕并整段缺席(与 `writeProtection` / `lsp` / `hooks` 同一条 * 族规:「在场但读不出 ⇒ 计一次并丢弃」;**不**自造第三态,词表/形的属主在 core)。 * 🔴 代价如实登记,与 `writeProtection` 那一格逐字同源:wire 上「读不出」与「真未武装」是**同一个形**, * 分得开它们的只有 `server.trace.projection-input-malformed` 这只计数 —— 而这一格比那一格更该说清楚, * 因为它读错的方向是**把一台正在外流记忆的部署渲染成没在外流**。故本段的读不出**必须**留痕,不许静默。 */ export declare function wiringManifestOperatorEventData(ev: { manifest?: unknown; eventId?: unknown; parentToolCallId?: unknown; }): Record; /** * `wiring_manifest` 的**唯一分叉点**(S-528)—— 两条知道调用方身份的 live 消费腿共用它: * `/v1/tasks/stream` 的 SSE 帧,与 `POST /v1/tasks`(非流式)200 体里的 `wiringManifest` 起手回执。 * * 🔴 **为什么是一只函数,而不是两处 `cond ? operator : tenant`**:那个三元判的是「这份产物给谁看」—— * 一件治理面的事([ref] 件一)。写两份就有漂的位置:core 往 operator 面加第 N 段时,跟上的那一处与没跟上 * 的那一处会让**同一次提交**在两条腿上投出两种宽度,而错的方向是把 operator-only 面发给租户。两条腿从此 * 只认这一只,新的第三条腿照抄它即可(禁再写一次三元)。 * * 判据本身(`explicitOperatorOk`)仍由调用腿**每请求求一次**再传进来:身份解析要读请求凭据与部署名单, * 那是 http 层的事,本文件(投影层)不认识 `req`,也不该认识。 * * `operatorFace` 的两个值都是**正面事实**,没有第三态(所以是 boolean 不是三值): * · `true` = 本请求的调用方经 `explicitOperatorOk` 判为 operator ⇒ 投 {@link wiringManifestOperatorEventData}; * · `false` = 其余全部(**含**「本部署根本没配 operator 名单」)⇒ 投 {@link wiringManifestEventData} 的租户形。 */ export declare function wiringManifestFaceEventData(ev: { manifest?: unknown; eventId?: unknown; parentToolCallId?: unknown; }, operatorFace: boolean): Record; /** * `human_input`(core 5.14.0 [ref] 新 `TaskEvent` 臂)—— 「谁把什么喂进了这条 run」的生命周期账本 * 帧(objective / 实时 steer / nextTurn / parked-steer 续跑 / wake 五个载体共用一个投影点)。 * * 信任划线(与 tool_approval 帧同族): * · `inputId` / `sessionSeq` / `carrier` / `source` / `delivery` / `entryId` —— 引擎铸的标识与枚举值, * **非用户内容** ⇒ verbatim。注意本帧**不带正文**(core 的臂里就没有 text 字段),所以没有内容脱敏面。 * · `issuer` 与 `actor.id` / `actor.issuer` —— 宿主从入口凭据派生的**身份串**,自由文本形(`slack:U123` * 这类命名空间串里可以嵌任何东西)⇒ 过 `redactSecrets`,与 `sourceAgentName` 同待遇。 * · `actor.hostAsserted` —— 布尔,verbatim。**它是消费端唯一能判「这个署名可信吗」的位**(core 的 * `[from …]` 渲染就是靠它决定加不加 `(unverified)`),剥掉它比剥掉 id 更坏:剩下一个看不出真假的名字。 * · `principal` —— **刻意不投影**(与 ToolApprovalFrame 的 `principal` 同裁):帧本就经 owner-scoped 流 * 投递,租户身份不必再由帧自述。要它请先提消费需求,别顺手加。 * * `actor` 逐键挑(禁 spread):core 已声明 `ActorAssertion` 是冻结三键形,但白名单的意义正是「core 加第 * 四键时由人处置」——身份对象上多出来的键裸上线是本文件反复登记的病族。 * 形参全可选 `unknown` 的理由同 {@link wiringManifestEventData}(两条 durable 腿手里只有 `{type:string}`)。 */ export declare function humanInputEventData(ev: { inputId?: unknown; sessionSeq?: unknown; carrier?: unknown; source?: unknown; delivery?: unknown; issuer?: unknown; actor?: unknown; principal?: unknown; entryId?: unknown; eventId?: unknown; parentToolCallId?: unknown; }): Record; /** S4 (SILENT-FALLBACK P0-a): the durable `brain_status` observation row, shared by the bg + resume append * sites. core marks the live `status` frame EPHEMERAL (never replayed by core on resume) — this row is the * SERVICE's own observation record (why a turn stalled: rate_limited/retrying/reconnecting/circuit_open, and * since core 7.3.1 / [ref] the pre-failure `waiting_first_token` wait), not a core event replay. * Whitelist: the closed phase union verbatim + neutral bounded detail (redacted) + the bounded numeric seats. */ export declare function brainStatusEventData(ev: { phase: string; detail?: string; retryInSec?: number; attempt?: number; maxRetries?: number; retryInMs?: number; errClass?: string; retryAtMs?: number; errorStatus?: number; elapsedMs?: number; timeoutMs?: number; eventId?: string; parentToolCallId?: string; }): Record; export declare function compactedEventData(ev: { eventId?: string; parentToolCallId?: string; trigger?: string; tokensBefore?: number; preserved_segment?: { firstKeptEntryId: string; }; attachedFiles?: Array<{ path: string; chars: number; truncated: boolean; }>; modelFallback?: true; fallbackReason?: "window"; clampedRatio?: number; clampReason?: "budget" | "tolerance"; tokensAfter?: number; triggerTokensBefore?: number; /** core 7.24.0 [ref]:引擎**自己**算的结构性净减(pre-messages 的结构和 减去 post-messages 的同一个和, * 铸点处 clamp ≥0),三条压缩车道(turn 边界 / prompt-too-long 恢复 / 任务末 settle)同一只 * `mintCompactedEvent` 出。🔴 **缺席 = post 测量失败,不是 0**:上面那两个旧键(`tokensAfter` / * `triggerTokensBefore`)自本版起在 core 的类型上写明是**诊断位、不可相减**(触发闸读的是 usage 锚定 * 的数、post 恒是结构和 + 固定提示词开销,两者的差混坐标,实测在普通 run 上是负数)。本仓的处置因此 * 是**三条都逐字透传、一条都不合成**:谁要画上下文表就读 `freedTokens`,读不到就渲染 unknown。 */ freedTokens?: number; durationMs?: number; phaseDurations?: { prepareMs?: number; summaryMs?: number; persistMs?: number; ptlRetries?: number; }; }): Record; export interface TraceTurn { /** per-task seq = the turn's terminating event seq (so history-up-to-H splices with stream seq>H). */ seq: number; ts: string; role: "system" | "user" | "assistant" | "tool"; blocks: TraceBlock[]; /** core 3.0.0([ref] 装车单 S3,§DESIGN-V2 V2-②):这是**本服务自有的投影契约**,不是 core wire 帧的 * verbatim 转发——`in` 刻意保持它历史上的「总量」语义不变(旧帧/新帧读端零伤),不随 core 的 * `promptTokens`/`turn_end.usage.inputTokens` 翻转为 cache-MISS。新增 `uncachedIn` 承接 core 3.0.0 才有的 * cache-MISS 分量(命名取自 core `uncachedInputTokensOf`),只在能确认自己踩在新契约帧上时才发 * (见 {@link toContractTokens})。 * * 🔴 **S-213⑥:五个子键**全部按同族 OMIT 契约走 —— 引擎那一侧报了才铸,没报就**缺席**,恒不折 `0`。 * `in`/`out` 此前是必填座、缺一键时写 `?? 0`:读端拿到的 `in:0` 与「这一轮真的零输入」逐字节同形, * 于是一次 usage 半缺(某些 provider 只回 output 侧)在面板上显示成一个可核对的真数字。 * 0 是一个数,未知不是 —— 两者同形正是本仓 `boundCallId`/`gateKind`/`contentKind` 一族一直在防的事。 * ⚠️ wire 面(`GET /v1/tasks/:id/turns` 与 trace SSE 的 `turn` 事件)因此可能给出**只有一侧**的 * `tokens`;做算术的消费端要按「键可能缺席」读(`tokens` 整只可缺席早就是契约的一部分)。 * 🔴 **7.74.0 / core 7.17.0([ref])**:引擎自陈 `usageMissing` 的一轮(usage 是六个 0 的占位)⇒ 整只缺席 —— * 「未知」在这张脸上只有缺席这一种拼法,不加第二枚座位(见 {@link toContractTokens})。 */ tokens?: { in?: number; out?: number; uncachedIn?: number; cacheRead?: number; cacheWrite?: number; }; } /** * `context_usage` 的白名单投影(core 1.414 新增一等 `TaskEvent` 分支;server [ref]⑥ 点名要的上下文用量面)。 * * 🔴 两条 core 明确交代的口径,必须原样传下去、不要在服务端"聪明地"重算: * · `usedTokens > compactAtTokens` **就是引擎自己喂 `shouldCompact` 的谓词** —— 消费方直接用,别重新推导阈值。 * · `windowTokens` 是 **autocompact 窗**,**不是**「模型上下文大小」(模型声明 `autoCompactTokens` 时物理请求 * 窗更大)。渲染成"模型上下文"会给用户一个错的分母。 * ⚠️ 它在**每个压缩边界**上报(不只压缩真发生时),所以一次任务里会有多条 —— 消费方按最后一条取当前值。 * ⚠️ 它**没有**挂在 `turn_end.usage` 上(core 解释:边界检查跑在 turn_end 之后、对 flush 后的消息集, * 挂那儿会是"另一次更早的测量顶着同一个名字")。去 turn_end 找是找不到的。 * 三个字段都是引擎派生的有限数标量 ⇒ 走 finite 门(与 compacted 的 1.289 标量同姿势),非有限即缺席。 */ export declare function contextUsageEventData(ev: { usedTokens?: unknown; windowTokens?: unknown; compactAtTokens?: unknown; sections?: unknown; eventId?: unknown; parentToolCallId?: unknown; }): Record; /** * `turn_end` 的白名单投影 —— 🔴 2026-07-25 补:`turn_end` 此前是**唯一没有 builder 的内容臂**,两条持久化点 * (ledger-sink 与 resume 腿)各自手写 `{ usage }`,于是 core 的另两个字段被静默剥掉: * · `stopReason` —— **这一轮为什么停**(max_tokens / stop_sequence / …); * · `usageMissing: true` —— **用量不可信**的诚实标记。缺了它,"没有 usage"会被读成"用量是 0", * 而不是"未知";这正是那种"回放后看起来一切正常"的丢失。 * 两者**在 live 腿看得见、durable 腿回放后永久消失** —— 与 [ref] 的 `label`、[ref] 的 workflowRunId 同一族 * (白名单没跟上 core 新增字段)。补 builder 的同时也就把它纳入了 `wire-whitelist-exhaustiveness` 的编译期差集门。 */ export declare function turnEndEventData(ev: { usage?: unknown; usageMissing?: true; stopReason?: string; eventId?: unknown; parentToolCallId?: unknown; }): Record; /** Map core's `turn_end.usage` (1.76 / core [R59]: inputTokens/outputTokens/cacheReadTokens/cacheWriteTokens/ * costMicroUsd; core 3.0.0 adds required `totalInputTokens`) → the contract's `TraceTurn.tokens`. * Absent/empty → undefined. * * core 3.0.0([ref] 装车单 S3,§DESIGN-V2 V2-②,codex 复审「异议」采纳):`TraceTurn.tokens` 是本服务自有 * 的投影契约,不是 wire verbatim——`in` 保持它一直以来的**总量**语义,3.0.0 起改取 `totalInputTokens` * (归一化总输入含 cache)优先,`inputTokens` 回落(旧帧/尚未升级的 core 下 `totalInputTokens` 缺席时, * `in` 落回旧值,读端零伤)。`uncachedIn` 只在**能确认自己踩在新契约帧上**(`totalInputTokens` 在场)时 * 才发——旧帧的 `inputTokens` 是族依赖的旧语义,标成「uncachedIn(cache-MISS)」会是谎言。 * * 🔴 S-213⑥:**一条规则管五个子键** —— 源侧在场就铸、缺席就不铸,没有 `?? 0`。此前 `in`/`out` 两座 * 各带一个默认 0,于是 usage 半缺(provider 只回 output 侧)被投影成 `in:0` = 一个看起来可核对的真 * 数字;`uncachedIn`/`cacheRead`/`cacheWrite` 三键从第一天起就是缺席制,`in`/`out` 只是漏进了默认值。 * 早退分支语义不变:输入两键(择一)与 `outputTokens` 全缺 ⇒ 整只 `tokens` 缺席(不铸空对象)。 * * 🔴 **7.74.0 / core 7.17.0([ref]):`usageMissing` 是本投影的输入,不只是事件面的一枚标。**引擎自该版起 * 在「这一轮 provider 没回 usage 帧」时也**恒发** `turn_end.usage` —— 内容是六个 0 的占位对象,旁边挂 * `usageMissing: true`。只看 usage 会把「未测量」投成一只全 0 对象,与「真的零用量」在 * `GET /v1/tasks/:id/turns` 与 trace SSE 的 `turn` 事件上逐字节同形 —— 正是 S-213⑥「0 是一个数,未知不是」 * 要消灭的那一形。契约里「未知 = `tokens` 整只缺席」早就是唯一读法 ⇒ 标在场就**不铸**(与 7.73.1 及以前的 * wire 读数逐字节相同),不按「六个 0」的值形猜、也不给 `TraceTurn.tokens` 加第二枚「未知」座(同一事实两种 * 拼法)。标的域是 core 的 `true | 缺席`(永不 `false`)⇒ 严判 `=== true`,别的值不是标。 */ export declare function toContractTokens(usage: unknown, usageMissing?: unknown): TraceTurn["tokens"] | undefined; /** [ref]C-13:tool_start/tool_end 的**共享挑键**——`projectEvents`(turns 面)与 `mapTraceEvent` * (trace SSE 面)对同一账本行的字段选择逐字同构,此前两处独立手写,structured/totalChars/label * 三次扩字段各改两遍(见两处对偶注)。收一份:两面各自包装键名皮(id/name/input vs 同名), * 字段集合从此单点演进。 */ export declare function toolCallFieldsOf(d: Record): Record; export declare function toolResultFieldsOf(d: Record): Record; export interface ProjectOpts { /** Prepend the user turn (the objective is not in the event log; the endpoint supplies it). */ objective?: string; } /** Project a per-task event slice (seq-ascending) into ordered `TraceTurn[]`. Pure + total (never throws). */ export declare function projectEvents(events: RunEvent[], opts?: ProjectOpts): TraceTurn[]; /** Structured `lastStep` (contract 2.1) from the latest event — UI maps an icon, never free text. */ export declare function lastStep(latestEventType: string | undefined, status: string): { type: "thinking" | "tool-call" | "responding" | "idle"; label?: string; }; export interface TaskSummary { id: string; status: string; scenario?: string; /** Work-view correlation id (optional) — lets the client group a list into logical tasks. */ jobId?: string; /** Credential-derived submitting system (oa/cc-mcp/portal…) — per-door attribution. */ source?: string; /** Redacted first ~120 chars of the objective — the "what is this task" glance. */ objectivePreview?: string; /** The run's owner principal — the operator/boss view needs "whose task". */ owner?: string; startedAt: string; endedAt?: string; turns?: number; tokens?: number; costMicroUsd?: number; lastStep: { type: "thinking" | "tool-call" | "responding" | "idle"; label?: string; }; } /** A run record → the cheap list summary (contract 2.1). Token/cost/turns come from the terminal result.stats; * scenario isn't denormalized on the run row (omitted — a later push-index/column refinement). */ export declare function taskSummary(run: RunRecord, latestEventType?: string): TaskSummary; /** [ref] B2(lens4 N8)—— 重放边界的**读旧**归一:把历史账本里的 `brain_status` 行改叫 `status`。 * * 同一条「大脑存活性」观测曾在两条腿上叫两个名字:live 腿(`POST /v1/tasks/stream`)发 `type:"status"`, * durable 腿的账本行落 `brain_status`,而 SDK 的公开 `AgentEvent` 联合**只有** `status` —— 于是走 * `GET /v1/runs/:id/events` 的消费端会收到一个公开联合之外的帧(穷举式 closed-oneOf 消费者当场抛)。 * * 写入侧已经改叫 `status`(ledger-sink.ts 与 resume 腿的 append 两处),但**存量行救不了**:任何 * <1.317 的部署账本里都躺着 `brain_status` 行,重放它们仍会漏出旧名。所以「写新」必须配一个「读旧」—— * 这个函数就是那个读边界,唯一调用点是 task_run SSE 的 `formatEvent`(routes/runs.ts)。 * * 🔴 刻意**只**认这一个键,不做成映射表:帧名归一是历史包袱的一次性偿还,不是一条可以长胖的兼容层。 * 再有第二个名字对不齐,应当在源头修,而不是往这里加一行。 */ export declare function normalizeRunEventType(type: string): string; /** Map one durable event → a sema-registry SSE trace event (contract 2.3). null = not a client event. The * durable log is block-grained, so each block ships as one "delta" the client appends (stream mode = "delta"). */ export declare function mapTraceEvent(type: string, seq: number, data: Record): { event: string; data: Record; } | null; export {}; //# sourceMappingURL=project.d.ts.map