# Edit History — 2026-04-02

## Summary

Added **Settings tab** (per-workspace LLM/API key configuration), **Terminal tab** (shell access into containers), **Restart button** for offline workspaces, **editable Agent Card**, and **coding-agent workspace template** (OpenClaw-style). Six rounds of code review fixes for security, cleanup, and robustness.

## Settings Tab (LLM & API Keys)

**New files:**
- `canvas/src/components/tabs/SettingsTab.tsx` — Quick-set rows for ANTHROPIC_API_KEY, OPENAI_API_KEY, GOOGLE_API_KEY, SERP_API_KEY, MODEL_PROVIDER. Custom env var editor. Values stored via `/workspaces/:id/secrets`, never exposed to browser.
- `platform/internal/handlers/secrets.go` — GET/POST /workspaces/:id/secrets (keys only), DELETE /workspaces/:id/secrets/:key, GET /workspaces/:id/model. UUID validation on workspace ID, BYTEA scan for future encryption compat.

## Terminal Tab (Container Shell Access)

**New files:**
- `canvas/src/components/tabs/TerminalTab.tsx` — xterm.js terminal with dark theme, WebSocket to `/workspaces/:id/terminal`, status bar, reconnect button, proper cleanup on unmount.
- `platform/internal/handlers/terminal.go` — WebSocket upgrade, Docker exec /bin/sh, bridges stdin/stdout. Restricted origins (localhost only), shared Docker client from provisioner, 30min idle timeout.

## Restart Button for Offline/Failed Workspaces

- `platform/internal/handlers/workspace.go` — Added `POST /workspaces/:id/restart`. Works for offline/failed/degraded. Stops existing container, resets to provisioning, auto-finds template by normalizing workspace name.
- `canvas/src/components/tabs/DetailsTab.tsx` — Green Restart/Retry button visible when workspace is offline, failed, or degraded.

## Editable Agent Card

- `canvas/src/components/tabs/DetailsTab.tsx` — AgentCardEditor component. Click "Edit Agent Card" → JSON textarea → Save pushes via `POST /registry/update-card`. Validates JSON, shows success toast.

## Coding Agent Template (OpenClaw-style)

**New template: `workspace-configs-templates/coding-agent/`**
- `config.yaml` — Tier 2, 4 skills, filesystem + web_search tools
- `system-prompt.md` — Full-stack engineer identity
- `skills/code-generation/` — SKILL.md + `file_ops.py` (read_file, write_file, list_files, search_code with path traversal protection)
- `skills/shell-exec/` — SKILL.md + `shell.py` (run_shell with timeout, output truncation, destructive command blocklist, process kill on timeout)
- `skills/code-review/` — SKILL.md (review checklist)
- `skills/debug-assist/` — SKILL.md (debug process)

**Infrastructure:**
- `workspace-template/Dockerfile` — Added `/workspace` volume
- `platform/internal/provisioner/provisioner.go` — Mount `ws-{id}-workspace` named volume for Tier 2+ (Tier 1 stays read-only)

## A2A Error Handling Fixes

- `workspace-template/a2a_executor.py` — Catch exceptions from `agent.astream()`, return as agent message. Handle Anthropic content blocks (list of dicts).
- `canvas/src/components/tabs/ChatTab.tsx` — Handle JSON-RPC error responses separately from results. Show "Agent error: ..." instead of "(empty response)".
- `platform/internal/handlers/workspace.go` — Inject `messageId` into A2A proxy requests (required by a2a-sdk v0.3+).

## Code Review Fixes (Rounds 4-6)

- **Round 4**: Remove debug logs, guard short ID slicing, fix findConfigDir name matching, handle WriteFile errors, handle DB error in agent Move, handle json.Unmarshal error, use optional chaining in isDescendant.
- **Round 5**: Restrict WebSocket origins to localhost, use request context, share Docker client, fix reconnect via connectKey, fix cleanup with refs, validate UUID, per-row saving state, add session timeout, scan BYTEA as []byte.
- **Round 6**: Fix path traversal in file_ops (_resolve validates path stays within WORKSPACE_DIR), add command blocklist in shell.py, kill subprocess on timeout, return error when no template found in Restart.

## SidePanel Updates

- Expanded from 5 tabs to 7 tabs (added Settings and Terminal)
- Widened from 420px to 480px
- Tab bar now scrollable with `overflow-x-auto`
- PanelTab type expanded: `"details" | "chat" | "settings" | "terminal" | "config" | "memory" | "events"`

## Arbitrary Prompt Files (prompt_files support)

**Problem:** Different agent frameworks use different file structures — OpenClaw has SOUL.md/AGENTS.md/HEARTBEAT.md/etc, Claude Code uses CLAUDE.md, Codex uses AGENTS.md. Our runtime only supported `system-prompt.md`.

**Solution:** Added `prompt_files` field to `config.yaml` that lists which markdown files to load (in order) as the system prompt:

```yaml
# OpenClaw-style
prompt_files: [SOUL.md, BOOTSTRAP.md, AGENTS.md, HEARTBEAT.md, TOOLS.md, USER.md]

# Claude Code-style
prompt_files: [CLAUDE.md]

# Default (backwards compatible — if omitted, loads system-prompt.md)
```

**Files changed:**
- `workspace-template/config.py` — Added `prompt_files` field to WorkspaceConfig
- `workspace-template/prompt.py` — `build_system_prompt()` loads prompt_files in order, falls back to `system-prompt.md`
- `workspace-template/main.py` — Pass `config.prompt_files` to `build_system_prompt()`

**Coding agent updated to use OpenClaw-style files:**
- Renamed `system-prompt.md` → `SOUL.md` (core identity)
- New `BOOTSTRAP.md` (project orientation on first task)
- New `TOOLS.md` (tool usage guidelines and safety rules)
- New `AGENTS.md` (multi-agent delegation protocol)
- `config.yaml` updated: `prompt_files: [SOUL.md, BOOTSTRAP.md, TOOLS.md, AGENTS.md]`

**Tested with 5 agent frameworks:**
- OpenClaw (7 files) — PASS
- Claude Code (CLAUDE.md) — PASS
- OpenAI Codex (AGENTS.md) — PASS
- SEO Agent (backwards compat, no prompt_files) — PASS
- Coding Agent (OpenClaw-style, 4 files) — PASS

Full Docker skill loading and prompt assembly tests also pass (4/4 templates, 9/9 content checks).
