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
16 changes: 12 additions & 4 deletions apps/server/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -721,7 +721,9 @@
"agent",
"template",
"deployment",
"file"
"file",
"identity",
"channel"
]
},
"name": {
Expand Down Expand Up @@ -752,7 +754,9 @@
"agent",
"template",
"deployment",
"file"
"file",
"identity",
"channel"
]
},
"name": {
Expand Down Expand Up @@ -787,7 +791,9 @@
"agent",
"template",
"deployment",
"file"
"file",
"identity",
"channel"
]
},
"name": {
Expand Down Expand Up @@ -843,7 +849,9 @@
"agent",
"template",
"deployment",
"file"
"file",
"identity",
"channel"
]
},
"name": {
Expand Down
16 changes: 12 additions & 4 deletions apps/webui/src/lib/api/generated/schema.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,9 @@ export interface paths {
| "agent"
| "template"
| "deployment"
| "file";
| "file"
| "identity"
| "channel";
name: string;
provider: string;
};
Expand All @@ -298,7 +300,9 @@ export interface paths {
| "agent"
| "template"
| "deployment"
| "file";
| "file"
| "identity"
| "channel";
name: string;
provider: string;
}[];
Expand All @@ -315,7 +319,9 @@ export interface paths {
| "agent"
| "template"
| "deployment"
| "file";
| "file"
| "identity"
| "channel";
name: string;
provider: string;
};
Expand All @@ -341,7 +347,9 @@ export interface paths {
| "agent"
| "template"
| "deployment"
| "file";
| "file"
| "identity"
| "channel";
name: string;
provider: string;
}[];
Expand Down
1 change: 1 addition & 0 deletions docs/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ The [`examples/`](../examples) directory has runnable configs for every provider
| Connect an MCP server + vault | [`examples/claude/with-mcp/`](../examples/claude/with-mcp/) · [`examples/qoder/with-mcp/`](../examples/qoder/with-mcp/) · [`examples/ark/with-mcp/`](../examples/ark/with-mcp/) |
| Use an official MCP server | [`examples/bailian/with-mcp/`](../examples/bailian/with-mcp/) |
| Use a vault | [`examples/bailian/with-vault/`](../examples/bailian/with-vault/) · [`examples/qoder/with-vault/`](../examples/qoder/with-vault/) |
| Connect a credential-based IM Channel | [`examples/qoder/with-channel/`](../examples/qoder/with-channel/) |
| Use memory stores | [`examples/qoder/with-memory/`](../examples/qoder/with-memory/) · [`examples/ark/full/`](../examples/ark/full/) |
| Upload local files (Files API) | [`examples/bailian/with-files/`](../examples/bailian/with-files/) · [`examples/ark/with-files/`](../examples/ark/with-files/) |
| Coordinate multiple agents | [`examples/claude/multiagent/`](../examples/claude/multiagent/) · [`examples/ark/multiagent/`](../examples/ark/multiagent/) |
Expand Down
41 changes: 27 additions & 14 deletions docs/guides/deploy-to-qoder.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ providers:

| Feature | Tier |
|---------|:----:|
| Environment, Vault, Skill, Agent, MCP Server, Memory Store, Deployment, Session | native |
| Environment, Vault, Skill, Agent, MCP Server, Memory Store, Deployment, Session, Identity, Channel | native |
| Multi-Agent | unsupported |

A `deployment run` on Qoder creates a native Deployment Run and associated Session. Cron schedules run server-side.
Expand Down Expand Up @@ -94,16 +94,18 @@ agents:
`agents plan/apply/destroy` then manage a Qoder Forward Template (`tmpl_...`) instead of a Managed Agent
(`agent_...`). The default remains Managed Agent delivery when `delivery` is omitted.

Identity is optional for local Forward Session testing. If omitted, OpenCMA finds the enabled Qoder Identity whose
`external_id` is `__qca_admin_identity__`, then sends its real `idn_...` id to the Session API. To run as a specific
user—or when the reserved Identity is unavailable—configure an existing Identity explicitly:
Forward Identity represents an end user in the integrating product. Declare it once by its stable business id and select
it as the project default:

```yaml
defaults:
provider: qoder
session:
qoder:
identity_id: idn_xxx
identity: chen

identities:
chen:
external_id: user_456
name: Chen
```

Then use the same session command to test a Forward-delivered agent:
Expand All @@ -112,14 +114,25 @@ Then use the same session command to test a Forward-delivered agent:
agents session run "Hello" --agent forward-assistant
```

The CLI resolves the applied Template and optional configured Identity, creates a Forward Session, and routes create, send,
stream, get, and archive operations through the Forward gateway. It never creates or updates an Identity as a side effect
of starting a Session. Use `--identity-id idn_xxx` to override the YAML default for one invocation. Managed sessions keep
using the Cloud gateway.
`agents apply` manages the declared Identity. The CLI resolves `defaults.identity` through state, creates a Forward
Session, and routes create, send, stream, get, and archive operations through the Forward gateway. Use
`--identity-id idn_xxx` only as a runtime escape hatch. Managed sessions keep using the Cloud gateway.

Credential-based messaging Channels are declarative too:

```yaml
channels:
support-dingtalk:
agent: forward-assistant
type: dingtalk
credentials:
client_id: ${DINGTALK_CLIENT_ID}
client_secret: ${DINGTALK_CLIENT_SECRET}
```

Production messaging delivery is a separate Forward Channel concern. An IM Channel explicitly binds a business Identity
and Template; do not treat the CLI's local testing Identity as an end-user Identity. Qoder
recommends QR-code authorization for Channel binding so channel credentials do not need to be stored locally.
The Channel inherits `defaults.identity`; the Qoder adapter resolves both remote ids and maps the generic declaration to
the Forward Channel request. Feishu uses `app_id`/`app_secret`, and WeCom uses `bot_id`/`secret`. Personal WeChat is QR-only
and is not supported by credential-based apply.

Forward-delivered agents still cannot be referenced by OpenAgentPack deployments; scheduled Managed Deployment runs
require an Agent resource.
Expand Down
2 changes: 1 addition & 1 deletion docs/guides/run-sessions.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ A **session** is a runtime conversation started from a managed agent. Sessions a
agents session run "Summarize the repo structure" --agent assistant
```

`session run` creates a session, sends the prompt, and polls until the response completes. Pass `--stream` to receive live events over SSE. When only one agent is configured, `--agent` is auto-detected. For a Qoder agent with `delivery.qoder.type: forward`, Identity is optional: without one OpenCMA looks up the enabled Identity whose `external_id` is `__qca_admin_identity__` and sends its real `idn_...` id. Configure `defaults.session.qoder.identity_id` or pass `--identity-id` to select an existing business Identity. OpenCMA never creates or updates Identity resources implicitly.
`session run` creates a session, sends the prompt, and polls until the response completes. Pass `--stream` to receive live events over SSE. When only one agent is configured, `--agent` is auto-detected. A Qoder agent with `delivery.qoder.type: forward` requires a declared Identity selected through `defaults.identity`; `agents apply` creates or resolves it before sessions run. Pass `--identity-id` only to override the resolved default with an existing provider Identity for one invocation.

Options:

Expand Down
2 changes: 1 addition & 1 deletion docs/reference/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ Manage runtime agent sessions.
| `session events <session-id>` | List event history for a session. |
| `session delete <session-id>` | Delete a session. |

`session create` / `session run` accept `--agent`, `--identity-id`, `--environment`, `--vault`, `--memory-stores`, `--title`, and `--provider`. `--identity-id` selects an existing Qoder Forward Identity and overrides `defaults.session.qoder.identity_id`; when neither is provided, OpenCMA resolves the Identity whose `external_id` is `__qca_admin_identity__`. OpenCMA never creates or updates an Identity. `session run` and `session send` use polling by default and accept `--stream` to opt into SSE streaming, plus `--json` for JSON output. `session list` accepts `--agent` and `--all`; `session events` accepts `--limit`, `--all`, `--json`.
`session create` / `session run` accept `--agent`, `--identity-id`, `--environment`, `--vault`, `--memory-stores`, `--title`, and `--provider`. Forward Sessions resolve the declared logical `defaults.identity`; `--identity-id` overrides it with an existing provider id for one invocation. `session run` and `session send` use polling by default and accept `--stream` to opt into SSE streaming, plus `--json` for JSON output. `session list` accepts `--agent` and `--all`; `session events` accepts `--limit`, `--all`, `--json`.

## `agents deployment`

Expand Down
56 changes: 53 additions & 3 deletions docs/reference/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,16 @@ version: "1"
providers: { <name>: <provider-config> }
defaults:
provider: <name> | "all"
session:
qoder: { identity_id: <existing-idn-id> }
identity: <identity-name>
environments: { <name>: EnvironmentDecl }
tunnels: { <name>: TunnelDecl }
vaults: { <name>: VaultDecl }
memory_stores:{ <name>: MemoryStoreDecl }
skills: { <name>: SkillDecl }
files: { <name>: FileDecl }
identities: { <name>: IdentityDecl }
agents: { <name>: AgentDecl }
channels: { <name>: ChannelDecl }
deployments: { <name>: DeploymentDecl }
```

Expand All @@ -26,18 +27,67 @@ deployments: { <name>: DeploymentDecl }
| `version` | string | yes | Schema version. Currently `"1"`. |
| `providers` | map | yes | One block per provider; each holds its credentials. |
| `defaults.provider` | string | no | Default target for `plan`/`apply`. `all` targets every declared provider. |
| `defaults.session.qoder.identity_id` | string | no | Existing Qoder Forward Identity used as the local Session default. If omitted, OpenCMA looks up the enabled Identity whose `external_id` is `__qca_admin_identity__` and sends its real `idn_...` id. Never created or managed by `apply`. |
| `defaults.identity` | string | no | Logical name of the default declared Identity used by identity-aware resources and Forward Sessions. |
| `environments` | map | no | Cloud runtimes. |
| `tunnels` | map | no | Existing Qoder BYOC tunnels referenced by sessions; OpenCMA does not manage their lifecycle. |
| `vaults` | map | no | Credential stores. |
| `memory_stores` | map | no | Persistent agent context (Qoder, Volcengine Ark). |
| `skills` | map | no | Reusable capability modules. |
| `files` | map | no | Local files uploaded to the Files API (Bailian, Volcengine Ark). |
| `identities` | map | no | Stable end-user identities. Provider support is capability-gated. |
| `agents` | map | no | The core managed-agent resources. |
| `channels` | map | no | External messaging channels bound to an Identity and Agent. Provider support is capability-gated. |
| `deployments` | map | no | Repeatable run units. |

Secrets use `${VAR_NAME}` and resolve from `.env` (walking up to the project root). `agents init` appends `agents.state.json` and `.env` to `.gitignore`.

## Identity

Managed identities use the integrating product's stable end-user id:

```yaml
identities:
chen:
provider: qoder
external_id: user_456
name: Chen
enabled: true
metadata:
department: engineering
```

`agents apply` creates or updates the remote Identity and stores its provider id in state. To reference an Identity managed outside this project, use the mutually exclusive external-reference form:

```yaml
identities:
chen:
provider: qoder
identity_id: idn_019eabc123
```

External references are verified and recorded but never updated or deleted.

## Channel

```yaml
channels:
support-dingtalk:
provider: qoder # optional; inherits defaults.provider
agent: support-agent
identity: chen # optional; inherits defaults.identity
type: dingtalk
name: Support DingTalk # optional; defaults to the YAML key
enabled: true # optional; defaults to true
credentials:
client_id: ${DINGTALK_CLIENT_ID}
client_secret: ${DINGTALK_CLIENT_SECRET}
options:
include_tool_calls: false
include_thinking: false
```

The declaration intentionally uses logical `agent` and `identity` references. Provider adapters resolve remote ids and map `type`, `credentials`, and `options` to provider wire fields. Qoder Channels require the referenced Agent to use Forward delivery. Credential-based Qoder support currently covers DingTalk, Feishu, and WeCom; personal WeChat remains QR-only.

## Provider configuration

Each provider under `providers` is validated by the provider's own config schema.
Expand Down
88 changes: 88 additions & 0 deletions examples/qoder/byoc/agents.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Qoder BYOC 端到端测试配置示例
#
# 用途:验证 Qoder BYOC 接入(工具调用)是否打通。
# 使用前请确保已设置环境变量:
# - QODER_PAT Qoder API 个人访问令牌(https://qoder.com/account/integrations)
# - BYOC_ENVIRONMENT_ID 已创建的 BYOC environment id
# - BYOC_TUNNEL_ID 已创建的 BYOC tunnel id
# - DINGTALK_CLIENT_ID / DINGTALK_CLIENT_SECRET 钉钉渠道凭证(可选)
#
# 关键约束:
# - environment_id / tunnel_id 指向的 BYOC 资源由平台侧开通,OpenCMA 只引用不修改。
# - tunnel 只是 agents.yaml 内的命名别名,直接引用 Qoder 下发的 tunnel_id。

version: "1"

providers:
qoder:
api_key: ${QODER_PAT}

defaults:
provider: qoder
identity: byoc

# Forward Identity 表示集成侧的终端用户。OpenCMA 按 external_id 创建/复用并管理远端 Identity。
identities:
byoc:
external_id: byoc
name: "byoc"
enabled: true
metadata:
purpose: byoc-assistant

channels:
byoc-dingtalk:
name: "byoc Agent DingTalk"
agent: byoc-agent
type: dingtalk
enabled: true
credentials:
client_id: ${DINGTALK_CLIENT_ID}
client_secret: ${DINGTALK_CLIENT_SECRET}
options:
include_tool_calls: false
include_thinking: false

# 已存在的 BYOC 环境,通过 environment_id 直接引用,不需要 state import。
environments:
byoc:
description: "BYOC self-hosted environment"
environment_id: ${BYOC_ENVIRONMENT_ID}
config:
type: self_hosted
metadata:
purpose: byoc-test

# 已存在的 BYOC tunnel。直接引用 Qoder 下发的 tunnel_id,OpenCMA 不会尝试创建或修改它。
tunnels:
internal:
description: "BYOC tunnel"
tunnel_id: ${BYOC_TUNNEL_ID}

agents:
byoc-agent:
description: "Assistant running inside BYOC"
model:
qoder: auto
instructions: |
You are a BYOC end-to-end test assistant running in a self-hosted environment.
Your goal is to verify that Qoder BYOC tool calling is working.
Answer user questions concisely. Use the available builtin tools when helpful.
If a tool call fails, report the error clearly so connectivity issues can be diagnosed.
environment: byoc
tunnel: internal
tools:
builtin: [Bash, Read, Write, Edit, Glob, Grep, WebFetch, WebSearch, DeliverArtifacts]
permissions:
Bash: allow
Read: allow
Write: allow
Edit: allow
Glob: allow
Grep: allow
WebFetch: allow
WebSearch: allow
DeliverArtifacts: allow
delivery:
qoder:
type: forward
12 changes: 12 additions & 0 deletions examples/qoder/with-channel/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Qoder credential-based Channel

This example declares a Forward Identity, Forward Template, and DingTalk Channel without copying remote Qoder ids.

Set `QODER_PAT`, `SUPPORT_USER_ID`, `DINGTALK_CLIENT_ID`, and `DINGTALK_CLIENT_SECRET`, then run:

```bash
agents plan
agents apply
```

For Feishu, set `type: feishu` and use `app_id` / `app_secret`. For WeCom, set `type: wecom` and use `bot_id` / `secret`. Personal WeChat requires QR binding and is outside this credential-based workflow.
Loading