mirror of
https://github.com/garrytan/gstack.git
synced 2026-05-01 19:25:10 +02:00
docs: Slate agent integration research + design doc (#782)
Comprehensive research on Slate (Random Labs) as a potential first-class coding agent host. Includes binary analysis findings, skill discovery paths, env vars, CLI flags, and npm package structure. Blocked on host config refactor before implementation. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,290 @@
|
||||
# Slate Host Integration — Research & Design Doc
|
||||
|
||||
**Date:** 2026-04-02
|
||||
**Branch:** garrytan/slate-agent-support
|
||||
**Status:** Research complete, blocked on host config refactor
|
||||
**Supersedes:** None
|
||||
|
||||
## What is Slate
|
||||
|
||||
Slate is a proprietary coding agent CLI from Random Labs.
|
||||
Install: `npm i -g @randomlabs/slate` or `brew install anthropic/tap/slate`.
|
||||
License: Proprietary. 85MB compiled Bun binary (arm64/x64, darwin/linux/windows).
|
||||
npm package: `@randomlabs/slate@1.0.25` (thin 8.8KB launcher + platform-specific optional deps).
|
||||
|
||||
Multi-model: dynamically selects Claude Sonnet/Opus/Haiku, plus other models.
|
||||
Built for "swarm orchestration" with extended multi-hour sessions.
|
||||
|
||||
## Slate is an OpenCode fork
|
||||
|
||||
**Confirmed via binary strings analysis** of the 85MB Mach-O arm64 binary:
|
||||
|
||||
- Internal name: `name: "opencode"` (literal string in binary)
|
||||
- All `OPENCODE_*` env vars present alongside `SLATE_*` equivalents
|
||||
- Shares OpenCode's tool/skill architecture, LSP integration, terminal management
|
||||
- Own branding, API endpoints (`api.randomlabs.ai`, `agent-worker-prod.randomlabs.workers.dev`), and config paths
|
||||
|
||||
This matters for integration: OpenCode conventions mostly apply, but Slate adds
|
||||
its own paths and env vars on top.
|
||||
|
||||
## Skill Discovery (confirmed from binary)
|
||||
|
||||
Slate scans ALL four directory families for skills. Error messages in binary confirm:
|
||||
|
||||
```
|
||||
"failed .slate directory scan for skills"
|
||||
"failed .claude directory scan for skills"
|
||||
"failed .agents directory scan for skills"
|
||||
"failed .opencode directory scan for skills"
|
||||
```
|
||||
|
||||
**Discovery paths (priority order from Slate docs):**
|
||||
|
||||
1. `.slate/skills/<name>/SKILL.md` — project-level, highest priority
|
||||
2. `~/.slate/skills/<name>/SKILL.md` — global
|
||||
3. `.opencode/skills/`, `.agents/skills/` — compatibility fallback
|
||||
4. `.claude/skills/` — Claude Code compatibility fallback (lowest)
|
||||
5. Custom paths via `slate.json`
|
||||
|
||||
**Glob patterns:** `**/SKILL.md` and `{skill,skills}/**/SKILL.md`
|
||||
|
||||
**Commands:** Same directory structure but under `commands/` subdirs:
|
||||
`/.slate/commands/`, `/.claude/commands/`, `/.agents/commands/`, `/.opencode/commands/`
|
||||
|
||||
**Skill frontmatter:** YAML with `name` and `description` fields (per Slate docs).
|
||||
No documented length limits on either field.
|
||||
|
||||
## Project Instructions
|
||||
|
||||
Slate reads both `CLAUDE.md` and `AGENTS.md` for project instructions.
|
||||
Both literal strings confirmed in binary. No changes needed to existing
|
||||
gstack projects... CLAUDE.md works as-is.
|
||||
|
||||
## Configuration
|
||||
|
||||
**Config file:** `slate.json` / `slate.jsonc` (NOT opencode.json)
|
||||
|
||||
**Config options (from Slate docs):**
|
||||
- `privacy` (boolean) — disables telemetry/logging
|
||||
- Permissions: `allow`, `ask`, `deny` per tool (`read`, `edit`, `bash`, `grep`, `webfetch`, `websearch`, `*`)
|
||||
- Model slots: `models.main`, `models.subagent`, `models.search`, `models.reasoning`
|
||||
- MCP servers: local or remote with custom commands and headers
|
||||
- Custom commands: `/commands` with templates
|
||||
|
||||
The setup script should NOT create `slate.json`. Users configure their own permissions.
|
||||
|
||||
## CLI Flags (Headless Mode)
|
||||
|
||||
```
|
||||
--stream-json / --output-format stream-json — JSONL output, "compatible with Anthropic Claude Code SDK"
|
||||
--dangerously-skip-permissions — bypass all permission checks (CI/automation)
|
||||
--input-format stream-json — programmatic input
|
||||
-q — non-interactive mode
|
||||
-w <dir> — workspace directory
|
||||
--output-format text — plain text output (default)
|
||||
```
|
||||
|
||||
**Stream-JSON format:** Slate docs claim "compatible with Anthropic Claude Code SDK."
|
||||
Not yet empirically verified. Given OpenCode heritage, likely matches Claude Code's
|
||||
NDJSON event schema (type: "assistant", type: "tool_result", type: "result").
|
||||
|
||||
**Need to verify:** Run `slate -q "hello" --stream-json` with valid credits and
|
||||
capture actual JSONL events before building the session runner parser.
|
||||
|
||||
## Environment Variables (from binary strings)
|
||||
|
||||
### Slate-specific
|
||||
```
|
||||
SLATE_API_KEY — API key
|
||||
SLATE_AGENT — agent selection
|
||||
SLATE_AUTO_SHARE — auto-share setting
|
||||
SLATE_CLIENT — client identifier
|
||||
SLATE_CONFIG — config override
|
||||
SLATE_CONFIG_CONTENT — inline config
|
||||
SLATE_CONFIG_DIR — config directory
|
||||
SLATE_DANGEROUSLY_SKIP_PERMISSIONS — bypass permissions
|
||||
SLATE_DIR — data directory override
|
||||
SLATE_DISABLE_AUTOUPDATE — disable auto-update
|
||||
SLATE_DISABLE_CLAUDE_CODE — disable Claude Code integration entirely
|
||||
SLATE_DISABLE_CLAUDE_CODE_PROMPT — disable Claude Code prompt loading
|
||||
SLATE_DISABLE_CLAUDE_CODE_SKILLS — disable .claude/skills/ loading
|
||||
SLATE_DISABLE_DEFAULT_PLUGINS — disable default plugins
|
||||
SLATE_DISABLE_FILETIME_CHECK — disable file time checks
|
||||
SLATE_DISABLE_LSP_DOWNLOAD — disable LSP auto-download
|
||||
SLATE_DISABLE_MODELS_FETCH — disable models config fetch
|
||||
SLATE_DISABLE_PROJECT_CONFIG — disable project-level config
|
||||
SLATE_DISABLE_PRUNE — disable session pruning
|
||||
SLATE_DISABLE_TERMINAL_TITLE — disable terminal title updates
|
||||
SLATE_ENABLE_EXA — enable Exa search
|
||||
SLATE_ENABLE_EXPERIMENTAL_MODELS — enable experimental models
|
||||
SLATE_EXPERIMENTAL — enable experimental features
|
||||
SLATE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS — bash timeout override
|
||||
SLATE_EXPERIMENTAL_DISABLE_COPY_ON_SELECT — disable copy on select
|
||||
SLATE_EXPERIMENTAL_DISABLE_FILEWATCHER — disable file watcher
|
||||
SLATE_EXPERIMENTAL_EXA — Exa search (alt flag)
|
||||
SLATE_EXPERIMENTAL_FILEWATCHER — enable file watcher
|
||||
SLATE_EXPERIMENTAL_ICON_DISCOVERY — icon discovery
|
||||
SLATE_EXPERIMENTAL_LSP_TOOL — LSP tool
|
||||
SLATE_EXPERIMENTAL_LSP_TY — LSP type checking
|
||||
SLATE_EXPERIMENTAL_MARKDOWN — markdown mode
|
||||
SLATE_EXPERIMENTAL_OUTPUT_TOKEN_MAX — output token limit
|
||||
SLATE_EXPERIMENTAL_OXFMT — oxfmt integration
|
||||
SLATE_EXPERIMENTAL_PLAN_MODE — plan mode
|
||||
SLATE_FAKE_VCS — fake VCS for testing
|
||||
SLATE_GIT_BASH_PATH — git bash path (Windows)
|
||||
SLATE_MODELS_URL — models config URL
|
||||
SLATE_PERMISSION — permission override
|
||||
SLATE_SERVER_PASSWORD — server auth
|
||||
SLATE_SERVER_USERNAME — server auth
|
||||
SLATE_TELEMETRY_DISABLED — disable telemetry
|
||||
SLATE_TEST_HOME — test home directory
|
||||
SLATE_TOKEN_DIR — token storage directory
|
||||
```
|
||||
|
||||
### OpenCode legacy (still functional)
|
||||
```
|
||||
OPENCODE_DISABLE_LSP_DOWNLOAD
|
||||
OPENCODE_EXPERIMENTAL_DISABLE_FILEWATCHER
|
||||
OPENCODE_EXPERIMENTAL_FILEWATCHER
|
||||
OPENCODE_EXPERIMENTAL_ICON_DISCOVERY
|
||||
OPENCODE_EXPERIMENTAL_LSP_TY
|
||||
OPENCODE_EXPERIMENTAL_OXFMT
|
||||
OPENCODE_FAKE_VCS
|
||||
OPENCODE_GIT_BASH_PATH
|
||||
OPENCODE_LIBC
|
||||
OPENCODE_TERMINAL
|
||||
```
|
||||
|
||||
### Critical env vars for gstack integration
|
||||
|
||||
**`SLATE_DISABLE_CLAUDE_CODE_SKILLS`** — When set, `.claude/skills/` loading is disabled.
|
||||
This makes publishing to `.slate/skills/` load-bearing, not just an optimization.
|
||||
Without native `.slate/` publishing, gstack skills vanish when this flag is set.
|
||||
|
||||
**`SLATE_TEST_HOME`** — Useful for E2E tests. Can redirect Slate's home directory
|
||||
to an isolated temp directory, similar to how Codex tests use a temp HOME.
|
||||
|
||||
**`SLATE_DANGEROUSLY_SKIP_PERMISSIONS`** — Required for headless E2E tests.
|
||||
|
||||
## Model References (from binary)
|
||||
|
||||
```
|
||||
anthropic/claude-sonnet-4.6
|
||||
anthropic/claude-opus-4
|
||||
anthropic/claude-haiku-4
|
||||
anthropic/slate — Slate's own model routing
|
||||
openai/gpt-5.3-codex
|
||||
google/nano-banana
|
||||
randomlabs/fast-default-alpha
|
||||
```
|
||||
|
||||
## API Endpoints (from binary)
|
||||
|
||||
```
|
||||
https://api.randomlabs.ai — main API
|
||||
https://api.randomlabs.ai/exaproxy — Exa search proxy
|
||||
https://agent-worker-prod.randomlabs.workers.dev — production worker
|
||||
https://agent-worker-dev.randomlabs.workers.dev — dev worker
|
||||
https://dashboard.randomlabs.ai — dashboard
|
||||
https://docs.randomlabs.ai — documentation
|
||||
https://randomlabs.ai/config.json — remote config
|
||||
```
|
||||
|
||||
Brew tap: `anthropic/tap/slate` (notable: under Anthropic's tap, not Random Labs)
|
||||
|
||||
## npm Package Structure
|
||||
|
||||
```
|
||||
@randomlabs/slate (8.8 kB, thin launcher)
|
||||
├── bin/slate — Node.js launcher (finds platform binary in node_modules)
|
||||
├── bin/slate1 — Bun launcher (same logic, import.meta.filename)
|
||||
├── postinstall.mjs — Verifies platform binary exists, symlinks if needed
|
||||
└── package.json — Declares optionalDependencies for all platforms
|
||||
|
||||
Platform packages (85MB each):
|
||||
├── @randomlabs/slate-darwin-arm64
|
||||
├── @randomlabs/slate-darwin-x64
|
||||
├── @randomlabs/slate-linux-arm64
|
||||
├── @randomlabs/slate-linux-x64
|
||||
├── @randomlabs/slate-linux-x64-musl
|
||||
├── @randomlabs/slate-linux-arm64-musl
|
||||
├── @randomlabs/slate-linux-x64-baseline
|
||||
├── @randomlabs/slate-linux-x64-baseline-musl
|
||||
├── @randomlabs/slate-darwin-x64-baseline
|
||||
├── @randomlabs/slate-windows-x64
|
||||
└── @randomlabs/slate-windows-x64-baseline
|
||||
```
|
||||
|
||||
Binary override: `SLATE_BIN_PATH` env var skips all discovery, runs the specified binary directly.
|
||||
|
||||
## What Already Works Today
|
||||
|
||||
gstack skills already work in Slate via the `.claude/skills/` fallback path.
|
||||
No changes needed for basic functionality. Users who install gstack for Claude Code
|
||||
and also use Slate will find their skills available in both agents.
|
||||
|
||||
## What First-Class Support Adds
|
||||
|
||||
1. **Reliability** — `.slate/skills/` is Slate's highest-priority path. Immune to
|
||||
`SLATE_DISABLE_CLAUDE_CODE_SKILLS`.
|
||||
2. **Optimized frontmatter** — Strip Claude-specific fields (allowed-tools, hooks, version)
|
||||
that Slate doesn't use. Keep only `name` and `description`.
|
||||
3. **Setup script** — Auto-detect `slate` binary, install skills to `~/.slate/skills/`.
|
||||
4. **E2E tests** — Verify skills work when invoked by Slate directly.
|
||||
|
||||
## Blocked On: Host Config Refactor
|
||||
|
||||
Codex's outside voice review identified that adding Slate as a 4th host (after Claude,
|
||||
Codex, Factory) is "host explosion for a path alias." The current architecture has:
|
||||
|
||||
- Hard-coded host names in `type Host = 'claude' | 'codex' | 'factory'`
|
||||
- Per-host branches in `transformFrontmatter()` with near-duplicate logic
|
||||
- Per-host config in `EXTERNAL_HOST_CONFIG` with similar patterns
|
||||
- Per-host functions in the setup script (`create_codex_runtime_root`, `link_codex_skill_dirs`)
|
||||
- Host names duplicated in `bin/gstack-platform-detect`, `bin/gstack-uninstall`, `bin/dev-setup`
|
||||
|
||||
Adding Slate means copying all of these patterns again. A refactor to make hosts
|
||||
data-driven (config objects instead of if/else branches) would make Slate integration
|
||||
trivial AND make future hosts (any new OpenCode fork, any new agent) zero-effort.
|
||||
|
||||
### Missing from the plan (identified by Codex)
|
||||
|
||||
- `lib/worktree.ts` only copies `.agents/`, not `.slate/` — E2E tests in worktrees won't
|
||||
have Slate skills
|
||||
- `bin/gstack-uninstall` doesn't know about `.slate/`
|
||||
- `bin/dev-setup` doesn't wire `.slate/` for contributor dev mode
|
||||
- `bin/gstack-platform-detect` doesn't detect Slate
|
||||
- E2E tests should set `SLATE_DISABLE_CLAUDE_CODE_SKILLS=1` to prove `.slate/` path
|
||||
actually works (not just falling back to `.claude/`)
|
||||
|
||||
## Session Runner Design (for later)
|
||||
|
||||
When the JSONL format is verified, the session runner should:
|
||||
|
||||
- Spawn: `slate -q "<prompt>" --stream-json --dangerously-skip-permissions -w <dir>`
|
||||
- Parse: Claude Code SDK-compatible NDJSON (assumed, needs verification)
|
||||
- Skills: Install to `.slate/skills/` in test fixture (not `.claude/skills/`)
|
||||
- Auth: Use `SLATE_API_KEY` or existing `~/.slate/` credentials
|
||||
- Isolation: Use `SLATE_TEST_HOME` for home directory isolation
|
||||
- Timeout: 300s default (same as Codex)
|
||||
|
||||
```typescript
|
||||
export interface SlateResult {
|
||||
output: string;
|
||||
toolCalls: string[];
|
||||
tokens: number;
|
||||
exitCode: number;
|
||||
durationMs: number;
|
||||
sessionId: string | null;
|
||||
rawLines: string[];
|
||||
stderr: string;
|
||||
}
|
||||
```
|
||||
|
||||
## Docs References
|
||||
|
||||
- Slate docs: https://docs.randomlabs.ai
|
||||
- Quickstart: https://docs.randomlabs.ai/en/getting-started/quickstart
|
||||
- Skills: https://docs.randomlabs.ai/en/using-slate/skills
|
||||
- Configuration: https://docs.randomlabs.ai/en/using-slate/configuration
|
||||
- Hotkeys: https://docs.randomlabs.ai/en/using-slate/hotkey_reference
|
||||
Reference in New Issue
Block a user