AgentRuntime:端口、依赖与方法装配

一个会话对应一个 AgentRuntime:它身上的八十多个状态字段、构造时注入的四十九项依赖,近百个方法文件怎样用声明合并加原型安装装到同一个类上,AgentRuntimeInternal 的作用,trace 上下文如何从根一路传到模型请求,以及 core 包对外导出了什么。

作者 David更新于 第 5 篇(共 47 篇)

ZCode 的 Agent 运行时住在 apps/zcode-cli/packages/core/src/runtime:163 个文件、约 3.7 万行,全部围绕一个类 AgentRuntime。终端 TUI、-p 无头模式、桌面端与 Web 背后的协议服务,最终都是拿到一个 AgentRuntime 实例,调它的方法、订阅它的事件。它自己不碰具体实现:文件、子进程、网络、存储与模型大多经“端口”注入,端口的接口声明在 apps/zcode-cli/packages/contracts(src/interfaces 下有 25 个 *.port.ts),实现在 adapters,由 bootstrap 组装。

这一篇只讲这个类本身:一个实例对应什么、身上挂着哪些状态、要哪些依赖、近百个方法文件怎样装到同一个类上,以及 trace 上下文怎么一路传下去。回合怎么跑、输入怎么受理、事件怎么落库,分别见回合循环、输入受理与会话事件流。

目录分工

位置(相对 core/src/runtime)内容
agent-runtime.ts类本体:82 个私有字段、构造器、两个关闭方法,以及同名 interface 声明的公开方法
methods/97 个文件、约 2.56 万行;每个文件导出若干以 this 为首参的函数,由 methods/index.ts 安装到原型上
helpers/53 个文件、约 7900 行;显式接收 runtime 参数或完全无状态的辅助函数
internal.ts、internal-methods.ts、internal-turn-methods.ts、internal-hook-methods.tsAgentRuntimeInternal:给方法文件用的内部视图
types.tsAgentRuntimeConfig、AgentRuntimeDeps 与各方法的入参、出参类型
deps.ts统一依赖出口:从 @zcode/contracts 与 core 其他目录再导出,外加两个 trace 包装函数
execution-state.ts、session-mode-port.ts、model-selection.ts权限模式与 Plan 开关、给工具用的模式端口、模型选择的防御性拷贝
command-queue.ts运行时命令队列,见输入受理
permission-full-access.ts、permission-grant-recovery.ts审批时一键切换完全访问,见权限模式与规则

一个会话,一个实例

构造器签名是 constructor(sessionId, config, deps)(apps/zcode-cli/packages/core/src/runtime/agent-runtime.ts:228)。sessionId 只在构造时赋值一次(agent-runtime.ts:230),类上没有任何改写它的方法,getSessionId() 原样返回它(apps/zcode-cli/packages/core/src/runtime/methods/config.ts:255),所以一个实例从生到死只服务一个会话。bootstrap 里有一段注释把这层关系写得很直白:ZCodeApp、AgentRuntime、sessionId 两两 1:1(apps/zcode-cli/packages/bootstrap/src/app/dynamic-workflow-run-progress-sink.ts:27)。

