CODING AGENT HARNESS · SOURCE AUDITREPORT 06 / 18
06

MonkeyCode

它是把多种 CLI 放入远程 VM 的控制平面;Agent 智能来自所选 CLI,平台负责隔离、凭据与协作。

Go/TS · Remote Agent Control PlaneAGPLmain
SOURCE
VERIFIED
Repository
chaitin/MonkeyCode
Commit
ddc3794fc31e95b58b1493ab99e96844640bdf40
Commit date
2026-07-27T20:39:07+08:00
Findings
21
Citations
61
Tracked files
1,799
EXECUTIVE READING

先给结论,再进入源码

核心机制

DB 预登记 → VM → Redis 交接 → 选定 CLI

上下文

只知道窗口上限;压缩和 memory 交给内层 Agent

安全边界

外层 VM 是主边界;内层 Codex sandbox 显式关闭

适用建设

多人远程 Coding Agent SaaS、隔离租户、统一审计

值得借鉴

  • 真实 VM 租户隔离
  • 上游凭据不下发给任务
  • 多 CLI 控制平面

需要警惕

  • Taskflow 失败吞掉可滞留 processing
  • 安全语义继承内层 CLI
  • 平台不治理子 Agent

直接带走

  • 任务临时 LLM 密钥
  • owner write gate
  • 资源三层覆盖与 zip-slip 防护
00 · METHOD

研究口径:先锁提交,再沿运行链读代码

README POLICY

README 只用于识别产品定位;结论来自 Go 控制面、Taskflow 协议、CLI 配置模板、MCP Hub、网络守卫、遥测与测试。

FACT POLICY

严格区分 MonkeyCode 自己实现的云控制平面、它下发给 Codex/Claude/OpenCode 的配置,以及仓库外 codingmatrix/Taskflow 实际执行器。

INFERENCE POLICY

外部 VM 的隔离强度、所选 CLI 的上下文压缩和子 Agent 能力不从接口名推断;无法由本仓库闭环证明的部分标为 partial 或 limitation。

L1运行实现
L2接口契约
L3测试证明
L4文档佐证
L5明确推断

本页引用 20 个不同源码/测试文件;证据角色分布:契约 14 · 实现 41 · 配置 5 · 测试 1。代码块是固定提交中的原文截取,长区间仅在中部折叠,首尾行号保持真实。

01 · TECHNICAL MAPS

架构总图与单轮执行链路

两张图均由本页证据账本生成,并通过 Archify showcase 9 项校验(0 error / 0 warning)。图可单独打开、搜索、缩放和追踪关系。

FIGURE 01MonkeyCode Harness 架构图全屏打开 ↗
FIGURE 02用户输入到工具回写的技术链路全屏打开 ↗
02 · COVERAGE MAP

审计维度与证据等级

入口、会话与主循环 verified L1 / L2

确认 MonkeyCode 编排 Codex/Claude/OpenCode,内层 Agent loop 由所选 CLI 提供。

Provider、流式与重试 verified L1 / L2 / L3

已审计三类协议代理、运行时密钥、模型匹配、流式 usage 捕获与 OpenCode 切模。

上下文、压缩与记忆 partial L1 / L2

平台保存轮次日志并生成 UI 摘要;内层 context/compaction 由所选 CLI 负责。

工具分发与结果治理 verified L1 / L2 / L3

已审计 MCP Hub 工具可见性、任务绑定、幂等调用、审计和上游转发。

执行环境与沙箱 partial L1 / L2

可证平台申请独立 VirtualMachine;底层 VM enforcement 不在仓库,且 Codex 内层 sandbox 被显式关闭。

权限与安全 verified L1 / L2 / L3

已审计 owner write gate、auto-approve、LLM proxy allowlist、SSRF guard 与资源包安全。

指令与 Prompt verified L1 / L2

已审计 system prompt hook、rules、skills、plugins 和各 CLI 模板。

工具、连接器与插件 verified L1 / L2 / L3

已审计 builtin/platform MCP、scope 合并、OpenCode plugin 注入和 zip 资源传递。

子 Agent 与协作 partial L1 / L2

有多人只读/可写远程会话与问答;平台没有可见的独立子 Agent 调度控制面。

持久化与观测 verified L1 / L2 / L3

已审计 WebSocket replay、Loki/ClickHouse 轮次、usage、OTLP 与字段消毒。

测试、评测与成熟度 partial L2 / L3

安全和数据路径测试较多;未发现针对 Coding Agent 成功率的端到端 eval harness。

01
DIMENSION · ENTRY-SESSION-LOOP

入口、会话与主循环

本章共 3 个可定位结论;结论按“实现事实 → 白话解释 → 工程影响 → 源码摘录”展开。

01
L1事实monkey-architecture-001

它是多 CLI 的任务控制平面,不是第四套 Agent loop

源码事实

Taskflow 协议把 CodingAgent 枚举为 Codex、Claude、MCAIReview、OpenCode;任务请求携带 system prompt、模型、配置文件、MCP 与资源,真正的推理—工具循环交给所选 CLI。

白话解释

MonkeyCode 更像机场塔台:它决定哪架飞机、在哪个跑道、带什么配置起飞,但不会替 Codex 或 Claude 亲自驾驶。

对自研 Harness 的含义

比较 Harness 时,应把平台编排能力与各 CLI 内核能力拆开计分。

