Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ The allowed direction is always `TypeScript -> Go -> LLM/Rust/Docker`. TypeScrip
| Area | Current capability |
|---|---|
| Agent runtime | Mission/Run, resumable Supervisor, strict lifecycle, checkpoints, cancellation, retry, budgets, and execution leases |
| Models and context | Mock and Anthropic-compatible providers, routing, qualification, streaming, context compaction, and structured memory |
| Models and context | Mock, Anthropic-compatible, OpenAI-compatible, and loopback-only Ollama providers, routing, qualification, capability probing, streaming, context compaction, and structured memory |
| Planning and collaboration | Plan/Delivery, work items, notes, up to two core children, 1/2/4/6 read-only fan-out tiers, shared budgets and cancellation |
| Tools and permissions | Tool Gateway, JSON Schema validation, Policy, Scope, human approval, four host-permission tiers, and fixed-command proposals |
| Code workflows | Native folder selection and Workspace import, workspace browsing, repository state/history, diff review, file-edit proposals, verification plans, Code Journey, and Handoff |
Expand Down Expand Up @@ -127,6 +127,8 @@ go run ./cmd/cyberagent tui

The default configuration uses the deterministic Mock Provider and requires no API key. Read [Model and Provider Commands](docs/usage.md#model-and-provider-commands) before connecting an external model. Credentials belong in the OS credential store or process environment, never in the repository.

Local Ollama is the only keyless Provider and enables only when `CYBERAGENT_OLLAMA_BASE_URL` (loopback `http` only, default `http://127.0.0.1:11434`) and `CYBERAGENT_OLLAMA_MODEL` are set explicitly. Non-loopback hosts, HTTPS, redirects, and proxy bypasses are rejected; tools/vision/JSON/context capabilities fail closed from `/api/show` probing, and Prayu never installs Ollama, pulls a model, or scans the LAN.

### Windows Desktop preview

```powershell
Expand Down
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ CLI / TUI / React / Windows Desktop / CI
| 领域 | 当前能力 |
|---|---|
| Agent 运行时 | Mission/Run、可恢复 Supervisor、严格生命周期、检查点、取消、重试、预算和执行租约 |
| 模型与上下文 | Mock、Anthropic-compatible 与 OpenAI-compatible Provider、模型路由、资格校验、流式响应、上下文压缩、结构化记忆 |
| 模型与上下文 | Mock、Anthropic-compatible、OpenAI-compatible 与 loopback-only Ollama Provider、模型路由、资格校验、能力探测、流式响应、上下文压缩、结构化记忆 |
| 计划与协作 | Plan/Delivery、工作项、备注、最多两个核心 child、1/2/4/6 档只读 Fan-out、共享预算与取消扇出 |
| 工具与权限 | Tool Gateway、JSON Schema 校验、Policy、Scope、人工审批、四档宿主权限、受控固定命令提案 |
| 代码工作流 | 系统目录选择与 Workspace 导入、工作区浏览、仓库状态、提交历史、Diff 审阅、文件编辑提案、验证计划、Code Journey 与 Handoff |
Expand Down Expand Up @@ -130,6 +130,11 @@ OpenAI-compatible 连接使用独立的 `CYBERAGENT_OPENAI_API_KEY`、
`https://api.openai.com` 和 `gpt-4.1-mini`。仓库内的 `configs/models.yaml`
只是无秘密示例,不会作为运行时配置源。

本地 Ollama 是唯一无凭证 Provider,只在显式设置 `CYBERAGENT_OLLAMA_BASE_URL`(仅
loopback `http`,默认 `http://127.0.0.1:11434`)与 `CYBERAGENT_OLLAMA_MODEL` 时启用;
非 loopback、HTTPS、redirect 与代理绕过一律拒绝。tools/vision/JSON/context 能力按
`/api/show` 探测结果失败关闭,不自动安装 Ollama、不 pull 模型、不扫描局域网。

### Windows Desktop 预览

```powershell
Expand Down
19 changes: 19 additions & 0 deletions configs/models.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,22 @@ openai_compatible_example:
compatibility_profile: strict_chat_completions
runtime_loaded: false
secrets: environment_or_system_store_only

# Documentation-only example for the keyless local provider. The registry
# enables Ollama only when the endpoint and model are configured explicitly
# in the trusted process environment:
# CYBERAGENT_OLLAMA_BASE_URL=http://127.0.0.1:11434
# CYBERAGENT_OLLAMA_MODEL=llama3.2:3b
# Only loopback hosts are accepted; the provider never scans the LAN, never
# follows a redirect or proxy, and never installs or pulls models.
ollama_example:
provider_name: ollama
type: ollama
transport: ollama_chat
default_base_url: http://127.0.0.1:11434
timeout_seconds: 60
request_headers: [Content-Type, Accept]
capability_probe: /api/show
compatibility_profile: strict_ollama_chat
runtime_loaded: false
secrets: none
20 changes: 17 additions & 3 deletions docs/PROJECT_MEMORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,23 @@

> Scope checkpoint (2026-08-13): continue only the general-purpose Agent Harness and Code workflow. CTF-specific solving/offensive automation is an optional add-on with no active slices; retain generic extension seams only. Historical Cyber percentages are not current planning metrics. See [PRODUCT_SCOPE.md](PRODUCT_SCOPE.md).

Last updated: 2026-08-14

## Current Single-Slice Checkpoint: Docker Sandbox Product Admission / Schema v99
Last updated: 2026-08-15

## Current Single-Slice Checkpoint: Ollama Loopback Provider / Issue #48

2026-08-15 的 issue #48 新增无凭证 loopback-only Ollama Provider(ADR 0100)。启用
条件只有显式 `CYBERAGENT_OLLAMA_BASE_URL`(仅 loopback `http`)+ `CYBERAGENT_OLLAMA_MODEL`;
非 loopback、HTTPS、URL 凭证/query/fragment、路径前缀、redirect 与代理 transport 拒绝。
原生 `/api/tags` 模型列表、`/api/chat`(同步 + NDJSON 流式,含取消/截断/trailing-event
拒绝)、`/api/show` 能力探测(tools/vision/JSON/context,未知即不支持)、usage 估算与
稳定错误映射(model_not_found/capacity/rate_limit/network + 服务未启动可解释诊断)。
no-tool 模型在 Harness(ToolStrategy=none)与 Provider(拒绝透传 Tool schema)两层都走
安全路径。Registry kind `ollama`/transport `ollama_chat` 接入 CLI/HTTP/Desktop/Web;
credential 枚举保持四位。不自动安装/pull/扫描局域网;本地模型仍过 Policy/redaction/
budget/Tool Gateway。本机无 Ollama,真实 smoke 为文档化可选步骤;fake-server 离线测试
覆盖全部路径。

## Previous Single-Slice Checkpoint: Docker Sandbox Product Admission / Schema v99

2026-08-14 的 issue #40 将 network-none Docker Sandbox 接入产品。新的
`DockerSandboxService` 是 readiness、admission、status、start、cancellation 和 startup
Expand Down
15 changes: 14 additions & 1 deletion docs/PROJECT_STATUS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,20 @@ Last updated: 2026-08-15

## Resume Context

当前单切片检查点是 issue #40 的 schema v99 Docker Sandbox 产品准入、执行与恢复。
当前单切片检查点是 issue #48 的 Ollama 本地 Provider 与能力探测(ADR 0100)。
新增无凭证的 loopback-only `ollama` Provider:只接受显式配置的 `http` loopback
endpoint(`CYBERAGENT_OLLAMA_BASE_URL` + `CYBERAGENT_OLLAMA_MODEL`),拒绝非
loopback、HTTPS、URL 凭证/query/fragment、路径前缀、redirect 与代理 transport;
使用原生 `/api/tags`、`/api/chat`(含 NDJSON 流式)与 `/api/show` 能力探测,
tools/vision/JSON/context 能力未知一律按不支持处理,no-tool 模型绝不收到 Tool
schema;usage 优先 daemon 计数、缺失时按字符/4 保守估算;稳定错误映射覆盖
model_not_found/capacity/rate_limit/network 与服务未启动的可解释诊断。Registry
kind `ollama`、transport `ollama_chat` 已接入 CLI/HTTP/OpenAPI/Desktop/Web 与
credential 枚举保持四位(Ollama 无凭证)。本机未安装 Ollama,真实 smoke 为
文档化的可选人工步骤;fake-server 离线测试覆盖 list/chat/stream/取消/不可达/
redirect/代理绕过/no-tool 安全路径与能力探测。

前一检查点是 issue #40 的 schema v99 Docker Sandbox 产品准入、执行与恢复。
新的 `DockerSandboxService` 将 v97 的 exact-owned 生命周期与 v98 的有界 I/O 组合到
CLI、认证 HTTP/OpenAPI、Desktop 与模型提案入口;模型
`sandbox_docker_run_propose` 只能请求 Admit,不能 Start 或构造 Docker 请求。全部入口
Expand Down
2 changes: 2 additions & 0 deletions docs/TASK_BOOK.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

## 当前基线

Ollama 本地 Provider(Issue #48)已落地(ADR 0100):无凭证 loopback-only 适配器只连接显式配置的 `http://127.0.0.1:11434` 类端点,拒绝非 loopback/HTTPS/redirect/代理绕过;`/api/tags` 模型列表、`/api/chat` 同步与 NDJSON 流式、usage 估算与稳定错误映射齐全;`/api/show` 能力探测把 tools/vision/JSON/context 按“未知即不支持”失败关闭,no-tool 模型绝不收到 Tool schema;能力探测在路由选择、qualification 与 diagnostic 前 best-effort 执行,探测到的 context window 进入真实预算规划。Registry kind `ollama` 与 transport `ollama_chat` 已接入 CLI/HTTP/OpenAPI/Desktop/Web;credential 枚举保持四位(Ollama 无凭证)。本机未安装 Ollama,真实 smoke 为 usage 文档化的可选人工步骤;fake-server 离线测试覆盖 list/chat/stream/取消/不可达/redirect/代理/no-tool 与探测语义。

Docker Sandbox 产品准入(Issue #40)已随 schema v99 落地(ADR 0099):同一个 Go Application 服务把 CLI、HTTP/OpenAPI、Desktop 与模型 admission proposal 接到 v97 生命周期和 v98 有界 I/O;真实 start 默认关闭,必须重新通过当前 Profile/权限/精确 per-call 审批/Policy/预算/30 秒 readiness 与进程内 capability。当前只开放 environment-free、secret-free 的 network-none 精确计划;managed allowlist 因缺少 Go-owned host/port/protocol guard 继续失败关闭。Issue #40 集成收口已完成:全仓 ordinary Go、vet、Web strict TypeScript/235 项测试/Vite build 与 OpenAPI 再生全绿;真实 Docker Desktop 29.6.2 上 lifecycle、readiness 与容器内主动探测 IPv4/IPv6/DNS/gateway/代理变量的断网验收全部通过(environment-free 夹具由 testdata/docker-lifecycle-fixture/build-fixture.ps1 可复现构建),并把 daemon /info 的 x86_64/aarch64 架构名规范化到 OCI amd64/arm64。

前一检查点是 schema v98 的 Docker 容器 I/O 合同(Issue #39,ADR 0098):只读输入投影、有界日志回执、严格输出暂存与原子 Artifact 提交已作为非授权内部边界完成;schema v99 不改写其历史 authority,而是以独立产品账本组合其 exact subset。
Expand Down
87 changes: 87 additions & 0 deletions docs/adr/0100-ollama-loopback-provider-and-capability-probing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# ADR 0100: Ollama 本地 Provider 与能力探测

Date: 2026-08-15

## Status

Accepted for the exact, explicitly-configured, loopback-only Ollama profile
described below. LAN scanning, implicit enablement, and automated install or
model pull remain unavailable.

## 背景 / Context

离线、本地优先场景需要一个不依赖云凭证的模型 Provider。Ollama 在
`http://127.0.0.1:11434` 提供本机 HTTP API(`/api/tags`、`/api/chat`、`/api/show`),
但不同模型的 tools、vision、JSON 与 context window 能力各不相同,且这些能力随
Ollama 版本和模型族变化。若因为“本地模型存在”就假设其具备完整 Agent 能力,会
把工具 schema 发给不支持工具的模型,或在能力未知时伪造调用。

## 决策 / Decision

### 1. 显式 loopback 端点;默认关闭

`ollama` 是唯一无凭证 Provider。Registry 只在 `CYBERAGENT_OLLAMA_BASE_URL` 与
`CYBERAGENT_OLLAMA_MODEL` 同时显式配置时启用它;缺省永不探测、永不扫描局域网。
Base URL 只接受 `http` scheme、loopback host(`127.0.0.0/8`、`::1`、`localhost`)、
无 userinfo/query/fragment/路径前缀;HTTPS、非 loopback、redirect、代理 transport
一律拒绝。构造出的 HTTP client 强制 `Proxy=nil`,即使进程环境定义了 `HTTP_PROXY`
也不会把“本地”请求路由到代理。模型名按有界 UTF-8 规范化(无空白/控制字符、
无 `..`、无 `//`、无前缀 `:` 或 `/`)。

### 2. 能力探测与失败关闭

`/api/tags` 的 `capabilities` 字段存在即表示该模型已探测;缺失表示能力未知。
`/api/show` 返回 `capabilities`(closed set:`completion|tools|vision`)与
`model_info` 中的 `<family>.context_length`(只接受数值,取最大值并限界)。
`SupportsTools`/`SupportsVision`/`SupportsJSONMode` 只读取进程内探测缓存;未知一律
false。JSON 视为 wire 级 `format=json` 特性(对任意已探测模型可用),而严格 JSON
输出有效性仍由独立 Harness qualification 验证。探测是 best-effort:路由选择、
qualification 与 diagnostic 前都会尝试,失败不阻塞操作,能力继续按不支持处理。
context window 探测成功后通过 `Router.SetContextWindow`(source=`ollama_probe`)
进入真实预算规划。

### 3. no-tool 安全路径

`DescribeModelHarness` 按探测结果输出 `ollama_chat` transport:tools 未确认时
`ToolStrategy=none`、JSON 未确认时 `JSONStrategy=none`,且 root 资格保持
`qualification_required`。Provider 在 `prepareRequest` 层再设一道防线:向未确认
tools 能力的模型发送 Tool schema 会直接失败,绝不透传 schema,也绝不伪造
tool call。这样 no-tool 模型只能走无工具安全路径(如 fanout/plain chat),不会
进入错误的 Tool loop。

### 4. 聊天、流式与 usage 估算

`/api/chat` 支持 `stream=false` 与 NDJSON `stream=true`。流式状态机逐行消费事件:
message content 增量、完整 `tool_calls`(合成确定性 ID)、终态 `done`/`done_reason`
(closed set:空、`stop`、`length`)与 `prompt_eval_count`/`eval_count`;done 之后
的非空事件、重复 tool_calls、超限文本、截断流与 EOF 缺失终态均按协议错误拒绝。
Ollama 的 tool call 没有 ID,adapter 按顺序合成 `ollama_call_<n>`;arguments 若被
上游序列化成 JSON 字符串则解包为对象。usage 优先采用 daemon 计数,缺失时按
字符/4 向上取整保守估算(input 按序列化消息字节、output 按响应文本),保证预算
记账始终有界。

### 5. 稳定错误映射与可解释诊断

错误只通过稳定分类暴露,原始 wire 文本绝不外泄:404 且正文含 `not found` →
`model_not_found`;正文含 `out of memory` → `capacity`;`rate limit` → `rate_limit`;
连接类错误 → `network`。loopback 连接被拒(服务未启动)映射为 retryable `network`,
消息固定为 “Ollama service is unreachable on the configured loopback endpoint”,
不携带 host/路径/payload。不自动安装 Ollama、不 pull 模型、不修改系统服务。

### 6. 入口接线

Registry 新增 `ProviderKindOllama`(kind `ollama`),OpenAPI 的 provider 路径枚举与
`transport_protocol` 枚举加入 `ollama`/`ollama_chat`;credential 枚举保持四位——
Ollama 无凭证,不属于系统凭证允许列表。CLI、HTTP/OpenAPI、Desktop 与 Web 复用现有
通用模型控制面(provider list/test/qualify、model set、模型可用性视图),Web 客户端的
kind/transport 校验同步接受新枚举。`configs/models.yaml` 仅保留文档级示例。

## 后果 / Consequences

- 显式配置的 loopback Ollama 可以完成模型列表、聊天、流式、取消、usage 估算与
能力探测;工具/视觉/JSON/上下文能力不明时一律按不支持处理。
- 本地模型仍走完整 Policy、redaction、budget 与 Tool Gateway;“本地”只是部署位置,
不是信任授权。
- 非 loopback 端点、代理绕过、重定向、隐式启用、LAN 扫描、自动安装与自动 pull
仍不属于本决策;真实 Ollama smoke 是可选人工步骤并记录在 usage 文档。

8 changes: 6 additions & 2 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -482,10 +482,14 @@ The LLM router remains independent from orchestration. Run snapshots record the

Environment adapters expose `mimo`, `deepseek`, and `anthropic` over the shared
Anthropic-compatible transport, plus the canonical `openai` adapter over OpenAI Chat
Completions. Each adapter reads only its dedicated API-key/base-URL/model namespace.
Completions, and the keyless `ollama` adapter over the native local daemon protocol.
Each adapter reads only its dedicated API-key/base-URL/model namespace.
For `openai`, that namespace is `CYBERAGENT_OPENAI_API_KEY`,
`CYBERAGENT_OPENAI_BASE_URL`, and `CYBERAGENT_OPENAI_MODEL`; its defaults are
`https://api.openai.com` and `gpt-4.1-mini`. The Provider object and secrets remain
`https://api.openai.com` and `gpt-4.1-mini`. For `ollama`, the namespace is the
loopback-only `CYBERAGENT_OLLAMA_BASE_URL` plus `CYBERAGENT_OLLAMA_MODEL`; there is
no credential, and the endpoint stays off unless both variables are set explicitly.
The Provider object and secrets remain
inside the Go control plane and never enter Run configuration or event payloads.
The production Registry supplies a 60-second client timeout, and internal injected
clients are clamped to that maximum. The adapter disables redirects and emits only the
Expand Down
4 changes: 2 additions & 2 deletions docs/http-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -537,9 +537,9 @@ cyberagent api openapi
cyberagent api openapi --output docs/openapi.json
```

运行时的 `/api/v1/openapi.json` 返回同一份原始文档,仍要求 loopback 与 read Bearer 认证,不接受 query 或 body。它使用 `application/vnd.oai.openapi+json`,不套普通 `api.v1` envelope。当前契约有 83 个 path、91 个 operation 和 203 个 schema。测试逐条命中公开 handler,并确认普通 DTO 不包含 Workspace root、Artifact/Skill/Session 正文、模型输出、工具参数、私有 lifecycle、operation/fencing/lease owner、API key、Provider Base URL 或环境变量名。CDP 权限响应只投影固定能力布尔值、当前进程闸门和四项 false authority,不包含 endpoint、browser path、Profile、Cookie、请求正文或 CDP payload。
运行时的 `/api/v1/openapi.json` 返回同一份原始文档,仍要求 loopback 与 read Bearer 认证,不接受 query 或 body。它使用 `application/vnd.oai.openapi+json`,不套普通 `api.v1` envelope。当前契约有 95 个 path、103 个 operation 和 232 个 schema。测试逐条命中公开 handler,并确认普通 DTO 不包含 Workspace root、Artifact/Skill/Session 正文、模型输出、工具参数、私有 lifecycle、operation/fencing/lease owner、API key、Provider Base URL 或环境变量名。CDP 权限响应只投影固定能力布尔值、当前进程闸门和四项 false authority,不包含 endpoint、browser path、Profile、Cookie、请求正文或 CDP payload。

The runtime `/api/v1/openapi.json` returns the same raw document under the loopback and read-bearer boundary and accepts neither a query nor a body. It uses `application/vnd.oai.openapi+json` rather than the ordinary `api.v1` envelope. The contract contains 83 paths, 91 operations, and 203 schemas. Tests exercise every handler and verify that ordinary DTOs omit Workspace roots, Artifact/Skill/Session bodies, model output, Tool arguments, private lifecycle, operation/fencing/lease-owner identities, API keys, Provider base URLs, and environment-variable names. CDP permission responses expose only closed capability booleans, current process gates, and four false authority fields; they contain no endpoint, browser path, Profile, Cookie, request body, or CDP payload.
The runtime `/api/v1/openapi.json` returns the same raw document under the loopback and read-bearer boundary and accepts neither a query nor a body. It uses `application/vnd.oai.openapi+json` rather than the ordinary `api.v1` envelope. The contract contains 95 paths, 103 operations, and 232 schemas. Tests exercise every handler and verify that ordinary DTOs omit Workspace roots, Artifact/Skill/Session bodies, model output, Tool arguments, private lifecycle, operation/fencing/lease-owner identities, API keys, Provider base URLs, and environment-variable names. CDP permission responses expose only closed capability booleans, current process gates, and four false authority fields; they contain no endpoint, browser path, Profile, Cookie, request body, or CDP payload.

## 主动取消 / Active-Call Cancellation

Expand Down
Loading