Skip to content

Commit ac13674

Browse files
authored
Merge pull request #1029 from openabdev/feat/control-directives-phase1
feat: implement Control Directives Phase 1
2 parents d7d0c8d + 4435473 commit ac13674

11 files changed

Lines changed: 792 additions & 30 deletions

config.toml.example

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,3 +177,12 @@ error_hold_ms = 2500
177177
# schedule = "0 0 * * 0"
178178
# channel = "123456789"
179179
# message = "generate weekly status report"
180+
181+
# Workspace aliases for control directives: [[ws:@alias]]
182+
# Users can specify a workspace when starting a session, e.g.:
183+
# @Bot [[ws:@openab]] help me debug the tests
184+
#
185+
# [workspace.aliases]
186+
# openab = "~/projects/openab"
187+
# infra = "~/projects/infra-cdk"
188+
# web = "~/projects/frontend"

docs/config-reference.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,30 @@ Speech-to-text transcription for voice messages. Uses an OpenAI-compatible `/aud
291291

292292
---
293293

294+
## `[workspace]`
295+
296+
Workspace aliases for [Control Directives](adr/control-directives.md). Users specify `[[ws:@alias]]` in their first message to set the session's working directory.
297+
298+
```toml
299+
[workspace.aliases]
300+
openab = "~/projects/openab"
301+
infra = "~/projects/infra-cdk"
302+
web = "~/projects/frontend"
303+
```
304+
305+
| Field | Type | Default | Description |
306+
|-------|------|---------|-------------|
307+
| `aliases` | map | `{}` | Key-value map of alias name → path. Users reference with `@` prefix: `[[ws:@openab]]`. Paths starting with `~` expand to `$HOME`. All paths must be within the bot's home directory (security boundary). |
308+
309+
**Security:**
310+
- Relative paths are rejected
311+
- `~` expands to bot home (`$HOME`)
312+
- Paths are canonicalized and must be within bot home subtree
313+
- Symlink escapes are caught by canonicalization
314+
- Target must be an existing directory (not a file)
315+
316+
---
317+
294318
## `[cron]`
295319

296320
Everything cron-related lives under `[cron]`.

docs/control-directives.md

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# Control Directives
2+
3+
## Overview
4+
5+
Users can configure session-level parameters by adding `[[key:value]]` directives to their first message. These are parsed and stripped before the prompt reaches the agent.
6+
7+
## Format
8+
9+
```
10+
@Bot [[ws:@openab]] [[title:Fix CI workflow]]
11+
help me debug the smoke test failures
12+
```
13+
14+
Rules:
15+
- Directives are only processed on the **first message** of a session
16+
- Consecutive `[[key:value]]` tokens at the start = directive header
17+
- First non-directive token/line = prompt content begins
18+
- Later `[[key:value]]` text in the message body is preserved verbatim
19+
- Duplicate keys: last value wins
20+
- Unknown keys: silently ignored (forward compatible)
21+
- Directives may span multiple lines:
22+
```
23+
[[ws:@openab]]
24+
[[title:Review PR]]
25+
please review this change
26+
```
27+
28+
## Available Directives
29+
30+
### `ws` — Workspace
31+
32+
Set the session's working directory. The agent starts with this path as cwd, loading steering files and skills from it.
33+
34+
```
35+
[[ws:~/projects/myapp]]
36+
[[ws:@openab]]
37+
```
38+
39+
**Alias syntax:** Use `@name` to reference a configured alias (see [Workspaces](workspaces.md)).
40+
41+
**Security:**
42+
- Path must be absolute (`~` or `/` prefix)
43+
- `~` expands to bot home directory (`$HOME`)
44+
- Path must exist and be a directory
45+
- Path must be within bot home subtree (symlink escapes caught by canonicalization)
46+
- Invalid paths abort the session with a user-visible error
47+
48+
### `title` — Thread Title
49+
50+
Set the initial thread/channel title (max 100 characters, truncated silently).
51+
52+
```
53+
[[title:Bug triage #42]]
54+
```
55+
56+
- Empty value (`[[title:]]`) = use auto-generated title
57+
- The agent may override this later per its own workflow
58+
59+
## Behavior
60+
61+
- Directives are **immutable** — once a session starts, parameters cannot be changed mid-conversation
62+
- To change workspace or title, start a new session
63+
- If no `[[ws:...]]` is specified, the session uses the bot's default working directory
64+
- If workspace resolution fails on a new session, the session is not created. However, `[[title:...]]` is applied independently before workspace validation — the thread title may already be set even if the session aborts.
65+
66+
## Relationship to Output Directives
67+
68+
| Aspect | Output Directives | Control Directives |
69+
|--------|-------------------|-------------------|
70+
| Direction | Agent → Platform | User → Bot |
71+
| Timing | Every response | First message only |
72+
| Syntax | `[[key:value]]` | `[[key:value]]` |
73+
74+
## Future Directives (Phase 2+)
75+
76+
| Directive | Purpose |
77+
|-----------|---------|
78+
| `[[model:...]]` | Select model for the session |
79+
| `[[repo:owner/repo]]` | Bind GitHub repository context |
80+
| `[[timeout:300]]` | Session timeout in seconds |
81+
82+
## See Also
83+
84+
- [Workspaces](workspaces.md) — configuring workspace aliases
85+
- [Output Directives](output-directives.md) — agent → platform directives
86+
- [ADR: Control Directives](adr/control-directives.md) — design rationale

docs/workspaces.md

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# Workspaces
2+
3+
## Overview
4+
5+
A single OAB bot instance can serve multiple projects. Workspaces let users switch project context at session start using the `[[ws:...]]` [control directive](control-directives.md).
6+
7+
When a workspace is set, the agent:
8+
- Uses the workspace path as its working directory
9+
- Loads steering rules from `AGENTS.md` and `.kiro/steering/`
10+
- Activates skills from `.kiro/skills/`
11+
- Has correct git context (branch, remote, history)
12+
13+
## Configuration
14+
15+
Define workspace aliases in `config.toml`:
16+
17+
```toml
18+
[workspace.aliases]
19+
openab = "~/projects/openab"
20+
infra = "~/projects/infra-cdk"
21+
web = "~/projects/frontend"
22+
```
23+
24+
Paths starting with `~` expand to the bot's home directory (`$HOME`).
25+
26+
## Usage
27+
28+
Reference aliases with `@` prefix in the first message:
29+
30+
```
31+
@Bot [[ws:@openab]] help me debug the smoke tests
32+
```
33+
34+
Or use raw paths:
35+
36+
```
37+
@Bot [[ws:~/projects/myapp]] investigate the build failure
38+
```
39+
40+
## Security Boundary
41+
42+
All workspace paths are validated before use:
43+
44+
1. **Must be absolute** — relative paths (e.g., `../secrets`) are rejected
45+
2. **`~` expands to bot home** — not the requesting user's home
46+
3. **Canonicalized** — symlinks, `..`, `.` are resolved
47+
4. **Must be within bot home subtree** — paths outside are rejected
48+
5. **Must be a directory** — file paths are rejected
49+
6. **Must exist** — non-existent paths are rejected with a clear error showing the expanded path
50+
51+
## Session Behavior
52+
53+
- Workspace is set **once** at session creation and is immutable
54+
- The workspace persists across session suspend/resume and eviction rebuilds
55+
- To change workspace, start a new session
56+
- If workspace resolution fails, no session is created (clean failure)
57+
58+
## Error Messages
59+
60+
| Scenario | Error |
61+
|----------|-------|
62+
| Unknown alias | `Unknown workspace alias @foo. Available: openab, infra, web` |
63+
| Relative path | `Workspace path must be absolute (start with ~ or /): relative/path` |
64+
| Outside home | `Workspace path is outside allowed directory: /etc/passwd` |
65+
| Not a directory | `Workspace path is not a directory: /home/bot/Cargo.toml` |
66+
| Does not exist | `Workspace path does not exist: ~/nope (expanded to /home/bot/nope)` |
67+
68+
## See Also
69+
70+
- [Control Directives](control-directives.md) — full directive syntax and rules
71+
- [Config Reference](config-reference.md#workspace)`[workspace.aliases]` configuration

0 commit comments

Comments
 (0)