import type { RuntimeActivity, RuntimeMetadata } from "./runtime-activity.js"; import type { RuntimePrompt } from "./runtime-prompt.js"; /** * Course-native 协议与 runtime 契约(spec: docs/specs/lightweight-course-daemon-package.md)。 * * 本包不依赖 BotCord Hub/room/owner-chat 语义;wire 类型与后端 * `services/course-api/botlearn_course/schemas.py` 的 daemon 契约严格对齐。 */ import type { ProgressStatus } from "./mcp/report-progress.js"; import type { PreparedRuntimeSkillProvider, RuntimeSkillEvent } from "./runtime-skills.js"; export interface RunInputAttachment { attachment_id: string; filename: string; content_type: string; size_bytes: number; sha256: string; } export interface MaterializedInputAttachment extends RunInputAttachment { workspace_path: string; } /** Agent Service `turn.start` 下发的 run.start 载荷(snake_case,与后端 RunStartPayloadOut 一致)。 */ export interface RunStartPayload { runtime_prompt?: RuntimePrompt; agent_run_id: string; trace_id?: string; course_run_id: string; lesson_id: string | null; task_id: string | null; agent_instance_id: string | null; runtime: { id: string; model?: string; reasoning_effort?: string; model_contract?: { adapter: string; runtime_model: string; context_window: number; max_output_tokens: number; max_output_tokens_limit: number; reasoning_efforts: string[]; }; [key: string]: unknown; }; input: { kind?: string; text?: string; locale?: string; attachments?: RunInputAttachment[]; }; context: { metadata?: Record; [key: string]: unknown; }; limits: { timeout_seconds?: number; max_output_chars?: number; max_tool_calls?: number; [key: string]: unknown; }; } /** * 经 Agent Service `turn.event` 上报的 run 事件类型。 * 必须与 Course Service 接受的事件类型一致,发送其他类型会被拒绝。 */ export type RunEventType = "run.accepted" | "run.started" | "run.block" | "run.observation" | "run.trace" | "run.message" | "run.completed" | "run.failed" | "run.cancelled"; export interface RunEvent { type: RunEventType; /** End-to-end correlation id assigned by Course Service. */ trace_id?: string; /** Stable across retries; Course Service deduplicates within the current worker attempt. */ event_id?: string; /** 1-based 单调递增。后端把 0/缺省视为「未设置」并自行计算,所以客户端 seq 必须从 1 开始。 */ seq?: number; role?: "assistant" | "user" | "system"; text?: string; error?: string; payload?: Record; } export interface RunEventReceipt { disposition: "accepted" | "retry"; candidate_attempt?: number; retry_feedback?: string; } /** 经 Agent Service 上报的文件候选。 */ export interface RunFileCandidate { event?: "created" | "modified" | "deleted"; /** workspace 相对路径,正斜杠;不得包含 `..`、绝对路径或反斜杠(后端 400)。 */ path: string; name?: string; mime_type?: string; size_bytes?: number; sha256?: string; preview_text?: string; } export interface RunFileRecord extends RunFileCandidate { id: string; agent_run_id: string; storage_url?: string | null; status: "candidate" | "saved" | "ignored" | "failed"; } /** runtime adapter 产出的普通归一化块;raw 仅进本地 transcript。 */ export interface RuntimeContentBlock { activity?: RuntimeActivity; kind: "text_delta" | "text" | "thinking" | "tool_call" | "tool_result" | "status" | "error"; text?: string; /** * Safe provider-normalized tool identifier. Raw envelopes stay local; the dispatcher may * derive a separate bounded/redacted teacher observation from explicit operation fields. */ name?: string; /** Public lifecycle metadata; never carries provider reasoning or tool output. */ phase?: "in_progress" | "completed"; status?: "completed" | "error"; raw?: unknown; } /** 已由 provider adapter 严格归一化、无 provider raw envelope 的进度遥测。 */ export interface RuntimeProgressBlock { kind: "progress"; runtime: string; summary: string; status: ProgressStatus; raw?: never; } export type RuntimeBlock = RuntimeContentBlock | RuntimeProgressBlock; /** runtime adapter 在发出进度块前丢弃的非内容处置计数。 */ export interface RuntimeProgressDispositions { invalid: number; deduplicated: number; over_limit: number; } /** Content-free model usage; adapters populate only fields actually reported upstream. */ export interface RuntimeUsage { /** Model identity resolved by the runtime/provider, not inferred by the daemon. */ model?: string; input_tokens?: number; cached_input_tokens?: number; output_tokens?: number; reasoning_tokens?: number; total_tokens?: number; cost_usd?: number; provider_request_ids?: string[]; } /** Content-free semantic reason for a runtime ending the current AgentRun successfully. */ export type RuntimeCompletionReason = "learner_input_requested"; /** Content-free shape of one runtime-native completion. */ export interface RuntimeCompletionSummary { assistant_message_count: number; reasoning_message_count: number; assistant_content_present: boolean; reasoning_content_present: boolean; tool_call_count: number; turn_status?: string; finish_reason?: string; } /** runtime 安装/版本探测结果。 */ export interface RuntimeProbe { available: boolean; path?: string; version?: string; } /** runtime 执行期间向 dispatcher 汇报的通道。adapter 不直接触碰 Course API。 */ export interface CourseRuntimeSink { runtimeMetadata?(metadata: RuntimeMetadata): Promise; block(block: RuntimeBlock): Promise; message(text: string): Promise; file(file: RunFileCandidate): Promise; /** 可选的 run-scoped 内部遥测;不得包含 summary 或 provider raw envelope。 */ progressDispositions?(dispositions: RuntimeProgressDispositions): Promise; /** Provider usage only; must never include prompts, model output, or raw envelopes. */ usage?(usage: RuntimeUsage): Promise; /** Persist the runtime-native thread/session id before a terminal turn event is emitted. */ runtimeSession?(sessionId: string): Promise; /** Optional semantic completion reason; never carries provider tool names or learner content. */ completionReason?(reason: RuntimeCompletionReason): Promise; /** Content-free technical evidence from the control-side Runtime Skill Provider. */ skillEvent?(event: RuntimeSkillEvent): Promise; } /** 一次 run 的本地执行上下文:服务器 payload + daemon 本地准备产物。 */ export interface RunExecution { payload: RunStartPayload; /** Runtime cwd. Managed persistent sessions reuse one session-scoped workspace. */ workspaceDir: string; /** Daemon-owned state directory for one managed RuntimeSession; never exposed to the model. */ runtimeStateDir?: string; /** Runtime-native thread/session id to resume; null creates the first native session. */ nativeSessionId?: string | null; /** Monotonic Course Service context revision accepted for this turn. */ contextRevision?: number; /** Scoped runtime-only environment (for example a short-lived model proxy grant). */ runtimeEnv?: NodeJS.ProcessEnv; /** Verified learner files materialized beneath the runtime workspace. */ inputAttachments?: MaterializedInputAttachment[]; /** Activation-scoped provider held by the daemon control process, never runtime env. */ skillProvider?: PreparedRuntimeSkillProvider; } export interface CourseRuntime { id: string; probe?(): Promise; /** * 执行一次课程任务。最终回答走 `sink.message`;中间块走 `sink.block`。 * 失败必须 reject(由 dispatcher 归一化为 run.failed);必须尊重 `signal`。 */ run(run: RunExecution, sink: CourseRuntimeSink, signal: AbortSignal): Promise; } /** runtime 执行失败(dispatcher 折叠为 run.failed)。 */ export declare class RuntimeExecutionError extends Error { readonly errorType: "runtime_error" | "runtime_unavailable" | "timeout" | "workspace_user_quota_exceeded"; readonly failure?: Partial | undefined; constructor(message: string, errorType?: "runtime_error" | "runtime_unavailable" | "timeout" | "workspace_user_quota_exceeded", failure?: Partial | undefined); } /** * 本地诊断用的失败摘要。完整结构只进脱敏日志/transcript;wire 仅允许 * run-dispatcher 构造的 failure_diagnostic 白名单子集。 */ export interface RuntimeFailureSummary { agent_run_id: string; runtime: string; cwd?: string; command?: string[]; exit_code?: number | null; signal?: string | null; duration_ms?: number; stderr_tail?: string; stdout_tail?: string; error_name?: string; error_code?: string; error_message?: string; model?: string; completion?: RuntimeCompletionSummary; } export interface RuntimeModule { id: string; displayName: string; /** PATH 上的规范二进制名。 */ binary: string; /** 覆盖二进制路径的环境变量;缺省为 `BOTLEARN__BIN`(id 大写、`-`→`_`)。 */ envVar?: string; /** 快速探测(安装/版本)。必须廉价,不得触发真实模型调用。 */ probe(): Promise; create(): CourseRuntime; /** probe 不可用时的安装提示。 */ installHint?: string; /** 仅测试/demo 用(如 fake),除非显式启用否则不注册。 */ hidden?: boolean; }