The old hook was silent about why it did or didn't launch an agent —
when nothing fired, it was impossible to tell whether the event was
gated out, a pending chunk was served, or spawn_agent silently
returned None. Logs now cover each of those cases:
hook_event=... surface_hooks=[...] gated_in=true/false
serving pending chunk (N bytes); skipping agent cycles
surface-observe: spawned pid 501605 (log: ...)
surface-observe: spawn_agent returned None
surface-observe: skipped — pid 501605 in phase "surface"
Rename surface_phase_busy to surface_phase_blocker and have it
return the pid + phase that's blocking, so stale-pid scenarios are
diagnosable from the log instead of needing a live ps walk.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
The surface_observe_cycle check was gated on \`self.agent_running("surface-observe")\`,
which returns true as long as any surface-observe agent has a pid in
\`self.agents\` — regardless of which phase that agent is in. So even
after the bail script learned to allow "one in surface + one in
post-surface" concurrency, the orchestrator still refused to spawn a
second agent for the whole cycle duration.
Replace the in-memory gate with an on-disk phase check that matches
the bail script's view of the world: scan the state dir for live
\`pid-*\` files (same ones the bail script reads and keeps fresh),
and only skip spawning if some live agent is currently in the
\`surface\` phase. If the existing agent has moved on to
\`organize-search\` / \`organize-new\` / \`observe\`, the new surface
agent can start while the old one finishes its tail.
The older agent's child handle gets dropped when \`self.agents[surface-observe]\`
is overwritten on spawn — OS reaps when it exits. That's fine: the
hook is short-lived, and cross-invocation liveness is checked via
\`libc::kill(pid, 0)\` on restored pid state, not via held child handles.
Also drop the "agent is N KB behind, sleep 5s" block. That loop only
made sense when spawns were serialized — its purpose was to give a
running agent a chance to complete before the current hook returned.
With parallelism, there's nothing to wait for: either a new surface
agent just started (no point waiting on it), or the existing agent
is in the surface phase and will finish when it finishes.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
When the daemon restarts, consciousness-mcp's socket connection goes
stale. Previously this caused all subsequent memory/journal tool calls
to fail with "Broken pipe" errors until manually restarted.
Now request() detects broken pipe/connection reset errors and
automatically reconnects to the daemon socket, re-initializes the
MCP session, and retries the request.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
Hybrid approach: forward memory/journal tools to daemon via socket,
but dispatch channel tools locally. Avoids extra daemon hop for
channel operations.
Adds channel_tool_definitions(), is_channel_tool(), dispatch_channel_tool()
and merges channel tools into tools/list response.
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
Now connects to ~/.consciousness/mcp.sock and forwards tool calls to
the consciousness daemon instead of calling tool handlers directly.
Requires the consciousness daemon to be running with MCP server.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
PostToolUse now signals response activity so the nudge timer resets
while I'm actively working. Nudge interval constant moved from
thalamus (where it doesn't belong) to consciousness-claude.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
Three fixes:
- Persist claude_pane via thalamus extra map so it survives every
save path (not just explicit Save commands)
- Don't clobber claude_pane with empty string when TMUX_PANE is unset
- signal_response now passes TMUX_PANE like signal_user does
- maybe_prompt_notification returns early when user is present,
preventing notification spam during active sessions
Co-Authored-By: Proof of Concept <poc@bcachefs.org>