You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Problem: The onset router's personalized greeting depends on vault files (PROFILE.md, KNOWLEDGE.md, DEMOS.md, sessions/) that are only populated by power-user workflows (dream cycles, autoresearch campaigns). A normal user — even one with 170+ runs — gets a generic greeting with no context about what they've been working on.
Approach: At session start, the amicode_context plugin reads recent sessions (last 7 days) from the opencode session DB via the SDK client, summarizes each unsummarized session with a cheap LLM call, caches the recap, and injects a ## Recent sessions block into the system prompt. The model naturally uses this to personalize the onset greeting.
Approaches Considered:
(A) Session-start LLM summarization (chosen) — always fresh, works universally, captures intent and unfinished threads. Latency mitigated by per-session caching.
(B) End-of-session persist — no start-time latency but misses abandoned sessions; requires reliable "session end" signal.
(C) Mechanical extraction — zero LLM cost but can't identify unfinished threads or conversational intent; brittle heuristics.
Scope:opencode-plugin/ only (the Bun-runtime plugin layer). No changes to the onset router text, no changes to stack_state.ts, no changes to vault files.
Assumptions:
The opencode SDK client (from PluginInput) can call client.sessions.list() and client.sessions.history() from within the plugin.
The experimental.chat.system.transform hook supports async (returns Promise<void>) — confirmed from source.
A "small model" is available for summarization via the provider's cheaper tier (Haiku-class or similar).
Acceptance Criteria
A returning user with 1+ prior sessions in the last 7 days sees a ## Recent sessions section in the system prompt of their new session
Each session recap is 1-2 lines: topic, key outcomes (fidelity numbers, errors), and unfinished threads
Recaps are cached to ~/.amico/session-recaps/<session-id>.json — an already-summarized session is never re-summarized
The current session is excluded from the recap list
Sessions with parent_id set (subagent sessions) are excluded
If the LLM summarization fails (provider error, no API key), the hook degrades gracefully — no recap section, no crash
A fresh user with 0 prior sessions sees no ## Recent sessions block (clean empty state)
Total added latency on session start is < 3s for a user with 5 unsummarized sessions (parallel calls)
Key Decisions
Plugin architecture
The existing amicode_context.ts plugin gains session-recap capability. The plugin factory accepts PluginInput (currently ignored) and captures input.client. The hook becomes async.
Model selection: Use the experimental.provider.small_model hook if available; otherwise use the user's default model. (This is a future-proof seam — when a small-model hook is wired, recaps become cheap automatically.)
Input: Text-type message parts from the session, truncated to ~4000 tokens (last messages first — recency matters more than opening).
Prompt: Fixed system prompt: "Summarize this Amicode session in 1-2 lines. Include: what was worked on, key numerical outcomes (fidelity values, iteration counts), and anything left unfinished or explicitly planned for next time. Be specific and concise."
Output: A single string, max ~200 tokens.
Cache format
{
"session_id": "ses_...",
"title": "Transmon X gate cold-start",
"created": "2026-08-23T10:30:00Z",
"recap": "Transmon X gate: launched 5 cold-start runs, best F=0.999954. Discussed min-time sweep to compress below 20ns.",
"summarized_at": "2026-08-23T14:00:00Z"
}
Ordered by recency (newest first). Capped at 10 entries to bound prompt size (~1000 tokens max).
Constraints & Invariants
No vault dependency. This feature reads from the session DB only. It does not read or write vault markdown files. The existing vault-based sections (PROFILE.md, KNOWLEDGE.md, etc.) remain as-is for power users who run dream cycles.
Graceful degradation. If client.sessions.list() fails, or the LLM call fails, or the cache directory is unwritable — the hook returns without injecting a recap section. Never crash the prompt build.
Single-export constraint. The plugin file must have exactly one export (opencode's legacy-plugin scan). session_recap.ts is a sibling module imported by amicode_context.ts, not a separate plugin.
No onset router changes. The model already has instructions to "build the moment from the live state." The ## Recent sessions block IS live state — the router naturally uses it without text changes.
Exclude noise. Skip sessions shorter than 2 assistant messages (likely false starts). Skip sessions whose title starts with "Compaction" or similar internal housekeeping.
Prior Art
opencode-plugin/stack_state.ts — the existing pattern for reading state and composing a markdown block for prompt injection
opencode-plugin/amicode_context.ts — the existing hook entry point this extends
The dream-distill skill — does a similar "summarize session transcripts" operation but writes to vault notes (heavyweight, offline)
Source
Brainstormed in Amicode session with JJ, 2026-08-23. The gap was identified by inspecting what data the onset router actually depends on (vault files populated only by dream cycles) versus what's universally available (the session DB).
Important
Problem: The onset router's personalized greeting depends on vault files (
PROFILE.md,KNOWLEDGE.md,DEMOS.md,sessions/) that are only populated by power-user workflows (dream cycles, autoresearch campaigns). A normal user — even one with 170+ runs — gets a generic greeting with no context about what they've been working on.Approach: At session start, the
amicode_contextplugin reads recent sessions (last 7 days) from the opencode session DB via the SDK client, summarizes each unsummarized session with a cheap LLM call, caches the recap, and injects a## Recent sessionsblock into the system prompt. The model naturally uses this to personalize the onset greeting.Approaches Considered:
Scope:
opencode-plugin/only (the Bun-runtime plugin layer). No changes to the onset router text, no changes tostack_state.ts, no changes to vault files.Assumptions:
client(fromPluginInput) can callclient.sessions.list()andclient.sessions.history()from within the plugin.experimental.chat.system.transformhook supports async (returnsPromise<void>) — confirmed from source.Acceptance Criteria
## Recent sessionssection in the system prompt of their new session~/.amico/session-recaps/<session-id>.json— an already-summarized session is never re-summarizedparent_idset (subagent sessions) are excluded## Recent sessionsblock (clean empty state)Key Decisions
Plugin architecture
The existing
amicode_context.tsplugin gains session-recap capability. The plugin factory acceptsPluginInput(currently ignored) and capturesinput.client. The hook becomes async.LLM summarization contract
experimental.provider.small_modelhook if available; otherwise use the user's default model. (This is a future-proof seam — when a small-model hook is wired, recaps become cheap automatically.)Cache format
{ "session_id": "ses_...", "title": "Transmon X gate cold-start", "created": "2026-08-23T10:30:00Z", "recap": "Transmon X gate: launched 5 cold-start runs, best F=0.999954. Discussed min-time sweep to compress below 20ns.", "summarized_at": "2026-08-23T14:00:00Z" }Injected prompt section
Ordered by recency (newest first). Capped at 10 entries to bound prompt size (~1000 tokens max).
Constraints & Invariants
PROFILE.md,KNOWLEDGE.md, etc.) remain as-is for power users who run dream cycles.client.sessions.list()fails, or the LLM call fails, or the cache directory is unwritable — the hook returns without injecting a recap section. Never crash the prompt build.session_recap.tsis a sibling module imported byamicode_context.ts, not a separate plugin.## Recent sessionsblock IS live state — the router naturally uses it without text changes.Prior Art
opencode-plugin/stack_state.ts— the existing pattern for reading state and composing a markdown block for prompt injectionopencode-plugin/amicode_context.ts— the existing hook entry point this extendsdream-distillskill — does a similar "summarize session transcripts" operation but writes to vault notes (heavyweight, offline)Source
Brainstormed in Amicode session with JJ, 2026-08-23. The gap was identified by inspecting what data the onset router actually depends on (vault files populated only by dream cycles) versus what's universally available (the session DB).