diff options
| author | Adam Malczewski <[email protected]> | 2026-06-24 00:13:43 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-06-24 00:13:43 +0900 |
| commit | b9abc1f8d8815f57e7b958b871ad85db205a9257 (patch) | |
| tree | 2fa7b7629d33ed067a00f6e5f31bfb2b7f9d5c0f | |
| parent | ebcaf59180bda1d04e6353ccd3ea78e45bed7088 (diff) | |
| download | dispatch-b9abc1f8d8815f57e7b958b871ad85db205a9257.tar.gz dispatch-b9abc1f8d8815f57e7b958b871ad85db205a9257.zip | |
docs: system-prompt builder FE courier handoff + tasks.md update
| -rw-r--r-- | frontend-system-prompt-handoff.md | 123 | ||||
| -rw-r--r-- | tasks.md | 30 |
2 files changed, 152 insertions, 1 deletions
diff --git a/frontend-system-prompt-handoff.md b/frontend-system-prompt-handoff.md new file mode 100644 index 0000000..c0739b3 --- /dev/null +++ b/frontend-system-prompt-handoff.md @@ -0,0 +1,123 @@ +# FE Courier Handoff: System Prompt Builder + +> Backend→FE courier. The user couriers this to `../dispatch-web` (FE agent `ffe3`). +> `@dispatch/transport-contract` bumped to `0.18.0` (additive types). + +## Overview + +A template-based system prompt builder. The user defines a template with variable +placeholders (`[type:name]`) and conditionals (`[if]`/`[else]`/`[endif]`). Variables are +resolved at construction time (once per conversation, persisted for cache safety — +reconstructed only on compaction). + +## API endpoints + +### `GET /system-prompt` → `SystemPromptTemplateResponse` +```ts +{ template: string } +``` +Returns the current global template. When no template is stored (or the service is +unavailable), returns the built-in `DEFAULT_TEMPLATE`. + +### `PUT /system-prompt` ← `SetSystemPromptTemplateRequest` +```ts +// Request body: +{ template: string } +// Response: +{ template: string } // echoed back +``` +- `template` can be empty (means "no system prompt"). +- 400 if `template` is missing or not a string. +- 503 if the system-prompt service is unavailable. + +### `GET /system-prompt/variables` → `SystemPromptVariablesResponse` +```ts +{ + variables: readonly SystemPromptVariable[] +} +// SystemPromptVariable: +{ + type: string; // "system", "file", "prompt", "git" + name: string; // "time", "date", "os", "cwd", etc. + description: string; // human-readable + dynamic?: boolean; // true for file: (any path is valid) +} +``` +Static catalog — always available (no service dependency). Use this to render the +variable selector buttons in the builder UI. + +## Template format + +### Variable insertion +``` +[type:name] +``` +Resolves the variable at construction time. Unknown type → blank string. Non-existent +variable (e.g. file not found) → blank string. + +### Conditional blocks +``` +[if type:name] + ...content if variable exists... +[else] + ...content if variable does NOT exist... +[endif] +``` +Negated condition: +``` +[if !type:name] + ...content if variable does NOT exist... +[endif] +``` +- Nested `[if]` blocks: supported. +- Multi-line content: supported. +- Unmatched `[if]`/`[endif]`: treated as literal text. + +## Available variables + +| Type:Name | Description | Dynamic? | +|---|---|---| +| `system:time` | Current time (ISO 8601) | No | +| `system:date` | Current date (YYYY-MM-DD) | No | +| `system:os` | Operating system | No | +| `system:hostname` | Machine hostname | No | +| `prompt:cwd` | Working directory | No | +| `prompt:model` | Current model name | No | +| `prompt:conversation_id` | Conversation ID | No | +| `git:branch` | Current git branch | No | +| `git:status` | Short git status | No | +| `file:<path>` | File contents (relative to cwd, or absolute if starts `/`) | **Yes** | + +For `file:<path>`, the FE should allow free-text input for the path. Any filename is +valid — the backend resolves it relative to the conversation's cwd (or absolute if it +starts with `/`). + +## Caching behavior (important for FE) + +The system prompt is **constructed once** (on the first turn of a new conversation) and +**persisted**. It is reused on all subsequent turns (no reconstruction — this preserves +the prompt cache). It is only **reconstructed on compaction** (fresh variable resolution). + +Changing the template (via `PUT /system-prompt`) does NOT affect existing conversations +until they are compacted. New conversations use the new template on their first turn. + +## Default template + +When no template is stored, the backend uses: +``` +You are a helpful coding assistant. + +[if file:AGENTS.md] +[file:AGENTS.md] +[endif] + +The current working directory is [prompt:cwd]. +``` + +## FE UI suggestion + +The builder is a full-page modal split into two: +1. **Text editor** (left/main): the template text. +2. **Variable selectors** (right/side): buttons grouped by type. Clicking a variable + inserts `[type:name]` at the cursor position. For `file:` (dynamic), show a text + input for the path + an "insert" button. @@ -5,7 +5,7 @@ > Keep this lean and current; do not let it re-accrete a step-by-step changelog. ## Status (current) -`tsc -b` EXIT 0 · biome clean · **1332 vitest** green. +`tsc -b` EXIT 0 · biome clean · **1396 vitest** green. ## LSP cwd resolution — server-default fallthrough + workspace assignment (DONE) Bug: `GET /conversations/:id/lsp` called `getEffectiveCwd` directly, which falls through @@ -757,3 +757,31 @@ sealed → the FE spinner spun forever AND the conversation was bricked (next `c - [ ] **Live-verify** (needs a fresh `bin/up` — the dev stack is currently wedged, the very symptom of this bug): start a hanging tool (`run_shell` sleep/grandchild), Stop, then send a NEW message → it must be ACCEPTED (conversation not bricked) and the spinner clears. + +## System prompt builder — template-based system context (DONE) +Design: `notes/system-prompt-design.md`. FE courier: `frontend-system-prompt-handoff.md`. +Problem: no system prompt was sent to the provider for regular turns (the messages array +started with the user message; `providerOpts.systemPrompt` was never set). This adds a +template-based system prompt builder with variable placeholders (`[type:name]`) and +conditionals (`[if]`/`[else]`/`[endif]`). +- **Cache constraint (critical):** the system prompt is constructed ONCE (first turn of + a new conversation) and persisted. Reused on all subsequent turns (no reconstruction — + cache-safe). Reconstructed only on **compaction** (fresh variable resolution + compaction + instructions appended). +- **Variable types:** `system:time/date/os/hostname`, `prompt:cwd/model/conversation_id`, + `git:branch/status`, `file:<path>` (dynamic — any path). +- **Wave 0 (orchestrator, contracts):** `@dispatch/transport-contract` `0.17.0→0.18.0` — + `SystemPromptTemplateResponse`, `SetSystemPromptTemplateRequest`, `SystemPromptVariable`, + `SystemPromptVariablesResponse`. +- **Wave 1 — `system-prompt` (NEW ext):** pure parser (29 tests) + variable resolver + (injected adapters, 12 tests) + catalog (3 tests) + service handle (`construct` + + `get` + `getTemplate` + `setTemplate`, 8 tests). 52 tests total. Default template: + persona + AGENTS.md if exists + cwd. +- **Wave 2 (parallel):** `session-orchestrator` (wire service: construct on first turn, + get on subsequent, construct+append on compaction; 12 tests) + `transport-http` + (GET/PUT `/system-prompt`, GET `/system-prompt/variables`; 6 tests). +- **Wave 3 — host-bin:** registered `system-prompt` in `CORE_EXTENSIONS`. +- [x] Verified: `tsc -b` EXIT 0, biome clean, **1396 vitest** pass. +- [ ] Live-verify (boot smoke: extension activates, `GET /system-prompt` returns default + template, `GET /system-prompt/variables` returns catalog). +- [x] **FE courier** sent to FE agent `ffe3`: `frontend-system-prompt-handoff.md`. |
