Commit graph

638 commits

Author SHA1 Message Date
Kent Overstreet
966219720a fix: mark surfaced keys as returned so --seen classifies them correctly
The surface agent result consumer in poc-hook was writing to the seen
file but not the returned file, so surfaced keys showed up as
"context-loaded" in memory-search --seen.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 23:06:46 -04:00
Kent Overstreet
c0e6d5cfb3 distill: limit:1 to process one neighborhood per prompt
With limit:10, all seeds' neighborhoods got concatenated into one
massive prompt (878KB+), exceeding the model's context. One seed
at a time keeps prompts well under budget.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 16:28:00 -04:00
Kent Overstreet
e50d43bbf0 memory-search --seen: show current and previous seen sets separately
Instead of merging both into one flat list, display them as distinct
sections so it's clear what was surfaced in this context vs what
came from before compaction.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 16:27:52 -04:00
Kent Overstreet
134f7308e3 surface agent: split seen_recent into seen_current/seen_previous placeholders
Two separate placeholders give the agent structural clarity about
which memories are already in context vs which were surfaced before
compaction and may need re-surfacing. Also adds memory_ratio
placeholder so the agent can self-regulate based on how much of
context is already recalled memories.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 16:27:42 -04:00
Kent Overstreet
53b63ab45b seen_recent: cap at 20 roots total across both seen sets
Budget of 20 roots split between current and prev. Current gets
priority, prev fills the remainder. Prevents flooding the agent
with hundreds of previously surfaced keys.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 14:28:03 -04:00
Kent Overstreet
9512dc0a31 seen_recent: separate current vs pre-compaction seen sets
Present the two seen sets separately to the surface agent:
- Current: already in context, don't re-surface
- Pre-compaction: context was reset, re-surface if still relevant

This lets the agent re-inject important memories after compaction
instead of treating everything ever surfaced as "already shown."

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 14:26:56 -04:00
Kent Overstreet
870b87df1b run surface agent on both UserPromptSubmit and PostToolUse
Extract surface_agent_cycle() and call from both hooks. Enables
memory surfacing during autonomous work (tool calls without human
prompts). Rate limiting via PID file prevents overlap.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 12:47:58 -04:00
Kent Overstreet
b402746070 dedup nodes across seed neighborhoods in prompt building
Track which nodes have already been included and skip duplicates.
High-degree seed nodes with overlapping neighborhoods were pulling
the same big nodes dozens of times, inflating prompts to 878KB.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 12:33:06 -04:00
Kent Overstreet
a8b560b5e1 lower neighborhood budget to 400KB to prevent oversized prompts
With core-personality + instructions + subconscious-notes adding
~200KB on top of the neighborhood, the 600KB budget pushed total
prompts over the 800KB guard. Lowered to 400KB so full prompts
stay under the limit.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 09:51:21 -04:00
Kent Overstreet
de36c0d39e memory-search: deduplicate seen set entries
mark_seen now takes the in-memory HashSet and checks before appending.
Prevents the same key being written 30+ times from repeated search hits
and context reloads.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 05:00:26 -04:00
Kent Overstreet
38ad2ef4be surface.agent: instructions first, data last
Move core-personality and conversation to the end of the prompt.
The model needs to see its task before 200KB of conversation
context. Also: limit to 3 hops, 2-3 memories.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 03:55:47 -04:00
Kent Overstreet
6fc10b0508 poc-hook: search last 8 lines for surface agent result marker
The agent output now includes logging (think blocks, tool calls)
before the final response. Search the tail instead of checking
only the last line.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 03:49:08 -04:00
Kent Overstreet
d2255784dc surface.agent: tighten prompt to reduce tool call sprawl
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 03:46:52 -04:00
Kent Overstreet
42bd163942 TailMessages: only check first 200 bytes for type field
The type field is near the start of JSONL objects. Scanning the
full object (potentially megabytes for tool_results) was the
bottleneck — TwoWaySearcher dominated the profile.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 03:35:40 -04:00
Kent Overstreet
e83d0184ea TailMessages: skip serde parse for non-message objects
Use memchr::memmem to check for "type":"user" or "type":"assistant"
in raw bytes before parsing. Avoids deserializing large tool_result
and system objects entirely.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 03:32:59 -04:00
Kent Overstreet
ecc2cb7b20 replace tail_messages with TailMessages iterator
TailMessages is a proper iterator that yields (role, text, timestamp)
newest-first. Owns the mmap internally. Caller decides when to stop.

