Commit graph

34 commits

Author SHA1 Message Date
ProofOfConcept
f086815eaa memory: add temperature support to agent defs, update reflect prompt
Thread temperature parameter from agent def header through the API
call chain. Agents can now specify {"temperature": 1.2} in their
JSON header to override the default 0.6.

Also includes Kent's reflect agent prompt iterations.
2026-03-24 20:29:17 -04:00
Kent Overstreet
aa46b1d5a6 poc-agent: read context_groups from config instead of hardcoded list
- Remove MEMORY_FILES constant from identity.rs
- Add ContextGroup struct for deserializing from config
- Load context_groups from ~/.config/poc-agent/config.json5
- Check ~/.config/poc-agent/ first for identity files, then project/global
- Debug screen now shows what's actually configured

This eliminates the hardcoded duplication and makes the debug output
match what's in the config file.
2026-03-24 01:53:28 -04:00
Kent Overstreet
e74d533748 fix: improve blocking read end-of-response detection
The original '>' detection was too broad and caught tool output lines.
Now we look for '> X: ' pattern (user prompt with speaker prefix) to
detect the start of a new user input, which marks the end of the
previous response.
2026-03-21 23:40:48 -04:00
Kent Overstreet
a3acf0a681 feat: add --block flag to poc-agent read
The --block flag makes poc-agent read block until a complete response
is received (detected by a new user input line starting with '>'),
then exit. This enables smoother three-way conversations where one
instance can wait for the other's complete response without polling.

The implementation:
- Added cmd_read_inner() with block parameter
- Modified socket streaming to detect '>' lines as response boundaries
- Added --block CLI flag to Read subcommand

The --follow flag continues to stream indefinitely.
The --block flag reads one complete response and exits.
Neither flag exits immediately if there's no new output.
2026-03-21 23:39:12 -04:00
Kent Overstreet
acc878b9a4 ui: two-column layout for conversation pane with marker gutter
Split conversation pane into 2-char gutter + text column. Gutter shows
● markers at turn boundaries (Cyan for user, Magenta for assistant),
aligned with the input area's ' > ' gutter.

Key changes:
- Added Marker enum (None/User/Assistant) and parallel markers vec
- Track turn boundaries via pending_marker field
- New draw_conversation_pane() with visual row computation for wrapping
- Both gutter and text scroll synchronously by visual line offset

This fixes the wrapping alignment issue where continuation lines
aligned under markers instead of under the text.
2026-03-21 19:15:13 -04:00
Kent Overstreet
78b22d6cae fix: buffer streaming tokens in observe log for readable transcripts
The observe log was writing each TextDelta SSE token as a separate
line, making poc-agent read show word-by-word fragments and causing
the read cursor to advance past partial responses.

Now TextDelta and Reasoning tokens are buffered and flushed as
complete messages on turn boundaries (tool calls, user input, etc).
The socket path (read -f) still streams live.

Also fixed a potential deadlock: replaced blocking_lock() with
.lock().await on the shared logfile mutex.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Qwen 3.5 27B <noreply@qwen.ai>
2026-03-21 16:51:12 -04:00
Kent Overstreet
5ae33a48ab refactor: typed args for grep, bash, and vision tools
Convert remaining tools from manual args["key"].as_str() parsing to
serde Deserialize structs. Also removes the now-unused get_str()
helper from grep.rs and simplifies capture_tmux_pane() signature
(takes lines directly instead of re-parsing args).

All 7 tool modules now use the same typed args pattern.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 16:31:34 -04:00
Kent Overstreet
74f05924ff refactor: use typed Deserialize structs for tool arguments
Convert read_file, write_file, edit_file, and glob from manual
args["key"].as_str() parsing to serde_json::from_value with typed
Args structs. Gives type safety, default values via serde attributes,
and clearer error messages on missing/wrong-type arguments.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 16:28:10 -04:00
Kent Overstreet
29db4ff409 refactor: extract identity/context assembly into identity.rs
Move file discovery (CLAUDE.md/POC.md, memory files, people/ glob),
prompt assembly, and context_file_info from config.rs into identity.rs.

All extracted functions are pure — they take paths and return strings,
with no dependency on AppConfig. config.rs calls into identity.rs
(one-way dependency).

