mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-11 03:13:24 +00:00
refactor: deprecate standalone mode, managed mode is now default (#126)
* refactor: remove managed/standalone mode distinction from codebase Standalone mode is deprecated; managed mode is now the only mode. Remove redundant "managed mode" qualifiers from comments, docs, and error messages. Error strings now reference "database stores" instead of "managed mode" for clarity. * improve(onboard): streamline onboard process and env setup Simplify onboard wizard, extract helpers to dedicated file, update env example and entrypoint for default managed mode, clean up prepare-env script, update i18n catalogs.
This commit is contained in:
1 parent
c5b886048e
commit
8ad580521d
32 files changed
+121
-110
No files matched your search
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
# Copy to .env and fill in values. For Docker Compose, do NOT use 'export' prefix.
|
# Copy to .env and fill in values. For Docker Compose, do NOT use 'export' prefix.
|
||||||
#
|
#
|
||||||
# Auto-generated by prepare-env.sh: GOCLAW_GATEWAY_TOKEN, GOCLAW_ENCRYPTION_KEY.
|
# Auto-generated by prepare-env.sh: GOCLAW_GATEWAY_TOKEN, GOCLAW_ENCRYPTION_KEY.
|
||||||
# LLM providers and channels are configured via the web dashboard setup wizard.
|
# LLM provider API keys: configure via the web dashboard setup wizard.
|
||||||
|
|
||||||
# --- Gateway (required) ---
|
# --- Gateway (required) ---
|
||||||
GOCLAW_GATEWAY_TOKEN=
|
GOCLAW_GATEWAY_TOKEN=
|
||||||
|
|||||||
+8
-8
@@ -9,13 +9,13 @@
|
|||||||
| POST | `/v1/chat/completions` | OpenAI-compatible chat API |
|
| POST | `/v1/chat/completions` | OpenAI-compatible chat API |
|
||||||
| POST | `/v1/responses` | Responses protocol |
|
| POST | `/v1/responses` | Responses protocol |
|
||||||
| POST | `/v1/tools/invoke` | Tool invocation |
|
| POST | `/v1/tools/invoke` | Tool invocation |
|
||||||
| GET/POST | `/v1/agents/*` | Agent management (managed mode) |
|
| GET/POST | `/v1/agents/*` | Agent management |
|
||||||
| GET/POST | `/v1/skills/*` | Skills management (managed mode) |
|
| GET/POST | `/v1/skills/*` | Skills management |
|
||||||
| GET/POST/PUT/DELETE | `/v1/tools/custom/*` | Custom tool CRUD (managed mode) |
|
| GET/POST/PUT/DELETE | `/v1/tools/custom/*` | Custom tool CRUD |
|
||||||
| GET/POST/PUT/DELETE | `/v1/mcp/*` | MCP server + grants management (managed mode) |
|
| GET/POST/PUT/DELETE | `/v1/mcp/*` | MCP server + grants management |
|
||||||
| GET | `/v1/traces/*` | Trace viewer (managed mode) |
|
| GET | `/v1/traces/*` | Trace viewer |
|
||||||
|
|
||||||
## Custom Tools (Managed Mode)
|
## Custom Tools
|
||||||
|
|
||||||
Define shell-based tools at runtime via HTTP API — no recompile or restart needed. The LLM can invoke custom tools identically to built-in tools.
|
Define shell-based tools at runtime via HTTP API — no recompile or restart needed. The LLM can invoke custom tools identically to built-in tools.
|
||||||
|
|
||||||
@@ -67,7 +67,7 @@ Connect external [Model Context Protocol](https://modelcontextprotocol.io) serve
|
|||||||
|
|
||||||
**Supported transports:** `stdio`, `sse`, `streamable-http`
|
**Supported transports:** `stdio`, `sse`, `streamable-http`
|
||||||
|
|
||||||
**Standalone mode** — configure in `config.json`:
|
**Static config** — configure in `config.json` (deprecated; use HTTP API for dynamic management):
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@@ -87,7 +87,7 @@ Connect external [Model Context Protocol](https://modelcontextprotocol.io) serve
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**Managed mode** — full CRUD via HTTP API with per-agent and per-user access grants:
|
**HTTP API** — full CRUD with per-agent and per-user access grants:
|
||||||
|
|
||||||
| Method | Path | Description |
|
| Method | Path | Description |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
|
|||||||
@@ -909,7 +909,6 @@ func runGateway() {
|
|||||||
slog.Info("goclaw gateway starting",
|
slog.Info("goclaw gateway starting",
|
||||||
"version", Version,
|
"version", Version,
|
||||||
"protocol", protocol.ProtocolVersion,
|
"protocol", protocol.ProtocolVersion,
|
||||||
"mode", "managed",
|
|
||||||
"agents", agentRouter.List(),
|
"agents", agentRouter.List(),
|
||||||
"tools", toolsReg.Count(),
|
"tools", toolsReg.Count(),
|
||||||
"channels", channelMgr.GetEnabledChannels(),
|
"channels", channelMgr.GetEnabledChannels(),
|
||||||
|
|||||||
+7
-7
@@ -37,25 +37,25 @@ func runOnboard() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Step 1: Postgres DSN ──
|
// ── Step 1: Postgres connection ──
|
||||||
postgresDSN := os.Getenv("GOCLAW_POSTGRES_DSN")
|
postgresDSN := os.Getenv("GOCLAW_POSTGRES_DSN")
|
||||||
if postgresDSN == "" {
|
if postgresDSN == "" {
|
||||||
postgresDSN = cfg.Database.PostgresDSN
|
postgresDSN = cfg.Database.PostgresDSN
|
||||||
}
|
}
|
||||||
if postgresDSN == "" {
|
if postgresDSN == "" {
|
||||||
var err error
|
fmt.Println("── Database Connection ──")
|
||||||
postgresDSN, err = promptString("Postgres DSN", "Connection string (e.g. postgres://user:pass@host:5432/dbname)", "")
|
fmt.Println(" Enter your PostgreSQL connection details (press Enter for defaults).")
|
||||||
|
fmt.Println()
|
||||||
|
|
||||||
|
dsn, err := promptPostgresFields()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Cancelled.")
|
fmt.Println("Cancelled.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
postgresDSN = dsn
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf(" Using Postgres DSN from environment\n")
|
fmt.Printf(" Using Postgres DSN from environment\n")
|
||||||
}
|
}
|
||||||
if postgresDSN == "" {
|
|
||||||
fmt.Println(" Error: Postgres DSN is required.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Step 2: Test connection ──
|
// ── Step 2: Test connection ──
|
||||||
fmt.Print(" Testing Postgres connection... ")
|
fmt.Print(" Testing Postgres connection... ")
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@@ -14,6 +15,44 @@ func onboardGenerateToken(bytes int) string {
|
|||||||
return hex.EncodeToString(b)
|
return hex.EncodeToString(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// promptPostgresFields prompts for individual database fields and builds a DSN.
|
||||||
|
func promptPostgresFields() (string, error) {
|
||||||
|
host, err := promptString("Host", "", "localhost")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
port, err := promptString("Port", "", "5432")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
dbName, err := promptString("Database name", "", "goclaw")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
user, err := promptString("Username", "", "postgres")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
password, err := promptPassword("Password", "Leave empty if no password")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
sslMode, err := promptString("SSL mode", "", "disable")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build DSN with proper escaping
|
||||||
|
var userInfo *url.Userinfo
|
||||||
|
if password != "" {
|
||||||
|
userInfo = url.UserPassword(user, password)
|
||||||
|
} else {
|
||||||
|
userInfo = url.User(user)
|
||||||
|
}
|
||||||
|
dsn := fmt.Sprintf("postgres://%s@%s:%s/%s?sslmode=%s", userInfo.String(), host, port, dbName, sslMode)
|
||||||
|
return dsn, nil
|
||||||
|
}
|
||||||
|
|
||||||
// onboardWriteEnvFile writes the minimal .env.local with only the 3 required secrets.
|
// onboardWriteEnvFile writes the minimal .env.local with only the 3 required secrets.
|
||||||
func onboardWriteEnvFile(path, postgresDSN, gatewayToken, encryptionKey string) {
|
func onboardWriteEnvFile(path, postgresDSN, gatewayToken, encryptionKey string) {
|
||||||
var lines []string
|
var lines []string
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ set -e
|
|||||||
|
|
||||||
case "${1:-serve}" in
|
case "${1:-serve}" in
|
||||||
serve)
|
serve)
|
||||||
# Managed mode: auto-upgrade (schema migrations + data hooks) before starting.
|
# Auto-upgrade (schema migrations + data hooks) before starting.
|
||||||
if [ "$GOCLAW_MODE" = "managed" ] && [ -n "$GOCLAW_POSTGRES_DSN" ]; then
|
if [ -n "$GOCLAW_POSTGRES_DSN" ]; then
|
||||||
echo "Managed mode: running upgrade..."
|
echo "Running database upgrade..."
|
||||||
/app/goclaw upgrade || \
|
/app/goclaw upgrade || \
|
||||||
echo "Upgrade warning (may already be up-to-date)"
|
echo "Upgrade warning (may already be up-to-date)"
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -233,7 +233,7 @@ sequenceDiagram
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 7. Managed Mode Wiring
|
## 7. Database Wiring
|
||||||
|
|
||||||
The `wireManagedExtras()` function in `cmd/gateway_managed.go` wires multi-tenant components:
|
The `wireManagedExtras()` function in `cmd/gateway_managed.go` wires multi-tenant components:
|
||||||
|
|
||||||
@@ -393,7 +393,7 @@ flowchart TD
|
|||||||
|------|---------|
|
|------|---------|
|
||||||
| `cmd/root.go` | Cobra CLI entry point, flag parsing |
|
| `cmd/root.go` | Cobra CLI entry point, flag parsing |
|
||||||
| `cmd/gateway.go` | Gateway startup orchestrator (`runGateway()`) |
|
| `cmd/gateway.go` | Gateway startup orchestrator (`runGateway()`) |
|
||||||
| `cmd/gateway_managed.go` | Managed mode wiring (`wireManagedExtras()`, `wireManagedHTTP()`) |
|
| `cmd/gateway_managed.go` | Database wiring (`wireManagedExtras()`, `wireManagedHTTP()`) |
|
||||||
| `cmd/gateway_callbacks.go` | Shared callbacks (user seeding, context file loading) |
|
| `cmd/gateway_callbacks.go` | Shared callbacks (user seeding, context file loading) |
|
||||||
| `cmd/gateway_consumer.go` | Inbound message consumer (subagent, delegate, teammate, handoff routing) |
|
| `cmd/gateway_consumer.go` | Inbound message consumer (subagent, delegate, teammate, handoff routing) |
|
||||||
| `cmd/gateway_providers.go` | Provider registration (config-based + DB-based) |
|
| `cmd/gateway_providers.go` | Provider registration (config-based + DB-based) |
|
||||||
@@ -423,7 +423,7 @@ flowchart TD
|
|||||||
| [02-providers.md](./02-providers.md) | LLM providers, retry logic, schema cleaning |
|
| [02-providers.md](./02-providers.md) | LLM providers, retry logic, schema cleaning |
|
||||||
| [03-tools-system.md](./03-tools-system.md) | Tool registry, policy engine, interceptors, custom tools, MCP grants |
|
| [03-tools-system.md](./03-tools-system.md) | Tool registry, policy engine, interceptors, custom tools, MCP grants |
|
||||||
| [04-gateway-protocol.md](./04-gateway-protocol.md) | WebSocket protocol v3, HTTP API, RBAC, identity propagation |
|
| [04-gateway-protocol.md](./04-gateway-protocol.md) | WebSocket protocol v3, HTTP API, RBAC, identity propagation |
|
||||||
| [05-channels-messaging.md](./05-channels-messaging.md) | Channel adapters, Telegram formatting, pairing, managed-mode user scoping |
|
| [05-channels-messaging.md](./05-channels-messaging.md) | Channel adapters, Telegram formatting, pairing, per-user scoping |
|
||||||
| [06-store-data-model.md](./06-store-data-model.md) | Store interfaces, PostgreSQL schema, session caching, custom tool store |
|
| [06-store-data-model.md](./06-store-data-model.md) | Store interfaces, PostgreSQL schema, session caching, custom tool store |
|
||||||
| [07-bootstrap-skills-memory.md](./07-bootstrap-skills-memory.md) | Bootstrap files, skills system, memory, skills grants |
|
| [07-bootstrap-skills-memory.md](./07-bootstrap-skills-memory.md) | Bootstrap files, skills system, memory, skills grants |
|
||||||
| [08-scheduling-cron.md](./08-scheduling-cron.md) | Scheduler lanes, cron lifecycle |
|
| [08-scheduling-cron.md](./08-scheduling-cron.md) | Scheduler lanes, cron lifecycle |
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ flowchart TD
|
|||||||
|
|
||||||
- Increment the `activeRuns` atomic counter (no mutex -- true concurrency, especially in group chats with `maxConcurrent = 3`).
|
- Increment the `activeRuns` atomic counter (no mutex -- true concurrency, especially in group chats with `maxConcurrent = 3`).
|
||||||
- Emit a `run.started` event to notify connected clients.
|
- Emit a `run.started` event to notify connected clients.
|
||||||
- Create a trace record (managed mode) with a generated trace UUID.
|
- Create a trace record with a generated trace UUID.
|
||||||
- Propagate context values: `WithAgentID()`, `WithUserID()`, `WithAgentType()`. Downstream tools and interceptors rely on these.
|
- Propagate context values: `WithAgentID()`, `WithUserID()`, `WithAgentType()`. Downstream tools and interceptors rely on these.
|
||||||
- Compute per-user workspace: `base + "/" + sanitize(userID)`. Inject via `WithToolWorkspace(ctx)` so all filesystem and shell tools use the correct directory.
|
- Compute per-user workspace: `base + "/" + sanitize(userID)`. Inject via `WithToolWorkspace(ctx)` so all filesystem and shell tools use the correct directory.
|
||||||
- Ensure per-user files exist. A `sync.Map` cache guarantees the seeding function runs at most once per user.
|
- Ensure per-user files exist. A `sync.Map` cache guarantees the seeding function runs at most once per user.
|
||||||
@@ -381,7 +381,7 @@ flowchart TD
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 10. Resolver (Managed Mode)
|
## 10. Resolver
|
||||||
|
|
||||||
The `ManagedResolver` lazy-creates Loop instances from PostgreSQL data when the Router encounters a cache miss.
|
The `ManagedResolver` lazy-creates Loop instances from PostgreSQL data when the Router encounters a cache miss.
|
||||||
|
|
||||||
@@ -392,7 +392,7 @@ flowchart TD
|
|||||||
PROV --> BOOT["Step 3: Load bootstrap files<br/>bootstrap.LoadFromStore(agentID)"]
|
PROV --> BOOT["Step 3: Load bootstrap files<br/>bootstrap.LoadFromStore(agentID)"]
|
||||||
BOOT --> DEFAULTS["Step 4: Apply defaults<br/>contextWindow <= 0 then 200K<br/>maxIterations <= 0 then 20"]
|
BOOT --> DEFAULTS["Step 4: Apply defaults<br/>contextWindow <= 0 then 200K<br/>maxIterations <= 0 then 20"]
|
||||||
DEFAULTS --> CREATE["Step 5: Create Loop<br/>NewLoop(LoopConfig)"]
|
DEFAULTS --> CREATE["Step 5: Create Loop<br/>NewLoop(LoopConfig)"]
|
||||||
CREATE --> WIRE["Step 6: Wire managed-mode hooks<br/>EnsureUserFilesFunc, ContextFileLoaderFunc"]
|
CREATE --> WIRE["Step 6: Wire hooks<br/>EnsureUserFilesFunc, ContextFileLoaderFunc"]
|
||||||
WIRE --> DONE["Return Loop to Router for caching"]
|
WIRE --> DONE["Return Loop to Router for caching"]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -202,9 +202,9 @@ The Anthropic provider calls `CleanSchemaForProvider("anthropic", ...)` when con
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 7. Managed Mode -- Providers from Database
|
## 7. Providers from Database
|
||||||
|
|
||||||
In managed mode, providers are loaded from the `llm_providers` table in addition to the config file. Database providers override config providers with the same name.
|
Providers are loaded from the `llm_providers` table in addition to the config file. Database providers override config providers with the same name.
|
||||||
|
|
||||||
### Loading Flow
|
### Loading Flow
|
||||||
|
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ Context keys ensure each tool call receives the correct per-call values without
|
|||||||
|
|
||||||
## 3. Filesystem Tools and Virtual FS Routing
|
## 3. Filesystem Tools and Virtual FS Routing
|
||||||
|
|
||||||
In managed mode, filesystem operations are intercepted before hitting the host disk. Two interceptor layers route specific paths to the database instead.
|
Filesystem operations are intercepted before hitting the host disk. Two interceptor layers route specific paths to the database instead.
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart TD
|
flowchart TD
|
||||||
@@ -570,9 +570,9 @@ GoClaw integrates with Model Context Protocol (MCP) servers via `internal/mcp/`.
|
|||||||
- Tools are registered with a prefix (e.g., `mcp_servername_toolname`)
|
- Tools are registered with a prefix (e.g., `mcp_servername_toolname`)
|
||||||
- Dynamic tool group registration: `mcp` and `mcp:{serverName}` groups
|
- Dynamic tool group registration: `mcp` and `mcp:{serverName}` groups
|
||||||
|
|
||||||
### Access Control (Managed Mode)
|
### Access Control
|
||||||
|
|
||||||
In managed mode, MCP server access is controlled through per-agent and per-user grants stored in PostgreSQL.
|
MCP server access is controlled through per-agent and per-user grants stored in PostgreSQL.
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart TD
|
flowchart TD
|
||||||
@@ -602,7 +602,7 @@ flowchart LR
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 13. Custom Tools (Managed Mode)
|
## 13. Custom Tools
|
||||||
|
|
||||||
Define shell-based tools at runtime via the HTTP API -- no recompile or restart needed. Custom tools are stored in the `custom_tools` PostgreSQL table and loaded dynamically into the agent's tool registry.
|
Define shell-based tools at runtime via the HTTP API -- no recompile or restart needed. Custom tools are stored in the `custom_tools` PostgreSQL table and loaded dynamically into the agent's tool registry.
|
||||||
|
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ flowchart TD
|
|||||||
|
|
||||||
Token comparison uses `crypto/subtle.ConstantTimeCompare` to prevent timing attacks.
|
Token comparison uses `crypto/subtle.ConstantTimeCompare` to prevent timing attacks.
|
||||||
|
|
||||||
In managed mode, `user_id` in the connect parameters is required for per-user session scoping and context file routing. GoClaw uses the **Identity Propagation** pattern — it trusts the upstream service to provide accurate user identity. The `user_id` is opaque (VARCHAR 255); multi-tenant deployments use the compound format `tenant.{tenantId}.user.{userId}`. See [00-architecture-overview.md Section 5](./00-architecture-overview.md) for details.
|
The `user_id` in the connect parameters is required for per-user session scoping and context file routing. GoClaw uses the **Identity Propagation** pattern — it trusts the upstream service to provide accurate user identity. The `user_id` is opaque (VARCHAR 255); multi-tenant deployments use the compound format `tenant.{tenantId}.user.{userId}`. See [00-architecture-overview.md Section 5](./00-architecture-overview.md) for details.
|
||||||
|
|
||||||
### Three Roles
|
### Three Roles
|
||||||
|
|
||||||
@@ -161,7 +161,7 @@ flowchart TD
|
|||||||
| `agent.wait` | Wait for an agent to become available |
|
| `agent.wait` | Wait for an agent to become available |
|
||||||
| `agent.identity.get` | Get agent identity (name, description) |
|
| `agent.identity.get` | Get agent identity (name, description) |
|
||||||
| `agents.list` | List all accessible agents |
|
| `agents.list` | List all accessible agents |
|
||||||
| `agents.create` | Create a new agent (managed mode) |
|
| `agents.create` | Create a new agent |
|
||||||
| `agents.update` | Update agent configuration |
|
| `agents.update` | Update agent configuration |
|
||||||
| `agents.delete` | Soft-delete an agent |
|
| `agents.delete` | Soft-delete an agent |
|
||||||
| `agents.files.list` | List agent context files |
|
| `agents.files.list` | List agent context files |
|
||||||
@@ -301,7 +301,7 @@ flowchart TD
|
|||||||
|
|
||||||
- `Authorization: Bearer <token>` -- timing-safe comparison via `crypto/subtle.ConstantTimeCompare`
|
- `Authorization: Bearer <token>` -- timing-safe comparison via `crypto/subtle.ConstantTimeCompare`
|
||||||
- No token configured: all requests allowed
|
- No token configured: all requests allowed
|
||||||
- `X-GoClaw-User-Id`: required in managed mode for per-user scoping
|
- `X-GoClaw-User-Id`: required for per-user scoping
|
||||||
- `X-GoClaw-Agent-Id`: specify target agent for the request
|
- `X-GoClaw-Agent-Id`: specify target agent for the request
|
||||||
|
|
||||||
### Endpoints
|
### Endpoints
|
||||||
@@ -334,9 +334,9 @@ Direct tool invocation without the agent loop. Supports `dryRun: true` to return
|
|||||||
|
|
||||||
Returns `{"status":"ok","protocol":3}`.
|
Returns `{"status":"ok","protocol":3}`.
|
||||||
|
|
||||||
#### Managed Mode CRUD Endpoints
|
#### CRUD Endpoints
|
||||||
|
|
||||||
All managed endpoints require `Authorization: Bearer <token>` and `X-GoClaw-User-Id` header for per-user scoping.
|
All CRUD endpoints require `Authorization: Bearer <token>` and `X-GoClaw-User-Id` header for per-user scoping.
|
||||||
|
|
||||||
**Agents** (`/v1/agents`):
|
**Agents** (`/v1/agents`):
|
||||||
|
|
||||||
@@ -482,9 +482,9 @@ Error responses include `retryable` (boolean) and `retryAfterMs` (integer) field
|
|||||||
| `internal/http/chat_completions.go` | POST /v1/chat/completions (OpenAI-compatible) |
|
| `internal/http/chat_completions.go` | POST /v1/chat/completions (OpenAI-compatible) |
|
||||||
| `internal/http/responses.go` | POST /v1/responses (OpenResponses protocol) |
|
| `internal/http/responses.go` | POST /v1/responses (OpenResponses protocol) |
|
||||||
| `internal/http/tools_invoke.go` | POST /v1/tools/invoke (direct tool execution) |
|
| `internal/http/tools_invoke.go` | POST /v1/tools/invoke (direct tool execution) |
|
||||||
| `internal/http/agents.go` | Agent CRUD HTTP handlers (managed mode) |
|
| `internal/http/agents.go` | Agent CRUD HTTP handlers |
|
||||||
| `internal/http/skills.go` | Skills HTTP handlers (managed mode) |
|
| `internal/http/skills.go` | Skills HTTP handlers |
|
||||||
| `internal/http/traces.go` | Traces HTTP handlers (managed mode) |
|
| `internal/http/traces.go` | Traces HTTP handlers |
|
||||||
| `internal/http/delegations.go` | Delegation history HTTP handlers |
|
| `internal/http/delegations.go` | Delegation history HTTP handlers |
|
||||||
| `internal/http/summoner.go` | LLM-powered agent setup (XML parsing, context file generation) |
|
| `internal/http/summoner.go` | LLM-powered agent setup (XML parsing, context file generation) |
|
||||||
| `internal/http/auth.go` | Bearer token authentication, timing-safe comparison |
|
| `internal/http/auth.go` | Bearer token authentication, timing-safe comparison |
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ flowchart LR
|
|||||||
|
|
||||||
Internal channels (`cli`, `system`, `subagent`) are silently skipped by the outbound dispatcher and never forwarded to external platforms.
|
Internal channels (`cli`, `system`, `subagent`) are silently skipped by the outbound dispatcher and never forwarded to external platforms.
|
||||||
|
|
||||||
### Handoff Routing (Managed Mode)
|
### Handoff Routing
|
||||||
|
|
||||||
Before normal agent routing, the consumer checks the `handoff_routes` table for an active routing override. If a handoff route exists for the incoming channel + chat ID, the message is redirected to the target agent instead of the original.
|
Before normal agent routing, the consumer checks the `handoff_routes` table for an active routing override. If a handoff route exists for the incoming channel + chat ID, the message is redirected to the target agent instead of the original.
|
||||||
|
|
||||||
@@ -504,7 +504,7 @@ flowchart TD
|
|||||||
WS2 --> USER3["user_charlie/"]
|
WS2 --> USER3["user_charlie/"]
|
||||||
```
|
```
|
||||||
|
|
||||||
In managed mode, channel instances are loaded from the database with their assigned agent ID. The agent key is resolved and propagated through the message pipeline, ensuring all filesystem tools, context files, and memory operations use the correct workspace.
|
Channel instances are loaded from the database with their assigned agent ID. The agent key is resolved and propagated through the message pipeline, ensuring all filesystem tools, context files, and memory operations use the correct workspace.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -570,7 +570,7 @@ flowchart TD
|
|||||||
|------|---------|
|
|------|---------|
|
||||||
| `internal/channels/channel.go` | Channel interface, BaseChannel, extended interfaces, HandleMessage |
|
| `internal/channels/channel.go` | Channel interface, BaseChannel, extended interfaces, HandleMessage |
|
||||||
| `internal/channels/manager.go` | Manager: registration, StartAll, StopAll, outbound dispatch, webhook collection |
|
| `internal/channels/manager.go` | Manager: registration, StartAll, StopAll, outbound dispatch, webhook collection |
|
||||||
| `internal/channels/instance_loader.go` | DB-based channel instance loading (managed mode) |
|
| `internal/channels/instance_loader.go` | DB-based channel instance loading |
|
||||||
| `internal/channels/telegram/channel.go` | Telegram core: long polling, mention gating, typing indicators |
|
| `internal/channels/telegram/channel.go` | Telegram core: long polling, mention gating, typing indicators |
|
||||||
| `internal/channels/telegram/handlers.go` | Message handling, media processing, forum topic detection |
|
| `internal/channels/telegram/handlers.go` | Message handling, media processing, forum topic detection |
|
||||||
| `internal/channels/telegram/topic_config.go` | Per-topic config layering and resolution |
|
| `internal/channels/telegram/topic_config.go` | Per-topic config layering and resolution |
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ flowchart TD
|
|||||||
|
|
||||||
## 4. Agent Access Control
|
## 4. Agent Access Control
|
||||||
|
|
||||||
In managed mode, agent access is checked via a 4-step pipeline.
|
Agent access is checked via a 4-step pipeline.
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart TD
|
flowchart TD
|
||||||
@@ -477,7 +477,7 @@ flowchart TD
|
|||||||
| Key | Type | Purpose |
|
| Key | Type | Purpose |
|
||||||
|-----|------|---------|
|
|-----|------|---------|
|
||||||
| `goclaw_user_id` | string | External user ID (e.g., Telegram user ID) |
|
| `goclaw_user_id` | string | External user ID (e.g., Telegram user ID) |
|
||||||
| `goclaw_agent_id` | uuid.UUID | Agent UUID (managed mode) |
|
| `goclaw_agent_id` | uuid.UUID | Agent UUID |
|
||||||
| `goclaw_agent_type` | string | Agent type: `"open"` or `"predefined"` |
|
| `goclaw_agent_type` | string | Agent type: `"open"` or `"predefined"` |
|
||||||
| `goclaw_sender_id` | string | Original individual sender ID (in group chats, `user_id` is group-scoped but `sender_id` preserves the actual person) |
|
| `goclaw_sender_id` | string | Original individual sender ID (in group chats, `user_id` is group-scoped but `sender_id` preserves the actual person) |
|
||||||
|
|
||||||
|
|||||||
@@ -186,7 +186,7 @@ This ensures resolver-injected virtual files (`DELEGATION.md`, `TEAM.md`) surviv
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 7. Agent Summoning (Managed Mode)
|
## 7. Agent Summoning
|
||||||
|
|
||||||
Creating a predefined agent requires 4 context files (SOUL.md, IDENTITY.md, AGENTS.md, TOOLS.md) with specific formatting conventions. Agent summoning generates all 4 files from a natural language description in a single LLM call.
|
Creating a predefined agent requires 4 context files (SOUL.md, IDENTITY.md, AGENTS.md, TOOLS.md) with specific formatting conventions. Agent summoning generates all 4 files from a natural language description in a single LLM call.
|
||||||
|
|
||||||
@@ -260,9 +260,9 @@ IDF is computed as: `log((N - df + 0.5) / (df + 0.5) + 1)`
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 11. Skills -- Embedding Search (Managed Mode)
|
## 11. Skills -- Embedding Search
|
||||||
|
|
||||||
In managed mode, skill search uses a hybrid approach combining BM25 and vector similarity.
|
Skill search uses a hybrid approach combining BM25 and vector similarity.
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart TD
|
flowchart TD
|
||||||
@@ -283,9 +283,9 @@ flowchart TD
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 12. Skills Grants & Visibility (Managed Mode)
|
## 12. Skills Grants & Visibility
|
||||||
|
|
||||||
In managed mode, skill access is controlled through a 3-tier visibility model with explicit agent and user grants.
|
Skill access is controlled through a 3-tier visibility model with explicit agent and user grants.
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart TD
|
flowchart TD
|
||||||
@@ -315,7 +315,7 @@ flowchart TD
|
|||||||
|
|
||||||
**Resolution**: `ListAccessible(agentID, userID)` performs a DISTINCT join across `skills`, `skill_agent_grants`, and `skill_user_grants` with the visibility filter, returning only active skills the caller can access.
|
**Resolution**: `ListAccessible(agentID, userID)` performs a DISTINCT join across `skills`, `skill_agent_grants`, and `skill_user_grants` with the visibility filter, returning only active skills the caller can access.
|
||||||
|
|
||||||
**Managed-mode Tier 4**: In managed mode, global skills (Tier 4 in the hierarchy) are loaded from the `skills` PostgreSQL table instead of the filesystem.
|
**Tier 4**: Global skills (Tier 4 in the hierarchy) are loaded from the `skills` PostgreSQL table instead of the filesystem.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -489,7 +489,7 @@ The flush is idempotent per compaction cycle -- it will not run again until the
|
|||||||
|
|
||||||
| Document | Relevant Content |
|
| Document | Relevant Content |
|
||||||
|----------|-----------------|
|
|----------|-----------------|
|
||||||
| [00-architecture-overview.md](./00-architecture-overview.md) | Startup sequence, managed mode wiring |
|
| [00-architecture-overview.md](./00-architecture-overview.md) | Startup sequence, database wiring |
|
||||||
| [01-agent-loop.md](./01-agent-loop.md) | Agent loop calls BuildSystemPrompt, compaction flow |
|
| [01-agent-loop.md](./01-agent-loop.md) | Agent loop calls BuildSystemPrompt, compaction flow |
|
||||||
| [03-tools-system.md](./03-tools-system.md) | ContextFileInterceptor routing read_file/write_file to DB |
|
| [03-tools-system.md](./03-tools-system.md) | ContextFileInterceptor routing read_file/write_file to DB |
|
||||||
| [06-store-data-model.md](./06-store-data-model.md) | memory_documents, memory_chunks tables |
|
| [06-store-data-model.md](./06-store-data-model.md) | memory_documents, memory_chunks tables |
|
||||||
+2
-2
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
Defense-in-depth with five independent layers from transport to isolation. Each layer operates independently -- even if one layer is bypassed, the remaining layers continue to protect the system.
|
Defense-in-depth with five independent layers from transport to isolation. Each layer operates independently -- even if one layer is bypassed, the remaining layers continue to protect the system.
|
||||||
|
|
||||||
> **Managed mode**: Adds AES-256-GCM encryption for secrets stored in PostgreSQL (LLM provider API keys, MCP server API keys, custom tool environment variables), plus agent-level access control via the 4-step `CanAccess` pipeline (see [06-store-data-model.md](./06-store-data-model.md)).
|
> AES-256-GCM encryption protects secrets stored in PostgreSQL (LLM provider API keys, MCP server API keys, custom tool environment variables). Agent-level access control uses the 4-step `CanAccess` pipeline (see [06-store-data-model.md](./06-store-data-model.md)).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -123,7 +123,7 @@ The workspace is injected into tools via `WithToolWorkspace(ctx)` context inject
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 2. Encryption (Managed Mode)
|
## 2. Encryption
|
||||||
|
|
||||||
AES-256-GCM encryption for secrets stored in PostgreSQL. Key provided via `GOCLAW_ENCRYPTION_KEY` environment variable.
|
AES-256-GCM encryption for secrets stored in PostgreSQL. Key provided via `GOCLAW_ENCRYPTION_KEY` environment variable.
|
||||||
|
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ The exporter lives in a separate sub-package (`internal/tracing/otelexport/`) so
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 5. Trace HTTP API (Managed Mode)
|
## 5. Trace HTTP API
|
||||||
|
|
||||||
| Method | Path | Description |
|
| Method | Path | Description |
|
||||||
|--------|------|-------------|
|
|--------|------|-------------|
|
||||||
@@ -126,7 +126,7 @@ The exporter lives in a separate sub-package (`internal/tracing/otelexport/`) so
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 6. Delegation History (Managed Mode)
|
## 6. Delegation History
|
||||||
|
|
||||||
Delegation history records are stored in the `delegation_history` table and exposed alongside traces for cross-referencing agent interactions.
|
Delegation history records are stored in the `delegation_history` table and exposed alongside traces for cross-referencing agent interactions.
|
||||||
|
|
||||||
|
|||||||
@@ -323,7 +323,7 @@ func NewManagedResolver(deps ResolverDeps) ResolverFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Managed mode: filter skills by visibility + agent grants.
|
// Filter skills by visibility + agent grants.
|
||||||
// Only public skills and explicitly granted internal skills appear in the system prompt.
|
// Only public skills and explicitly granted internal skills appear in the system prompt.
|
||||||
var skillAllowList []string
|
var skillAllowList []string
|
||||||
if deps.SkillAccessStore != nil {
|
if deps.SkillAccessStore != nil {
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ func (m *AgentsMethods) handleCreate(ctx context.Context, client *gateway.Client
|
|||||||
}
|
}
|
||||||
|
|
||||||
if m.agentStore != nil {
|
if m.agentStore != nil {
|
||||||
// --- Managed mode: create agent in DB ---
|
// --- DB-backed: create agent in store ---
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
// Check if agent already exists in DB
|
// Check if agent already exists in DB
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ func (m *AgentsMethods) handleDelete(ctx context.Context, client *gateway.Client
|
|||||||
var removedBindings int
|
var removedBindings int
|
||||||
|
|
||||||
if m.agentStore != nil {
|
if m.agentStore != nil {
|
||||||
// --- Managed mode: delete from DB ---
|
// --- DB-backed: delete from store ---
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
ag, err := m.agentStore.GetByKey(ctx, params.AgentID)
|
ag, err := m.agentStore.GetByKey(ctx, params.AgentID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ func (m *AgentsMethods) handleFilesList(ctx context.Context, client *gateway.Cli
|
|||||||
}
|
}
|
||||||
|
|
||||||
if m.agentStore != nil {
|
if m.agentStore != nil {
|
||||||
// --- Managed mode: list from DB ---
|
// --- DB-backed: list from store ---
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
ag, err := m.agentStore.GetByKey(ctx, params.AgentID)
|
ag, err := m.agentStore.GetByKey(ctx, params.AgentID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -135,7 +135,7 @@ func (m *AgentsMethods) handleFilesGet(ctx context.Context, client *gateway.Clie
|
|||||||
}
|
}
|
||||||
|
|
||||||
if m.agentStore != nil {
|
if m.agentStore != nil {
|
||||||
// --- Managed mode: read from DB ---
|
// --- DB-backed: read from store ---
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
ag, err := m.agentStore.GetByKey(ctx, params.AgentID)
|
ag, err := m.agentStore.GetByKey(ctx, params.AgentID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -234,7 +234,7 @@ func (m *AgentsMethods) handleFilesSet(ctx context.Context, client *gateway.Clie
|
|||||||
}
|
}
|
||||||
|
|
||||||
if m.agentStore != nil {
|
if m.agentStore != nil {
|
||||||
// --- Managed mode: write to DB ---
|
// --- DB-backed: write to store ---
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
ag, err := m.agentStore.GetByKey(ctx, params.AgentID)
|
ag, err := m.agentStore.GetByKey(ctx, params.AgentID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ func (m *AgentsMethods) handleIdentityGet(_ context.Context, client *gateway.Cli
|
|||||||
}
|
}
|
||||||
|
|
||||||
if m.agentStore != nil {
|
if m.agentStore != nil {
|
||||||
// --- Managed mode: read identity from DB ---
|
// --- DB-backed: read identity from store ---
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
ag, err := m.agentStore.GetByKey(ctx, params.AgentID)
|
ag, err := m.agentStore.GetByKey(ctx, params.AgentID)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ func (m *AgentsMethods) handleUpdate(ctx context.Context, client *gateway.Client
|
|||||||
}
|
}
|
||||||
|
|
||||||
if m.agentStore != nil {
|
if m.agentStore != nil {
|
||||||
// --- Managed mode: update agent in DB ---
|
// --- DB-backed: update agent in store ---
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
ag, err := m.agentStore.GetByKey(ctx, params.AgentID)
|
ag, err := m.agentStore.GetByKey(ctx, params.AgentID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
+10
-10
@@ -160,52 +160,52 @@ func (s *Server) BuildMux() *http.ServeMux {
|
|||||||
mux.Handle("/v1/tools/invoke", toolsHandler)
|
mux.Handle("/v1/tools/invoke", toolsHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Managed mode: agent CRUD + shares API
|
// Agent CRUD + shares API
|
||||||
if s.agentsHandler != nil {
|
if s.agentsHandler != nil {
|
||||||
s.agentsHandler.RegisterRoutes(mux)
|
s.agentsHandler.RegisterRoutes(mux)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Managed mode: skill management API
|
// Skill management API
|
||||||
if s.skillsHandler != nil {
|
if s.skillsHandler != nil {
|
||||||
s.skillsHandler.RegisterRoutes(mux)
|
s.skillsHandler.RegisterRoutes(mux)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Managed mode: LLM trace listing API
|
// LLM trace listing API
|
||||||
if s.tracesHandler != nil {
|
if s.tracesHandler != nil {
|
||||||
s.tracesHandler.RegisterRoutes(mux)
|
s.tracesHandler.RegisterRoutes(mux)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Managed mode: MCP server management API
|
// MCP server management API
|
||||||
if s.mcpHandler != nil {
|
if s.mcpHandler != nil {
|
||||||
s.mcpHandler.RegisterRoutes(mux)
|
s.mcpHandler.RegisterRoutes(mux)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Managed mode: custom tool CRUD API
|
// Custom tool CRUD API
|
||||||
if s.customToolsHandler != nil {
|
if s.customToolsHandler != nil {
|
||||||
s.customToolsHandler.RegisterRoutes(mux)
|
s.customToolsHandler.RegisterRoutes(mux)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Managed mode: channel instance CRUD API
|
// Channel instance CRUD API
|
||||||
if s.channelInstancesHandler != nil {
|
if s.channelInstancesHandler != nil {
|
||||||
s.channelInstancesHandler.RegisterRoutes(mux)
|
s.channelInstancesHandler.RegisterRoutes(mux)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Managed mode: provider & model CRUD API
|
// Provider & model CRUD API
|
||||||
if s.providersHandler != nil {
|
if s.providersHandler != nil {
|
||||||
s.providersHandler.RegisterRoutes(mux)
|
s.providersHandler.RegisterRoutes(mux)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Managed mode: delegation history API
|
// Delegation history API
|
||||||
if s.delegationsHandler != nil {
|
if s.delegationsHandler != nil {
|
||||||
s.delegationsHandler.RegisterRoutes(mux)
|
s.delegationsHandler.RegisterRoutes(mux)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Managed mode: builtin tool management API
|
// Builtin tool management API
|
||||||
if s.builtinToolsHandler != nil {
|
if s.builtinToolsHandler != nil {
|
||||||
s.builtinToolsHandler.RegisterRoutes(mux)
|
s.builtinToolsHandler.RegisterRoutes(mux)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Managed mode: pending messages API
|
// Pending messages API
|
||||||
if s.pendingMessagesHandler != nil {
|
if s.pendingMessagesHandler != nil {
|
||||||
s.pendingMessagesHandler.RegisterRoutes(mux)
|
s.pendingMessagesHandler.RegisterRoutes(mux)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ func init() {
|
|||||||
// HTTP API
|
// HTTP API
|
||||||
MsgInvalidAuth: "invalid authentication",
|
MsgInvalidAuth: "invalid authentication",
|
||||||
MsgMsgsRequired: "messages is required",
|
MsgMsgsRequired: "messages is required",
|
||||||
MsgUserIDHeader: "X-GoClaw-User-Id header is required in managed mode",
|
MsgUserIDHeader: "X-GoClaw-User-Id header is required",
|
||||||
MsgFileTooLarge: "file too large or invalid multipart form",
|
MsgFileTooLarge: "file too large or invalid multipart form",
|
||||||
MsgMissingFileField: "missing 'file' field",
|
MsgMissingFileField: "missing 'file' field",
|
||||||
MsgInvalidFilename: "invalid filename",
|
MsgInvalidFilename: "invalid filename",
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ func init() {
|
|||||||
// HTTP API
|
// HTTP API
|
||||||
MsgInvalidAuth: "xác thực không hợp lệ",
|
MsgInvalidAuth: "xác thực không hợp lệ",
|
||||||
MsgMsgsRequired: "messages là bắt buộc",
|
MsgMsgsRequired: "messages là bắt buộc",
|
||||||
MsgUserIDHeader: "header X-GoClaw-User-Id là bắt buộc ở chế độ managed",
|
MsgUserIDHeader: "header X-GoClaw-User-Id là bắt buộc",
|
||||||
MsgFileTooLarge: "tệp quá lớn hoặc form multipart không hợp lệ",
|
MsgFileTooLarge: "tệp quá lớn hoặc form multipart không hợp lệ",
|
||||||
MsgMissingFileField: "thiếu trường 'file'",
|
MsgMissingFileField: "thiếu trường 'file'",
|
||||||
MsgInvalidFilename: "tên tệp không hợp lệ",
|
MsgInvalidFilename: "tên tệp không hợp lệ",
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ func init() {
|
|||||||
// HTTP API
|
// HTTP API
|
||||||
MsgInvalidAuth: "身份验证无效",
|
MsgInvalidAuth: "身份验证无效",
|
||||||
MsgMsgsRequired: "messages 是必填项",
|
MsgMsgsRequired: "messages 是必填项",
|
||||||
MsgUserIDHeader: "托管模式下需要 X-GoClaw-User-Id 请求头",
|
MsgUserIDHeader: "需要 X-GoClaw-User-Id 请求头",
|
||||||
MsgFileTooLarge: "文件过大或 multipart 表单无效",
|
MsgFileTooLarge: "文件过大或 multipart 表单无效",
|
||||||
MsgMissingFileField: "缺少 'file' 字段",
|
MsgMissingFileField: "缺少 'file' 字段",
|
||||||
MsgInvalidFilename: "文件名无效",
|
MsgInvalidFilename: "文件名无效",
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import (
|
|||||||
|
|
||||||
// BridgeToolNames is the subset of GoClaw tools exposed via the MCP bridge.
|
// BridgeToolNames is the subset of GoClaw tools exposed via the MCP bridge.
|
||||||
// Excluded: spawn (agent loop), create_forum_topic (channels),
|
// Excluded: spawn (agent loop), create_forum_topic (channels),
|
||||||
// handoff/delegate_search/evaluate_loop/team_* (managed mode stores).
|
// handoff/delegate_search/evaluate_loop/team_* (require database stores).
|
||||||
var BridgeToolNames = map[string]bool{
|
var BridgeToolNames = map[string]bool{
|
||||||
// Filesystem
|
// Filesystem
|
||||||
"read_file": true,
|
"read_file": true,
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ type Manager struct {
|
|||||||
// DB-backed servers
|
// DB-backed servers
|
||||||
store store.MCPServerStore
|
store store.MCPServerStore
|
||||||
|
|
||||||
// Shared connection pool (nil = standalone mode)
|
// Shared connection pool (nil = config-only mode)
|
||||||
pool *Pool
|
pool *Pool
|
||||||
poolServers map[string]struct{} // server names acquired from pool (for cleanup)
|
poolServers map[string]struct{} // server names acquired from pool (for cleanup)
|
||||||
poolToolNames map[string][]string // per-agent tool names for pool-backed servers
|
poolToolNames map[string][]string // per-agent tool names for pool-backed servers
|
||||||
@@ -179,7 +179,7 @@ func (m *Manager) LoadForAgent(ctx context.Context, agentID uuid.UUID, userID st
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Standalone mode: create per-agent connection
|
// Per-agent mode: create per-agent connection
|
||||||
if err := m.connectServer(ctx, srv.Name, srv.Transport, srv.Command,
|
if err := m.connectServer(ctx, srv.Name, srv.Transport, srv.Command,
|
||||||
args, env, srv.URL, headers,
|
args, env, srv.URL, headers,
|
||||||
srv.ToolPrefix, srv.TimeoutSec); err != nil {
|
srv.ToolPrefix, srv.TimeoutSec); err != nil {
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import (
|
|||||||
func (dm *DelegateManager) prepareDelegation(ctx context.Context, opts DelegateOpts, mode string) (*DelegationTask, *store.AgentLinkData, error) {
|
func (dm *DelegateManager) prepareDelegation(ctx context.Context, opts DelegateOpts, mode string) (*DelegationTask, *store.AgentLinkData, error) {
|
||||||
sourceAgentID := store.AgentIDFromContext(ctx)
|
sourceAgentID := store.AgentIDFromContext(ctx)
|
||||||
if sourceAgentID == uuid.Nil {
|
if sourceAgentID == uuid.Nil {
|
||||||
return nil, nil, fmt.Errorf("delegation requires managed mode (no agent ID in context)")
|
return nil, nil, fmt.Errorf("delegation requires database stores (no agent ID in context)")
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceAgent, err := dm.agentStore.GetByID(ctx, sourceAgentID)
|
sourceAgent, err := dm.agentStore.GetByID(ctx, sourceAgentID)
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ func (t *HandoffTool) executeTransfer(ctx context.Context, args map[string]any)
|
|||||||
// Get current agent and channel context
|
// Get current agent and channel context
|
||||||
sourceAgentID := store.AgentIDFromContext(ctx)
|
sourceAgentID := store.AgentIDFromContext(ctx)
|
||||||
if sourceAgentID == uuid.Nil {
|
if sourceAgentID == uuid.Nil {
|
||||||
return ErrorResult("handoff requires managed mode")
|
return ErrorResult("handoff requires database stores")
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceAgent, err := t.delegateMgr.agentStore.GetByID(ctx, sourceAgentID)
|
sourceAgent, err := t.delegateMgr.agentStore.GetByID(ctx, sourceAgentID)
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ func (m *TeamToolManager) SetDelegateManager(dm *DelegateManager) {
|
|||||||
func (m *TeamToolManager) resolveTeam(ctx context.Context) (*store.TeamData, uuid.UUID, error) {
|
func (m *TeamToolManager) resolveTeam(ctx context.Context) (*store.TeamData, uuid.UUID, error) {
|
||||||
agentID := store.AgentIDFromContext(ctx)
|
agentID := store.AgentIDFromContext(ctx)
|
||||||
if agentID == uuid.Nil {
|
if agentID == uuid.Nil {
|
||||||
return nil, uuid.Nil, fmt.Errorf("no agent context — team tools require managed mode")
|
return nil, uuid.Nil, fmt.Errorf("no agent context — team tools require database stores")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check cache first
|
// Check cache first
|
||||||
|
|||||||
@@ -81,33 +81,6 @@ else
|
|||||||
echo " [exists] GOCLAW_GATEWAY_TOKEN"
|
echo " [exists] GOCLAW_GATEWAY_TOKEN"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 4. Check provider API key
|
|
||||||
has_provider=false
|
|
||||||
for key in GOCLAW_OPENROUTER_API_KEY GOCLAW_ANTHROPIC_API_KEY GOCLAW_OPENAI_API_KEY \
|
|
||||||
GOCLAW_MINIMAX_API_KEY GOCLAW_GROQ_API_KEY GOCLAW_DEEPSEEK_API_KEY \
|
|
||||||
GOCLAW_GEMINI_API_KEY GOCLAW_MISTRAL_API_KEY GOCLAW_XAI_API_KEY \
|
|
||||||
GOCLAW_COHERE_API_KEY GOCLAW_PERPLEXITY_API_KEY; do
|
|
||||||
val="$(get_env_val "$key")"
|
|
||||||
if [ -n "$val" ]; then
|
|
||||||
has_provider=true
|
|
||||||
echo " [exists] $key"
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ "$has_provider" = false ]; then
|
|
||||||
echo " [missing] No LLM provider API key found"
|
|
||||||
echo ""
|
|
||||||
echo " Add at least one provider key to .env before starting:"
|
|
||||||
echo " GOCLAW_OPENROUTER_API_KEY=sk-or-..."
|
|
||||||
echo " GOCLAW_ANTHROPIC_API_KEY=sk-ant-..."
|
|
||||||
echo " GOCLAW_MINIMAX_API_KEY=..."
|
|
||||||
echo ""
|
|
||||||
echo "=== Done (action required) ==="
|
|
||||||
echo ""
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "=== Done ==="
|
echo "=== Done ==="
|
||||||
echo ""
|
echo ""
|
||||||
|
|||||||
Reference in new issue
Block a user