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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ coverage.out
.memory
codex-home
.tmp/
.vscode
2 changes: 1 addition & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,5 @@ Security concerns include but are not limited to:

- Never commit `config.yaml` containing real credentials
- Use `log_level: debug` only temporarily for troubleshooting — debug logs may contain rendered prompts
- Keep `runtime_http_addr` bound to `127.0.0.1` unless you know what you're doing
- The runtime API listens on a Unix domain socket by default — no network exposure
- Rotate your `runtime_http_token` periodically if exposed
2 changes: 1 addition & 1 deletion book/en/development/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ Important keys:
- `group_scenes.chat`, `group_scenes.work`
- `private_scenes.chat`, `private_scenes.work`
- `permissions`
- `runtime_http_addr`
- `runtime_socket`
- `workspace_dir`, `prompt_dir`, `codex_home`

Behavior worth calling out:
Expand Down
4 changes: 2 additions & 2 deletions book/en/explanation/runtime-api-design.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Alice runs bundled skills as subprocess scripts. These scripts need to interact

### 1. Local-Only by Default

The API binds to `127.0.0.1` (configurable via `runtime_http_addr`). It is not designed to be exposed to the network. If you need remote access, use a reverse proxy or SSH tunnel — but this is not the intended use case.
The API listens on a Unix domain socket (configurable via `runtime_socket`). It is not designed to be exposed to the network. If you need remote access, use a reverse proxy or SSH tunnel — but this is not the intended use case.

### 2. Bearer Token Auth

Expand Down Expand Up @@ -59,7 +59,7 @@ Both accept `multipart/form-data` with an optional `caption` field.
Skills don't need to know the API address or token. Alice injects them:

```bash
ALICE_RUNTIME_API_BASE_URL="http://127.0.0.1:7331"
ALICE_RUNTIME_API_BASE_URL="unix:///home/user/.alice/runtime.sock"
ALICE_RUNTIME_API_TOKEN="<auto-generated>"
ALICE_RUNTIME_BIN="/usr/local/bin/alice"
```
Expand Down
4 changes: 2 additions & 2 deletions book/en/how-to/troubleshoot.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,11 @@ permissions:
**Check API connectivity:**
```bash
# From the machine running Alice
curl -s -H "Authorization: Bearer <token>" http://127.0.0.1:7331/healthz
curl -s --unix-socket ~/.alice/runtime.sock -H "Authorization: Bearer <token>" http://unix/healthz
# Should return {"status":"ok"}
```

The runtime HTTP API binds to the address in `runtime_http_addr` (default `127.0.0.1:7331`). Multi-bot setups auto-increment the port.
The runtime API listens on a Unix domain socket. The socket path defaults to `$ALICE_HOME/runtime.sock`.

## Configuration changes don't apply

Expand Down
10 changes: 8 additions & 2 deletions book/en/how-to/use-builtin-commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,19 @@ Shows a status card with:

## `/clear`

Resets the current `chat` scene session. The next message starts a fresh conversation with no prior context.
Clears the backend session binding for the current conversation. The next message starts a fresh conversation on a new backend session with no prior context.

```
/clear
```

> Only affects `chat` scenes. `work` scenes are thread-scoped and reset naturally when the thread ends.
When to use:

- **Direct messages**: clears the DM's backend session binding.
- **Group chats with `chat` scene enabled**: clears the chat-scene backend session binding for the group.
- After switching providers (for example, migrating from codex to opencode): the old backend session id cannot be resumed by the new provider — `/clear` unbinds it so the next message starts a fresh session.

> Does not affect `work` scene threads. Each `work` thread has its own session; to rebind one, use `/session <backend-session-id>` inside that thread.

## `/stop`

Expand Down
8 changes: 4 additions & 4 deletions book/en/reference/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -512,16 +512,16 @@ Additional session scope: `"per_message"` — each DM with `#work` creates a fre

---

### Runtime HTTP API
### Runtime API

#### `runtime_http_addr`
#### `runtime_socket`

| Field | Value |
|-------|-------|
| Type | `string` |
| Default | `"127.0.0.1:7331"` |
| Default | `"runtime.sock"` |

Listen address for the runtime HTTP API. Multi-bot setups auto-increment the port (`7332`, `7333`, ...).
Unix domain socket path for the runtime API, resolved relative to `alice_home`. Each bot has its own socket under its own `alice_home`, so no port conflicts.

#### `runtime_http_token`