config.rs: 663 → 440 lines (-223)
identity.rs: 232 lines (new)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 16:19:27 -04:00
Kent Overstreet
db48d57917 refactor: extract context window building into context.rs
Move context window construction (build_context_window, plan_context,
render_journal_text, assemble_context), token counting, error
classification, and related helpers from agent.rs into context.rs.

All extracted functions are pure — they take inputs and return values
with no mutable state access. State mutation stays in agent.rs
(compact, restore_from_log, load_startup_journal).

agent.rs: 1504 → 987 lines (-517)
context.rs: 365 lines (new)
Net: -152 lines (duplicate comments removed)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 15:59:15 -04:00
Kent Overstreet
d04d41e993 refactor: extract context building into context.rs
Move context window building functions from agent.rs to context.rs:
- build_context_window, plan_context, render_journal_text, assemble_context
- truncate_at_section, find_journal_cutoff, msg_token_count_fn
- model_context_window, context_budget_tokens
- is_context_overflow, is_stream_error, msg_token_count

Also moved ContextPlan struct to types.rs.

Net: -307 lines in agent.rs, +232 in context.rs, +62 in types.rs
2026-03-21 15:42:44 -04:00
Kent Overstreet
e79f17c2c8 refactor: move ContextState and ContextBudget to types.rs
These are data structures, not agent logic. Moving them to types.rs
makes them available to other modules (context.rs, etc.) without
creating circular dependencies.
2026-03-21 15:40:36 -04:00
Kent Overstreet
b22d836287 refactor: extract tool call parsing into parsing.rs
Move parse_leaked_tool_calls, strip_leaked_artifacts, and their
helpers (normalize_xml_tags, parse_qwen_tag, parse_xml_tool_call,
parse_json_tool_call) from agent.rs into their own module.

These functions have zero dependency on Agent or ContextState —
they're pure text parsing. All 4 existing tests move with them.

Reduces agent.rs by ~200 lines.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Qwen 3.5 27B <noreply@qwen.ai>
2026-03-21 15:29:45 -04:00
Kent Overstreet
45b7bba22a refactor: clean up tool dispatch and extract helpers
- Move working_stack tool to tools/working_stack.rs (was orphaned in agent.rs)
- Create control.rs for pause/switch_model/yield_to_user with Result<ToolOutput>
- Add ToolOutput::error() and ToolOutput::text() helper constructors
- Clean up dispatch() with Option<Result<ToolOutput>> pattern for rich tools
- Refactor memory.rs: extract cmd(), write_node(), supersede(), get_str(), get_f64()
- Merge run_rg() and run_grep() into unified run_search() in grep.rs
- Extract truncate_output() helper shared by bash, grep, glob tools

Net: -77 lines, better structure, less duplication
2026-03-21 15:18:53 -04:00
Kent Overstreet
f1bee024e8 api: use debug formatting for reqwest errors to show full cause chain 2026-03-21 12:19:40 -04:00
Kent Overstreet
b28b7def19 api: proper error messages for connection failures and HTTP errors
- Connection errors now show cause (refused/timeout/request error),
  URL, and the underlying error without redundant URL repetition
- HTTP errors show status code, URL, and up to 1000 chars of body
- Unparseable SSE events logged with content preview instead of
  silently dropped — may contain error info from vllm/server
- Stream errors already had good context (kept as-is)

You can't debug what you can't see.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 12:15:08 -04:00
Kent Overstreet
34937932ab timestamp sanitization, CoT logging, reasoning field fix, persistent queue
- store/types.rs: sanitize timestamps on capnp load — old records had
  raw offsets instead of unix epoch, breaking sort-by-timestamp queries
- agents/api.rs: drain reasoning tokens from UI channel into LLM logs
  so we can see Qwen's chain-of-thought in agent output
- agents/daemon.rs: persistent task queue (pending-tasks.jsonl) —
  tasks survive daemon restarts. Push before spawn, remove on completion,
  recover on startup.
- api/openai.rs: only send reasoning field when explicitly configured,
  not on every request (fixes vllm warning)
- api/mod.rs: add 600s total request timeout as backstop for hung
  connections
- Cargo.toml: enable tokio-console feature for task introspection

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 11:33:36 -04:00
Kent Overstreet
9d476841b8 cleanup: fix all build warnings, delete dead DMN context code
- Delete poc-daemon/src/context.rs dead code (git_context, work_state,
  irc_digest, recent_commits, uncommitted_files) — replaced by
  where-am-i.md and memory graph
