Changelog, dev startup, README, AgentHub 0.4.0, model catalog, and landing site (#7)
Branch-length batch covering tooling, the model layer, the Web App and the public surfaces. Highlights: - Changelog: a per-release `changelog/<version>/` tree, grouped by the surface each change touches, with a root CHANGELOG.md holding one line per release. - Dev startup: `scripts/dev-prebuild.mjs` serializes the skills+core prebuild behind a lock and keeps `pnpm install` current; `pnpm dev` runs server+web together. - AgentHub 0.3.3 -> 0.4.0: OmniMessage complete payloads carry one opaque `fidelity` object in place of item-level `signature`/`phase`, threaded verbatim through Trace, replay and resume; malformed classification adapted to the new error types. - Model layer: a model is always referenced by an explicit `(provider, model_id)` pair. The provider is never inferred, guessed or defaulted -- both the catalog inference and the unique-match config resolution are gone, and CLI, SDK, server routes and run_subagent all require the complete pair. Catalog gains the Qwen Token Plan, Qwen Pay-As-You-Go and Fireworks AI gateways, plus an expanded OpenRouter group. - Web App: catalog preset sync and per-group speed test on the Models page, positional slash commands, a markdown renderer, skill-library update reminders, and a vertically centred draft page whose upward menus size themselves to the room available. - Public surfaces: restructured READMEs, the penguin.ooo landing site and blog, refreshed benchmark results for both suites, and the demo videos playing on the landing page. Includes the fixes from a full review of the branch: 23 confirmed findings, among them a provider-inference bug that could send one vendor's API key to another vendor's endpoint, and an Escape handler that destroyed the composer's contents unrecoverably. Verified on the branch head: pnpm test (1127 passing, 7 packages), pnpm typecheck and pnpm format:check clean, Playwright e2e 14/14.
This commit is contained in:
@@ -7,7 +7,7 @@ The CLI ships as the npm package `@prismshadow/penguin-cli`; the command is `pen
|
||||
|
||||
## Global conventions
|
||||
|
||||
- Model references: a model's identity is always the `(provider, model_id)` pair. `--model-id` takes the upstream model id and pairs with `--provider`. When `run` / `chat` omit `--provider`, the `--model-id` matches only if it is globally unique in the configuration; ambiguity is an error.
|
||||
- Model references: a model's identity is always the `(provider, model_id)` pair. `--model-id` takes the upstream model id and `--provider` the group it belongs to; the provider is never inferred, guessed, or defaulted. On `run` / `chat` the pair as a whole is optional — pass both to pick a model, or neither to use the Project's default model — but passing one without the other is an error.
|
||||
- Data root: `--root <dir>` overrides the data root directory. Priority: `--root` > the `PENGUIN_HOME` env var > `~/.penguin/data`.
|
||||
|
||||
## penguin run
|
||||
@@ -21,8 +21,8 @@ penguin run -m "Summarize the code structure of this directory"
|
||||
| Option | Description |
|
||||
| --- | --- |
|
||||
| `-m, --message <message>` | Required; the message to send |
|
||||
| `--model-id <id>` | Model to use; defaults to the Project's default model |
|
||||
| `--provider <group>` | Provider group of the model |
|
||||
| `--model-id <id>` | Upstream id of the model to use; requires `--provider`. Omit both to use the Project's default model |
|
||||
| `--provider <group>` | Provider group of the model; required whenever `--model-id` is given |
|
||||
| `--project-id <id>` | Project to use |
|
||||
| `--agent-id <id>` | Agent to use |
|
||||
| `--workspace <path>` | Workspace directory; defaults to the current directory and must exist |
|
||||
@@ -74,13 +74,13 @@ Manages a Project's model configuration, per-Agent vault environment variables,
|
||||
Add or update a model entry:
|
||||
|
||||
```bash
|
||||
penguin config model add --model-id deepseek-v4-pro --api-key sk-... --set-default
|
||||
penguin config model add --provider deepseek --model-id deepseek-v4-pro --api-key sk-... --set-default
|
||||
```
|
||||
|
||||
| Option | Description |
|
||||
| --- | --- |
|
||||
| `--model-id <id>` | Required; the upstream model id |
|
||||
| `--provider <group>` | Provider group; inferred from the built-in catalog when omitted |
|
||||
| `--provider <group>` | Required; the provider group the entry belongs to. It is never derived from the model id: gateways resell vendor models under their upstream ids, so a guessed group would write the credential onto another vendor's endpoint. Use `custom` for any endpoint outside the built-in groups. |
|
||||
| `--api-key <key>` | API key, stored inline in the Project's hidden `.project_config.toml` |
|
||||
| `--base-url <url>` | Custom endpoint base URL |
|
||||
| `--context-window <n>` | Context window size |
|
||||
|
||||
@@ -7,7 +7,7 @@ CLI 由 npm 包 `@prismshadow/penguin-cli` 提供,命令为 `penguin`。不带
|
||||
|
||||
## 全局约定
|
||||
|
||||
- 模型引用:模型身份始终是 `(provider, model_id)` 二元组。`--model-id` 填上游模型 id,与 `--provider` 组成配对引用。`run` / `chat` 省略 `--provider` 时,仅当该 `--model-id` 在配置中全局唯一才会匹配,存在歧义则报错。
|
||||
- 模型引用:模型身份始终是 `(provider, model_id)` 二元组。`--model-id` 填上游模型 id,`--provider` 填其所属分组;provider 绝不推断、绝不猜测、也没有缺省值。`run` / `chat` 上这对参数整体可选——两个都给即指定模型,两个都不给则使用 Project 默认模型——但只给其中一个是错误。
|
||||
- 数据根目录:`--root <dir>` 覆盖数据根目录,优先级为 `--root` > 环境变量 `PENGUIN_HOME` > `~/.penguin/data`。
|
||||
|
||||
## penguin run
|
||||
@@ -21,8 +21,8 @@ penguin run -m "总结当前目录的代码结构"
|
||||
| 选项 | 说明 |
|
||||
| --- | --- |
|
||||
| `-m, --message <message>` | 必填,要发送的消息 |
|
||||
| `--model-id <id>` | 指定模型,缺省使用 Project 默认模型 |
|
||||
| `--provider <group>` | 模型所属 Provider 分组 |
|
||||
| `--model-id <id>` | 指定模型的上游 id,须与 `--provider` 同时给出;两者都不给时使用 Project 默认模型 |
|
||||
| `--provider <group>` | 模型所属 Provider 分组,给出 `--model-id` 时必填 |
|
||||
| `--project-id <id>` | 指定 Project |
|
||||
| `--agent-id <id>` | 指定 Agent |
|
||||
| `--workspace <path>` | Workspace 目录,默认当前目录,必须已存在 |
|
||||
@@ -74,13 +74,13 @@ Ctrl-C 的行为依状态而定:
|
||||
新增或更新模型条目:
|
||||
|
||||
```bash
|
||||
penguin config model add --model-id deepseek-v4-pro --api-key sk-... --set-default
|
||||
penguin config model add --provider deepseek --model-id deepseek-v4-pro --api-key sk-... --set-default
|
||||
```
|
||||
|
||||
| 选项 | 说明 |
|
||||
| --- | --- |
|
||||
| `--model-id <id>` | 必填,上游模型 id |
|
||||
| `--provider <group>` | Provider 分组,缺省时根据内置目录推断 |
|
||||
| `--provider <group>` | 必填,条目所属的 Provider 分组。它绝不由模型 id 推导:网关会以上游 id 转售厂商模型,猜错分组会把凭据写到另一家厂商的接口上。内置分组之外的接口一律用 `custom`。 |
|
||||
| `--api-key <key>` | API Key,内联存入 Project 隐藏文件 `.project_config.toml` |
|
||||
| `--base-url <url>` | 自定义接口地址 |
|
||||
| `--context-window <n>` | 上下文窗口大小 |
|
||||
|
||||
@@ -35,7 +35,7 @@ The openrouter, siliconflow, and custom groups speak the OpenAI-compatible proto
|
||||
|
||||
## Project config
|
||||
|
||||
`<root>/<project>/.project_config.toml` is the Project's single config file: a hidden file written with mode 0600, with credentials inlined on the model entries. Model identity is always the `(provider, model_id)` pair — string concatenation is forbidden everywhere.
|
||||
`<root>/<project>/.project_config.toml` is the Project's single config file: a hidden file written with mode 0600, with credentials inlined on the model entries. Model identity is always the `(provider, model_id)` pair — string concatenation is forbidden everywhere, and every reference into this file carries both halves: the provider is never inferred from a bare `model_id`.
|
||||
|
||||
| Field | Description |
|
||||
| --- | --- |
|
||||
@@ -143,9 +143,11 @@ compaction:
|
||||
| `{{PLATFORM}}` | Runtime platform |
|
||||
| `{{OS_VERSION}}` | Operating system version |
|
||||
| `{{DATE}}` | Current date |
|
||||
| `{{CWD}}` | Workspace path |
|
||||
| `{{AGENT_ID}}` | Agent id |
|
||||
| `{{PROJECT_DIR}}` | Project directory |
|
||||
| `{{AGENT_ID}}` | Agent id |
|
||||
| `{{CWD}}` | Workspace path |
|
||||
| `{{PROVIDER}}` | Model provider group |
|
||||
| `{{MODEL_ID}}` | Upstream model id |
|
||||
| `{{SESSION_ID}}` | Session id |
|
||||
|
||||
`agent_state/AGENTS.md` is the developer-editable instruction file, injected via `{{AGENTS_MD}}` and empty by default — it is also the file an optimizer edits most (see [Self-Improvement](/self-improvement)).
|
||||
@@ -157,6 +159,7 @@ compaction:
|
||||
- Key names must match `^[A-Za-z_][A-Za-z0-9_]*$` (shell environment variable naming rules);
|
||||
- Values are injected only into tool subprocess environments and never enter the model context or the Trace;
|
||||
- Only key names are disclosed in the system prompt via `{{VAULT_KEYS}}`;
|
||||
- Saving through the Web/API invalidates the Agent's cached Session runtimes: the next Task on any of its Sessions re-resumes and runs with the new values; a Task already in flight keeps the values it started with (a direct CLI file edit reaches a running server only when a Session is next created or resumed);
|
||||
- Managed via `penguin config vault set/list/remove` or the Web Vault tab.
|
||||
|
||||
## Schedules
|
||||
@@ -172,7 +175,7 @@ Each file `agent_state/schedule/<name>.toml` describes one scheduled task (the f
|
||||
| `end_at` | no | End time; must be later than `start_at` |
|
||||
| `session_id` | no | Bind to an existing Session; mutually exclusive with the three fields below |
|
||||
| `workspace` | no | Workspace for new-Session mode |
|
||||
| `provider` / `model_id` | no | Paired model reference for new-Session mode |
|
||||
| `provider` / `model_id` | no | Paired model reference for new-Session mode; write both or neither — a lone `model_id` is rejected, and with neither the Project's default model is used |
|
||||
|
||||
```toml
|
||||
prompt = "Check yesterday's builds and summarize the failures"
|
||||
|
||||
@@ -35,7 +35,7 @@ openrouter、siliconflow 与 custom 分组走 OpenAI 兼容协议,因此复用
|
||||
|
||||
## Project 配置
|
||||
|
||||
`<root>/<project>/.project_config.toml` 是 Project 唯一的配置文件:隐藏文件,落盘权限 0600,凭证内联在模型条目上。模型身份始终是 `(provider, model_id)` 成对引用,禁止任何形式的字符串拼接。
|
||||
`<root>/<project>/.project_config.toml` 是 Project 唯一的配置文件:隐藏文件,落盘权限 0600,凭证内联在模型条目上。模型身份始终是 `(provider, model_id)` 成对引用,禁止任何形式的字符串拼接;指向本文件的每一处引用都要带上两半,provider 绝不由裸 `model_id` 推断。
|
||||
|
||||
| 字段 | 说明 |
|
||||
| --- | --- |
|
||||
@@ -143,9 +143,11 @@ compaction:
|
||||
| `{{PLATFORM}}` | 运行平台 |
|
||||
| `{{OS_VERSION}}` | 操作系统版本 |
|
||||
| `{{DATE}}` | 当前日期 |
|
||||
| `{{CWD}}` | Workspace 路径 |
|
||||
| `{{AGENT_ID}}` | Agent id |
|
||||
| `{{PROJECT_DIR}}` | Project 目录 |
|
||||
| `{{AGENT_ID}}` | Agent id |
|
||||
| `{{CWD}}` | Workspace 路径 |
|
||||
| `{{PROVIDER}}` | 模型 provider 分组 |
|
||||
| `{{MODEL_ID}}` | 上游模型 id |
|
||||
| `{{SESSION_ID}}` | Session id |
|
||||
|
||||
`agent_state/AGENTS.md` 是开发者可编辑的指令文件,经 `{{AGENTS_MD}}` 注入系统提示词,缺省为空——它也是优化器最常改动的文件(见[自我进化](/self-improvement))。
|
||||
@@ -157,6 +159,7 @@ compaction:
|
||||
- 键名须匹配 `^[A-Za-z_][A-Za-z0-9_]*$`(shell 环境变量命名规则);
|
||||
- 值只注入工具子进程的环境变量,永远不进入模型上下文与 Trace;
|
||||
- 系统提示词中经 `{{VAULT_KEYS}}` 只披露键名;
|
||||
- 经 Web/API 保存会使该 Agent 已缓存的 Session 运行时失效:其任意 Session 的下一个任务会重新恢复(resume)并使用新值;进行中的任务保持其启动时的值(CLI 直接改文件对运行中的 server 则要等 Session 下次创建或恢复时生效);
|
||||
- 通过 CLI `penguin config vault set/list/remove` 或 Web 的 Vault 标签页管理。
|
||||
|
||||
## 定时任务
|
||||
@@ -172,7 +175,7 @@ compaction:
|
||||
| `end_at` | 否 | 结束时刻,须晚于 `start_at` |
|
||||
| `session_id` | 否 | 绑定既有 Session;与下列三项互斥 |
|
||||
| `workspace` | 否 | 新建 Session 模式的 Workspace |
|
||||
| `provider` / `model_id` | 否 | 新建 Session 模式的模型成对引用 |
|
||||
| `provider` / `model_id` | 否 | 新建 Session 模式的模型成对引用;要写就两个都写,只写 `model_id` 会被拒绝,两个都不写则使用 Project 默认模型 |
|
||||
|
||||
```toml
|
||||
prompt = "检查昨日构建结果并汇总失败原因"
|
||||
|
||||
@@ -13,7 +13,7 @@ description: Install PenguinHarness via the install script, npm, or from source.
|
||||
On Linux / macOS:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://github.com/Prism-Shadow/penguin-harness/releases/latest/download/install.sh | sh
|
||||
curl -fsSL https://penguin.ooo/install.sh | sh
|
||||
```
|
||||
|
||||
The script downloads the matching `penguin-{linux,darwin}-{x64,arm64}.tar.gz`, which bundles an official Node.js runtime. Other platforms do **not** fall back automatically: the script exits and asks you to install Node.js >= 24 and re-run with `--universal`, which selects the runtime-less `penguin-universal.tar.gz`.
|
||||
@@ -34,7 +34,7 @@ penguin -v
|
||||
| Integrity check | Downloads are sha256-verified when the Release ships checksum assets |
|
||||
| Upgrade | Re-run the install script; files are swapped atomically |
|
||||
|
||||
Script flags are passed as `curl ... | sh -s -- --universal`.
|
||||
Script flags go after `sh -s --`, e.g. `curl -fsSL https://penguin.ooo/install.sh | sh -s -- --universal`.
|
||||
|
||||
### Data directory
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ description: 通过安装脚本、npm 或源码安装 PenguinHarness。
|
||||
在 Linux / macOS 上执行:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://github.com/Prism-Shadow/penguin-harness/releases/latest/download/install.sh | sh
|
||||
curl -fsSL https://penguin.ooo/install.sh | sh
|
||||
```
|
||||
|
||||
脚本按平台下载 `penguin-{linux,darwin}-{x64,arm64}.tar.gz`,其中捆绑了官方 Node.js 运行时。其他平台**不会自动回退**:脚本会退出并提示先安装 Node.js >= 24、再携带 `--universal` 重新执行,改用不含运行时的 `penguin-universal.tar.gz`。
|
||||
@@ -34,7 +34,7 @@ penguin -v
|
||||
| 完整性校验 | Release 提供 checksum 资产时自动进行 sha256 校验 |
|
||||
| 升级 | 重新执行安装脚本即可,文件原子替换 |
|
||||
|
||||
脚本参数通过 `curl ... | sh -s -- --universal` 的形式传入。
|
||||
脚本参数写在 `sh -s --` 之后,例如 `curl -fsSL https://penguin.ooo/install.sh | sh -s -- --universal`。
|
||||
|
||||
### 数据目录
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ interface GenerativeModelConfig {
|
||||
`GenerativeModel` (`packages/core/src/llm/generative-model.ts`) grounds the contract on the `AutoLLMClient` of the `@prismshadow/agenthub` model gateway:
|
||||
|
||||
- the gateway maintains conversation history **statefully**, receiving only new messages each turn; resuming a Session replays committed history through a one-time `setHistory`;
|
||||
- an internal `EventTranslator` translates gateway stream events into `partial_*` fragments plus complete messages, preserving the `signature` / `phase` fidelity fields; complete messages settle in thinking → text → tool_call order;
|
||||
- an internal `EventTranslator` translates gateway stream events into `partial_*` fragments plus complete messages, preserving each item's opaque `fidelity` payload verbatim; segmentation mirrors the gateway's own aggregation — a thinking block is closed by its fidelity payload and a run of equal fidelity stays one block (OpenAI-compatible clients stamp every delta with the same `{ reasoning_field }`, which must not split blocks), while a text segment splits on a differing `fidelity.phase` and closes on a `fidelity.signature`, fidelity keys accumulating on merge; complete messages settle in thinking → text → tool_call order;
|
||||
- `ToolCallIdAllocator` disambiguates providers that use the function name as the call id (append `#n` inbound, strip outbound), scoped to the whole Session;
|
||||
- provider differences (tool-call formats, reasoning content, streaming events) are absorbed entirely inside the gateway — see [Models & Providers](/models).
|
||||
|
||||
@@ -169,7 +169,7 @@ A tool emits only content deltas; framing, timeouts, truncation, `stop_reason` p
|
||||
Human is deliberately not an interface class. The SDK caller *is* the Human:
|
||||
|
||||
```ts
|
||||
const session = await agent.createSession({ workspaceDir, modelId });
|
||||
const session = await agent.createSession({ workspaceDir, provider, modelId });
|
||||
|
||||
session.run(
|
||||
newMessages: OmniMessage[], // input: the Prompt
|
||||
|
||||
@@ -89,7 +89,7 @@ interface GenerativeModelConfig {
|
||||
`GenerativeModel`(`packages/core/src/llm/generative-model.ts`)把契约落到模型网关 `@prismshadow/agenthub` 的 `AutoLLMClient` 上:
|
||||
|
||||
- 网关**有状态**地维护会话历史,每轮只接收新消息;恢复 Session 时经一次性的 `setHistory` 重放已提交历史;
|
||||
- 内部的 `EventTranslator` 把网关流式事件翻译为 `partial_*` 分片 + 完整消息,保留 `signature` / `phase` 保真字段,完整消息按 thinking → text → tool_call 顺序落盘;
|
||||
- 内部的 `EventTranslator` 把网关流式事件翻译为 `partial_*` 分片 + 完整消息,逐条原样保留不透明的 `fidelity` 保真负载;分段与网关自身的聚合一致——thinking 块由其 fidelity 负载闭合,连续相同的 fidelity 归为同一块(OpenAI 兼容客户端给每条增量盖同一个 `{ reasoning_field }`,不能因此切块),text 段遇到不同的 `fidelity.phase` 即切分、遇到 `fidelity.signature` 即闭合,合并时 fidelity 键累积;完整消息按 thinking → text → tool_call 顺序落盘;
|
||||
- `ToolCallIdAllocator` 处理个别 Provider 用函数名充当调用 id 的情况(入站追加 `#n`、出站剥离),作用域覆盖整个 Session;
|
||||
- Provider 协议差异(工具调用格式、思考内容、流式事件)全部在网关内抹平,见[模型与 Provider](/models)。
|
||||
|
||||
@@ -169,7 +169,7 @@ interface ToolDefinitionConfig {
|
||||
Human 刻意不设计为接口类。SDK 的调用方就是 Human:
|
||||
|
||||
```ts
|
||||
const session = await agent.createSession({ workspaceDir, modelId });
|
||||
const session = await agent.createSession({ workspaceDir, provider, modelId });
|
||||
|
||||
session.run(
|
||||
newMessages: OmniMessage[], // 输入:Prompt
|
||||
|
||||
@@ -11,6 +11,8 @@ All model access goes through one gateway library: `@prismshadow/agenthub` (Auto
|
||||
|
||||
A model's identity is always the `(provider, model_id)` pair: `provider` is a config group name, `model_id` the upstream request id sent to AgentHub unchanged. The two are independent fields — concatenating them into one string is forbidden anywhere in the pipeline.
|
||||
|
||||
Every interface that names a model takes the complete pair: the CLI, the HTTP API, and the SDK all reject half a reference instead of completing it. The provider is never inferred from the model id and has no default, because gateways resell vendor models under their upstream ids — a guessed group would send the entry's credential to a vendor nobody named. Where a model reference is optional at all (`penguin run` / `chat`, Session creation, Schedules), the choice is between the whole pair and nothing: omit both halves to take the Project's default model.
|
||||
|
||||
## The per-Project model table
|
||||
|
||||
Each Project's available models are recorded in the hidden `.project_config.toml`, maintained via the CLI (`penguin config model add / default / list`, see [CLI Reference](/cli)) or the Web UI — never hand-edited. `ModelEntry` fields:
|
||||
@@ -57,7 +59,10 @@ Built-in groups and their env-var fallbacks (catalog source: `packages/core/src/
|
||||
| --- | --- | --- |
|
||||
| deepseek | `DEEPSEEK_API_KEY` | Group of the default model |
|
||||
| openrouter | `OPENAI_API_KEY` | OpenAI-compatible gateway, preset base URL `https://openrouter.ai/api/v1` |
|
||||
| fireworks | `OPENAI_API_KEY` | Fireworks AI (OpenAI-compatible), preset base URL `https://api.fireworks.ai/inference/v1`; API model ids look like `accounts/fireworks/models/<slug>` |
|
||||
| siliconflow | `OPENAI_API_KEY` | OpenAI-compatible gateway, preset base URL `https://api.siliconflow.cn/v1` |
|
||||
| qwen-token-plan | `OPENAI_API_KEY` | Qwen Token Plan subscription gateway, preset base URL `https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1`; pricing from each model page's official list price (the preview model has only a quota-multiplier promo, no list price) |
|
||||
| qwen-pay-as-you-go | `OPENAI_API_KEY` | Qwen pay-as-you-go (DashScope's OpenAI-compatible endpoint), preset base URL `https://dashscope.aliyuncs.com/compatible-mode/v1`; resold third-party models keep vendor-prefixed ids (e.g. `kimi/kimi-k3`) |
|
||||
| google | `GEMINI_API_KEY` | |
|
||||
| anthropic | `ANTHROPIC_API_KEY` | |
|
||||
| openai | `OPENAI_API_KEY` | |
|
||||
@@ -65,9 +70,9 @@ Built-in groups and their env-var fallbacks (catalog source: `packages/core/src/
|
||||
| moonshot | `MOONSHOT_API_KEY` | |
|
||||
| custom | `OPENAI_API_KEY` | Any OpenAI-protocol endpoint |
|
||||
|
||||
The gateway groups (openrouter / siliconflow) go through AgentHub's OpenAI client, so with blank credentials they read `OPENAI_API_KEY` — not a gateway-specific variable.
|
||||
The gateway groups (openrouter / fireworks / siliconflow / qwen-token-plan / qwen-pay-as-you-go) go through AgentHub's OpenAI client, so with blank credentials they read `OPENAI_API_KEY` — not a gateway-specific variable.
|
||||
|
||||
Some models in the preset catalog: deepseek-v4-pro / deepseek-v4-flash, gemini-3.1-pro-preview, claude-opus-4-8 / claude-sonnet-4-6, gpt-5.5, glm-5.2, kimi-k2.6 (not exhaustive).
|
||||
Some models in the preset catalog: deepseek-v4-pro / deepseek-v4-flash, gemini-3.1-pro-preview, claude-opus-4-8 / claude-sonnet-4-6, gpt-5.5, glm-5.2, kimi-k2.6, qwen3.8-max-preview (not exhaustive).
|
||||
|
||||
## Thinking levels
|
||||
|
||||
|
||||
@@ -11,6 +11,8 @@ description: 经 AgentHub 单一网关接入模型,以 (provider, model_id)
|
||||
|
||||
模型身份永远是 `(provider, model_id)` 成对表示:`provider` 是配置分组名,`model_id` 是原样发给上游的请求 id。二者是两个独立字段,任何环节都不允许拼接成一个字符串。
|
||||
|
||||
所有涉及模型的接口都要求完整的二元组:CLI、HTTP API 与 SDK 都会拒绝半个引用,而不会替你补全。provider 绝不由模型 id 推断,也没有缺省值——网关会以上游 id 转售厂商模型,猜出来的分组会把该条目的凭据发往无人指定的厂商。凡是模型引用本身可省略之处(`penguin run` / `chat`、创建 Session、定时任务),可选的是整对:两半都省略即使用 Project 默认模型。
|
||||
|
||||
## Project 模型表
|
||||
|
||||
每个 Project 的可用模型记录在隐藏文件 `.project_config.toml` 中,由 CLI(`penguin config model add / default / list`,见 [CLI 参考](/cli))或 Web 界面维护,不手工编辑。`ModelEntry` 字段:
|
||||
@@ -57,7 +59,10 @@ api_key = "sk-..."
|
||||
| --- | --- | --- |
|
||||
| deepseek | `DEEPSEEK_API_KEY` | 默认模型所在分组 |
|
||||
| openrouter | `OPENAI_API_KEY` | OpenAI 兼容网关,预置 base URL `https://openrouter.ai/api/v1` |
|
||||
| fireworks | `OPENAI_API_KEY` | Fireworks AI(OpenAI 兼容),预置 base URL `https://api.fireworks.ai/inference/v1`;API 模型 id 形如 `accounts/fireworks/models/<slug>` |
|
||||
| siliconflow | `OPENAI_API_KEY` | OpenAI 兼容网关,预置 base URL `https://api.siliconflow.cn/v1` |
|
||||
| qwen-token-plan | `OPENAI_API_KEY` | Qwen Token Plan 订阅网关,预置 base URL `https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1`;定价取各模型页官方牌价(预览模型仅配额倍率促销、无牌价) |
|
||||
| qwen-pay-as-you-go | `OPENAI_API_KEY` | Qwen 按量付费(DashScope OpenAI 兼容端),预置 base URL `https://dashscope.aliyuncs.com/compatible-mode/v1`;转售第三方模型保留厂商前缀 id(如 `kimi/kimi-k3`) |
|
||||
| google | `GEMINI_API_KEY` | |
|
||||
| anthropic | `ANTHROPIC_API_KEY` | |
|
||||
| openai | `OPENAI_API_KEY` | |
|
||||
@@ -65,9 +70,9 @@ api_key = "sk-..."
|
||||
| moonshot | `MOONSHOT_API_KEY` | |
|
||||
| custom | `OPENAI_API_KEY` | 任意 OpenAI 协议端点 |
|
||||
|
||||
网关分组(openrouter / siliconflow)经 AgentHub 的 OpenAI 客户端请求,因此凭证留空时读取的是 `OPENAI_API_KEY`,而非网关自己的变量名。
|
||||
网关分组(openrouter / fireworks / siliconflow / qwen-token-plan / qwen-pay-as-you-go)经 AgentHub 的 OpenAI 客户端请求,因此凭证留空时读取的是 `OPENAI_API_KEY`,而非网关自己的变量名。
|
||||
|
||||
预置目录中的部分模型:deepseek-v4-pro / deepseek-v4-flash、gemini-3.1-pro-preview、claude-opus-4-8 / claude-sonnet-4-6、gpt-5.5、glm-5.2、kimi-k2.6 等(非完整清单)。
|
||||
预置目录中的部分模型:deepseek-v4-pro / deepseek-v4-flash、gemini-3.1-pro-preview、claude-opus-4-8 / claude-sonnet-4-6、gpt-5.5、glm-5.2、kimi-k2.6、qwen3.8-max-preview 等(非完整清单)。
|
||||
|
||||
## 思考等级
|
||||
|
||||
|
||||
@@ -54,15 +54,16 @@ On resume, the engine takes this Trace line as the runtime config — the model,
|
||||
|
||||
## model_msg: complete payloads
|
||||
|
||||
Seven content payloads, discriminated by `payload.type`. Shared optional fields: `stop_reason` (marks an abnormal terminal state) and `signature` (a provider-fidelity field, see below):
|
||||
Seven content payloads, discriminated by `payload.type`. Shared optional fields: `stop_reason` (marks an abnormal terminal state) and `fidelity` (an opaque provider-fidelity payload, see below):
|
||||
|
||||
```ts
|
||||
type Fidelity = Record<string, unknown>; // opaque provider-fidelity payload (see below)
|
||||
|
||||
interface TextPayload {
|
||||
type: "text";
|
||||
role: "user" | "assistant";
|
||||
text: string;
|
||||
phase?: string | null; // segmentation marker (e.g. GPT-5 phases)
|
||||
signature?: string;
|
||||
fidelity?: Fidelity; // e.g. { phase } segment marker (GPT-5), { signature }
|
||||
stop_reason?: StopReason;
|
||||
}
|
||||
|
||||
@@ -70,7 +71,7 @@ interface ThinkingPayload {
|
||||
type: "thinking";
|
||||
role: "assistant";
|
||||
thinking: string;
|
||||
signature?: string; // required by some models to replay history
|
||||
fidelity?: Fidelity; // required by some models to replay history
|
||||
stop_reason?: StopReason;
|
||||
}
|
||||
|
||||
@@ -79,7 +80,7 @@ interface InlineThinkingPayload {
|
||||
role: "assistant";
|
||||
data: string; // reasoning content in binary form
|
||||
mime_type: string;
|
||||
signature?: string;
|
||||
fidelity?: Fidelity;
|
||||
stop_reason?: StopReason;
|
||||
}
|
||||
|
||||
@@ -89,7 +90,7 @@ interface ToolCallPayload {
|
||||
name: string;
|
||||
arguments: string; // arguments as a JSON string
|
||||
tool_call_id: string;
|
||||
signature?: string;
|
||||
fidelity?: Fidelity;
|
||||
stop_reason?: StopReason;
|
||||
}
|
||||
|
||||
@@ -114,7 +115,7 @@ interface InlineDataPayload {
|
||||
role: "user" | "assistant";
|
||||
data: string; // other binary content
|
||||
mime_type: string;
|
||||
signature?: string;
|
||||
fidelity?: Fidelity;
|
||||
stop_reason?: StopReason;
|
||||
}
|
||||
```
|
||||
@@ -273,7 +274,20 @@ Errors never cross an interface boundary as exceptions — they *are* messages.
|
||||
|
||||
## Provider-fidelity fields
|
||||
|
||||
Provider-specific fields such as `signature` and `phase` pass through and persist verbatim end to end — some models require them byte-for-byte when history is replayed, and any rewriting would break compatibility. This is one of the preconditions for lossless Session recovery from the Trace.
|
||||
Provider-specific wire data travels in a single optional field, `fidelity` — an arbitrary JSON object the LLM client records to reproduce the original message on replay: thinking signatures, `phase` segment labels, GPT-5 encrypted reasoning, the OpenAI-compatible upstream reasoning field name:
|
||||
|
||||
```ts
|
||||
// Claude: a thinking block closed by its signature
|
||||
{ type: "thinking", thinking: "…", fidelity: { signature: "EqQBCkYIBxgCKkB…" } }
|
||||
|
||||
// GPT-5: encrypted reasoning (empty thinking text, fidelity only)
|
||||
{ type: "thinking", thinking: "", fidelity: { id: "rs_0d3…", encrypted_content: "gAAAA…" } }
|
||||
|
||||
// OpenAI-compatible: the upstream field the reasoning text came from
|
||||
{ type: "thinking", thinking: "…", fidelity: { reasoning_field: "reasoning_content" } }
|
||||
```
|
||||
|
||||
The payload is opaque to PenguinHarness: it passes through and persists verbatim end to end — some models require it byte-for-byte when history is replayed, and any rewriting (or loss) would break compatibility. This is one of the preconditions for lossless Session recovery from the Trace.
|
||||
|
||||
## Three jobs, one protocol
|
||||
|
||||
|
||||
@@ -54,15 +54,16 @@ interface ToolDefinition {
|
||||
|
||||
## model_msg:完整消息
|
||||
|
||||
七种内容 payload,以 `payload.type` 判别。公共可选字段:`stop_reason`(非正常收尾时标注终态)与 `signature`(Provider 保真字段,见下文):
|
||||
七种内容 payload,以 `payload.type` 判别。公共可选字段:`stop_reason`(非正常收尾时标注终态)与 `fidelity`(不透明的 Provider 保真负载,见下文):
|
||||
|
||||
```ts
|
||||
type Fidelity = Record<string, unknown>; // 不透明的 Provider 保真负载(见下文)
|
||||
|
||||
interface TextPayload {
|
||||
type: "text";
|
||||
role: "user" | "assistant";
|
||||
text: string;
|
||||
phase?: string | null; // 分段标记(如 GPT-5 的 phase)
|
||||
signature?: string;
|
||||
fidelity?: Fidelity; // 如 { phase } 分段标记(GPT-5)、{ signature }
|
||||
stop_reason?: StopReason;
|
||||
}
|
||||
|
||||
@@ -70,7 +71,7 @@ interface ThinkingPayload {
|
||||
type: "thinking";
|
||||
role: "assistant";
|
||||
thinking: string;
|
||||
signature?: string; // 部分模型历史回放所必需
|
||||
fidelity?: Fidelity; // 部分模型历史回放所必需
|
||||
stop_reason?: StopReason;
|
||||
}
|
||||
|
||||
@@ -79,7 +80,7 @@ interface InlineThinkingPayload {
|
||||
role: "assistant";
|
||||
data: string; // 二进制形态的思考内容
|
||||
mime_type: string;
|
||||
signature?: string;
|
||||
fidelity?: Fidelity;
|
||||
stop_reason?: StopReason;
|
||||
}
|
||||
|
||||
@@ -89,7 +90,7 @@ interface ToolCallPayload {
|
||||
name: string;
|
||||
arguments: string; // 参数 JSON 字符串
|
||||
tool_call_id: string;
|
||||
signature?: string;
|
||||
fidelity?: Fidelity;
|
||||
stop_reason?: StopReason;
|
||||
}
|
||||
|
||||
@@ -114,7 +115,7 @@ interface InlineDataPayload {
|
||||
role: "user" | "assistant";
|
||||
data: string; // 其他二进制内容
|
||||
mime_type: string;
|
||||
signature?: string;
|
||||
fidelity?: Fidelity;
|
||||
stop_reason?: StopReason;
|
||||
}
|
||||
```
|
||||
@@ -272,7 +273,20 @@ type StopReason = "completed" | "failed" | "aborted" | "timeout" | "malformed";
|
||||
|
||||
## 保真字段
|
||||
|
||||
`signature` 与 `phase` 等 Provider 专有字段在整条链路上原样透传、原样存储——部分模型在历史回放时要求逐字一致,任何转写都会破坏兼容性。这是 Trace 能够无损恢复 Session 的前提之一。
|
||||
Provider 专有的线上数据统一收拢在一个可选字段 `fidelity` 中——LLM 客户端为历史回放记录的任意 JSON 对象:思考签名、`phase` 分段标记、GPT-5 加密推理、OpenAI 兼容上游的推理字段名:
|
||||
|
||||
```ts
|
||||
// Claude:由签名闭合的 thinking 块
|
||||
{ type: "thinking", thinking: "…", fidelity: { signature: "EqQBCkYIBxgCKkB…" } }
|
||||
|
||||
// GPT-5:加密推理(thinking 文本为空,仅有 fidelity)
|
||||
{ type: "thinking", thinking: "", fidelity: { id: "rs_0d3…", encrypted_content: "gAAAA…" } }
|
||||
|
||||
// OpenAI 兼容:思考内容来自上游哪个字段
|
||||
{ type: "thinking", thinking: "…", fidelity: { reasoning_field: "reasoning_content" } }
|
||||
```
|
||||
|
||||
该负载对 PenguinHarness 完全不透明:在整条链路上原样透传、原样存储——部分模型在历史回放时要求逐字一致,任何转写或丢失都会破坏兼容性。这是 Trace 能够无损恢复 Session 的前提之一。
|
||||
|
||||
## 协议的三种职责
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ description: Install PenguinHarness, configure a model, and run your first Task.
|
||||
One-liner for Linux / macOS:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://github.com/Prism-Shadow/penguin-harness/releases/latest/download/install.sh | sh
|
||||
curl -fsSL https://penguin.ooo/install.sh | sh
|
||||
```
|
||||
|
||||
For other options (npm, from source), see [Installation](/installation).
|
||||
@@ -18,10 +18,10 @@ For other options (npm, from source), see [Installation](/installation).
|
||||
PenguinHarness ships with no built-in model credentials, so configure a model first. Use the Models page in the Web UI, or the CLI:
|
||||
|
||||
```bash
|
||||
penguin config model add --model-id deepseek-v4-pro --api-key sk-... --set-default
|
||||
penguin config model add --provider deepseek --model-id deepseek-v4-pro --api-key sk-... --set-default
|
||||
```
|
||||
|
||||
- When `--provider` is omitted, the Provider is inferred from the built-in catalog.
|
||||
- A model is always referenced as a `(provider, model_id)` pair, so `--provider` and `--model-id` are both required — the Provider is never inferred from the model id. See [Models & Providers](/models) for the built-in groups.
|
||||
- The API key can also come from environment variables: when a model entry has no inline api_key, AgentHub (the LLM gateway library) reads variables such as `DEEPSEEK_API_KEY`, `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, and `GEMINI_API_KEY`. A `.env` file in the working directory is loaded automatically.
|
||||
|
||||
## Start the Web App
|
||||
@@ -30,7 +30,7 @@ penguin config model add --model-id deepseek-v4-pro --api-key sk-... --set-defau
|
||||
penguin web
|
||||
```
|
||||
|
||||
The service runs at http://127.0.0.1:7364 and opens your browser (`--no-open` to skip). First login is `admin` / `admin123` — change it right away. `penguin server` starts the same process headless.
|
||||
The service runs at http://127.0.0.1:7364 and opens your browser (`--no-open` to skip). First login is `admin` / `penguin-2026` — change it right away. `penguin server` starts the same process headless.
|
||||
|
||||
## One-shot run
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ description: 安装 PenguinHarness、配置模型并运行第一个 Task。
|
||||
Linux / macOS 一键安装:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://github.com/Prism-Shadow/penguin-harness/releases/latest/download/install.sh | sh
|
||||
curl -fsSL https://penguin.ooo/install.sh | sh
|
||||
```
|
||||
|
||||
其他方式(npm、源码)见[安装](/installation)。
|
||||
@@ -18,10 +18,10 @@ curl -fsSL https://github.com/Prism-Shadow/penguin-harness/releases/latest/downl
|
||||
PenguinHarness 不内置任何模型凭据,使用前需要先配置一个模型。可以在 Web UI 的 Models 页面完成,也可以用 CLI:
|
||||
|
||||
```bash
|
||||
penguin config model add --model-id deepseek-v4-pro --api-key sk-... --set-default
|
||||
penguin config model add --provider deepseek --model-id deepseek-v4-pro --api-key sk-... --set-default
|
||||
```
|
||||
|
||||
- 省略 `--provider` 时,根据内置目录自动推断 Provider。
|
||||
- 模型引用始终是 `(provider, model_id)` 二元组,因此 `--provider` 与 `--model-id` 均为必填——Provider 绝不由模型 id 推断。内置分组见[模型与 Provider](/models)。
|
||||
- API Key 也可以来自环境变量:当模型条目没有内联 api_key 时,LLM 网关库 AgentHub 会读取 `DEEPSEEK_API_KEY`、`ANTHROPIC_API_KEY`、`OPENAI_API_KEY`、`GEMINI_API_KEY` 等变量;工作目录下的 `.env` 会被自动加载。
|
||||
|
||||
## 启动 Web App
|
||||
@@ -30,7 +30,7 @@ penguin config model add --model-id deepseek-v4-pro --api-key sk-... --set-defau
|
||||
penguin web
|
||||
```
|
||||
|
||||
服务运行在 http://127.0.0.1:7364 并自动打开浏览器(`--no-open` 跳过)。首次登录使用 `admin` / `admin123`,请立即修改密码。`penguin server` 启动同一进程的 headless 版本。
|
||||
服务运行在 http://127.0.0.1:7364 并自动打开浏览器(`--no-open` 跳过)。首次登录使用 `admin` / `penguin-2026`,请立即修改密码。`penguin server` 启动同一进程的 headless 版本。
|
||||
|
||||
## 单次运行
|
||||
|
||||
|
||||
@@ -35,12 +35,12 @@ packages/server/src
|
||||
|
||||
- Cookie session: `penguin_session` (HttpOnly, SameSite=Lax), valid for 7 days with sliding renewal;
|
||||
- Passwords are stored as scrypt hashes; the server keeps only the sha256 of the session token, never the plaintext;
|
||||
- No open registration: the built-in admin `admin` / `admin123` is seeded at startup, and all other accounts are created by an admin;
|
||||
- No open registration: the built-in admin `admin` / `penguin-2026` is seeded at startup, and all other accounts are created by an admin;
|
||||
- Same-origin only — no CORS middleware is enabled.
|
||||
|
||||
```bash
|
||||
curl -c cookies.txt -H "Content-Type: application/json" \
|
||||
-d '{"userId":"admin","password":"admin123"}' \
|
||||
-d '{"userId":"admin","password":"penguin-2026"}' \
|
||||
http://127.0.0.1:7364/api/auth/login
|
||||
```
|
||||
|
||||
@@ -87,6 +87,8 @@ Member writes are owner-only.
|
||||
| PUT | /api/projects/:projectId/models | Full-table replace, keyed by `(provider, modelId)` |
|
||||
| POST | /api/projects/:projectId/models/test | Connectivity test: `{provider, modelId, …}` → `{ok, latencyMs?, message?}` |
|
||||
|
||||
Every endpoint that names a model takes the complete `(provider, modelId)` pair. Nothing is inferred: a request carrying only one half is a 400, never a lookup. Where the reference itself is optional (Session creation, Schedules), omitting both halves selects the Project's default model.
|
||||
|
||||
### Agents
|
||||
|
||||
The paths below omit the `/api/projects/:projectId` prefix.
|
||||
@@ -110,7 +112,7 @@ The paths below omit the `/api/projects/:projectId` prefix.
|
||||
| GET / POST | /agents/:agentId/schedules | List scheduled tasks / create one (409 if the name exists) |
|
||||
| GET / PUT / DELETE | /agents/:agentId/schedules/:name | Read / update / delete a single task |
|
||||
|
||||
Schedule writes are owner-only.
|
||||
Schedule writes are owner-only. A task in new-Session mode carries `modelId` and `provider` together or not at all; the pair is checked against the Project's model table when the task is saved and again when the scheduler reconciles it.
|
||||
|
||||
### Session Creation and Directory Browsing
|
||||
|
||||
@@ -120,7 +122,7 @@ Schedule writes are owner-only.
|
||||
| POST | /agents/:agentId/sessions | Create a Session: `{modelId?, provider?, workspace?, approvalMode?}` → 201 |
|
||||
| GET | /dirs?path= | Server-side directory browser (backs the Workspace picker) |
|
||||
|
||||
On Session creation, the model defaults to the Project's default model, the Workspace defaults to an auto-created temporary directory, and the approval mode defaults to `allow-all`.
|
||||
On Session creation, `modelId` and `provider` are both-or-neither: send the complete pair to pick a model, or omit both to take the Project's default model — one without the other is a 400. The Workspace defaults to an auto-created temporary directory, and the approval mode defaults to `allow-all`.
|
||||
|
||||
### Usage and Traces (Agent Level)
|
||||
|
||||
@@ -147,7 +149,7 @@ The paths below omit the `/api/sessions/:sessionId` prefix. For the storage mode
|
||||
| POST | /abort | Interrupt the current Task: 202 when triggered, 204 when idle |
|
||||
| POST | /compact | Trigger context compaction: 202; 409 `nothing_to_compact` when there is nothing to compact |
|
||||
| GET | /files?path= | Browse the Workspace directory |
|
||||
| GET | /files/content?path=&download= | Read a Workspace file (`download=1` serves it as an attachment) |
|
||||
| GET | /files/content?path=&download=&preview= | Read a Workspace file (`download=1` serves it as an attachment, `preview=1` renders it in a sandbox — see below) |
|
||||
| POST | /files/stat | Batch existence check: `{paths}` |
|
||||
| PUT | /files/content?path= | Upload a file: `{dataBase64}`, capped at 14MB |
|
||||
| GET | /traces | List this Session's Trace files |
|
||||
@@ -157,6 +159,16 @@ The paths below omit the `/api/sessions/:sessionId` prefix. For the storage mode
|
||||
|
||||
General conventions: Sessions the user cannot access always return 404 — their existence is never leaked; only one Task or compaction runs per Session at a time, and conflicts return 409 (`task_in_progress` / `compacting`).
|
||||
|
||||
Workspace files may be Agent-generated, so `GET /files/content` treats them as untrusted: every response carries `X-Content-Type-Options: nosniff`, and the rest of the headers depend on the two flags (`download=1` wins over `preview=1`):
|
||||
|
||||
| Query | Content-Type | Content-Disposition | Content-Security-Policy |
|
||||
| --- | --- | --- | --- |
|
||||
| neither | `text/plain; charset=utf-8` for `.html` / `.htm` / `.svg`, the real type otherwise | `inline` | — |
|
||||
| `preview=1` | the real type (`text/html`, `image/svg+xml`, …) | `inline` | `sandbox allow-scripts allow-popups allow-modals allow-forms`, sent only for `.html` / `.htm` / `.svg` |
|
||||
| `download=1` | the real type | `attachment` | — |
|
||||
|
||||
The filename always rides along as `filename*=UTF-8''` with percent-encoding. `preview=1` is what backs "open in a new tab": the document keeps its real type and does render and run, but the sandbox deliberately omits `allow-same-origin`, so it lands in an opaque origin and can reach neither this origin's cookies nor the API — while the request itself still authenticates, because a top-level GET sends the SameSite=Lax session cookie.
|
||||
|
||||
Key request bodies (explicit keys):
|
||||
|
||||
```ts
|
||||
|
||||
@@ -35,12 +35,12 @@ packages/server/src
|
||||
|
||||
- Cookie 会话:`penguin_session`(HttpOnly、SameSite=Lax),有效期 7 天,滑动续期;
|
||||
- 密码以 scrypt 哈希存储;服务端只保存会话 Token 的 sha256,不落明文;
|
||||
- 不开放注册:启动时种子化内置管理员 `admin` / `admin123`,其余账号由管理员创建;
|
||||
- 不开放注册:启动时种子化内置管理员 `admin` / `penguin-2026`,其余账号由管理员创建;
|
||||
- 仅限同源访问,未启用 CORS 中间件。
|
||||
|
||||
```bash
|
||||
curl -c cookies.txt -H "Content-Type: application/json" \
|
||||
-d '{"userId":"admin","password":"admin123"}' \
|
||||
-d '{"userId":"admin","password":"penguin-2026"}' \
|
||||
http://127.0.0.1:7364/api/auth/login
|
||||
```
|
||||
|
||||
@@ -87,6 +87,8 @@ curl -c cookies.txt -H "Content-Type: application/json" \
|
||||
| PUT | /api/projects/:projectId/models | 全表替换,条目以 `(provider, modelId)` 为键 |
|
||||
| POST | /api/projects/:projectId/models/test | 连通性测试:`{provider, modelId, …}` → `{ok, latencyMs?, message?}` |
|
||||
|
||||
所有涉及模型的接口都要求完整的 `(provider, modelId)` 二元组,不做任何推断:只带一半的请求一律 400,绝不会退化为一次查找。模型引用本身可省略的场景(创建 Session、定时任务)省略的是整对,两半都不给即选用 Project 默认模型。
|
||||
|
||||
### Agent
|
||||
|
||||
以下路径均省略前缀 `/api/projects/:projectId`。
|
||||
@@ -110,7 +112,7 @@ curl -c cookies.txt -H "Content-Type: application/json" \
|
||||
| GET / POST | /agents/:agentId/schedules | 定时任务列表 / 创建(重名返回 409) |
|
||||
| GET / PUT / DELETE | /agents/:agentId/schedules/:name | 读取 / 更新 / 删除单个任务 |
|
||||
|
||||
Schedule 写操作仅限 Owner。
|
||||
Schedule 写操作仅限 Owner。新建 Session 模式的任务,`modelId` 与 `provider` 要么成对给出、要么都不给;该二元组会在任务保存时以及调度器对账时对照 Project 模型表校验。
|
||||
|
||||
### Session 创建与目录浏览
|
||||
|
||||
@@ -120,7 +122,7 @@ Schedule 写操作仅限 Owner。
|
||||
| POST | /agents/:agentId/sessions | 创建 Session:`{modelId?, provider?, workspace?, approvalMode?}` → 201 |
|
||||
| GET | /dirs?path= | 服务器端目录浏览(Workspace 选择器数据源) |
|
||||
|
||||
创建 Session 时,模型默认取 Project 默认模型,Workspace 默认自动创建临时目录,审批模式默认 `allow-all`。
|
||||
创建 Session 时,`modelId` 与 `provider` 要么成对给出、要么都不给:给出完整二元组即指定模型,两个都省略则取 Project 默认模型,只给一个返回 400。Workspace 默认自动创建临时目录,审批模式默认 `allow-all`。
|
||||
|
||||
### 用量与 Trace(Agent 级)
|
||||
|
||||
@@ -147,7 +149,7 @@ Schedule 写操作仅限 Owner。
|
||||
| POST | /abort | 中断当前 Task:已触发返回 202,无任务返回 204 |
|
||||
| POST | /compact | 触发上下文压缩:202;无可压缩内容返回 409 `nothing_to_compact` |
|
||||
| GET | /files?path= | 浏览 Workspace 目录 |
|
||||
| GET | /files/content?path=&download= | 读取 Workspace 文件(`download=1` 时作为附件下载) |
|
||||
| GET | /files/content?path=&download=&preview= | 读取 Workspace 文件(`download=1` 时作为附件下载,`preview=1` 以沙箱方式预览 —— 见下) |
|
||||
| POST | /files/stat | 批量存在性检查:`{paths}` |
|
||||
| PUT | /files/content?path= | 上传文件:`{dataBase64}`,上限 14MB |
|
||||
| GET | /traces | 本 Session 的 Trace 文件列表 |
|
||||
@@ -157,6 +159,16 @@ Schedule 写操作仅限 Owner。
|
||||
|
||||
通用约定:无权访问的 Session 一律返回 404,不泄露其存在性;每个 Session 同时只允许一个 Task 或压缩在运行,冲突时返回 409(`task_in_progress` / `compacting`)。
|
||||
|
||||
Workspace 文件可能由 Agent 生成,`GET /files/content` 一律按不可信内容处理:所有响应都带 `X-Content-Type-Options: nosniff`,其余响应头取决于两个开关(`download=1` 优先于 `preview=1`):
|
||||
|
||||
| 查询参数 | Content-Type | Content-Disposition | Content-Security-Policy |
|
||||
| --- | --- | --- | --- |
|
||||
| 都不带 | `.html` / `.htm` / `.svg` 降级为 `text/plain; charset=utf-8`,其余为真实类型 | `inline` | 无 |
|
||||
| `preview=1` | 真实类型(`text/html`、`image/svg+xml` 等) | `inline` | `sandbox allow-scripts allow-popups allow-modals allow-forms`,仅对 `.html` / `.htm` / `.svg` 下发 |
|
||||
| `download=1` | 真实类型 | `attachment` | 无 |
|
||||
|
||||
文件名始终以 `filename*=UTF-8''` 形式携带(百分号编码)。`preview=1` 支撑的是“在新标签页打开”:文档保留真实类型,可以正常渲染并执行脚本,但沙箱刻意不含 `allow-same-origin`,因此它落在一个不透明源里,既拿不到本源的 Cookie,也调不动 API;而请求本身仍然要鉴权 —— 顶层 GET 会带上 SameSite=Lax 的会话 Cookie。
|
||||
|
||||
关键请求体(明确键名):
|
||||
|
||||
```ts
|
||||
|
||||
@@ -81,7 +81,7 @@ Special case: if the latest Trace file ends with a completed compaction, that co
|
||||
|
||||
## Field fidelity
|
||||
|
||||
Provider-specific fields (such as `signature` and `phase`) are preserved verbatim in the Trace and sent back verbatim — some models require them byte-for-byte on history replay, and any rewriting would break compatibility. This is one reason the Trace stores raw OmniMessage envelopes rather than a post-processed format.
|
||||
Each content message's opaque provider `fidelity` payload (thinking signatures, phase labels, encrypted reasoning, …) is preserved verbatim in the Trace and sent back verbatim — some models require it byte-for-byte on history replay, and any rewriting would break compatibility. This is one reason the Trace stores raw OmniMessage envelopes rather than a post-processed format.
|
||||
|
||||
## Observability
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ Trace 是恢复的唯一事实来源,没有独立的会话数据库需要与
|
||||
|
||||
## 字段保真
|
||||
|
||||
Provider 专有字段(如 `signature`、`phase`)在 Trace 中原样保存、原样回传——部分模型在历史回放时要求这些字段逐字一致,任何转写都会破坏兼容性。这也是 Trace 直接存储 OmniMessage 信封而非二次加工格式的原因之一。
|
||||
内容消息携带的不透明 Provider 保真负载 `fidelity`(思考签名、phase 分段标记、加密推理等)在 Trace 中原样保存、原样回传——部分模型在历史回放时要求该负载逐字一致,任何转写都会破坏兼容性。这也是 Trace 直接存储 OmniMessage 信封而非二次加工格式的原因之一。
|
||||
|
||||
## 可观测性
|
||||
|
||||
|
||||
@@ -58,16 +58,17 @@ The built-in Skills, by group (the group manifest is `SKILL_GROUPS` in `packages
|
||||
|
||||
| Group | Skill | Purpose |
|
||||
| --- | --- | --- |
|
||||
| Agent Development | `agent-creation` | Turn a user requirement into a concrete agent: write the target agent's AGENTS.md and install the skills it needs |
|
||||
| Office Productivity | `data-analysis` | Complete data-analysis tasks with bounded evidence inspection, explicit answer-changing decisions, native artifact handling and final output verification |
|
||||
| | `firecrawl` | Web search and page scraping into clean markdown via the Firecrawl API |
|
||||
| Software Development | `web-design` | Penguin visual language for generated web pages and app UIs: design tokens, components, light/dark themes and chat layouts |
|
||||
| | `software-engineering` | Complete software-engineering tasks: investigate and review code, implement fixes, features and refactors with minimal scope, validate changes, and report verified outcomes |
|
||||
| AI App Development | `penguin-sdk` | Build AI and RAG apps on the SDK: the createSession/run streaming loop plus a complete retrieval recipe with chunk-revealing citations |
|
||||
| | `penguin-cli` | Manage model API keys, default models and per-agent Vault secrets with the penguin CLI |
|
||||
| | `agenthub-models` | Call model APIs through `@prismshadow/agenthub`: streaming text, image generation, speech synthesis and embeddings |
|
||||
| Agent Tuning | `agent-creation` | Turn a user requirement into a concrete agent: write the target agent's AGENTS.md and install the skills it needs |
|
||||
| | `benchmark-design` | Design and calibrate a multi-Case capability Benchmark with repeated independent evaluations and a traceable baseline |
|
||||
| | `agent-evaluation` | Run and score exactly one Benchmark Case run, with CLI execution, Trace provenance checks and private Rubric isolation |
|
||||
| | `agent-optimization` | Improve an Agent State from direct feedback or versioned multi-Case Benchmark scores and score-linked Traces |
|
||||
| Data Analysis | `data-analysis` | Complete data-analysis tasks with bounded evidence inspection, explicit answer-changing decisions, native artifact handling and final output verification |
|
||||
| Penguin Development | `penguin-sdk` | Build AI apps on the SDK (the createSession/run streaming loop) |
|
||||
| | `penguin-cli` | Manage model API keys, default models and per-agent Vault secrets with the penguin CLI |
|
||||
| | `agenthub-models` | Call model APIs through `@prismshadow/agenthub`: streaming text, image generation, speech synthesis and embeddings |
|
||||
| Web Development | `web-design` | Default visual language for generated web pages: minimal black-white-gray |
|
||||
| Software Engineering | `software-engineering` | Complete software-engineering tasks: investigate and review code, implement fixes, features and refactors with minimal scope, validate changes, and report verified outcomes |
|
||||
|
||||
## Writing and optimizing Skills
|
||||
|
||||
|
||||
@@ -58,16 +58,17 @@ Skill 库以 npm 包 `@prismshadow/penguin-skills` 发布,tarball 直接携带
|
||||
|
||||
| 分组 | Skill | 说明 |
|
||||
| --- | --- | --- |
|
||||
| Agent 开发 | `agent-creation` | 把用户需求变成具体的 Agent:撰写目标 Agent 的 AGENTS.md 并安装所需 Skill |
|
||||
| 办公效率 | `data-analysis` | 以有界的证据检查、显式的改答案决策、原生产物处理与最终输出校验完成数据分析任务 |
|
||||
| | `firecrawl` | 经 Firecrawl API 做网络搜索与页面抓取,产出干净的 Markdown |
|
||||
| 软件开发 | `web-design` | 生成网页与应用界面的 Penguin 视觉语言:设计令牌、组件配方、明暗主题与聊天布局 |
|
||||
| | `software-engineering` | 完成软件工程任务:调查与审查代码,以最小改动实现修复、特性与重构,验证改动并报告经过确认的结果 |
|
||||
| AI 应用开发 | `penguin-sdk` | 基于 SDK 构建 AI 与 RAG 应用:createSession/run 流式循环,外加带可溯源引用的完整检索配方 |
|
||||
| | `penguin-cli` | 用 penguin CLI 管理模型 API Key、默认模型与各 Agent 的 Vault 密钥 |
|
||||
| | `agenthub-models` | 经 `@prismshadow/agenthub` 调用模型 API:流式文本、图像生成、语音合成与 Embedding |
|
||||
| Agent 调优 | `agent-creation` | 把用户需求变成具体的 Agent:撰写目标 Agent 的 AGENTS.md 并安装所需 Skill |
|
||||
| | `benchmark-design` | 设计并校准多 Case 的能力评测 Benchmark,含重复独立评测与可追溯基线 |
|
||||
| | `agent-evaluation` | 隔离执行并评分单个 Benchmark Case:CLI 执行、Trace 溯源检查、Rubric 私有隔离 |
|
||||
| | `agent-optimization` | 依据直接反馈或带版本的多 Case Benchmark 分数与关联 Trace 改进 Agent State |
|
||||
| 数据分析 | `data-analysis` | 以有界的证据检查、显式的改答案决策、原生产物处理与最终输出校验完成数据分析任务 |
|
||||
| Penguin 开发 | `penguin-sdk` | 基于 SDK 构建 AI 应用(createSession/run 流式循环) |
|
||||
| | `penguin-cli` | 用 penguin CLI 管理模型 API Key、默认模型与各 Agent 的 Vault 密钥 |
|
||||
| | `agenthub-models` | 经 `@prismshadow/agenthub` 调用模型 API:流式文本、图像生成、语音合成与 Embedding |
|
||||
| 网页开发 | `web-design` | 生成网页的默认视觉规范:极简黑白灰 |
|
||||
| 软件工程 | `software-engineering` | 完成软件工程任务:调查与审查代码,以最小改动实现修复、特性与重构,验证改动并报告经过确认的结果 |
|
||||
|
||||
## 编写与优化
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ penguin web
|
||||
# open http://127.0.0.1:7364
|
||||
```
|
||||
|
||||
The initial account is `admin` / `admin123`. There is no self-registration: accounts are created by an admin on the user-management page, and every new user automatically gets an independent initial Project named `<userId>-default_project`. While the initial password is still in use, a banner prompts the user to change it.
|
||||
The initial account is `admin` / `penguin-2026`. There is no self-registration: accounts are created by an admin on the user-management page, and every new user automatically gets an independent initial Project named `<userId>-default_project`. While the initial password is still in use, a banner prompts the user to change it.
|
||||
|
||||
Logins persist for 7 days with sliding renewal; an admin password reset invalidates all of that user's login sessions.
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ penguin web
|
||||
# 打开 http://127.0.0.1:7364
|
||||
```
|
||||
|
||||
初始账号为 `admin` / `admin123`。系统不开放自助注册:账号由管理员在用户管理页创建;每个新用户会自动获得一个独立的初始 Project,命名为 `<userId>-default_project`。仍在使用初始密码时,页面会以横幅提示尽快修改。
|
||||
初始账号为 `admin` / `penguin-2026`。系统不开放自助注册:账号由管理员在用户管理页创建;每个新用户会自动获得一个独立的初始 Project,命名为 `<userId>-default_project`。仍在使用初始密码时,页面会以横幅提示尽快修改。
|
||||
|
||||
登录状态保持 7 天(滑动续期);管理员重置密码会使该用户的全部登录会话失效。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user