Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,58 @@ This is especially useful with `weave-claude-code install --non-interactive`, wh

---

## Tracing Modes: Daemon (default) vs. Daemonless

The plugin can build traces two ways. **The default is `daemon` and nothing
changes unless you opt in** — upgrading is a no-op, and settings files written
before this option existed are treated as `daemon`.

| Mode | How it works | Background process? |
|------|--------------|---------------------|
| `daemon` *(default)* | A persistent background daemon holds OTel spans open and streams them as hook events arrive across every session on the machine. | Yes — one long-lived process + a Unix socket. |
| `session-end` | No daemon. When a session ends, a `SessionEnd` hook rebuilds that session's **entire** span tree from its transcript in one pass and uploads it, then exits. | No — nothing persistent. |

### Why you might switch to `session-end`

The daemon is a single global process holding a Unix socket and in-memory
session state. When it dies ungracefully (closing a terminal / `SIGHUP`, OOM, or
`kill`), the socket goes stale: events are silently dropped, you see
`Unknown session` errors, and tracing stops until it's restarted. `session-end`
removes that entire failure class — there's no shared process or socket to go
stale, each session is traced independently, and a per-instance setting like
`WANDB_BASE_URL` is picked up fresh every session (no daemon-restart dance).

**Choose `session-end` if** you hit daemon flakiness (stale socket /
`Unknown session` / needing to restart), you run agent-teams or long sessions,
or you simply want a stateless setup with no background process. **Stay on
`daemon`** otherwise — it streams spans live during a session, which
`session-end` does not (it uploads once, at session end).

### Trade-off

`session-end` reconstructs everything **structural** from the transcript —
turns, chat/LLM calls, tool calls and results, nested subagents and agent-teams
teammates, token usage, and models. It does **not** currently capture two
hook-only enrichments: permission-request span events and context-compaction
stats. Everything you need to read a trace is present; these two extras are
planned for a follow-up.

### How to switch

```bash
# Opt in to daemonless tracing
weave-claude-code config set trace_mode session-end

# Revert to the default daemon
weave-claude-code config set trace_mode daemon
```

Then reload plugins (`/reload-plugins` inside a session) or relaunch `claude`.
`WEAVE_TRACE_MODE=session-end` can also be set in the environment to switch the
hook routing for the current shell without changing the saved setting.

---

## Sending Traces to a Dedicated or Private W&B Instance

If you use W&B Dedicated Cloud or a self-hosted instance, set `WANDB_BASE_URL` to point the plugin at your deployment before launching Claude Code:
Expand Down
22 changes: 22 additions & 0 deletions hooks/hook-handler.sh
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,28 @@ if [ -z "${WANDB_API_KEY_VALUE}" ] && [ -z "${WANDB_API_KEY:-}" ]; then
exit 0
fi

# ── daemonless (session-end) mode ─────────────────────────────────────────────
#
# When trace_mode is "session-end" the persistent daemon is bypassed entirely.
# Every hook event still fires (hooks.json is static), but only SessionEnd does
# work: it pipes the payload to `weave-claude-code session-end`, which rebuilds
# the full span tree from the now-complete transcript in one pass and uploads.
# All other events exit 0 immediately — no daemon, no socket.

TRACE_MODE_VALUE=$(grep -o '"trace_mode" *: *"[^"]*"' "${SETTINGS_FILE}" 2>/dev/null | grep -o '"[^"]*"$' | tr -d '"')
TRACE_MODE_VALUE="${WEAVE_TRACE_MODE:-${TRACE_MODE_VALUE}}"

if [ "${TRACE_MODE_VALUE}" = "session-end" ]; then
PAYLOAD="$(cat)"
case "${PAYLOAD}" in
*'"hook_event_name":"SessionEnd"'*|*'"hook_event_name": "SessionEnd"'*)
printf '%s' "${PAYLOAD}" | weave-claude-code session-end >> "${ERROR_LOG}" 2>&1 || \
echo "$(date -Iseconds) | ERROR | session-end trace build failed" >> "${ERROR_LOG}"
;;
esac
exit 0
fi

# ── start daemon if needed ────────────────────────────────────────────────────
#
# The socket file alone is NOT proof that the daemon is alive. When the daemon
Expand Down
Loading
Loading