- Remove unused imports (BufWriter, Context, similarity)
- Prefix unused variables (_store, _avg_cc, _episodic_ratio, _message)
- #[allow(dead_code)] on public API surface that's not yet wired
  (Message::assistant, ConversationLog::message_count/read_all,
  Config::context_message, ContextInfo fields)
- Fix to_capnp macro dead_code warning
- Rename _rewrite_store_DISABLED to snake_case

Only remaining warnings are in generated capnp code (can't fix).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 14:20:34 -04:00
Kent Overstreet
9517b1b310 refactor: move working_stack tool to tools/working_stack.rs
The working_stack tool was defined in tools/mod.rs but implemented
in agent.rs as Agent::handle_working_stack(). This orphaned the tool
from the rest of the tool infrastructure.

Move the implementation to tools/working_stack.rs so it follows the
same pattern as other tools. The tool still needs special handling
in agent.rs because it requires mutable access to context state,
but the implementation is now in the right place.

Changes:
- Created tools/working_stack.rs with handle() and format_stack()
- Updated tools/mod.rs to use working_stack::definition()
- Removed handle_working_stack() and format_stack() from Agent
- Agent now calls tools::working_stack::handle() directly
2026-03-20 13:15:01 -04:00
Kent Overstreet
0922562a4d tools: fix weight-set CLI path (top-level, not admin subcommand)
memory_weight_set and memory_supersede called
"poc-memory admin weight-set" but weight-set is a top-level command.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 13:14:35 -04:00
Kent Overstreet
35f2707c50 api: include underlying error in API send failure message
"Failed to send request to API" swallowed the reqwest error via
.context(), making connection issues impossible to diagnose. Now
includes the actual error (timeout, connection refused, DNS, etc).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 13:12:59 -04:00
Kent Overstreet
5ef9098deb memory: fix timestamp and provenance on agent writes
Two bugs: upsert_provenance didn't update node.timestamp, so history
showed the original creation date for every version. And native memory
tools (poc-agent dispatch) didn't set POC_PROVENANCE, so all agent
writes showed provenance "manual" instead of "agent:organize" etc.

Fix: set node.timestamp = now_epoch() in upsert_provenance. Thread
provenance through memory::dispatch as Option<&str>, set it via
.env("POC_PROVENANCE") on each subprocess Command. api.rs passes
"agent:{name}" for daemon agent calls.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 12:16:45 -04:00
Kent Overstreet
f45f663dc0 tui: fix scroll by using Paragraph::line_count()
Replace homegrown wrapping math (wrapped_height, wrapped_height_line,
auto_scroll, force_scroll, wrapped_line_count) with ratatui's own
Paragraph::line_count() which exactly matches its rendering. The old
approach used ceiling division that didn't account for word wrapping,
causing bottom content to be clipped.

Also add terminal.clear() on resize to force full redraw — fixes the
TUI rendering at old canvas size after terminal resize.

Requires the unstable-rendered-line-info feature flag on ratatui.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 12:16:35 -04:00
Kent Overstreet
6d22f70192 Native memory tools + MCP server + distill agent improvements
Tools:
- Add native memory_render, memory_write, memory_search,
  memory_links, memory_link_set, memory_link_add, memory_used
  tools to poc-agent (tools/memory.rs)
- Add MCP server (~/bin/memory-mcp.py) exposing same tools
  for Claude Code sessions
- Wire memory tools into poc-agent dispatch and definitions
- poc-memory daemon agents now use memory_* tools instead of
  bash poc-memory commands — no shell quoting issues

Distill agent:
- Rewrite distill.agent prompt: "agent of PoC's subconscious"
  framing, focus on synthesis and creativity over bookkeeping
- Add {{neighborhood}} placeholder: full seed node content +
  all neighbors with content + cross-links between neighbors
- Remove content truncation in prompt builder — agents need
  full content for quality work
- Remove bag-of-words similarity suggestions — agents have
  tools, let them explore the graph themselves
- Add api_reasoning config option (default: "high")
- link-set now deduplicates — collapses duplicate links
- Full tool call args in debug logs (was truncated to 80 chars)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 22:58:54 -04:00
Kent Overstreet
f2c2c02a22 tui: fix cursor position with proper word-wrap simulation
The previous approach scanned ratatui's rendered buffer to find the
cursor position, but couldn't distinguish padding spaces from text
spaces, causing incorrect cursor placement on wrapped lines.

Replace with a word_wrap_breaks() function that computes soft line
break positions by simulating ratatui's Wrap { trim: false } algorithm
(break at word boundaries, fall back to character wrap for long words).
cursor_visual_pos() then maps a character index to (col, row) using
those break positions.

Also fixes the input area height calculation to use word-wrap semantics
instead of character-wrap, matching the actual Paragraph rendering.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 01:09:55 -04:00
ProofOfConcept
2e3943b89f tui: use explicit found flag for cursor scan
Clean up the break logic by using an explicit flag instead of
checking cursor_x/cursor_y values.
2026-03-19 00:48:52 -04:00
ProofOfConcept
0f3edebcb3 tui: handle empty cells in cursor scan
When scanning the buffer for cursor position, also check empty cells.
The cursor might be positioned at an empty cell (e.g., end of line
or after all visible characters).
2026-03-19 00:47:46 -04:00
ProofOfConcept
1fa298cbdd tui: fix cursor position to use character count, not byte count
self.cursor is a byte index into the string. When scanning the buffer,
we need to compare character positions, not byte positions or widths.

Convert self.cursor to a character count before comparing with the
buffer scan. Count each non-empty cell as 1 character (the buffer
already represents visual cells, so width doesn't matter here).
2026-03-19 00:46:17 -04:00
ProofOfConcept
6a7ec9732b tui: fix cursor position calculation
The cursor index is into self.input, but the rendered buffer contains
the prompt prepended to the first line. Need to add prompt.len() to
get the correct character position when scanning the buffer.
2026-03-19 00:45:07 -04:00
ProofOfConcept
ec79d60fbd tui: fix cursor desync by scanning rendered buffer
Instead of simulating ratatui's word wrapping algorithm, scan the
rendered buffer to find the actual cursor position. This correctly
handles word wrapping, unicode widths, and any other rendering
nuances that ratatui applies.

The old code computed wrapped_height() and cursor position based on
simple character counting, which diverged from ratatui's WordWrapper
that respects word boundaries.

Now we render first, then walk the buffer counting visible characters
until we reach self.cursor. This is O(area) but the input area is
small (typically < 200 cells), so it's negligible.
2026-03-19 00:40:05 -04:00
Kent Overstreet
5308c8e3a4 tui: fix cursor desync on line wrap
Use unicode display width (matching ratatui's Wrap behavior) instead
of chars().count() for both wrapped_height calculation and cursor
positioning. The mismatch caused the cursor to drift when input
wrapped to multiple lines.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 00:30:45 -04:00
Kent Overstreet
f83325b44d Fix poc-agent for vllm/Qwen 3.5: reasoning display, tool parser
- Always display reasoning tokens regardless of reasoning_effort
  setting — Qwen 3.5 thinks natively and the reasoning parser
  separates it into its own field
- Remove chat_template_kwargs that disabled thinking when
  reasoning_effort was "none"
- Add chat_template_kwargs field to ChatRequest for vllm compat
- Update provision script: qwen3_xml tool parser, qwen3 reasoning
  parser, 262K context, 95% GPU memory utilization

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 00:06:26 -04:00
Kent Overstreet
55326a1c47 Add lib target to poc-agent, make poc-memory depend on it
Split poc-agent into lib + bin so its API client, types, and tool
dispatch can be imported by poc-memory. This is the foundation for
replacing claude CLI subprocess calls with direct API calls to
vllm/OpenAI-compatible endpoints.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 22:56:48 -04:00
Kent Overstreet
57fcfb472a Move poc-agent into workspace, improve agent prompts
Move poc-agent (substrate-independent AI agent framework) into the
memory workspace as a step toward using its API client for direct
LLM calls instead of shelling out to claude CLI.

Agent prompt improvements:
- distill: rewrite from hub-focused to knowledge-flow-focused.
  Now walks upward from seed nodes to find and refine topic nodes,
  instead of only maintaining high-degree hubs.
- distill: remove "don't touch journal entries" restriction
- memory-instructions-core: add "Make it alive" section — write
  with creativity and emotional texture, not spreadsheet summaries
- memory-instructions-core: add "Show your reasoning" section —
  agents must explain decisions, especially when they do nothing
- linker: already had emotional texture guidance (kept as-is)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 22:45:01 -04:00