关键源码 · 契约
backend/pkg/taskflow/types.go · L554–L587
  554  // ==================== CreateTask 类型 ====================
  555  
  556  // CodingAgent 编码代理类型
  557  type CodingAgent int
  558  
  559  const (
  560  	CodingAgentCodex CodingAgent = iota + 1
  561  	CodingAgentClaude
  562  	CodingAgentMCAIReview
  563  	CodingAgentOpenCode
  564  )
  565  
  566  // LLM 模型配置
  567  type LLM struct {
  568  	ApiKey      string   `json:"api_key"`
  569  	BaseURL     string   `json:"base_url"`
  570  	Model       string   `json:"model"`
  571  	ApiType     string   `json:"api_type,omitempty"` // 接口类型 anthropic | openai
  572  	Temperature *float32 `json:"temperature,omitempty"`
  573  }
  574  
  575  // ConfigFile 配置文件
  576  type ConfigFile struct {
  577  	Path    string  `json:"path"`
  578  	Content string  `json:"content"`
  579  	Mode    *uint32 `json:"mode,omitempty"`
  580  }
  581  
  582  // TaskExecutionConfig 任务运行配置
  583  type TaskExecutionConfig struct {
  584  	Envs           map[string]string `json:"envs,omitempty"`
  585  	ConfigFiles    []ConfigFile      `json:"config_files,omitempty"`
  586  	McpServers     []McpServerConfig `json:"mcp_servers,omitempty"`
  587  	AgentResources *AgentResources   `json:"agent_resources,omitempty"`
查看全部 3 处证据
  • 契约 backend/pkg/taskflow/types.go:554–587 CodingAgent 枚举和执行配置。
  • 契约 backend/pkg/taskflow/types.go:626–640 完整任务下发协议。
  • 实现 backend/biz/task/usecase/task.go:875–928 按 CLI 渲染 Claude/Codex/OpenCode 配置。
02
L1事实monkey-lifecycle-001

任务创建拆成数据库预登记、VM 创建、Redis 交接、运行态启动

源码事实

Create 先校验 host/model/concurrency 并预创建任务记录,再请求 2 核 8GiB VM,将完整 CreateTaskReq 以 TTL 写入 Redis,最后切到 pending;VM ready hook 取出请求并调用 TaskManager.Create。

白话解释

先把工单和工作间登记好,再等工作间真的上线,最后才把任务交给里面的 Agent。

对自研 Harness 的含义

分阶段便于恢复和审计,但 Redis 交接键成为启动链路的关键依赖。

关键源码 · 实现
backend/biz/task/usecase/task.go · L556–L617
  556  	limit, err := a.resolveTaskConcurrencyLimit(ctx, user.ID)
  557  	if err != nil {
  558  		return nil, err
  559  	}
  560  	ctx = entx.WithTaskConcurrencyLimit(ctx, limit)
  561  
  562  	vmID := fmt.Sprintf("agent_%s", uuid.NewString())
  563  	prepared, err := a.repo.PrepareCreate(ctx, user, req, token, vmID)
  564  	if err != nil {
  565  		a.logger.With("error", err, "req", req).ErrorContext(ctx, "failed to create task")
  566  		return nil, err
  567  	}
  568  	if prepared == nil || prepared.ProjectTask == nil || prepared.Model == nil || prepared.Image == nil {
  569  		return nil, fmt.Errorf("failed to prepare task")
  570  	}
  571  	pt := prepared.ProjectTask
  572  	m := prepared.Model
  573  	i := prepared.Image
  574  	t := pt.Edges.Task
  575  	if t == nil {
  576  		return nil, fmt.Errorf("task edge is nil")
  577  	}
  578  	if git.URL == "" {
  579  		git.URL = pt.RepoURL
      … 28 lines omitted; exact range 556–617 …
  608  			Provider: taskflow.LlmProviderOpenAI,
  609  			ApiKey:   m.APIKey,
  610  			BaseURL:  m.BaseURL,
  611  			Model:    m.Model,
  612  		},
  613  		Cores:    "2",
  614  		Memory:   8 << 30,
  615  		Envs:     env,
  616  		LogStore: normalizeTaskLogStore(t.LogStore),
  617  	})
查看全部 3 处证据
  • 实现 backend/biz/task/usecase/task.go:556–617 并发门、预登记与 VM 请求。
  • 实现 backend/biz/task/usecase/task.go:622–687 VM ID 校验、Redis handoff 与 pending transition。
  • 实现 backend/pkg/lifecycle/taskhook.go:92–130 VM ready 后消费 CreateTaskReq。
03
L1风险monkey-lifecycle-002

Taskflow Create 失败被记录但吞掉,任务可能滞留 processing

源码事实

handleProcessing 在先写入 processing 后调用 TaskManager.Create;若调用失败只写 error log,不 return err,外层 withError 无法把任务转入 error。

白话解释

工单已经盖了“处理中”,但真正开工失败后只记了一条日志,状态机可能还以为工作在继续。

对自研 Harness 的含义

应把 Create 错误返回给生命周期管理器,并为 processing-without-session 增加 watchdog。

