import { defaultTaskRegistry, type CascadeConfig, type CheckpointToken, type RunInternals, type Runner, type SubagentSteerHandle, type TaskResult, type TaskSpec, type TaskStream, type TerminalCause } from "@sema-agent/core"; import type { SendUserFileEmitter } from "./capabilities/send-user-file-tool.js"; import type { RunStore } from "./plugins/store-backend.js"; import type { Metrics } from "./observability/metrics.js"; import type { ModelUsageTracker, PromptManifestTracker } from "./budget.js"; import type { ElicitationCoordinator } from "./elicitation.js"; import type { QuestionCoordinator } from "./question.js"; import { type ToolApprovalCoordinator } from "./tool-approval.js"; import { type FleetRunPublisher } from "./fleet/fleet-bus.js"; import { type WorkflowCompletionInbox } from "./orchestration/workflow-completion-inbox.js"; import type { VerifyRoundsSpec } from "./http/verify-rounds.js"; /** * stoppedBy (core 1.252): a service cancel is a USER stop — mark the run's still-running background * children BEFORE core's teardown reaps them (bare abort = attribution falls back to "system"; core's own * reap marks "parent", but first-marker-wins means our earlier "user" is the one that lands). Register this * FIRST on the cancel signal so it runs ahead of any later-registered core listener (listener-order * guarantee); one registration covers every abort route on that signal (same-replica fast path + the * cross-replica flag poll). Advisory only — the cancel itself must never fail on attribution. * * Access = TASK-scoped children ONLY: owner = the parent taskId (on a RESUME leg: the sessionId — core's * canonical taskId there), scope = `principal ?? "default"` (core registers children under * `spec.principal ?? "default"`, so an anonymous run's children live in scope "default" and an access * WITHOUT scope matched nothing). Session-scoped children are DELIBERATELY not marked: a cancelled parent * does NOT take them down (CC Backgrounded semantics — core's teardown reaps with skipSessionScoped), so * pre-marking them "user" would misattribute a stop that isn't happening. * * core 1.256: `markStopSourceForOwner` replaces the old `list()+markStopSource` * per-row loop, closing BOTH documented holes — ① the EXPLICIT `sessionScoped` flag * (`skipSessionScoped:true`) makes the resume leg safe (its owner key == sessionId COINCIDES with * session-scoped children's; the old code couldn't tell them apart and took an honest degrade instead), * ② a direct handle-map walk (no `list()` 500-row display cap under-marking >500-children runs). Covers * core's full markable set (background_agent / background_bash / monitor); running-only + * first-marker-wins guards live inside the seam. */ /** [ref]③(b) + codex R2: the bg agent-handle OUTPUT read, kind-gated BEFORE any poll. `pollTask` is the * generic registry face and polling is NOT side-effect-free for other kinds — a terminal workflow poll * fires `onServedTerminal` (acknowledges the completion as served → suppresses its notification) and a * bash poll advances the output cursor. `getAccessibleTask` is a pure lookup: resolve + access-check + * type-check FIRST; only a `background_agent` row is ever polled (non-blocking). Everything else returns * the registry's own not_found shape, indistinguishable from an unknown handle (the route 404s it). * * 1.250 durable arm (core 1.364, [ref] follow-up delivered): a FULL in-process miss (`!row`) falls * through to `pollTask(…, { agentStore })` — core's durable arm only fires for `a*`-shaped handles * (DURABLE_AGENT_HANDLE_RE) behind `canAccessAgentRecord`, and with NO in-process handle the live * workflow/bash branches are unreachable, so the kind-gate's reason (onServedTerminal / cursor advance) * cannot trigger. Terminal rows serve the durable snapshot cross-instance/post-restart; a foreign-writer * running row answers the honest "outcome unknown here"; anything else is core's own not_found (byte-same * route 404 as today). A row that IS in-process but of the wrong type stays a hard 404 — the process * knows the truth, falling back would sidestep the kind gate. */ export declare function backgroundAgentOutput(registry: typeof defaultTaskRegistry, handle: string, access: { owner: string; scope: string; sessionId?: string; }, agentStore?: import("@sema-agent/core").BackgroundAgentStore): Promise<{ content: string; details: unknown; }>; export declare function taskHandleOutput(registry: typeof defaultTaskRegistry, handle: string, access: { owner: string; scope: string; sessionId?: string; }, agentStore?: import("@sema-agent/core").BackgroundAgentStore): Promise<{ content: string; details: unknown; }>; /** [ref] CC TaskStop 人侧对位 — stop a background task handle (bash kill / monitor stop / agent abort). * Same kind gate as the read face (a workflow stops via its own cancel face, never this verb). stopTask is * idempotent on a terminal handle (the registry answers honestly); the stop is attributed "user" via the * registry's existing markStopSource machinery (an HTTP stop is the human's hand — parity with the shell's * TaskStop attribution). The pre-mark is first-write-wins and MUST come first (core's own stop path marks * "parent" internally — marking after would lose the attribution race on success, the common path); the * trade (codex R3) is that on a kill-that-didn't-land the pending "user" mark is not clearable through the * public registry face — the route surfaces that arm as 409 stop.not_landed, and the atomic fix (a `source` * option on stopTask) is a core ask. */ export declare function taskHandleStop(registry: typeof defaultTaskRegistry, handle: string, access: { owner: string; scope: string; sessionId?: string; }, agentStore?: import("@sema-agent/core").BackgroundAgentStore): Promise<{ content: string; details: unknown; }>; /** * Attribute this signal's abort as a USER stop for the owner's task-scoped children. * * 🔴 已经 abort 的信号在这里**立刻**记账,不是静默无操作([ref] 件3 的配套)。调用点在 [ref] 件3 之后 * 一律挪到了「run 认领成功之后」—— 认领是一次 await,断连完全可能落在它之前。`addEventListener` 对一个 * 已 fire 的信号永不回调,那样一次真实的人为断连会被吞成「无归因」(子代 settle 时记成 system)。 * 语义上这两种时序是同一件事:本请求赢下了这条 session,而这条 session 的连接是被人断掉的。 */ export declare function markChildrenStoppedByUserOnAbort(signal: AbortSignal, taskId: string, principal: string | null | undefined): void; /** * A session CAS conflict is swallowed by core into a failed `TaskResult` (it is not thrown from * `runTaskStream`), so detect it from the result. Prefer the structured `code` (core ≥1.8); fall * back to the conflict wording only for older results without one. */ export declare function isSessionConflictResult(result: { terminal: TerminalCause; }): boolean; /** * E18 resume-at CALLER error → an HTTP 4xx. A bad/stale resume-at target (the resolved entryId is gone or is not a * settled message boundary) surfaces as a FAILED TaskResult carrying a `resume_at.*` errorCode (core swallows the * prepare-throw into the result, exactly like a session conflict — runtask catch → status:"failed"+errorCode). The * sync path otherwise 200s a failed result; map these to 4xx so a caller mistake reads as one. `not_found` (the * entry is genuinely absent) → 404; everything else (not_a_message / conflicts_resume / no_session) → 422. * * [ref] Phase3: `requireExistingSession` on a genuinely-missing/purged session ALSO surfaces as a failed result * (core prepare-task maps the store's `not_found` → `resume.session_not_found`); map it to 404 (the session is gone), * the same "caller mistake reads as a 4xx, not a silent 200 fresh run" discipline. * * [ref] rewind exclusive mode (core 1.292 resumeAtMode:"before"): its two edge rejections ride the same `resume_at.*` * prefix (before_target_not_user / before_root_unsupported → 422 via the existing branch, errorCode passed through * UNCHANGED for the shell). `rewind_snapshot.unresolvable` (rewindFiles + "before": no snapshot at/above the branch * point) is core's third rejection with a DIFFERENT prefix — same caller-mistake shape (prepare-throw, nothing * billed), map it to 422 explicitly so it doesn't fall through to a 200-with-failed body. * * [ref] 件A([ref] 件4): core 5.13.0's org-memory admission rides the SAME prepare-throw pipe * (GOVERNANCE_CODES). `memory.admission_denied` is TERMINAL — the principal has no grant on that tenant * plane → 403 (a retry cannot change the verdict). `memory.admission_required` is TRANSIENT fail-closed — * the directory is unreachable / no resolver is wired → 503 (retry later; the sync leg forwards the * result's `retryAfterMs` as body `retryAfterSec`, same family shape as usage.window_exhausted). Exact * codes only, no memory.* family grab — an unknown sibling stays a 200-with-failed-body until cataloged. * * [ref] (core 7.0.0 片2/3, [ref] S-3): the session capture opt-out's four prepare-throw refusals ride the * same pipe. `memory.capture_optout_denied` is TERMINAL (the principal's `allowMemoryOptOut` verdict is an * explicit `false`, or the posture is `capture-required` / `governed`-with-absent-verdict — a retry cannot * change it) → 403, the `memory.admission_denied` twin. `memory.capture_optout_unpersisted` is the * declaration arm's "control-plane record could not be written" refusal — a store outage, retry may succeed * → 503 (transient fail-closed, the `memory.admission_required` twin; no retryAfterMs on this one — core * mints none, so the sync leg forwards no hint rather than inventing one). `config.memory_capture_spelling` * is a TaskSpec authoring mistake (the only legal spelling is the string "off") → 400, the * `config.memory_erasure_request` family shape. `config.memory_capture_unsupported` is the DEPLOYMENT SHAPE * refusing the declaration (remote/per-run execution with no durable capture-record carrier: the record * would not survive to a resume replica) → 409 — the request is well-formed, the deployment's state * conflicts with it, the `config.refresh_unavailable` family shape ("change the deployment, not the request"). * Status choices are pinned by the `config.`/`memory.` family status sets in error-code-catalog-live.test.ts. * * S-125 件⑥(core 7.4.0 [ref]):`rewind.child_scope_unsupported` —— 这条腿是一个**委派子代**,它的文件编辑 * 记在**根 session** 的文件历史里,所以「把文件还原到某个 entry」是根 session 的动作、不是子代的;core 在 * prepare 期响亮拒(零计费,文件一个字节没动)。映射 **422**:请求形式合法、语义上找错了执行者 —— 与 * `resume_at.*` 的「调用方指错了锚」同一族同一档,而不是 404(目标在,只是不归你)也不是 409(部署状态没冲突)。 * ⚠️ **前缀不吞**:本码前缀是 `rewind.`,与下面那条 `resume_at.` **开集前缀臂**不同族 —— 它不会被那一臂顺手 * 兜走(兜走会把一条「找错执行者」的拒答成「锚解析不了」),也不是 `rewind_snapshot.`(那是「快照找不到」)。 * 三个前缀各自成族,逐码对表见 `test/resume-anchor.test.ts` 的 S-125 `rewind.` 族格。 * 🔴 **可达性如实**:本仓的 HTTP 面**今天铸不出**这条腿 —— 子代由 core 内部的 Agent 工具派生, * `fileHistoryLineage` 是 core prepare 的 internals(不在 `TaskSpec` 上),server 从不直接 prepare 一条带 * lineage 的子代腿。本臂是**分诊面的完备性**(core 说 X ⇒ wire 形是 Y),不是一条今天在跑的路;将来的 * 委派入口接线那天零改动即生效。同族其余五码今天**仍落 200-with-failed-body**,那是本批**之前**就存在的 * 缺口(不是本批回归),已在上述对表格里逐码显式登记为债务,并由那道门看住不许陈腐。 */ export declare function resumeAtHttpStatus(result: { terminal: TerminalCause; }): number | undefined; /** * E18 resume-at — per-turn anchor capture, shared by runInBackground AND the resume-leg driveResume so the (single, * tricky) capture rule can't drift between the two stream-drain loops. * * A "turn" in core = ONE assistant message + its tool batch. resume-at is valid ONLY at a SETTLED rewindable boundary * — core's prepare-task accepts "user messages or finished assistant-text turns" and REJECTS an assistant message that * ends mid-tool-call (resume_at.not_a_message). So we capture an anchor ONLY for a turn that produced text AND ran NO * tools: then the turn's leaf at turn_end IS that assistant-text message (the rewindable target). For a tool-using * turn neither candidate works — at turn_end the leaf is the turn's LAST tool-result (core appends tool results before * emitting turn_end; a tool-result is stored as a `message` so core would SILENTLY branch AFTER the tools ran), and * the assistant message itself ends mid-tool-call so core would reject it. The shell still gets a per-message eventId * handle on every text event (E2 identity); a rewind to a non-captured (tool-using) message simply 404s — honest, * never a silent wrong-point branch. (The adversarial-review HIGH: capturing at turn_end unconditionally mis-anchored * every tool-using turn — the common agentic shape — to the tool-result leaf.) * * Best-effort: a capture failure must NEVER fail the run (it only makes that one turn non-rewindable). `onCaptureFail` * surfaces the degrade (a metric) so a SYSTEMATIC failure — e.g. an un-migrated DB missing `resume_anchor` — is * observable instead of a silent feature outage. */ export declare class TurnAnchorCapture { private readonly capture; private readonly onCaptureFail?; /** R8 (CC-parity rewind): capture a USER-message anchor straight from the * `message_committed{role:"user"}` event — its `entryId` is the persisted user-message `SessionTreeEntry.id`, * ALWAYS a valid `resumeAt` target (core accepts a user message; it rejects a tool-result / mid-tool-call * assistant). This is the CC rewind target ("rewind to the prompt" = the code-restore parity path — a code * change happens in a tool turn WITHIN a prompt's run, so rewinding to the prompt + rewindFiles undoes it). * Keyed by the run's taskId (the handle the shell already holds from the POST response — no eventId→entryId * side map / no `getLeafId` inference needed). The assistant-turn capture above stays as an additive bonus. */ private readonly captureUser?; /** The turn's FIRST text_delta eventId = the message's stable resume handle (also stamped on the durable text event). */ firstTextEventId: string | undefined; private turnHadTool; constructor(capture: ((eventId: string) => Promise) | undefined, onCaptureFail?: (() => void) | undefined, /** R8 (CC-parity rewind): capture a USER-message anchor straight from the * `message_committed{role:"user"}` event — its `entryId` is the persisted user-message `SessionTreeEntry.id`, * ALWAYS a valid `resumeAt` target (core accepts a user message; it rejects a tool-result / mid-tool-call * assistant). This is the CC rewind target ("rewind to the prompt" = the code-restore parity path — a code * change happens in a tool turn WITHIN a prompt's run, so rewinding to the prompt + rewindFiles undoes it). * Keyed by the run's taskId (the handle the shell already holds from the POST response — no eventId→entryId * side map / no `getLeafId` inference needed). The assistant-turn capture above stays as an additive bonus. */ captureUser?: ((entryId: string) => Promise) | undefined); /** A `message_committed` arrived — capture a USER-message rewind anchor (CC parity). Best-effort (a failure only * makes that one prompt non-rewindable). Only `role==="user"`; assistant/toolResult committed entries are handled * by the turn-text path (assistant) or deliberately not anchored (toolResult — `resumeAt` rejects them). */ onMessageCommitted(role: string | undefined, entryId: string | undefined): Promise; /** A text_delta arrived — latch the first DEFINED eventId as the message handle (eventId is optional on the stream). */ onText(eventId: string | undefined): void; /** A tool ran this turn — mark it non-rewindable (its leaf will be a tool-result, not a settled assistant-text message). */ onTool(): void; /** turn_end — capture the anchor iff this was a settled assistant-TEXT turn, then reset for the next turn. */ onTurnEnd(): Promise; } /** [ref] 投影腿的最小 checkpoint 读口(结构型——真店 `CheckpointStore.get` 直接对上)。 */ type ParkCheckpointReader = { get(token: CheckpointToken): Promise<{ pendingAction?: { kind?: unknown; toolCallId?: unknown; }; } | null>; }; /** * [ref](cli 判别子 v3 请托)—— park 结果的**待批工具调用身份**:suspended/needs_review 结果在 * strip 之前手握 checkpointToken,用它 `cs.get(token)` 换 `pendingAction`,`tool_approval` 臂直读 * `toolCallId`(与 tool_approval 帧 ≥1.307 同键同义)。核心 `TaskResult` 上没有这个键(CheckpointGate * 只带 toolName),checkpoint 本体是 park 的事实源——同源判据([ref] 教训)。 * * 缺席形照 core `CheckpointSummary` 的 [ref]② OMITTED 契约:tool-less park(resource_limit / * plan_review / task_done)回 `undefined` ⇒ 调用方**不铸键**,绝不编 null。读失败=F 类兜底 * (展示/关联键,缺席=该键到货前的现状),`recordFailOpen` 留痕,绝不挡 done/suspended 终局写链。 */ export declare function parkToolCallId(result: unknown, cs: ParkCheckpointReader | undefined): Promise; /** [ref] 整对象形:park 结果顶层 additive 上 `toolCallId` 键(与 `checkpointId` 同位,[ref] 先例; * 不塞进 `checkpointGate`——那是 core 的类型,server 不改其形)。非 park / 无 token / tool-less / * 读失败 ⇒ **同一引用**原样返回(键缺席)。graft 不负责 strip——组合序恒为 graft → strip。 */ export declare function graftParkToolCallId(result: T, cs: ParkCheckpointReader | undefined): Promise; /** Evict a stale warm-cache entry after a cross-instance write conflict (broken-affinity backstop). */ export declare function evictIfConflict(runner: Runner, sessionId: string | undefined, result: TaskResult): void; /** * Drive a task to completion in the background, persisting its events to the durable run log (S2). * * Text deltas are coalesced into one `text` event per turn (the durable stream is turn-grained for * text); tool/lifecycle events are recorded individually. Per-token live streaming stays on the * synchronous `/v1/tasks/stream` endpoint. Never throws — failures are recorded on the run. * * Cancellation (POST /v1/runs/:id/cancel): the run registers an AbortController in `inflight` (same-replica * fast path — the cancel handler aborts it directly) and its heartbeat tick polls the durable * `cancel_requested` flag (cross-replica, ≤HEARTBEAT_MS lag). Either way the run settles to * status "failed" + errorCode "cancelled" (metric label "cancelled"); the entry is removed in `finally`. * * Preemption (POST /v1/assistant/tasks/:id/preempt, [ref] seam #2): a SECOND, INDEPENDENT controller * (`preemptCtrl`, registered in `preemptable`) carries the scheduler's graceful "yield this task" — when raised, * core durably SUSPENDS the task at the next clean turn boundary (gate resource_limit, reason "preempt") so it is * RESUMABLE, the opposite of cancel's kill. It is wired ONLY on the plain-stream path: the verify/cascade * branches finalize with `setTerminal` unconditionally (no `setSuspended`), so a preempt-suspend there would write * a non-terminal "suspended" as terminal and release the session lock — and preempt's contract is graceful, not a * failure. Cross-replica via the `preempt_requested` flag (same heartbeat tick). Eligibility (resourceSuspend + * durable store + durable tool-results + remote env) is core's call: an ineligible task's preemptSignal no-ops. * * Steering (POST /v1/runs/:id/steer, [ref]): the live core `TaskStream` is registered in `steerable` so the * HTTP route can inject a mid-task message via `stream.steer()` (drained at the next turn boundary). Same-replica * only — a suspended run is steered durably via the checkpoint's `setPendingSteer` instead, and only the * plain-stream path holds a steerable stream (the verify/cascade branches return a result, not a stream). */ export declare function runInBackground(runner: Runner, spec: TaskSpec, runStore: RunStore, taskId: string, metrics?: Metrics, principal?: string, verify?: VerifyRoundsSpec, // 快审 F1:number 参升整对象——cost 顶随传 cascade?: CascadeConfig, instrumentDegenerate?: (result: TaskResult) => void, planCacheProbe?: { record: (scope: string | undefined, objective: string, completed: boolean) => void; }, persistThinking?: boolean, inflight?: Map, preemptable?: Map, steerable?: Map, modelUsage?: ModelUsageTracker, elicitation?: ElicitationCoordinator, /** E23: the VERIFIED principal (gatedPrincipal) that may answer this run's elicitations — matches the respond * owner-gate's identity source (NOT the cost-attribution `principal`, which on a direct door is the self-asserted * header → can be null while a JWT principal exists). Falls back to the run owner when not supplied. */ elicitOwner?: string | null, /** E18 resume-at: capture the turn's (firstTextEventId → leaf entryId) anchor at each turn_end. Built by the caller * closing over {sessionId, owner, sessionStore.getLeafId, resumeAnchorStore}; undefined ⇒ no anchor store wired. */ captureTurnAnchor?: (eventId: string) => Promise, /** MF-Fleet (shell-host contract): the run-scoped fleet-row publisher. `onStart` adds the live row; `onEvent` accrues * tokens (turn_end) + subagent child rows (task_progress); `onTerminal` flips/removes it. Fired for ALL legs * (verify/cascade/plain-stream) so a background run TRANSITIONS its `GET /v1/fleet/stream` row exactly like the * sync leg. No-op when no fleet bus is wired (the publisher is a null-object). */ fleetPublisher?: FleetRunPublisher, /** R8 (CC-parity rewind): capture a USER-message anchor (keyed by this run's taskId) from `message_committed{role:"user"}` * — the CC "rewind to the prompt" target. undefined ⇒ no anchor store wired. */ captureUserMessageAnchor?: (entryId: string) => Promise, /** §4④ : AskUserQuestion LIVE coordinator — establishes a per-run onQuestion ALS context (like `elicitation`) * so an AskUserQuestion tool call routes to this run's stream + owner-gated respond. undefined ⇒ headless default. */ question?: QuestionCoordinator, /** P1 ①② follow-on (core): the async-workflow completion inbox, drained at the START of this background * leg (parity with the sync stream-open drain) into durable `workflow_complete` events a tailing client replays. * Owner gate = `elicitOwner ?? owner` (the same verified-principal-with-run-owner-fallback the elicit/question * contexts use). undefined ⇒ no push half wired (the WorkflowStatus poll floor stands alone). */ workflowCompletionInbox?: WorkflowCompletionInbox, /** C2 (core 1.219): the replica-local Task-subagent steer-handle registry. When present, this leg * opts into core's `onSubagentSpawn` sink — each SYNC delegation's steer handle registers under THIS run's * taskId (auto-evicted on child settle) so POST /v1/runs/:id/subagents/:target/steer can route into it. */ subagentSteer?: { register(runId: string, handle: SubagentSteerHandle): () => void; }, /** ask①: emit-target diagnostic sink for the completion-inbox drain below (route/connection/ * deliveryLagMs per delivered `workflow_complete`). runs.ts has no logger of its own — the caller closes over * deps.logger. undefined ⇒ silent (behavior unchanged). */ completionDiagLog?: (msg: string, meta?: Record) => void, /** SendUserFile(切片2):per-run file_link 帧发射 ALS(question/elicitation 同款)。emit=durable append。 */ sendUserFile?: SendUserFileEmitter, /** [ref]② the shared prompt-manifest accumulator (tracer records at prepare; this leg drains the pending * record into a durable `prompt_assembled` event — turns/stream read it). undefined ⇒ not wired. */ promptManifests?: PromptManifestTracker, /** * [ref] 车3 刀 3b:**bg 腿的流内审批 ctx**([ref] §4.3(b))。今天为止这条腿连 approval ALS 都没有 * ——`deps.onAsk` 拿不到 ctx ⇒ 恒 `"unavailable"` ⇒ durable park,一张卡都不产。接上之后,bg run 的 * 权限 ask 走本腿的 **durable events tail**(`GET /v1/runs/:id/events`,唯一带 SSE `id:` 的面)。 * * `streamApprovalOn` = 协议上场判据(`resolveStreamApprovalGate`,由路由层求值后传进来 —— 本函数是纯 * 执行腿,不该自己读 backend/config)。为假 ⇒ 只接既有四键 ctx(呈卡口/腿轴都不接),本腿字节逐字不变。 */ /** `windowMs` 收成 `number | undefined`([ref] + codex r1-[medium]):`undefined` = **这份装配没有 * `streamApproval` 段**(stub-harness),不是「运维显式关窗」—— 两者自 [ref] 起结局不同(后者即答 * 无人值守终局),路由层因此不许再折算,见 `resolveApprovalLeg` 的 `windowMs` 域注。 */ approval?: { coordinator: ToolApprovalCoordinator; streamApprovalOn: boolean; windowMs: number | undefined; windowMarginMs: number; }, /** [ref]:park 投影腿的 checkpoint 读口(`deps.checkpointStore`)。在场时,durable park 的 * `suspended` 事件带 `toolCallId`(graft 见 {@link parkToolCallId});缺席=旧字节逐字不变。 */ parkCheckpointReader?: { get(token: CheckpointToken): Promise<{ pendingAction?: { kind?: unknown; toolCallId?: unknown; }; } | null>; }, /** L-167:装配期(`prepareSpec` → `resolveSpec`)铸下的用户面通告 —— 那一刻本腿的通告口还没开, * 所以由本腿在 `registerEngineNoticeLeg` 那一拍连同 `pending` 一起投(投递单点仍是 `route()`)。 * 缺席 ⇒ 一个字节不投(既有调用方逐字不变)。 */ pendingEngineNotices?: readonly { code: string; message: string; detail?: Record; sessionId?: unknown; }[], /** * 🔴 **S-218:部署方铸的 TRUSTED run internals**(今天唯一成员 `workflowParkedResume`)。 * * 它**不是** `TaskSpec` 字段、不是工具参数、不进请求体 —— 模型永远不能自己决定一次审批。铸点只有 * 一个:`/decide` 的 workflow 第三条车道(判据属主 = `parked-decide.ts` 的 `planWorkflowParkedDecide` * 顶注 + `http/durable-run-leg.ts` 的 `TrustedRunInternals`)。本腿只负责把它摊进**下面那只**共享 * 供给座席,core 的 `prepareTask` 据它给 Workflow 工具挂 `parkedResume` dep。 * * 位置在座席对象的**最后**:座席里那几位(onActivity/onForwardEvent/onTaskNotification/onSubagentSpawn) * 是本腿自己的执行面供给,调用方无权覆盖它们 —— 真要撞名,撞的也只能是本腿自己新加的位,而那时 * `TrustedRunInternals` 的白名单会在编译期先红一次(它只开部署方可以决定的那一格)。 * 缺席 ⇒ 座席与本位存在之前逐字相同。 */ trustedInternals?: Pick): Promise; /** How often a running instance refreshes its run's updated_at (liveness, independent of events). * Must stay strictly below `runStaleSec` (asserted at startup AND on every hot config candidate) or the * reaper would race live runs. [ref]:数值的单源是 `config-invariants.ts`(那条不变量的属主),两个消费面 * ——心跳环与 run-stale 判据——因此不可能各读各的。 */ export declare const HEARTBEAT_MS = 30000; export {}; //# sourceMappingURL=runs.d.ts.map