docs: add condensed design documentation for runtime loop, commands, and launcher

This commit is contained in:
mingzhi1 2026-03-03 12:46:40 +08:00
parent 6b15769010
commit cab55ed2fd
4 changed files with 221 additions and 0 deletions

View file

@ -0,0 +1,50 @@
# PicoClaw Launcher — Wails v2 Migration
## Overview
Desktop GUI for PicoClaw using Wails v2, replacing the previous HTTP server + browser approach.
## Architecture
```
cmd/picoclaw-launcher/
├── main.go # Wails bootstrap
├── app.go # App struct with Go↔JS bindings
├── helpers.go # Gateway process management
├── tray.go # System tray integration
├── wails.json # Wails project config
├── frontend/
│ ├── index.html # Single-file UI (3 tabs)
│ └── wailsjs/ # Auto-generated bindings
└── internal/server/
├── setup.go # Config setup helpers
└── setup_chat.go # AI chat server
```
## UI: 3-Tab Layout
| Tab | Features |
|-----|----------|
| **Status** | Gateway start/stop/restart, real-time status |
| **Chat** | AI chat interface, first-run setup questionnaire |
| **Settings** | Model/channel/API key config, save/reload |
## Key Design Choices
- **Wails bindings** instead of HTTP: `window.go.main.App.GetConfig()` replaces `fetch('/api/config')`
- **System tray**: minimize-to-tray on close, right-click menu for gateway control
- **First-run setup**: guided overlay when no config exists → AI chat questionnaire
- **Single index.html**: intentional for v1 (no build toolchain), will split if complexity grows
## Build
```bash
go tool wails build -o picoclaw-launcher
# or via Taskfile:
task build-launcher
```
## Dependencies
- Wails v2 (declared as `tool` in go.mod, not linked into picoclaw binary)
- WebView2 (Windows, auto-installed), GTK/WebKit (Linux), WebKit (macOS)

View file

@ -0,0 +1,39 @@
# Runtime Commands
## Slash Commands
All commands start with `/`, handled synchronously by `Reflector.HandleCommand()`.
| Command | Usage | Description |
|---------|-------|-------------|
| `/help` | `/help` | List all commands |
| `/memory list` | `/memory list` | Show recent memories |
| `/memory add` | `/memory add <text> #tags` | Add a memory |
| `/memory delete` | `/memory delete <id>` | Delete by ID |
| `/memory search` | `/memory search <tags>` | Search by tags |
| `/memory stats` | `/memory stats` | Memory statistics |
| `/cot feedback` | `/cot feedback <1\|0\|-1>` | Rate last CoT strategy |
| `/cot stats` | `/cot stats` | CoT performance stats |
| `/cot history` | `/cot history [N]` | Recent CoT usage |
| `/shell` | `/shell <cmd> [args]` | Execute shell command |
| `/show model` | `/show model` | Current model |
| `/list agents` | `/list agents` | List agents |
| `/switch model to` | `/switch model to <name>` | Switch model |
| `/runtime status` | `/runtime status` | Runtime diagnostics |
## /shell Architecture
```
/shell <cmd> <args>
├─ Built-in (pure Go, cross-platform)
│ ls, cat, head, tail, grep, wc, find, diff, tree,
│ stat, pwd, echo, touch, mkdir, cp, mv
└─ Dev Tool Passthrough (via exec tool)
go, git, node, python, npm, cargo, make, jq, rg
```
## Security
- Built-in: Go stdlib only, auto-skip `.git`/`node_modules`, output capped at 4000 chars
- Passthrough: whitelist + deny patterns (`| sh`, `$()`, etc.) + ExecTool workspace restriction
- Unknown commands: rejected

View file