Expand Down
2 changes: 1 addition & 1 deletion book/zh/development/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ Alice 暴露一个本地认证的 Runtime API,面向 bundled skill 和薄运
- `group_scenes.chat`、`group_scenes.work`
- `private_scenes.chat`、`private_scenes.work`
- `permissions`
- `runtime_http_addr`
- `runtime_socket`
- `workspace_dir`、`prompt_dir`、`codex_home`

值得注意的行为:
Expand Down
4 changes: 2 additions & 2 deletions book/zh/explanation/runtime-api-design.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Alice 将 bundled skill 作为子进程脚本运行。这些脚本需要与运

### 1. 默认仅本地访问

API 绑定到 `127.0.0.1`(可通过 `runtime_http_addr` 配置)。它并非设计为对外暴露。如果需要远程访问,请使用反向代理或 SSH 隧道 — 但这并非预期用例。
API 监听在 Unix 域套接字上(可通过 `runtime_socket` 配置)。它并非设计为对外暴露。如果需要远程访问,请使用反向代理或 SSH 隧道 — 但这并非预期用例。

### 2. Bearer Token 认证

Expand Down Expand Up @@ -59,7 +59,7 @@ Runtime API **没有**纯文本发送端点。为什么?
Skill 无需知道 API 地址或 token。Alice 注入它们:

```bash
ALICE_RUNTIME_API_BASE_URL="http://127.0.0.1:7331"
ALICE_RUNTIME_API_BASE_URL="unix:///home/user/.alice/runtime.sock"
ALICE_RUNTIME_API_TOKEN="<auto-generated>"
ALICE_RUNTIME_BIN="/usr/local/bin/alice"
```
Expand Down
4 changes: 2 additions & 2 deletions book/zh/how-to/troubleshoot.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,11 @@ permissions:
**检查 API 连通性:**
```bash
# 在运行 Alice 的机器上执行
curl -s -H "Authorization: Bearer <token>" http://127.0.0.1:7331/healthz
curl -s --unix-socket ~/.alice/runtime.sock -H "Authorization: Bearer <token>" http://unix/healthz
# 应返回 {"status":"ok"}
```

Runtime HTTP API 绑定在 `runtime_http_addr` 指定的地址(默认 `127.0.0.1:7331`)。多 bot 设置会自动递增端口
Runtime API 监听在 Unix 域套接字上,默认路径为 `$ALICE_HOME/runtime.sock`

## 配置更改不生效

Expand Down
10 changes: 8 additions & 2 deletions book/zh/how-to/use-builtin-commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,19 @@ Alice 提供多个斜杠命令,这些命令绕过 LLM,由连接器直接处

## `/clear`

重置当前 `chat` 场景的 session。下一条消息将以全新对话开始,不带有之前的上下文。
清空当前会话的后端 session 绑定。下一条消息会以全新对话开始,并在后端开新的 session,不带有之前的上下文。

```
/clear
```

> 仅影响 `chat` 场景。`work` 场景是基于话题的,话题结束时自然重置。
适用场景:

- **私聊**:直接清空 DM 的后端 session 绑定。
- **群聊 `chat` 模式**:清空当前群聊 `chat` 场景的后端 session 绑定。
- 切换 provider(例如从 codex 迁移到 opencode)后,旧的后端 session id 在新 provider 上无法 resume——用 `/clear` 解绑即可让下一条消息开新 session。

> 不影响 `work` 场景的话题。`work` 是按话题独立的,需要重绑请在该话题里使用 `/session <backend-session-id>`。

## `/stop`

Expand Down
8 changes: 4 additions & 4 deletions book/zh/reference/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -512,16 +512,16 @@ Reaction 反馈的飞书表情名称(如 `"OK"`、`"WINK"`、`"THUMBSUP"`)

---

### Runtime HTTP API
### Runtime API

#### `runtime_http_addr`
#### `runtime_socket`

| 字段 | 值 |
|-------|-------|
| 类型 | `string` |
| 默认值 | `"127.0.0.1:7331"` |
| 默认值 | `"runtime.sock"` |

Runtime HTTP API 的监听地址。多 bot 设置会自动递增端口(`7332`、`7333`……)
Runtime API 的 Unix 域套接字路径,相对于 `alice_home` 解析。每个 bot 在其自己的 `alice_home` 下拥有独立的套接字,因此不会发生端口冲突

#### `runtime_http_token`