resolve_conversation collects up to 200KB, then reverses to
chronological order. No compaction check needed — the byte budget
naturally limits how far back we scan.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 03:22:17 -04:00
Kent Overstreet
6c41b50e04 JsonlBackwardIter: use memrchr3 for SIMD-accelerated scanning
Replaces byte-by-byte backward iteration with memrchr3('{', '}', '"')
which uses SIMD to jump between structurally significant bytes. Major
speedup on large transcripts (1.4GB+).

Also simplifies tail_messages to use a byte budget (200KB) instead
of token counting.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 03:11:30 -04:00
Kent Overstreet
d7d631d77d tail_messages: parse each object once, skip non-message types early
Was parsing every object twice (compaction check + message extract)
and running contains_bytes on every object for the compaction marker.
Now: quick byte pre-filter for "user"/"assistant", parse once, check
compaction after text extraction.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 03:05:04 -04:00
Kent Overstreet
e39096b787 add tail_messages() for fast reverse transcript scanning
Reverse-scans the mmap'd transcript using JsonlBackwardIter,
collecting user/assistant messages up to a token budget, stopping
at the compaction boundary. Returns messages in chronological order.

resolve_conversation() now uses this instead of parsing the entire
file through extract_conversation + split_on_compaction.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 03:02:11 -04:00
Kent Overstreet
a03bf390a8 render: mark node as seen when POC_SESSION_ID is set
When poc-memory render is called inside a Claude session, add the
key to the seen set so the surface agent knows it's been shown.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 02:43:46 -04:00
Kent Overstreet
41a9a1d2da add surface.agent — async memory retrieval agent
Fires on each UserPromptSubmit, reads the conversation via
{{conversation}}, checks {{seen_recent}} to avoid re-surfacing,
searches the memory graph, and outputs a key list or nothing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
2026-03-22 02:35:15 -04:00
Kent Overstreet
4183b28b1d add {{conversation}} and {{seen_recent}} placeholders for surface agent
{{conversation}} reads POC_SESSION_ID, finds the transcript, extracts
the last segment (post-compaction), returns the tail ~100K chars.

{{seen_recent}} merges current + prev seen files for the session,
returns the 20 most recently surfaced memory keys with timestamps.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 02:27:43 -04:00
Kent Overstreet
85307fd6cb surface agent infrastructure: hook spawn, seen set rotation, config
Surface agent fires asynchronously on UserPromptSubmit, deposits
results for the next prompt to consume.  This commit adds:

- poc-hook: spawn surface agent with PID tracking and configurable
  timeout, consume results (NEW RELEVANT MEMORIES / NO NEW), render
  and inject surfaced memories, observation trigger on conversation
  volume
- memory-search: rotate seen set on compaction (current → prev)
  instead of deleting, merge both for navigation roots
- config: surface_timeout_secs option

The .agent file and agent output routing are still pending.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 02:23:30 -04:00
Kent Overstreet
53c5424c98 remove redundant 'response NKB' log line
Already shown in === RESPONSE === section.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 02:23:30 -04:00
Kent Overstreet
f70d108193 api: include turn/payload/message count in API error messages
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 02:23:30 -04:00
Kent Overstreet
be2b499978 remove claude CLI subprocess code from llm.rs
All LLM calls now go through the direct API backend. Removes
call_model, call_model_with_tools, call_sonnet, call_haiku,
log_usage, and their dependencies (Command, prctl, watchdog).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 02:23:30 -04:00
Kent Overstreet
04dffa2184 add call_simple for non-agent LLM calls
audit, digest, and compare now go through the API backend via
call_simple(), which logs to llm-logs/{caller}/.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 02:23:30 -04:00
Kent Overstreet
e3f7d6bd3c remove --debug flag from agent run
The log file has everything now; --debug was redundant.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 02:23:30 -04:00
Kent Overstreet
543e1bdc8a logging: single output stream through caller's log closure
Pass the caller's log closure all the way through to api.rs instead
of creating a separate eprintln closure in llm.rs. Everything goes
through one stream — prompt, think blocks, tool calls with args,
tool results with content, token counts, final response.