@ -0,0 +1,77 @@
# Runtime Loop Design
> Status: Implemented | Date: 2026-03-02
## Architecture
```
Message ──→ Phase 1 (Analyse) ──→ Phase 2 (Execute) ──→ Phase 3 (Reflect)
```
| Phase | File | Responsibility |
|-------|------|----------------|
| **Analyse** | `analyser.go` | Lightweight LLM → intent, tags, CoT prompt |
| **Execute** | `executor.go` | LLM iteration loop + tool calling |
| **Reflect** | `reflector.go` | Turn scoring, TurnRecord persistence, slash commands |
## Turn Definition
One Turn = user message + Phase 1 result + all Phase 2 iterations + Phase 3 output.
Multiple tool-call iterations within Phase 2 count as **one Turn**.
## Phase 1: Analyse
- Uses configurable `analyser_model` (fast/cheap model, falls back to main model)
- Inputs: user message, Active Context, available tags, CoT learning data
- Outputs: `intent`, `tags[]`, `cot_prompt`
- After analysis: memory retrieval by tags, CoT injection into system prompt
## Phase 2: Execute
- LLM → tool call → tool result loop until no more tool calls
- Retry logic for context window overflow with automatic compression
- Reasoning output forwarded to dedicated channels
## Phase 3: Reflect
- **SyncPhase3** (< 2ms, before response sent): turn scoring + Active Context update
- **AsyncPhase3** (after response): `go turnStore.Insert(record)` → SQLite
- Slash commands: `/memory`, `/cot`, `/shell`, `/show`, `/list`, `/switch`, `/help`
## Scoring Rules
| Condition | Score |
|-----------|-------|
| Has tool calls | +3 |
| Write/edit tools | +2 |
| intent = task/code/debug | +3 |
| Reply > 500 chars | +2 |
| Short exchange < 80 chars | -2 |
## Memory Hierarchy
| Layer | Lifetime | Purpose |
|-------|----------|---------|
| Instant Memory | Per-turn | Dynamic window from TurnStore (score + tag filtering) |
| Active Context | Per-session | `CurrentFiles` + `RecentErrors`, injected into user prompt |
| Long-term Memory | Persistent | MemoryDigest batch extraction → `memory.db` |
## Multi-Model Support
| Config Field | Phase | Fallback |
|-------------|-------|----------|
| `model_name` | Phase 2 | — |
| `analyser_model` | Phase 1 | → `model_name` |
| `digest_model` | MemoryDigest | → `model_name` |
## Message Ordering (KV Cache Friendly)
```
[system_prompt] → always cached
[long_term_memory by tags] → cached when same tags
[always_keep turns (score≥7)] → fixed position, append-only
[recent turns] → rolling window
[current user message] → new each turn
```
Active Context injected as **user message** (not system prompt) to keep system prompt prefix stable.

View file

@ -0,0 +1,55 @@
# Runtime Loop Implementation Tasks
> Design ref: `docs/design/picoclaw_runtime_loop_design.md`
## Design Decisions
| Decision | Conclusion |
|----------|------------|
| Turn storage | SQLite `turns.db` |
| Active Context fields | `CurrentFiles` + `RecentErrors` only |
| Phase 1 short-circuit | No — short messages need Active Context most |
| Async write | Direct `go insert()`, no channel buffer |
| Token budget check | Periodic time-based archival instead |
| Tag-gated tools | Deferred until tool count > 15 |
| KV Cache | Fixed ordering (high-score first, by ID ASC) |
## Milestones
### M1: Turn Score + Phase 3 Timing — ✅
- [x] `score.go`: `CalcTurnScore(input) int`
- [x] Split `RunPostLLM``SyncPhase3` (sync, < 2ms) + `AsyncPhase3` (goroutine)
- [x] Adjust `runAgentLoop` timing: score → publish → async write
### M2: Active Context — ✅
- [x] `active_context.go`: per `channel:chatID` store
- [x] Fields: `CurrentFiles` (5), `RecentErrors` (3)
- [x] Injected as user message (not system prompt) for KV cache stability
- [x] Flush to JSON on shutdown, load on startup
### M3: TurnStore — ✅
- [x] `turn_store.go`: SQLite `turns.db` with WAL mode
- [x] Methods: Insert, QueryPending, QueryByScore, QueryByTags, QueryRecent, ArchiveOldProcessed
- [x] Async insert via goroutine in AsyncPhase3
### M4: MemoryDigest — ✅
- [x] `memory_digest.go`: background worker (5min interval)
- [x] QueryPending → group by channel → LLM batch extraction → MemoryStore
- [x] Removed `MemoryExtractor` and `CotEvaluator` processors (kept `ErrorTracker`)
### M5: Instant Memory + KV Cache Ordering — ✅
- [x] `instant_memory.go`: dynamic window from TurnStore
- [x] Cache-friendly message ordering: system → memory → high-score → recent → current
- [x] Legacy SessionManager kept as fallback
## Deferred
| Item | Reason |
|------|--------|
| Tag-gated tool loading | Tool count < 10 currently |
| Summary Anchor | Fixed ordering sufficient for v1 |