整个仓库里 new AgentRuntime( 只出现在四处:

位置创建的是事件存储
apps/zcode-cli/packages/bootstrap/src/app/create-app.ts:726每个 ZCodeApp 的主会话宿主传入,缺省新建内存存储(create-app.ts:730)
apps/zcode-cli/packages/core/src/runtime/methods/subagent.ts:239子 Agent 的子会话与父共用(subagent.ts:296)
apps/zcode-cli/packages/bootstrap/src/app/script-workflow-child-runtime.ts:111动态工作流 actor 与旧脚本工作流的子会话与父共用(script-workflow-child-runtime.ts:191)
apps/zcode-cli/packages/bootstrap/src/app/workflow-facade.ts:283专家工作流每个活动的子会话另建一个内存存储(workflow-facade.ts:299)

一个进程里可以同时存在多个实例。TUI 同一时刻只持有一个 ZCodeApp,换会话时新建、再关掉旧的(apps/zcode-cli/packages/cli/src/tui-prompt-handler.ts:63、tui-prompt-handler.ts:123);桌面端与 Web 拉起的协议服务则用一张 Map 管着多条会话记录(apps/zcode-cli/packages/bootstrap/src/zcode-protocol/server.ts:260),每条记录各有自己的事件存储、ZCodeApp 与 runtime(apps/zcode-cli/packages/bootstrap/src/zcode-protocol/server-operations.ts:3300、server-operations.ts:3323),空闲的由常驻池回收,见会话事件流的“会话驻留”一节。

它持有什么

82 个字段全部声明为 private(agent-runtime.ts:133 到 agent-runtime.ts:226),都只在内存里;需要跨进程存活的部分写进 SessionStorePort,冷启动时由 resumeFromStore 读回,见 SQLite 会话库。按用途归纳:

类别代表字段
身份与配置sessionId、turnNumber、config、appVersion、workingDirectory(Bash cd 会改)、workspaceRoot(不随 cd 漂移)、sessionModelSelection
端口与协作对象permissionService、permissionBroker、toolScheduler、registry、executor、hookRunner、modelFactory、executionPort、fileSystemPort、mcpPort、subagentPort 等二十余个
对话与上下文messageHistory、readFileState、cachedTools、contextBuilder、contextInitialized、contextSourceSnapshot、memoryRoot
事件与观测eventReducer、eventStore、eventSinks(一个 Set)、rootTraceContext、logger、agentTelemetry
调度与回合runtimeCommandQueue、runtimeCommandDrainActive、activeForegroundExecution、foregroundPromotionLease、activeTurn、activeTurnStartReservation、pendingInputReservations、queueAutoDrain
持久化与投影标记sessionPersisted、latestConversationMessageId、latestAssistantTurnId、pendingModelChangeTimeline、sessionTitleGenerationAttempted
计数与保护mainTurnCacheHitAggregate、currentTurnFileChanges、autoCompactConsecutiveFailures、branchGeneration(回退后丢弃旧分支的后台结果)
生命周期mcpStartupPromise、residencyBlockingWorkCount、shuttingDown、backgroundTaskNotificationsSealed

依赖哪些 Port

AgentRuntimeDeps 共 49 项(apps/zcode-cli/packages/core/src/runtime/types.ts:309),只有事件存储 eventStore 与模型工厂 modelFactory 必填(types.ts:314、types.ts:317),其余缺席就意味着对应能力不存在。下表中端口接口所在文件都在 apps/zcode-cli/packages/contracts/src/interfaces/ 下,另注明的除外:

类别依赖接口定义
会话与事件eventStore、eventSink、sessionStore、sessionMailboxPortsession.port.ts:57、session.port.ts:76、session-store.port.ts:1089、session-mailbox.port.ts:12
模型modelFactory、providerRuntimeHeadersPort、modelRequestAdmission、modelCatalogPort、resolveEffectiveModelSelection、modelIoDirtypes.ts:386、types.ts:395(core);apps/zcode-cli/packages/contracts/src/model/index.ts:74;model-catalog.port.ts:29
本地 I/OexecutionPort、fileSystemPort、httpClientPort、imageProcessorPort、pdfDocumentPort、artifactStore、browserControlPortexecution.port.ts:278、file-system.port.ts:280、http-client.port.ts:89、image-processor.port.ts:78、pdf-document.port.ts:41、tool-artifact-store.port.ts:106、browser-control.port.ts:537
上下文与扩展contextSourcePort、skillPort、mcpPort、contextBuilder、memoryRootcontext-source.port.ts:92、apps/zcode-cli/packages/contracts/src/skills/index.ts:108、mcp.port.ts:290
权限与钩子permissionService、permissionBroker、hookRunner、workspaceHookAdmission、workspaceHookSnapshotpermission.port.ts:95;其余是 core 自己的类型
工具装配toolRegistry、toolExecutor、toolScheduler、runtimeTaskRegistrycore 内部类型,缺省在构造器里新建
多 Agent 与工作流subagentPort、coordinatorResponsePort、workflowPort、workflowSubmitPort、workflowSubmitSchema、workflowEscalatePort、dynamicWorkflowRunPort、dynamicWorkflowSnippetPortsubagent.port.ts:113、coordinator-response.port.ts:18、workflow.port.ts:36、workflow-submit.port.ts:45、workflow-escalate.port.ts:47、dynamic-workflow-run.port.ts:600、dynamic-workflow-snippet.port.ts:49
自动化automationPort、offPeakPortautomation.port.ts:43、off-peak.port.ts:47
观测与环境agentTelemetry 及其两项因果配置、logger、traceContext、appVersion、now、isRemoteWorkspaceapps/zcode-cli/packages/contracts/src/telemetry/agent-execution.ts:277

有一部分端口 runtime 自己并不保存,只在构造时转交给工具执行器,例如 httpClientPort、automationPort、offPeakPort、三个工作流端口(apps/zcode-cli/packages/core/src/runtime/helpers/runtime-tools.ts:154)。端口在不在场,也直接决定注册哪些内置工具(runtime-tools.ts:49):

  registerBuiltInTools(runtime.registry, {
    bashTimeoutPolicy: runtime.config.bashTimeoutPolicy,
    includeSkill: Boolean(runtime.skillPort),
    includeAgent: Boolean(runtime.subagentPort),
    includeSendMessage: runtime.subagentPort?.sendMessage !== undefined,
    includeRespondToCoordinator:
      runtime.config.taskType === "subagent_child" && Boolean(deps.coordinatorResponsePort),
    // submit_result 只在注入了 workflowSubmitPort 的 workflow actor 会话注册。以端口存在为门,
    // 与 taskType 无关:workflow actor 是 workflow_child,其 runtimeScope 目前是 "main"。
    includeSubmitResult: Boolean(deps.workflowSubmitPort),
    // ...
    includeWorkflow: Boolean(deps.workflowPort),
    includeAutomation: Boolean(deps.automationPort) && runtime.config.taskType !== "subagent_child",

例外写在注释里:十个动态工作流工具的端口在任何 CLI 里都装配齐全,灰度由宿主决定,所以不看端口,看 config.dynamicWorkflowEnabled(runtime-tools.ts:70);node_repl 与浏览器控制也不因宿主给了 browserControlPort 就暴露,而是看官方插件推导出的 runtimeFeatures(runtime-tools.ts:74)。

端口的边界并不绝对。apps/zcode-cli/AGENTS.md 要求业务模块不直接调用 fs、child_process、process.env 等底层 API(apps/zcode-cli/AGENTS.md:53),但 core/src 里仍有 13 个文件直接 import 了 node:fs(直接起子进程的一个也没有),例如 apps/zcode-cli/packages/core/src/runtime/methods/bash-shell-snapshot.ts:1,多数是 Bash、任务输出与工作流相关的工具,另有浏览器客户端、钩子与子 Agent 的几处。端口的实现从哪来、怎样按配置组装,见下一篇 bootstrap。

近百个方法文件怎样装到一个类上

类文件只有 664 行,因为方法体都不在里面。agent-runtime.ts 先声明一个只有字段和构造器的 class,再声明一个同名的 interface 列出公开方法,两者被 TypeScript 合并成同一个类型;文件末尾一行把实现装上原型(agent-runtime.ts:131):

// oxlint-disable typescript-eslint/no-unsafe-declaration-merging
export class AgentRuntime {
  private sessionId: SessionId;
  // ...
}

export interface AgentRuntime {
  lastPermissionGrantId?: string;
  beginShutdown(): void;
  // ...
  getMode(): CollaborationMode;
  // ...
}

installAgentRuntimeMethods(AgentRuntime);

安装函数就是一长串赋值(apps/zcode-cli/packages/core/src/runtime/methods/index.ts:197):

type AgentRuntimeConstructor = { prototype: object };

export function installAgentRuntimeMethods(ctor: AgentRuntimeConstructor): void {
  const proto = ctor.prototype as Record<string, unknown>;
  proto.updateConfig = updateConfig;
  proto.setExecutionState = setExecutionState;
  proto.grantPermissionFullAccess = grantPermissionFullAccess;
  proto.initializeSessionShellEnvironmentIfNeeded = initializeSessionShellEnvironmentIfNeeded;
  proto.getSessionShellSelection = getSessionShellSelection;
  proto.getMode = getMode;
  // ...
  proto.isProjectMemoryEnabled = isProjectMemoryEnabled;
}

被安装的都是普通函数,用 TypeScript 的 this 参数声明调用者类型,例如 export function getMode(this: AgentRuntimeInternal)(config.ts:86)。一共装了 187 个方法,来自 46 个方法模块和另外两个文件(permission-full-access.ts 与 helpers/project-memory-extraction.ts)。其中 87 个出现在公开 interface 里,再加上类体里的 beginShutdown 与 closeBrowserSession,公开方法共 89 个;另外 100 个只给内部互相调用。方法文件里还有两种不安装的写法:只在模块内用的 this 函数经 .call(this, …) 调用,如 runRuntimeCommand.call(this, firstCommand)(apps/zcode-cli/packages/core/src/runtime/methods/runtime-command-queue.ts:61);显式收 runtime 参数的函数,如 applyRuntimeExecutionState(runtime, input, cause)(apps/zcode-cli/packages/core/src/runtime/execution-state.ts:43)。

为什么这么拆?CLI 的 AGENTS.md 规定单个源文件默认不超过 400 行(apps/zcode-cli/AGENTS.md:12);internal-hook-methods.ts 开头的注释说,它是为了让 internal-methods.ts 不越过 400 行、通过一个名为 runtime-module-boundary 的测试而单独拆出来的(apps/zcode-cli/packages/core/src/runtime/internal-hook-methods.ts:5)。这个测试不在开源仓库里,全仓只有 4 个测试文件,都不在 apps/zcode-cli。规则也没有被严格执行:97 个方法文件里有 22 个超过 400 行,最大的 session-fork.ts 有 1487 行、steering.ts 有 1403 行,types.ts 则干脆关掉了 max-lines 检查(types.ts:2)。

代价是编译器不再替你核对“原型上真的有这个方法”,这正是被关掉的那条 lint 规则要防的事。从代码看,新增一个方法要改三处:方法文件、methods/index.ts 的安装行、内部或公开的方法声明,漏掉安装行只会在运行时以“不是函数”的形式暴露。

AgentRuntimeInternal:内部视图

字段是 private 的,类体之外的函数按理读不到。AgentRuntimeInternal(apps/zcode-cli/packages/core/src/runtime/internal.ts:60)把同样的字段以公开属性重新声明一遍,并继承三组方法声明:AgentRuntimeCoreMethods、AgentRuntimeTurnMethods、AgentRuntimeHookMethods,分别在 internal-methods.ts、internal-turn-methods.ts、internal-hook-methods.ts。方法文件一律把 this 声明成它,于是可以随意读写状态、互相调用内部方法;bootstrap 与协议层拿到的仍是合并后的 AgentRuntime 类型,看不到私有字段,也看不到那 100 个内部方法。

由于同名字段一边是 private、一边是公开属性,两个类型互不兼容,构造器只能先做一次 this as unknown as AgentRuntimeInternal 的双重断言(agent-runtime.ts:229),再调用 createDefaultSubagentPort、startMcpStartup 等内部方法。内部视图比类多出 4 个字段:sessionMailboxPort、permissionFullAccessPending、pendingInputDrains、lastPermissionGrantId(internal.ts:117、internal.ts:136)。后三个是运行中动态挂上的,例如 permissionFullAccessPending 在 apps/zcode-cli/packages/core/src/runtime/permission-full-access.ts:34 置位;sessionMailboxPort 则只有声明、从未被赋值,邮箱端口实际直接从 deps 读(runtime-tools.ts:109)。还有些状态干脆不挂在实例上,而是放进以实例为键的 WeakMap,例如“事务已提交、事件尚未发布”的权限授予(apps/zcode-cli/packages/core/src/runtime/permission-grant-recovery.ts:4)。

构造时做了什么

构造器依次做配置投影、建缺省对象、派生根 trace 与子 logger、保存端口、新建消息历史与命令队列、装配工具,最后提前启动 MCP,主干如下(agent-runtime.ts:228):

  constructor(sessionId: SessionId, config: AgentRuntimeConfig, deps: AgentRuntimeDeps) {
    const runtime = this as unknown as AgentRuntimeInternal;
    this.sessionId = sessionId;
    this.turnNumber = 0;
    // ...
    this.permissionBroker = deps.permissionBroker ?? createDenyPermissionBroker();
    // ...
    this.eventReducer = new EventReducer();
    this.eventStore = deps.eventStore;
    this.sessionStore = deps.sessionStore;
    this.rootTraceContext = deps.traceContext ?? createRootTraceContext({ sessionId });
    // ...
    this.subagentPort = deps.subagentPort ?? runtime.createDefaultSubagentPort(deps);
    // ...
    const tooling = initializeRuntimeTooling(runtime, deps, sessionId);
    this.hookRunner = tooling.hookRunner;
    this.workspaceHookAdmission = deps.workspaceHookAdmission;
    this.executor = tooling.executor;

    this.contextBuilder = deps.contextBuilder ?? null;
    if (this.contextBuilder) {
      runtime.initializeMessageHistoryFromContext(this.contextBuilder, this.rootTraceContext);
      this.contextInitialized = true;
    }
    runtime.startMcpStartup(this.rootTraceContext);
  }

几个值得注意的缺省:

  • 审批缺省拒绝。宿主不注入 permissionBroker 时用 DenyPermissionBroker,任何需要问人的工具调用都会以“No permission client configured”被拒(apps/zcode-cli/packages/core/src/permission/broker.ts:24)。
  • 上下文预算策略被强制改写。AgentRuntimeConfig 允许传 "legacy" 或 "preflight-v1"(types.ts:130),但构造器无论传什么都覆盖成共享默认值(agent-runtime.ts:232),即 "preflight-v1"(packages/shared/src/zcode-protocol/index.ts:1696);注释说 3.12.2 仍接受旧宿主传入 legacy,但运行时、日志与子 Agent 只用 preflight。
  • 模型选择可以缺席。旧会话恢复时可能没有完整选择,构造器不替它挑默认模型(agent-runtime.ts:274),真正开跑时才由 createRuntimeModel 拒绝(apps/zcode-cli/packages/core/src/runtime/methods/runtime-model.ts:21)。
  • MCP 在构造时就开始连。startMcpStartup 立即发起配置里各服务器的连接,并登记为阻止会话回收的在飞工作(apps/zcode-cli/packages/core/src/runtime/methods/mcp.ts:111),首轮回合开始时多半已经连好,细节见 MCP。

trace 上下文怎么传下去

CLI 的 AGENTS.md 对可观测性的要求很硬:所有任务执行都携带可传播的 traceId,它默认对应一次顶层会话的完整任务链,子会话、子 Agent、重试与后台任务都归属同一个 traceId;sessionId、turnId、spanId 等是它之下的结构化子标识(apps/zcode-cli/AGENTS.md:74、apps/zcode-cli/AGENTS.md:75)。上下文对象定义在 apps/zcode-cli/packages/contracts/src/tracing/tracer.ts:14,字段是 traceId、queryId、spanId、parentSpanId、parentId、sessionId、turnId 与自由属性 attributes。派生子上下文沿用 traceId、生成新的 spanId、把父 spanId 记成 parentSpanId,并合并属性(tracer.ts:212):

export function createChildTraceContext(
  parent: TraceContext,
  options: {
    queryId?: QueryId;
    sessionId?: SessionId;
    turnId?: TurnId;
    attributes?: Record<string, string | number | boolean>;
  } = {},
): TraceContext {
  return {
    traceId: parent.traceId,
    queryId: options.queryId ?? parent.queryId,
    spanId: generateSpanId(),
    parentSpanId: parent.spanId,
    parentId: parent.spanId,
    sessionId: options.sessionId ?? parent.sessionId,
    turnId: options.turnId ?? parent.turnId,
    attributes: {
      ...parent.attributes,
      ...options.attributes,
    },
  };
}

deps.ts 在这个函数和 traceContextToLogContext 外面又各包了一层,显式把 queryId 带上(apps/zcode-cli/packages/core/src/runtime/deps.ts:18、deps.ts:34),runtime 内部一律用包装后的版本。一条消息的 trace 链路是这样串起来的:

图表加载中…
  • 根。协议层建会话记录时,如果宿主请求带了 traceId 与 spanId 就沿用,否则新生成一个 UUID(apps/zcode-cli/packages/bootstrap/src/zcode-protocol/server-types.ts:274);进程内的 TUI 由 createZCodeApp 自己新建(create-app.ts:153)。runtime 拿到后存进 rootTraceContext,并用它派生带 module: "core.runtime" 的子 logger(agent-runtime.ts:258、agent-runtime.ts:260)。
  • 回合。受理输入时派生回合上下文,写入新的 turnId、queryId 与属性 turnNumber(apps/zcode-cli/packages/core/src/runtime/methods/prompt-admission.ts:88),整个回合在 runWithContextAsync 里执行(apps/zcode-cli/packages/core/src/runtime/methods/turn.ts:184),底层是 AsyncLocalStorage(tracer.ts:166)。
  • 模型步。每次模型请求再派生一层,带上 providerId、modelId、iteration 与 querySource(apps/zcode-cli/packages/core/src/runtime/methods/turn-model-step.ts:158);标题生成这类旁路请求同样从回合上下文派生。回合中途注入一条 guide 后,后续请求改挂在那条输入的 queryId 上(apps/zcode-cli/packages/core/src/runtime/methods/turn-guide-drain.ts:52)。
  • 隐式取用。工具执行器、钩子运行器发事件时没有显式上下文,一律取 getCurrentTraceContext() ?? runtime.rootTraceContext(runtime-tools.ts:99、runtime-tools.ts:161)。
  • 落到事件与子会话。事件本身只记 traceId 与 turnId(apps/zcode-cli/packages/core/src/runtime/methods/events.ts:69),spanId 这一层只进日志;子 Agent 的 runtime 直接拿请求里的 trace 作为自己的根(subagent.ts:366),与父会话共享 traceId。

执行状态、模式端口与模型选择

执行状态只有两个字段:权限模式 mode(build、edit、yolo 与内部用的 auto;plan 只在读旧格式时接受)和 Plan 开关 planEnabled(packages/shared/src/execution-state.ts:3)。修改统一走 core 的 applyRuntimeExecutionState(apps/zcode-cli/packages/core/src/runtime/execution-state.ts:43):完全访问的切换进行中则拒绝;Plan 与进行中的 Goal 不能同时生效;先把状态写成会话条目 runtime/execution_state,成功后才改内存,最后发 SessionModeChanged 事件,注释写明“保存失败不发布成功快照,也不提前改内存”(runtime/execution-state.ts:42)。

模式端口 createRuntimeSessionModePort(apps/zcode-cli/packages/core/src/runtime/session-mode-port.ts:5)把进出 Plan 模式包装成端口交给工具执行器(runtime-tools.ts:193),Plan 相关工具经它切换状态,不直接碰 runtime 字段;工具一侧的行为见 Todo、提问与 Plan 模式。

模型选择 cloneModelSelection 只做一件事:逐字段拷贝 providerId、modelId 与 options(apps/zcode-cli/packages/core/src/runtime/model-selection.ts:3)。读写 sessionModelSelection 时两头都拷贝(config.ts:94),外部拿到的对象改了也不会串回 runtime。模型怎样按选择创建,见模型适配层。

对外公开的方法

89 个公开方法按用途归纳:

类别个数主要方法
输入与回合10admitPrompt、executeTurn、steerTurn、enqueueDeferredInput、stopActiveForegroundExecution、recordExternalUserPrompt
队列管理10removePendingInputById、reservePendingInputById、editPendingInputById、reorderPendingInput、setQueueAutoDrain、setFollowupMode
配置与模式12updateConfig、setExecutionState、getMode、grantPermissionFullAccess、setSessionModelSelection、emitModelSelected
事件与投影7subscribeEvents、appendEvent、getSessionEventStore、getProjection、isSessionPersisted
会话标题3setCustomSessionTitle、maybeStartSessionTitleGenerationFromExternalInput
工具与权限9getToolRegistry、invalidateToolCache、scheduleTools、executeTools、resolvePermission、createChildClientPorts
目标5recordTargetChanged、continueActiveTargetIfIdle、continueActiveTargetLoop
后台任务与工作流10readBackgroundBashOutput、stopBackgroundTask、sealBackgroundTaskNotifications、startSavedWorkflowRun、amendWorkflowRunSettings
恢复、回退与分叉9resumeFromStore、rewindConversationToMessage、forkStableConversationAtMessage、previewWorkspaceFileRewind
驻留与生命周期5hasActiveOrQueuedTurnWork、hasResidencyBlockingWork、trackResidencyBlockingWork、beginShutdown、closeBrowserSession
其他9getSessionId、getProjectId、getSkillCatalog、generateWorkspaceText、testModelConnectivity、drainMemoryExtractions

createChildClientPorts、getSessionEventStore、notifyExternalChildSessionEvent 三个方法是给“在类外构造子 runtime”的 bootstrap 留的接缝:子会话必须共用父的事件存储,否则 transcript 会是一片空白;审批与请求头端口必须路由回父会话,注释记录了曾经因为身份错位让子代理在首个模型请求前挂起 80 分钟的事故(config.ts:177、config.ts:191、config.ts:216)。

RuntimeFactory 与 core 的导出面

@zcode/core 的 package.json 声明了四个入口:主入口、./repl、./browser-client 与 ./create-workflow-graph-bounds(apps/zcode-cli/packages/core/package.json:8)。主入口把 agent、context、compact、tool、hooks、mcp、subagent、runtime-task、workflow、permission 等子目录整体再导出,运行时部分在 apps/zcode-cli/packages/core/src/index.ts:132 导出 AgentRuntime,另外点名导出几个 bootstrap 也要用的函数,例如 fork 与 dwf 截断共用的消息克隆器,注释解释了为什么不让 bootstrap 再抄一份(core/src/index.ts:135)。

core/src/runtime.ts 是运行时的二级出口,除了 AgentRuntime 与一批类型,还声明了一个工厂接口 RuntimeFactory,只有一个方法 create(config),返回 Promise<AgentRuntime>(apps/zcode-cli/packages/core/src/runtime.ts:38)。它经 index.ts 以类型导出,但在整个仓库里找不到任何实现或使用方,四处创建实例都直接 new AgentRuntime(...)。从签名看它也装不下今天的构造器:只收 config,既没有 sessionId 也没有 deps,更像早期设计留下的契约。

下一篇:bootstrap:把运行时拼起来——这四十九项依赖从哪里来:配置分层、数据目录、模型工厂,以及会话怎样创建与恢复。

本页目录