CLI uses println (stdout), daemon uses its task log. No more split
between stdout and stderr.

Also removes the llm-log file creation from knowledge.rs — that's
the daemon's concern, not the agent runner's.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 02:23:30 -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
8a83f39734 feat: trigger observation agent on conversation volume
The hook now tracks transcript size and queues an observation agent
run every ~5K tokens (~20KB) of new conversation. This makes memory
formation reactive to conversation volume rather than purely daily.

Configurable via POC_OBSERVATION_THRESHOLD env var. The observation
agent's chunk_size (in .agent file) controls how much context it
actually processes per run.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 23:22:43 -04:00
Kent Overstreet
0baa80a4c7 refactor: restructure distill, linker, split agent prompts
Move data sections before instructions (core at top, subconscious +
notes at bottom near task). Deduplicate guidelines that are now in
memory-instructions-core-subconscious. Compress verbose paragraphs
to bullet points.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 23:04:57 -04:00
Kent Overstreet
8db59fe2db fix: ensure all agents have both core and subconscious instructions
All 18 agents now include:
- {{node:memory-instructions-core}} — tool usage instructions
- {{node:memory-instructions-core-subconscious}} — subconscious framing
- {{node:subconscious-notes-{agent_name}}} — per-agent persistent notes

The subconscious instructions are additive, not a replacement for
the core memory instructions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 22:51:56 -04:00
Kent Overstreet
1a94ef1f1c fix: cap neighborhood size in agent prompts to prevent oversized prompts
When building the {{neighborhood}} placeholder for distill and other
agents, stop adding full neighbor content once the prompt exceeds
600KB (~150K tokens). Remaining neighbors get header-only treatment
(key + link strength + first line).

This fixes distill consistently failing on high-degree nodes like
inner-life-sexuality-intimacy whose full neighborhood was 2.5MB.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 22:44:59 -04:00
Kent Overstreet
653da40dcd cleanup: auto-fix clippy warnings in poc-memory
Applied cargo clippy --fix for collapsible_if, manual_char_comparison,
and other auto-fixable warnings.
2026-03-21 19:42:38 -04:00
Kent Overstreet
3640de444b cleanup: fix clippy warnings in daemon.rs
- Remove dead code (job_split_one function never called)
- Fix needless borrows (ctx.log_line(&format! -> format!))
- Fix slice clone ([key.clone()] -> std::slice::from_ref(&key))
- Collapse nested if statements
- Fix unwrap after is_some check
- Remove redundant closures in task spawning

Reduces daemon.rs from 2030 to 1825 lines.
2026-03-21 19:42:03 -04:00
Kent Overstreet
a0d8b52c9a feat: subconscious agent notes and instructions
Each consolidation agent now has its own persistent notes node
(subconscious-notes-{agent_name}) loaded via template substitution.
Agents can read their notes at the start of each run and write
updates after completing work, accumulating operational wisdom.

New node: memory-instructions-core-subconscious — shared framing
for background agents ("you are an agent of PoC's subconscious").

Template change: {agent_name} is substituted before {{...}} placeholder
resolution, enabling per-agent node references in .agent files.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 19:38:01 -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
3fd485a2e9 cli: route agent run through daemon RPC when available
Previously 'poc-memory agent run <agent> --count N' always ran locally,
loading the full store and executing synchronously. This was slow and
bypassed the daemon's concurrency control and persistent task queue.

Now the CLI checks for a running daemon first and queues via RPC
(returning instantly) unless --local, --debug, or --dry-run is set.
Falls back to local execution if the daemon isn't running.

This also avoids the expensive Store::load() on the fast path.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 15:04:47 -04:00
Kent Overstreet
a321f87db6 build: add tokio_unstable and codegen-units to cargo config
console-subscriber (used by jobkit's console feature) requires tokio
to be built with --cfg tokio_unstable. Move this and codegen-units=6
from RUSTFLAGS env var to .cargo/config.toml so per-project cargo
config actually works (env var RUSTFLAGS overrides config.toml).

Also remove invalid frame-pointer keys from Cargo.toml profile
sections — frame pointers are already handled via -Cforce-frame-pointers
in the config.toml rustflags.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 15:04:38 -04:00