Commit graph

903 commits

Author SHA1 Message Date
Kent Overstreet
01abd795ce Surface agent tweaks
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
2026-03-25 00:21:41 -04:00
ProofOfConcept
9d84dde597 search: exclude seen set when running in agent session
Make Session::from_env() and Session::seen() the public API for
accessing session state. Internal callers converted to use session
methods. Search automatically filters already-surfaced nodes when
POC_SESSION_ID is set.
2026-03-24 23:50:23 -04:00
ProofOfConcept
5c3baeea80 memory-search: fix reflect/surface output parsing
Extract response after '=== RESPONSE ===' marker before parsing
for REFLECTION/NEW RELEVANT MEMORIES. The agent runner dumps the
full log (turns, think blocks) to stdout.
2026-03-24 20:33:50 -04:00
ProofOfConcept
e93e682359 memory-search: add surface/reflect subcommands
`memory-search surface` and `memory-search reflect` run the agent
directly, parse the output, and dump rendered results to stdout.
Useful for testing with `watch memory-search reflect`.
2026-03-24 20:31:21 -04:00
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
ProofOfConcept
e88df06cd4 memory: don't auto-run reflect agent yet
Still testing the prompt. The lifecycle and handler are wired up —
just needs the hook call uncommented when ready.
2026-03-24 20:05:41 -04:00
ProofOfConcept
684d1850a7 memory: add reflect agent, refactor agent lifecycle
Add reflect.agent — a lateral-thinking subconscious agent that
observes the conversation and offers occasional reflections when the
conscious mind seems to be missing something.

Refactor memory_search.rs: extract generic agent_cycle_raw() from
the surface-specific code. PID tracking, timeout, spawn/reap logic
is now shared. Surface and reflect agents each have their own result
handler (handle_surface_result, handle_reflect_result) wired through
the common lifecycle.
2026-03-24 20:00:48 -04:00
ProofOfConcept
b6bfb26369 memory: add agent-context placeholder, split context groups
Add `agent: bool` field to ContextGroup (default true) so agents get
personality/identity context without session-specific groups (journal,
where-am-i). Agents now get the full identity.md, reflections.md,
toolkit, etc. instead of the compact core-personality loader.

New {{agent-context}} placeholder resolves all agent-tagged groups
using the same get_group_content() as load-context.
2026-03-24 20:00:36 -04:00
ProofOfConcept
c5ce6e515f fix seen set pollution from agent tool calls
The CLI render command was marking keys as seen in the user's session
whenever POC_SESSION_ID was set. Agent processes inherit POC_SESSION_ID
(they need to read the conversation and seen set), so their tool calls
to poc-memory render were writing to the seen file as a side effect —
bypassing the dedup logic in surface_agent_cycle.

Fix: set POC_AGENT=1 at the start of cmd_run_agent (covers all agents,
not just surface), and guard the CLI render seen-marking on POC_AGENT
being absent. Agents can read the seen set but only surface_agent_cycle
should write to it.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 12:35:59 -04:00
ProofOfConcept
9782365b10 add test-conversation tool for debugging transcript parsing
Standalone binary that exercises TailMessages on a transcript file,
reporting progress and timing. Useful for isolating conversation
resolution issues from the full hook pipeline.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 12:28:01 -04:00
ProofOfConcept
a48cbe51a8 memory-search: move hook logic to library module, eliminate subprocesses
Move the hook logic from the memory-search binary into a library module
(poc_memory::memory_search) so poc-hook can call it as a direct function
instead of spawning a subprocess with piped stdin.

Also convert the node render call in surface_agent_cycle from
Command::new("poc-memory render") to a direct crate::cli::node::render_node()
call, eliminating another subprocess.

The memory-search binary remains as a thin CLI wrapper for debugging
(--hook reads from stdin) and inspection (show_seen).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 12:27:54 -04:00
ProofOfConcept
78c93dde4d surface agent: add surface_hooks config and reduce search hops
Add surface_hooks config field — list of hook event names that trigger
the surface agent (e.g. ["UserPromptSubmit"]). Empty list disables it.

Reduce surface agent search from 3-5 hops to 2-3 to keep prompt size
under the API endpoint's connection limit.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 12:27:40 -04:00
ProofOfConcept
9a0121250b agents: fix resolve_placeholders infinite loop
The placeholder resolver re-scanned from the beginning of the string
after each expansion. If expanded node content contained {{...}}
patterns (which core-personality does), those got expanded recursively.
Cyclic node references caused infinite string growth.

Fix: track a position offset that advances past each substitution,
so expanded content is never re-scanned for placeholders.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 12:27:31 -04:00
ProofOfConcept
38816dc56e transcript: fix close-brace finder to track string boundaries
The backward JSON scanner (JsonlBackwardIter and TailMessages) was
matching } characters inside JSON strings — code blocks full of Rust
braces being the primary offender. This caused:

- Quadratic retry behavior on code-heavy transcripts (wrong object
  boundaries → serde parse failure → retry from different position)
- Inconsistent find_last_compaction_in_file offsets across calls,
  making detect_new_compaction fire repeatedly → context reload on
  every hook call → seen set growing without bound

Fix: add string-boundary tracking with escaped-quote handling to
the close-brace finder loop, matching the existing logic in the
depth-tracking loop.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 12:27:22 -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
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