关键源码 · 实现
backend/pkg/lifecycle/taskhook.go · L104–L123
  104  		reqKey := fmt.Sprintf("task:create_req:%s", id.String())
  105  		val, err := h.redis.Get(ctx, reqKey).Result()
  106  		if err != nil {
  107  			h.logger.With("task_id", id, "error", err).ErrorContext(ctx, "failed to get CreateTaskReq from redis")
  108  			return fmt.Errorf("failed to get CreateTaskReq from Redis: %w", err)
  109  		}
  110  
  111  		defer h.redis.Del(ctx, reqKey)
  112  
  113  		if err := h.repo.Update(ctx, &domain.User{}, id, func(up *db.TaskUpdateOne) error {
  114  			up.SetStatus(consts.TaskStatusProcessing)
  115  			return nil
  116  		}); err != nil {
  117  			return fmt.Errorf("failed to update task status: %w", err)
  118  		}
  119  
  120  		var createReq taskflow.CreateTaskReq
  121  		if err := json.Unmarshal([]byte(val), &createReq); err != nil {
  122  			h.logger.With("task_id", id, "error", err).ErrorContext(ctx, "failed to unmarshal CreateTaskReq")
  123  			return fmt.Errorf("failed to unmarshal CreateTaskReq: %w", err)
查看全部 2 处证据
  • 实现 backend/pkg/lifecycle/taskhook.go:104–123 读取请求并先更新 processing。
  • 实现 backend/pkg/lifecycle/taskhook.go:129–136 Create 错误仅日志化后返回 nil。
02
DIMENSION · EXECUTION-SANDBOX

执行环境与沙箱

本章共 1 个可定位结论;结论按“实现事实 → 白话解释 → 工程影响 → 源码摘录”展开。

04
L1限制monkey-sandbox-001

隔离边界主要依赖仓库外 VM,Codex 内层 sandbox 明确关闭

源码事实

平台协议申请带 host、image、CPU、memory、git 和 TTL 的 VirtualMachine;但 Codex 模板设置 sandbox_mode=danger-full-access,并将 /workspace 标为 trusted。仓库内只有 VM API 合同,没有底层 namespace/hypervisor enforcement。

白话解释

Codex 在房间里面拿的是万能钥匙;安全取决于这个“房间”到底是不是一间真正隔离的 VM,而造房间的代码不在本仓库。

对自研 Harness 的含义

部署评审必须联审 codingmatrix/Taskflow 执行器,不能因类型名叫 VirtualMachine 就自动认定强隔离。

关键源码 · 契约
backend/pkg/taskflow/types.go · L72–L92
   72  // VirtualMachine 虚拟机信息
   73  type VirtualMachine struct {
   74  	ID            string               `json:"id"`
   75  	AccessToken   string               `json:"access_token,omitempty"`
   76  	EnvironmentID string               `json:"environment_id"`
   77  	HostID        string               `json:"host_id"`
   78  	Hostname      string               `json:"hostname"`
   79  	Arch          string               `json:"arch"`
   80  	OS            string               `json:"os"`
   81  	Name          string               `json:"name"`
   82  	Repository    string               `json:"repository"`
   83  	Status        VirtualMachineStatus `json:"status"`
   84  	StatusMessage string               `json:"status_message"`
   85  	Cores         int32                `json:"cores"`
   86  	Memory        uint64               `json:"memory"`
   87  	Disk          uint64               `json:"disk"`
   88  	TTL           TTL                  `json:"ttl"`
   89  	ExternalIP    string               `json:"external_ip"`
   90  	CreatedAt     int64                `json:"created_at"`
   91  	Version       string               `json:"version"`
   92  }
查看全部 3 处证据
  • 契约 backend/pkg/taskflow/types.go:72–92 VirtualMachine 对外状态和资源合同。
  • 契约 backend/pkg/taskflow/types.go:122–139 VM 创建请求中的镜像、资源和环境。
  • 配置 backend/templates/codex.tmpl:1–13 Codex danger-full-access 与 trusted workspace。
03
DIMENSION · SUBAGENTS-COLLABORATION

子 Agent 与协作

本章共 2 个可定位结论;结论按“实现事实 → 白话解释 → 工程影响 → 源码摘录”展开。

05
L1事实monkey-remote-001

远程协作以 owner write gate、历史回放和实时流为核心

源码事实

WebSocket stream 根据任务 owner 决定 writable;非 owner 的上行消息被丢弃。attach 模式先订阅并回放历史,再消费实时事件;用户可继续输入、停止、取消当前操作、切 auto-approve 和回答 Agent 问题。

白话解释

旁观者能看直播,任务主人才能按按钮和继续对话;掉线重连后还能从历史接上。

对自研 Harness 的含义

它实现了人—Agent 远程接管,而不是多 Agent 共享计划或黑板。

关键源码 · 契约
backend/biz/task/handler/v1/task.go · L323–L384
  323  // Stream 任务数据流 WebSocket
  324  //
  325  //	@Summary		任务数据流 WebSocket
  326  //	@Description	功能定位:该接口通过 WebSocket 转发任务运行数据。任务对话继续输入使用 `type=user-input`。
  327  //	@Description	数据格式约定:当前仅支持文本帧透传。服务端将 Agent 的原始文本数据包装为如下结构返回给前端(对应 domain.TaskStream):
  328  //	@Description	```json
  329  //	@Description	{ "type": "string", "data": "string", "kind": "string", "timestamp": 0 }
  330  //	@Description	```
  331  //	@Description	user-input 上行新格式:
  332  //	@Description	```json
  333  //	@Description	{ "type": "user-input", "data": "{\"content\":\"57un57ut5aSE55CG6L+Z5Liq6Zeu6aKY\",\"attachments\":[{\"url\":\"https://example-bucket.oss-cn-hangzhou.aliyuncs.com/temp/a.txt\",\"filename\":\"a.txt\"}]}" }
  334  //	@Description	```
  335  //	@Description	user-input 上行旧格式仍兼容:
  336  //	@Description	```json
  337  //	@Description	{ "type": "user-input", "data": "继续处理这个问题" }
  338  //	@Description	```
  339  //	@Description	user-input 下行和历史返回统一使用新 JSON payload 字符串:
  340  //	@Description	```json
  341  //	@Description	{ "type": "user-input", "data": "{\"content\":\"57un57ut5aSE55CG6L+Z5Liq6Zeu6aKY\",\"attachments\":[]}", "timestamp": 0 }
  342  //	@Description	```
  343  //	@Description	`attachments` 为可选附件列表,最多 10 个;每项包含 `url` 和 `filename`,URL 需要匹配后端配置的附件白名单前缀。
  344  //	@Description	type 字段说明:
  345  //	@Description	- task-started: 本轮任务启动
  346  //	@Description	- task-ended: 本轮任务结束
      … 28 lines omitted; exact range 323–384 …
  375  	user := middleware.GetUser(c)
  376  	task, owner, err := h.usecase.Info(c.Request().Context(), user, req.ID)
  377  	if err != nil {
  378  		return err
  379  	}
  380  
  381  	if req.Mode == "" {
  382  		req.Mode = "new"
  383  	}
  384  	return h.stream(c, user, task, owner, req.Mode)
查看全部 3 处证据
  • 契约 backend/biz/task/handler/v1/task.go:323–384 流事件、权限与模式协议。
  • 实现 backend/biz/task/handler/v1/task.go:387–423 owner writable 与 attach 流程。
  • 实现 backend/biz/task/handler/v1/task.go:640–721 只读 gate 与用户控制路由。
06
L2限制monkey-subagent-001

平台层没有可见的子 Agent 调度与独立治理实体

源码事实

Taskflow CreateTaskReq 只有单一 CodingAgent、单 VM、单 task/session;WebSocket 和 MCP 审计也都按同一 task/VM 归因。仓库未定义 child task、parent task、handoff 或 child capability 合同。

白话解释

如果 Codex/OpenCode 内部自己再派子 Agent,MonkeyCode 目前只会把它们看成同一间 VM 里的同一项任务,无法逐个授权、暂停和计费。

对自研 Harness 的含义

多 Agent 能力最多继承 runtime,本平台尚未形成可观测、可治理的协作控制面。

关键源码 · 契约
backend/pkg/taskflow/types.go · L626–L640
  626  // CreateTaskReq 创建任务请求
  627  type CreateTaskReq struct {
  628  	ID             uuid.UUID         `json:"id"`
  629  	VMID           string            `json:"vm_id"`
  630  	SystemPrompt   string            `json:"system_prompt,omitempty"`
  631  	Text           string            `json:"text,omitempty"`
  632  	Attachments    []Attachment      `json:"attachments,omitempty"`
  633  	LLM            LLM               `json:"llm,omitzero"`
  634  	CodingAgent    CodingAgent       `json:"coding_agent,omitempty"`
  635  	Configs        []ConfigFile      `json:"configs,omitzero"`
  636  	McpConfigs     []McpServerConfig `json:"mcp_configs,omitzero"`
  637  	Env            map[string]string `json:"env,omitempty"`
  638  	LogStore       string            `json:"log_store,omitempty"`
  639  	AgentResources *AgentResources   `json:"agent_resources,omitempty"` // skill/plugin presigned URLs + rule content forwarded to codingmatrix agent
  640  }
查看全部 3 处证据
  • 契约 backend/pkg/taskflow/types.go:626–640 单 Agent/VM 任务合同。
  • 契约 backend/biz/mcphub/auth/service.go:22–28 工具主体只有 user/task/model。
  • 实现 backend/biz/task/handler/v1/task.go:585–610 实时事件按单 task stream 归因。
04
DIMENSION · PERMISSIONS-SECURITY

权限与安全

本章共 3 个可定位结论;结论按“实现事实 → 白话解释 → 工程影响 → 源码摘录”展开。

07
L1事实monkey-permission-001

平台只转发 auto-approve,具体权限语义继承所选 CLI

源码事实

AutoApprove API 只把 task ID 和布尔值传给 Taskflow;Codex 模板使用 approval_policy=untrusted,OpenCode 模板却允许 doom_loop、外部目录和 .env 读取。

白话解释

平台提供“自动点同意”的总开关,但什么动作需要同意、允许后能做多大,仍由里面那套 CLI 决定。

对自研 Harness 的含义

统一 UI 不等于统一安全语义,需要为每个 runtime 建立可比较的 capability policy。

关键源码 · 实现
backend/biz/task/usecase/task.go · L157–L163
  157  // AutoApprove implements domain.TaskUsecase.
  158  func (a *TaskUsecase) AutoApprove(ctx context.Context, _ *domain.User, id uuid.UUID, approve bool) error {
  159  	return a.taskflow.TaskManager().AutoApprove(ctx, taskflow.TaskApproveReq{
  160  		ID:          id,
  161  		AutoApprove: &approve,
  162  	})
  163  }
查看全部 3 处证据
  • 实现 backend/biz/task/usecase/task.go:157–163 auto-approve 纯转发。
  • 配置 backend/templates/codex.tmpl:1–6 Codex approval 与 sandbox 配置。
  • 配置 backend/templates/opencode.tmpl:40–50 OpenCode 指令、权限和技能路径。
08
L1事实monkey-resource-003

zip 资源有文件数、单文件、总量和 zip-slip 双重校验

源码事实

默认限制 1000 文件、单文件 32MiB、总解压 256MiB;先查声明尺寸,再用 LimitReader 验证实际尺寸,并拒绝绝对路径、反斜线和 .. 逃逸。

白话解释

插件压缩包不能假装很小再突然炸开,也不能用 ../../ 偷写 VM 里的其他路径。

对自研 Harness 的含义

插件供应链仍需签名/哈希治理,但基础解包攻击面处理得较完整。

关键源码 · 契约
backend/biz/agentresource/unpack.go · L12–L31
   12  // UnzipLimits guards the in-memory unzipper against zip bombs and overly
   13  // large archives. All limits are inclusive of the value (size == limit is OK).
   14  type UnzipLimits struct {
   15  	MaxFileSize  int64 // per-entry uncompressed size
   16  	MaxTotalSize int64 // sum of all entries' uncompressed sizes
   17  	MaxFiles     int   // max number of file entries
   18  }
   19  
   20  // DefaultUnzipLimits is the policy used by the Resolver.
   21  var DefaultUnzipLimits = UnzipLimits{
   22  	MaxFileSize:  32 << 20,  // 32 MiB
   23  	MaxTotalSize: 256 << 20, // 256 MiB
   24  	MaxFiles:     1000,
   25  }
   26  
   27  // unzipToMemory reads a zip archive entirely from memory and returns its
   28  // regular file entries. Directories are skipped. Paths are validated to
   29  // reject zip-slip ("../..") and absolute paths. Entries that exceed the
   30  // limits cause the entire archive to be rejected so callers can fall back
   31  // to skipping the whole asset (matching the Resolver's per-skill policy).
查看全部 3 处证据
  • 契约 backend/biz/agentresource/unpack.go:12–31 默认解包限额和策略。
  • 实现 backend/biz/agentresource/unpack.go:38–98 声明与实际尺寸双检。
  • 实现 backend/biz/agentresource/unpack.go:101–126 zip-slip 与路径校验。
09
L1风险monkey-security-001

SSRF guard 能防 DNS rebinding,但私有化示例默认关闭

源码事实

netguard 校验 http/https、特殊 IPv4 写法、localhost/private/metadata IP,并在 dial 时使用已验证 IP;测试覆盖十进制、八进制、十六进制、IPv6 和私有代理。然而示例配置明确为 SaaS 开启、私有化默认 false。

白话解释

防护能力本身很认真,但自建用户照示例部署时默认不会打开这扇防火门。

对自研 Harness 的含义

需要按部署信任边界决定默认值,并在关闭时明确告警。

关键源码 · 实现
backend/pkg/netguard/guard.go · L53–L69
   53  func (g *Guard) ValidateURL(ctx context.Context, rawURL string) error {
   54  	u, err := url.Parse(strings.TrimSpace(rawURL))
   55  	if err != nil {
   56  		return fmt.Errorf("parse url: %w", err)
   57  	}
   58  	if u.Scheme != "http" && u.Scheme != "https" {
   59  		return fmt.Errorf("unsupported scheme %q", u.Scheme)
   60  	}
   61  	if u.Hostname() == "" {
   62  		return errors.New("empty host")
   63  	}
   64  	if !g.Enabled() {
   65  		return nil
   66  	}
   67  	_, err = g.resolveHost(ctx, u.Hostname())
   68  	return err
   69  }
查看全部 4 处证据
  • 实现 backend/pkg/netguard/guard.go:53–69 URL 与启用门。
  • 实现 backend/pkg/netguard/guard.go:143–179 请求和 dial 阶段防重绑定。
  • 测试 backend/pkg/netguard/guard_test.go:22–59 私网和特殊 IP 测试。
  • 配置 backend/config/server/config.yaml.example:7–10 私有化默认关闭。
05
DIMENSION · PROVIDER-STREAMING

Provider、流式与重试

本章共 3 个可定位结论;结论按“实现事实 → 白话解释 → 工程影响 → 源码摘录”展开。

10
L1事实monkey-provider-001

LLM proxy 用 VM 绑定临时密钥隐藏真实上游凭据

源码事实

任务拿到 ModelApiKey/runtime token 和平台 proxy URL;proxy 再从数据库解析 user、VM、provider、真实 BaseURL/API key,重写 Authorization/X-Api-Key 后访问上游。

白话解释

工作 VM 拿的是代金券,不是模型厂商的保险柜钥匙;平台看到券后再替它换成真正凭据。

对自研 Harness 的含义

可撤销密钥、统一用量和上游切换都集中在控制面。

关键源码 · 实现
backend/biz/task/usecase/task.go · L585–L590
  585  	var runtimeToken string
  586  	if keys := m.Edges.Apikeys; len(keys) > 0 {
  587  		m.APIKey = keys[0].APIKey
  588  		m.BaseURL = a.cfg.LLMProxy.BaseURL + "/v1"
  589  		runtimeToken = keys[0].APIKey
  590  	}
查看全部 3 处证据
  • 实现 backend/biz/task/usecase/task.go:585–590 运行时使用 proxy URL 与临时 key。
  • 实现 backend/biz/llmproxy/proxy.go:160–185 token 解析为真实模型上下文。
  • 实现 backend/biz/llmproxy/proxy.go:203–236 上游地址和凭据重写。
11
L1事实monkey-provider-002

代理只放行三种 LLM 协议,并阻止任务偷换模型

源码事实

入口只接受 chat/completions、responses、messages;请求必须有 bearer 或 X-api-key,body 中 model 若与 token 绑定模型不一致直接 403。

白话解释

这不是任意 HTTP 隧道;票上写的是哪个模型,就只能点那个模型。

对自研 Harness 的含义

减小了凭据代理被滥用成通用外网代理或越权消费的空间。

关键源码 · 契约
backend/biz/llmproxy/proxy.go · L28–L34
   28  const upstreamFailureMessage = "连接上游模型失败,请检查模型配置,或重试"
   29  
   30  var allowPaths = map[string]string{
   31  	"/v1/chat/completions": "/chat/completions",
   32  	"/v1/responses":        "/responses",
   33  	"/v1/messages":         "/messages",
   34  }
查看全部 2 处证据
  • 契约 backend/biz/llmproxy/proxy.go:28–34 三条允许路径。
  • 实现 backend/biz/llmproxy/proxy.go:116–150 路径、认证、请求解析与模型匹配。
12
L1限制monkey-provider-003

运行中切模仅支持 OpenCode,并通过 restart 恢复 session

源码事实

SwitchModel 要求 processing、owner/privileged、模型授权;重建配置后若 runtime 不是 OpenCode 就报错,随后调用 TaskManager.Restart 并可选择 LoadSession。

白话解释

换发动机不是所有车都支持:目前只有 OpenCode 能中途换模型,且本质是重启运行时再接回会话。

对自研 Harness 的含义

Codex/Claude 的长任务无法享受平台级模型升降档。

关键源码 · 实现
backend/biz/task/usecase/task.go · L165–L218
  165  // SwitchModel 切换运行中任务使用的模型
  166  func (a *TaskUsecase) SwitchModel(ctx context.Context, user *domain.User, taskID uuid.UUID, req domain.SwitchTaskModelReq) (*domain.SwitchTaskModelResp, error) {
  167  	t, owner, err := a.Info(ctx, user, taskID)
  168  	if err != nil {
  169  		return nil, err
  170  	}
  171  	if !owner && !a.isPrivileged(ctx, user.ID) {
  172  		return nil, errcode.ErrForbidden
  173  	}
  174  	if t.Status != consts.TaskStatusProcessing {
  175  		return nil, fmt.Errorf("task is not processing")
  176  	}
  177  	if t.VirtualMachine == nil {
  178  		return nil, fmt.Errorf("task virtual machine is nil")
  179  	}
  180  
  181  	taskOwnerID := t.UserID
  182  	if a.modelHook != nil {
  183  		if err := a.modelHook.ValidateAccess(ctx, taskOwnerID, req.ModelID.String()); err != nil {
  184  			return nil, err
  185  		}
  186  	}
  187  	model, err := a.modelRepo.Get(ctx, taskOwnerID, req.ModelID)
  188  	if err != nil {
      … 20 lines omitted; exact range 165–218 …
  209  	if t.Extra != nil {
  210  		skillIDs = t.Extra.SkillIDs
  211  		pluginIDs = t.Extra.PluginIDs
  212  	}
  213  	coding, configs, agentRes, err := a.getCodingConfigs(ctx, t.CliName, model, skillIDs, pluginIDs, a.userScope(ctx, user), false)
  214  	if err != nil {
  215  		return nil, err
  216  	}
  217  	if coding != taskflow.CodingAgentOpenCode {
  218  		return nil, fmt.Errorf("switch model only supports opencode runtime")
查看全部 2 处证据
  • 实现 backend/biz/task/usecase/task.go:165–218 切模授权、配置重建和 OpenCode 限制。
  • 实现 backend/biz/task/usecase/task.go:253–299 restart 与切模结果持久化。
06
DIMENSION · OBSERVABILITY-PERSISTENCE

持久化与观测

本章共 2 个可定位结论;结论按“实现事实 → 白话解释 → 工程影响 → 源码摘录”展开。

13
L1事实monkey-observe-001

模型流被旁路解析,用量归因到 task/user/VM

源码事实

成功响应 body 被 UsageCapture 包装,兼容流式与非流式;token 结果被组装为含 task、user、provider、model、input/output/cache/total、request ID 的 usage event。

白话解释

回答照常流给 Agent,同时平台在旁边读水表,不必让每个 CLI 各写一套计费代码。

对自研 Harness 的含义

统一代理是跨 Harness 成本观测的高价值控制点。

关键源码 · 实现
backend/biz/llmproxy/proxy.go · L246–L264
  246  func (p *Proxy) modifyResponse(resp *http.Response) error {
  247  	if resp == nil || resp.Body == nil {
  248  		return nil
  249  	}
  250  	if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusMultipleChoices {
  251  		return nil
  252  	}
  253  	ctx, ok := resp.Request.Context().Value(contextKey{}).(*proxyContext)
  254  	if !ok || ctx == nil || ctx.model == nil {
  255  		return nil
  256  	}
  257  	resp.Body = NewUsageCapture(p.logger, resp.Body, &UsageCaptureContext{
  258  		ctx:      resp.Request.Context(),
  259  		path:     normalizeUsageCapturePath(resp.Request.URL.Path),
  260  		stream:   ctx.stream,
  261  		proxyCtx: ctx,
  262  		proxy:    p,
  263  	})
  264  	return nil
查看全部 2 处证据
  • 实现 backend/biz/llmproxy/proxy.go:246–264 成功响应挂载 usage capture。
  • 实现 backend/biz/llmproxy/proxy.go:280–317 usage 事件归因字段。
14
L1事实monkey-observe-002

遥测采用 OTLP,但输出前做严格 allowlist 消毒

源码事实

OTLP exporter 用 batch processor;导出前过滤 span attributes、events、links 和 status,仅保留 task/session/request/VM、HTTP/RPC/DB 等允许字段,exception 只留类型并清空 status 描述。

白话解释

它不是把请求体、URL 和异常详情整包发给观测平台,而是先过一遍“只准这些字段出门”的白名单。

对自研 Harness 的含义

降低 prompt、token、密钥进入第三方 trace backend 的风险,但也牺牲部分排障细节。

关键源码 · 实现
backend/pkg/telemetry/telemetry.go · L29–L65
   29  func Setup(ctx context.Context, cfg Config) (Shutdown, error) {
   30  	otel.SetTextMapPropagator(propagation.TraceContext{})
   31  	if !enabled(cfg) {
   32  		return func(context.Context) error { return nil }, nil
   33  	}
   34  
   35  	opts := []otlptracegrpc.Option{}
   36  	if endpoint := strings.TrimSpace(cfg.Endpoint); endpoint != "" {
   37  		if strings.Contains(endpoint, "://") {
   38  			opts = append(opts, otlptracegrpc.WithEndpointURL(endpoint))
   39  		} else {
   40  			opts = append(opts, otlptracegrpc.WithEndpoint(endpoint))
   41  		}
   42  	}
   43  	if cfg.Insecure {
   44  		opts = append(opts, otlptracegrpc.WithInsecure())
   45  	}
   46  
   47  	exporter, err := otlptracegrpc.New(ctx, opts...)
   48  	if err != nil {
   49  		return nil, err
   50  	}
   51  	tp, ok := otel.GetTracerProvider().(*sdktrace.TracerProvider)
   52  	if !ok {
      … 3 lines omitted; exact range 29–65 …
   56  
   57  	processor := sdktrace.NewBatchSpanProcessor(
   58  		&resourceExporter{SpanExporter: exporter, resource: buildResource(cfg)},
   59  		sdktrace.WithMaxQueueSize(2048),
   60  		sdktrace.WithMaxExportBatchSize(512),
   61  		sdktrace.WithBatchTimeout(5*time.Second),
   62  		sdktrace.WithExportTimeout(5*time.Second),
   63  	)
   64  	tp.RegisterSpanProcessor(processor)
   65  	return processor.Shutdown, nil
查看全部 4 处证据
  • 实现 backend/pkg/telemetry/telemetry.go:29–65 OTLP batch exporter。
  • 实现 backend/pkg/telemetry/telemetry.go:95–107 导出前消毒包装。
  • 实现 backend/pkg/telemetry/sanitize.go:10–78 属性 allowlist。
  • 实现 backend/pkg/telemetry/sanitize.go:102–127 事件、links 和 status 消毒。
07
DIMENSION · CONTEXT-COMPACTION-MEMORY

上下文、压缩与记忆

本章共 2 个可定位结论;结论按“实现事实 → 白话解释 → 工程影响 → 源码摘录”展开。

15
L1限制monkey-context-001

平台知道模型窗口上限,但不管理内层 compaction

源码事实

OpenCode 配置缺省 context=200000、output=32000;Taskflow 请求只下发 prompt/config/session,MonkeyCode 仓库没有在推理循环中计算 live context 或触发 compaction 的代码。

白话解释

平台知道油箱标称多大,却不看里面还剩多少油;何时压缩历史由 OpenCode/Codex/Claude 自己决定。

对自研 Harness 的含义

跨 runtime 的长任务可靠性会随各 CLI 内核不同而波动。

关键源码 · 实现
backend/biz/task/usecase/task.go · L788–L792
  788  func modelRuntimeDefaults(m *db.Model) (thinking bool, contextLimit int, outputLimit int) {
  789  	thinking = m.ThinkingEnabled
  790  	contextLimit = cmp.Or(m.ContextLimit, 200000)
  791  	outputLimit = cmp.Or(m.OutputLimit, 32000)
  792  	return thinking, contextLimit, outputLimit
查看全部 3 处证据
  • 实现 backend/biz/task/usecase/task.go:788–792 模型窗口和输出缺省值。
  • 配置 backend/templates/opencode.tmpl:27–40 窗口下发给 OpenCode provider。
  • 契约 backend/pkg/taskflow/types.go:626–640 平台任务合同中无 compaction control。
16
L1事实monkey-context-002

任务摘要是异步 UI 元数据,不是 Agent 记忆回写

源码事实

TaskSummaryService 从持久化 task log 取 conversation,用独立 LLM 延迟生成 summary 并写入 Task.summary;任务 Create/Continue 协议未读取该字段回灌内层会话。

白话解释

它会给长对话写一段“给人看的剧情简介”,但这段简介不会自动塞回 Agent 的脑子里。

对自研 Harness 的含义

摘要改善列表浏览和通知,不应被误计为上下文压缩或长期记忆。

关键源码 · 实现
backend/biz/task/service/tasksummary.go · L32–L92
   32  // TaskSummaryService 任务摘要生成服务
   33  type TaskSummaryService struct {
   34  	cfg                *config.Config
   35  	db                 *db.Client
   36  	llm                *llm.Client
   37  	summaryQueue       *delayqueue.TaskSummaryQueue
   38  	logger             *slog.Logger
   39  	conversationReader ConversationReader
   40  
   41  	// 生命周期管理
   42  	cancel context.CancelFunc
   43  	wg     sync.WaitGroup
   44  }
   45  
   46  type tasklogGateway interface {
   47  	QueryTurns(ctx context.Context, taskID uuid.UUID, taskCreatedAt time.Time, opts tasklog.QueryTurnsOpts, store consts.LogStore) (*tasklog.QueryTurnsResp, error)
   48  }
   49  
   50  type ConversationReader interface {
   51  	Fetch(ctx context.Context, taskID uuid.UUID, createdAt time.Time, store consts.LogStore, initialContent string, maxRounds int) ([]llm.Message, error)
   52  }
   53  
   54  type tasklogConversationReader struct {
   55  	gateway tasklogGateway
      … 27 lines omitted; exact range 32–92 …
   83  		llm:                llmClient,
   84  		summaryQueue:       sq,
   85  		logger:             logger,
   86  		conversationReader: newTasklogConversationReader(tlg, logger),
   87  	}
   88  
   89  	// 启动消费者
   90  	s.Start(context.Background())
   91  
   92  	return s, nil
查看全部 3 处证据
  • 实现 backend/biz/task/service/tasksummary.go:32–92 独立 summary LLM 与 tasklog reader。
  • 实现 backend/biz/task/service/tasksummary.go:121–145 延迟队列。
  • 实现 backend/biz/task/service/tasksummary.go:148–186 摘要写入数据库字段。
08
DIMENSION · INSTRUCTIONS-PROMPTS

指令与 Prompt

本章共 1 个可定位结论;结论按“实现事实 → 白话解释 → 工程影响 → 源码摘录”展开。

17
L1事实monkey-resource-001

rules、skills、plugins 是三条不同投放链

源码事实

rules 以内联 ConfigFile 写入 .ai-ready/rules;skills 对三种 CLI 都以 24h presigned zip URL 下发;plugins 只给 OpenCode,并将 entry 变成 file:// URL 注入 opencode.json。

白话解释

规则是小纸条直接塞进去,技能是压缩包让 VM 自己下载,插件还要告诉 OpenCode 从哪个入口文件启动。

对自研 Harness 的含义

资源协议兼顾小文本与大包,但各 runtime 的插件能力并不对齐。

关键源码 · 契约
backend/biz/task/usecase/task.go · L795–L803
  795  // agentRuleBaseDir / agentSkillBaseDir / agentPluginBaseDir 是 codingmatrix 在
  796  // VM 内部约定的 .ai-ready/ 投放路径。rule 走 .md 平铺;skill / plugin 解 zip
  797  // 后按目录结构展开;plugin 的 entry 字段再以 file:// 注入到 opencode.json 的
  798  // `plugin` 数组里。Claude / Codex 不消费 plugin(spec §6.3)。
  799  const (
  800  	agentRuleBaseDir   = "${HOME}/.codingmatrix/project-tpl/.ai-ready/rules/"
  801  	agentSkillBaseDir  = "${HOME}/.codingmatrix/project-tpl/.ai-ready/skills/"
  802  	agentPluginBaseDir = "${HOME}/.codingmatrix/project-tpl/.ai-ready/plugins/"
  803  )
查看全部 4 处证据
  • 契约 backend/biz/task/usecase/task.go:795–803 .ai-ready 路径和 CLI 差异。
  • 实现 backend/biz/task/usecase/task.go:955–1002 rules inline 与 skills URL。
  • 实现 backend/biz/task/usecase/task.go:1004–1041 OpenCode-only plugins 与 file URL 注入。
  • 契约 backend/biz/agentresource/resolver.go:13–29 24h presigned URL 交接。
09
DIMENSION · TOOLS-CONNECTORS-PLUGINS

工具、连接器与插件

本章共 2 个可定位结论;结论按“实现事实 → 白话解释 → 工程影响 → 源码摘录”展开。

18
L1事实monkey-resource-002

资源按 global/team/user 合并,同名 user 覆盖 team 覆盖 global

源码事实

ScopeFilter 可同时包含三个层级,调用方按 user>team>global 做 name override;dispatch 选择用户勾选项与 force-delivery,并过滤 disabled。

白话解释

公司给默认技能,团队可换一版,个人还能再覆盖同名版本;被禁用的资源不会因强制投放而复活。

对自研 Harness 的含义

这已经接近企业 Agent 的策略分层,而不是单用户插件目录。

关键源码 · 契约
backend/biz/agentresource/types.go · L99–L126
   99  // ScopeFilter constrains a listing or dispatch query to a subset of the
  100  // three scope tiers. Used by the *Scoped Repo methods.
  101  //
  102  // Semantic: rows whose scope_type is "global" are included when
  103  // IncludeGlobal is true; rows whose scope is "team" are included when
  104  // TeamID != nil and matches; same for user. Multiple flags may be set —
  105  // the result is the union, with name-based override (user > team > global)
  106  // applied by the call sites that need overrides (Listing / dispatch).
  107  type ScopeFilter struct {
  108  	IncludeGlobal bool
  109  	TeamID        *uuid.UUID
  110  	UserID        *uuid.UUID
  111  }
  112  
  113  // GlobalOnlyScope is the default scope for back-compat call sites that
  114  // haven't migrated to ScopeFilter yet. Matches the historical "global only"
  115  // behavior of the unsuffixed Repo methods.
  116  func GlobalOnlyScope() ScopeFilter {
  117  	return ScopeFilter{IncludeGlobal: true}
  118  }
  119  
  120  // SkillSelection bundles a ScopeFilter with the user-picked skill IDs for
  121  // the dispatch path. ListActiveSkillsScoped takes this so the same struct
  122  // flows from the task usecase down to the SQL.
  123  type SkillSelection struct {
  124  	Scope           ScopeFilter
  125  	UserSelectedIDs []uuid.UUID
  126  }
查看全部 3 处证据
  • 契约 backend/biz/agentresource/types.go:99–126 三层 scope 与覆盖语义。
  • 契约 backend/biz/agentresource/resolver.go:49–54 scoped resolver 与 disabled 过滤。
  • 实现 backend/biz/task/usecase/task.go:982–1010 技能/插件 scoped selection。
19
L1事实monkey-mcp-001

MCP Hub 把工具身份绑定到具体 user、task 和 VM

源码事实

任务拿到本地 builtin MCP 和带 runtime token 的平台 MCP;Hub 认证先由 ModelApiKey 找到 VirtualMachineID,再查唯一 TaskVirtualMachine,形成 user/task/model subject。

白话解释

工具调用不是“拿到平台 token 就随便用”,而是能追到哪台 VM、哪个人、哪个任务。

对自研 Harness 的含义

为审计、限额和撤销提供了细粒度主体。

关键源码 · 实现
backend/biz/task/usecase/task.go · L741–L767
  741  func (a *TaskUsecase) buildMCPConfigs(taskID uuid.UUID, token string) []taskflow.McpServerConfig {
  742  	mcps := []taskflow.McpServerConfig{
  743  		{
  744  			Type: "http",
  745  			Name: "mcaiBuiltin",
  746  			Url:  proto.String(fmt.Sprintf("http://127.0.0.1:65510/mcp?task_id=%s", taskID.String())),
  747  		},
  748  	}
  749  
  750  	if token != "" {
  751  		mcps = append(mcps, taskflow.McpServerConfig{
  752  			Type: "http",
  753  			Name: "monkeycode-ai",
  754  			Url:  proto.String(fmt.Sprintf("%s/mcp", strings.TrimRight(a.cfg.Server.BaseURL, "/"))),
  755  			Headers: []*taskflow.McpHttpHeader{
  756  				{
  757  					Name:  "Authorization",
  758  					Value: fmt.Sprintf("Bearer %s", token),
  759  				},
  760  			},
  761  			Command: new(string),
  762  			Args:    []string{},
  763  			Env:     map[string]string{},
  764  		})
  765  	}
  766  
  767  	return mcps
查看全部 2 处证据
  • 实现 backend/biz/task/usecase/task.go:741–767 builtin 与平台 MCP 下发。
  • 实现 backend/biz/mcphub/auth/service.go:45–80 token→VM→task subject 绑定。
10
DIMENSION · TOOL-DISPATCH

工具分发与结果治理

本章共 2 个可定位结论;结论按“实现事实 → 白话解释 → 工程影响 → 源码摘录”展开。

20
L1事实monkey-mcp-002

工具调用具备有效集过滤、幂等 replay 和状态审计

源码事实

Hub 合并 platform/user/team 工具和用户开关,过滤 disabled/deleted/missing upstream;调用 ID 由 task、JSON-RPC ID、tool、canonical args 哈希,重复请求复用结果;首次调用写 pending,再标 success/failed/unknown。

白话解释

同一个任务重试同一张工具工单不会重复扣动外部系统;每次调用都有完整状态轨迹。

对自研 Harness 的含义

这是比把 MCP URL 直接交给 Agent 更成熟的连接器治理层。

关键源码 · 实现
backend/biz/mcphub/runtime/registry/service.go · L56–L80
   56  func (s *Service) ListEffectiveTools(ctx context.Context, userID uuid.UUID) ([]repo.ToolSnapshot, error) {
   57  	platform, err := s.listPlatformTools(ctx)
   58  	if err != nil {
   59  		return nil, err
   60  	}
   61  	userTools, err := s.repo.ListUserPublishedTools(ctx, userID)
   62  	if err != nil {
   63  		return nil, err
   64  	}
   65  	teamTools, err := s.repo.ListTeamPublishedTools(ctx, userID)
   66  	if err != nil {
   67  		return nil, err
   68  	}
   69  
   70  	settings := map[uuid.UUID]bool{}
   71  	if s.userSettings != nil {
   72  		settings, err = s.userSettings.ListEnabledMap(ctx, userID)
   73  		if err != nil {
   74  			return nil, err
   75  		}
   76  	}
   77  
   78  	tools := append(platform, userTools...)
   79  	tools = append(tools, teamTools...)
   80  	return applyUserSettingsDefaultEnabled(tools, settings), nil
查看全部 4 处证据
  • 实现 backend/biz/mcphub/runtime/registry/service.go:56–80 三类工具与用户开关合并。
  • 实现 backend/biz/mcphub/runtime/gateway/handler.go:144–185 有效工具与 upstream 可达性过滤。
  • 实现 backend/biz/mcphub/runtime/gateway/handler.go:187–260 幂等 replay、pending 与终态审计。
  • 实现 backend/biz/mcphub/runtime/gateway/handler.go:263–267 确定性 request ID。
21
L1限制monkey-mcp-003

MCP 计费接口已预留,但当前实现是 Noop

源码事实

gateway 在调用前后执行 CanConsume/Consume,但默认 billing.Noop 两个方法都直接返回 nil。

白话解释

水表接口和插座都装好了,但现在里面还没有真正的计费表芯。

对自研 Harness 的含义

不能仅凭调用链存在就宣称已实现工具预算或付费配额。

关键源码 · 实现
backend/biz/mcphub/runtime/gateway/handler.go · L228–L251
  228  	if h.billing != nil {
  229  		if err := h.billing.CanConsume(ctx, call); err != nil {
  230  			_ = h.calls.MarkFailed(ctx, call.ID, err.Error(), "")
  231  			return nil, err
  232  		}
  233  	}
  234  
  235  	result, upstreamRequestID, err := h.upstream.CallTool(ctx, upstream, tool, CallToolParams{
  236  		Name:      tool.Name,
  237  		Arguments: args,
  238  	})
  239  	switch classifyUpstreamError(err) {
  240  	case repo.ToolCallStatusSuccess:
  241  		if err := h.calls.MarkSuccess(ctx, call.ID, result, upstreamRequestID); err != nil {
  242  			return nil, err
  243  		}
  244  		if h.billing != nil {
  245  			completed := *call
  246  			completed.Status = repo.ToolCallStatusSuccess
  247  			completed.ResultJSON = result
  248  			completed.UpstreamRequestID = upstreamRequestID
  249  			if err := h.billing.Consume(ctx, &completed); err != nil {
  250  				return nil, err
  251  			}
查看全部 2 处证据
  • 实现 backend/biz/mcphub/runtime/gateway/handler.go:228–251 计费前检与消费调用点。
  • 实现 backend/biz/mcphub/billing/noop.go:9–20 当前 Noop 计费实现。
APPENDIX · SOURCE INDEX

本报告引用过的实现文件

这是一份代码阅读索引,不是仓库文件总表。机器候选扫描覆盖整个仓库;进入结论的文件必须经人工沿调用链复核。

  1. 01backend/pkg/taskflow/types.goL554–587, 626–640, 72–92, 122–139, 626–640, 626–640
  2. 02backend/biz/task/usecase/task.goL875–928, 556–617, 622–687, 157–163, 585–590, 165–218, 253–299, 788–792, 795–803, 955–1002, 1004–1041, 982–1010, 741–767
  3. 03backend/pkg/lifecycle/taskhook.goL92–130, 104–123, 129–136
  4. 04backend/templates/codex.tmplL1–13, 1–6
  5. 05backend/biz/task/handler/v1/task.goL323–384, 387–423, 640–721, 585–610
  6. 06backend/templates/opencode.tmplL40–50, 27–40
  7. 07backend/biz/llmproxy/proxy.goL160–185, 203–236, 28–34, 116–150, 246–264, 280–317
  8. 08backend/biz/task/service/tasksummary.goL32–92, 121–145, 148–186
  9. 09backend/biz/agentresource/resolver.goL13–29, 49–54
  10. 10backend/biz/agentresource/types.goL99–126
  11. 11backend/biz/agentresource/unpack.goL12–31, 38–98, 101–126
  12. 12backend/biz/mcphub/auth/service.goL45–80, 22–28
  13. 13backend/biz/mcphub/runtime/registry/service.goL56–80
  14. 14backend/biz/mcphub/runtime/gateway/handler.goL144–185, 187–260, 263–267, 228–251
  15. 15backend/biz/mcphub/billing/noop.goL9–20
  16. 16backend/pkg/netguard/guard.goL53–69, 143–179
  17. 17backend/pkg/netguard/guard_test.goL22–59
  18. 18backend/config/server/config.yaml.exampleL7–10
  19. 19backend/pkg/telemetry/telemetry.goL29–65, 95–107
  20. 20backend/pkg/telemetry/sanitize.goL10–78, 102–127