Expand Down
2 changes: 1 addition & 1 deletion cmd/connector/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@ func runConnector(configPath, pidFilePath string, pidFileExplicit bool, runtimeO
}
logging.Infof("automation engine enabled bot=%s state_file=%s", built.Config.BotID, built.AutomationStatePath)
if built.RuntimeAPI != nil {
logging.Infof("runtime http api enabled bot=%s addr=%s", built.Config.BotID, built.RuntimeAPIBaseURL)
logging.Infof("runtime api enabled bot=%s socket=%s", built.Config.BotID, built.RuntimeAPISocket)
}
}
if runtimeOnly {
Expand Down
6 changes: 3 additions & 3 deletions cmd/connector/runtime_root.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@ func withRuntimeClient(
}

func loadRuntimeClient() (*runtimeapi.Client, sessionctx.SessionContext, error) {
baseURL := strings.TrimSpace(os.Getenv(runtimeapi.EnvBaseURL))
if baseURL == "" {
addr := strings.TrimSpace(os.Getenv(runtimeapi.EnvBaseURL))
if addr == "" {
return nil, sessionctx.SessionContext{}, fmt.Errorf("missing %s", runtimeapi.EnvBaseURL)
}
client := runtimeapi.NewClient(baseURL, os.Getenv(runtimeapi.EnvToken))
client := runtimeapi.NewClient(addr, os.Getenv(runtimeapi.EnvToken))
if client == nil || !client.IsEnabled() {
return nil, sessionctx.SessionContext{}, fmt.Errorf("runtime api client is unavailable")
}
Expand Down
12 changes: 7 additions & 5 deletions config.example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -261,11 +261,13 @@ bots:
no_reply_token: ""
create_feishu_thread: true

# ── Runtime HTTP API ───────────────────────────────────────────────────
# Alice exposes a local HTTP API for bundled skills and automation tasks.
# Listen address for the runtime HTTP API.
# Default: 127.0.0.1:7331. Multi-bot auto-increments the port (7332, 7333, ...).
runtime_http_addr: ""
# ── Runtime API ────────────────────────────────────────────────────────
# Alice exposes a local API over a Unix domain socket for bundled skills
# and automation tasks.
# Socket path (relative to alice_home, or absolute). Each bot gets its own
# socket under its own alice_home, so no port conflicts.
# Default: runtime.sock (resolved as $ALICE_HOME/runtime.sock).
runtime_socket: ""
# Bearer token for API authentication. Auto-generated if empty.
# Set explicitly for cross-process calls or manual debugging.
# Example: runtime_http_token: "my-static-token"
Expand Down
4 changes: 2 additions & 2 deletions internal/bootstrap/config_reload.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,8 @@ func diffRestartRequiredFields(current, next config.Config) []string {
if current.FeishuBaseURL != next.FeishuBaseURL {
changed = append(changed, "feishu_base_url")
}
if current.RuntimeHTTPAddr != next.RuntimeHTTPAddr {
changed = append(changed, "runtime_http_addr")
if current.RuntimeSocket != next.RuntimeSocket {
changed = append(changed, "runtime_socket")
}
if current.RuntimeHTTPToken != next.RuntimeHTTPToken {
changed = append(changed, "runtime_http_token")
Expand Down
6 changes: 3 additions & 3 deletions internal/bootstrap/config_reload_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ func TestDiffRestartRequiredFields(t *testing.T) {
FeishuAppID: "cli_a",
FeishuAppSecret: "sec_a",
FeishuBaseURL: "https://open.feishu.cn",
RuntimeHTTPAddr: "127.0.0.1:7331",
RuntimeSocket: "/tmp/alice-test.sock",
RuntimeHTTPToken: "token_a",
WorkspaceDir: "/workspace/a",
PromptDir: "prompts",
Expand All @@ -25,7 +25,7 @@ func TestDiffRestartRequiredFields(t *testing.T) {
next := current
next.TriggerMode = "prefix"
next.TriggerPrefix = "!alice"
next.RuntimeHTTPAddr = "127.0.0.1:7332"
next.RuntimeSocket = "/tmp/alice-test-2.sock"
next.QueueCapacity = 512
next.WorkerConcurrency = 3
next.AuthStatusTimeoutSecs = 20
Expand All @@ -36,7 +36,7 @@ func TestDiffRestartRequiredFields(t *testing.T) {
"auth_status_timeout_secs",
"queue_capacity",
"runtime_api_shutdown_timeout_secs",
"runtime_http_addr",
"runtime_socket",
"worker_concurrency",
}
if !reflect.DeepEqual(got, want) {
Expand Down
2 changes: 1 addition & 1 deletion internal/bootstrap/connector_runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ type ConnectorRuntime struct {
Processor *connector.Processor
AutomationEngine *automation.Engine
RuntimeAPI *runtimeapi.Server
RuntimeAPIBaseURL string
RuntimeAPISocket string
RuntimeAPIToken string
AutomationStatePath string
SessionStatePath string
Expand Down
8 changes: 4 additions & 4 deletions internal/bootstrap/connector_runtime_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ func (b *connectorRuntimeBuilder) Build() (*ConnectorRuntime, error) {
Processor: b.processor,
AutomationEngine: b.automationEngine,
RuntimeAPI: b.apiServer,
RuntimeAPIBaseURL: runtimeapi.BaseURL(b.cfg.RuntimeHTTPAddr),
RuntimeAPISocket: runtimeapi.BaseURL(b.cfg.RuntimeSocket),
RuntimeAPIToken: b.apiToken,
AutomationStatePath: b.paths.automationStatePath,
SessionStatePath: b.paths.sessionStatePath,
Expand Down Expand Up @@ -148,7 +148,7 @@ func (b *connectorRuntimeBuilder) buildProcessor() error {
workDisable := b.cfg.GroupScenes.Work.DisableIdentityHints != nil && *b.cfg.GroupScenes.Work.DisableIdentityHints
processor.SetSceneIdentityHints(chatDisable, workDisable)
processor.SetRuntimeAPI(
runtimeapi.BaseURL(b.cfg.RuntimeHTTPAddr),
runtimeapi.BaseURL(b.cfg.RuntimeSocket),
b.resolveRuntimeAPIToken(),
ResolveRuntimeBinary(b.cfg.WorkspaceDir),
)
Expand Down Expand Up @@ -184,7 +184,7 @@ func (b *connectorRuntimeBuilder) buildAutomationEngine() error {
automationEngine.SetUserTaskTimeout(b.cfg.AutomationTaskTimeout)
automationEngine.SetLLMRunner(b.backend)
automationEngine.SetRunEnv(map[string]string{
runtimeapi.EnvBaseURL: runtimeapi.BaseURL(b.cfg.RuntimeHTTPAddr),
runtimeapi.EnvBaseURL: runtimeapi.BaseURL(b.cfg.RuntimeSocket),
runtimeapi.EnvToken: b.resolveRuntimeAPIToken(),
runtimeapi.EnvBin: ResolveRuntimeBinary(b.cfg.WorkspaceDir),
})
Expand Down Expand Up @@ -219,7 +219,7 @@ func (b *connectorRuntimeBuilder) buildAutomationEngine() error {

func (b *connectorRuntimeBuilder) buildRuntimeAPI() {
b.apiServer = runtimeapi.NewServer(
b.cfg.RuntimeHTTPAddr,
b.cfg.RuntimeSocket,
b.resolveRuntimeAPIToken(),
b.sender,
b.automationStore,
Expand Down
8 changes: 4 additions & 4 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ const DefaultImmediateFeedbackMode = ImmediateFeedbackModeReaction
// DefaultImmediateFeedbackReaction is the default reaction emoji for immediate feedback.
const DefaultImmediateFeedbackReaction = "OK"

// DefaultRuntimeHTTPAddr is the default runtime HTTP listen address.
const DefaultRuntimeHTTPAddr = "127.0.0.1:7331"
// DefaultRuntimeSocket is the default runtime API Unix socket filename, resolved relative to AliceHome.
const DefaultRuntimeSocket = "runtime.sock"

// DefaultWorkerConcurrency is the default worker pool size.
const DefaultWorkerConcurrency = 3
Expand Down Expand Up @@ -143,7 +143,7 @@ type BotConfig struct {
LLMProfiles map[string]LLMProfileConfig `mapstructure:"llm_profiles"`
GroupScenes *GroupScenesConfig `mapstructure:"group_scenes"`
PrivateScenes *GroupScenesConfig `mapstructure:"private_scenes"`
RuntimeHTTPAddr string `mapstructure:"runtime_http_addr"`
RuntimeSocket string `mapstructure:"runtime_socket"`
RuntimeHTTPToken string `mapstructure:"runtime_http_token"`
FailureMessage string `mapstructure:"failure_message"`
ThinkingMessage string `mapstructure:"thinking_message"`
Expand Down Expand Up @@ -188,7 +188,7 @@ type Config struct {
CodexEnv map[string]string `mapstructure:"env"`
CodexHome string `mapstructure:"codex_home"`

RuntimeHTTPAddr string `mapstructure:"runtime_http_addr"`
RuntimeSocket string `mapstructure:"runtime_socket"`
RuntimeHTTPToken string `mapstructure:"runtime_http_token"`
FailureMessage string `mapstructure:"failure_message"`
ThinkingMessage string `mapstructure:"thinking_message"`
Expand Down
6 changes: 3 additions & 3 deletions internal/config/config_load.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func setBotDefaults(v *viper.Viper) {
}
}

func setCommonConfigDefaults(v *viper.Viper, prefix string, includeRuntimeHTTPAddr bool) {
func setCommonConfigDefaults(v *viper.Viper, prefix string, includeRuntimeSocket bool) {
if v == nil {
return
}
Expand All @@ -67,8 +67,8 @@ func setCommonConfigDefaults(v *viper.Viper, prefix string, includeRuntimeHTTPAd
v.SetDefault(configKey(prefix, "trigger_prefix"), "")
v.SetDefault(configKey(prefix, "immediate_feedback_mode"), DefaultImmediateFeedbackMode)
v.SetDefault(configKey(prefix, "immediate_feedback_reaction"), DefaultImmediateFeedbackReaction)
if includeRuntimeHTTPAddr {
v.SetDefault(configKey(prefix, "runtime_http_addr"), DefaultRuntimeHTTPAddr)
if includeRuntimeSocket {
v.SetDefault(configKey(prefix, "runtime_socket"), DefaultRuntimeSocket)
}
v.SetDefault(configKey(prefix, "runtime_http_token"), "")
v.SetDefault(configKey(prefix, "failure_message"), "暂时不可用,请稍后重试。")
Expand Down
2 changes: 1 addition & 1 deletion internal/config/config_normalize.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ func normalizeLoadedConfig(cfg Config, rootEnv map[string]string) Config {
cfg.GroupScenes = normalizeGroupScenes(cfg.GroupScenes)
cfg.PrivateScenes = normalizePrivateScenes(cfg.PrivateScenes)
cfg.CodexEnv = normalizeEnvMap(rootEnv)
cfg.RuntimeHTTPAddr = strings.TrimSpace(cfg.RuntimeHTTPAddr)
cfg.RuntimeSocket = strings.TrimSpace(cfg.RuntimeSocket)
cfg.RuntimeHTTPToken = strings.TrimSpace(cfg.RuntimeHTTPToken)
cfg.FailureMessage = strings.TrimSpace(cfg.FailureMessage)
cfg.ThinkingMessage = strings.TrimSpace(cfg.ThinkingMessage)
Expand Down
2 changes: 1 addition & 1 deletion internal/config/config_validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func validatePureMultiBotRootConfig(v *viper.Viper) error {
"kimi_command",
"kimi_timeout_secs",
"kimi_prompt_prefix",
"runtime_http_addr",
"runtime_socket",
"runtime_http_token",
"failure_message",
"thinking_message",
Expand Down
6 changes: 3 additions & 3 deletions internal/config/multibot.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,14 @@ func finalizeConfig(cfg Config, requireCredentials bool) (Config, error) {
cfg.ImmediateFeedbackReaction = DefaultImmediateFeedbackReaction
}
cfg.CodexEnv = applyDefaultCodexEnv(cfg.CodexEnv)
if cfg.RuntimeHTTPAddr == "" {
cfg.RuntimeHTTPAddr = DefaultRuntimeHTTPAddr
}
if cfg.AliceHome == "" {
cfg.AliceHome = AliceHomeDir()
} else {
cfg.AliceHome = ResolveAliceHomeDir(cfg.AliceHome)
}
if cfg.RuntimeSocket == "" {
cfg.RuntimeSocket = filepath.Join(cfg.AliceHome, DefaultRuntimeSocket)
}
if cfg.WorkspaceDir == "" {
cfg.WorkspaceDir = WorkspaceDirForAliceHome(cfg.AliceHome)
} else {
Expand Down
2 changes: 1 addition & 1 deletion internal/config/multibot_normalize.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func normalizeBots(in map[string]BotConfig) map[string]BotConfig {
normalized := normalizePrivateScenes(*bot.PrivateScenes)
bot.PrivateScenes = &normalized
}
bot.RuntimeHTTPAddr = strings.TrimSpace(bot.RuntimeHTTPAddr)
bot.RuntimeSocket = strings.TrimSpace(bot.RuntimeSocket)
bot.RuntimeHTTPToken = strings.TrimSpace(bot.RuntimeHTTPToken)
bot.FailureMessage = strings.TrimSpace(bot.FailureMessage)
bot.ThinkingMessage = strings.TrimSpace(bot.ThinkingMessage)
Expand Down
Loading