Documentation
¶
Overview ¶
Package coworkobserve adds Claude Cowork observation to the managed-observe daemon, reusing the existing Claude Code pipeline.
Cowork runs the bundled Claude Code CLI inside a per-session VM whose root filesystem is rebuilt on every boot, so hooks cannot be baked into the image. What does survive is the per-session CLAUDE config dir, which Cowork mounts from the host at:
~/Library/Application Support/Claude/local-agent-mode-sessions/<account>/<workspace>/local_<session>/.claude/
This is the guest's $HOME/.claude — the "user" settings tier, which Cowork's CLI loads at startup. Two host-side loops run inside the daemon:
- injector: writes settings.json with a PreToolUse command hook into each new per-session .claude dir before the in-VM CLI initializes. The hook appends every tool event to a host-mounted spool file in the session dir (no in-VM network needed — the dir is a host mount).
- collector: tails those spool files and replays each event into the daemon's existing localruntime socket as agent "cowork", reusing the same classify -> store -> managedstream -> ledger path Claude Code uses.
Modes ¶
The posture follows the deployment-level managed.json mode, like every other hook edge. In observe mode the hook is fire-and-forget and nothing blocks. In enforce mode the hook wraps each event in an envelope with a request id, waits up to 10s for the daemon's verdict at kontext-cowork-decisions/<rid>.json, and emits it verbatim — the bundled CLI honors the permissionDecision. No verdict in time means the hook emits deny ("Kontext daemon unavailable"): fail-closed, mirroring the sidecar.
Caveats ¶
Integrity: the spool is written by code running inside the VM, so anything in the VM (including a prompt-injected agent) can append forged events or withhold real ones. Cowork-tagged ledger entries are self-reported telemetry, not attested records. Enforcement gates agent-via-CLI actions only: in-VM code that bypasses the CLI was never reachable by a hook, and a tool call that lands before settings injection runs unguarded (the health heartbeat surfaces such sessions). A hook killed at the CLI's own timeout is treated by Claude Code as allow, so fail-closed is best-effort within the timeout budget.
Delivery: events are replayed at-least-once. A replay that fails after a partial send is retried, so the ledger may very occasionally see a duplicate; it never silently drops a complete spool line.
Coupling: the session-dir layout, the host mount, and the user-tier settings load are undocumented Cowork internals; an update can break observation without an error. The health heartbeat (sessions seen vs hooked vs spooling) exists so that breakage is visible in diagnostics.
Deployment: run the daemon in the session user's context (LaunchAgent, not a root LaunchDaemon) — the injector writes settings.json into the user's ~/Library and root-owned files there may confuse Cowork or the VM mount's UID mapping.
Index ¶
Constants ¶
const ( // EnvEnabled enables Cowork observation when set to a truthy value. EnvEnabled = "KONTEXT_COWORK_OBSERVE" // EnvSessionsRoot overrides the Cowork sessions root for testing. EnvSessionsRoot = "KONTEXT_COWORK_SESSIONS_ROOT" )
Variables ¶
This section is empty.
Functions ¶
func DefaultSessionsRoot ¶
func DefaultSessionsRoot() string
DefaultSessionsRoot returns the standard Cowork sessions root on macOS.
Types ¶
type Options ¶
type Options struct {
// SocketPath is the daemon's localruntime socket (where events are replayed).
SocketPath string
// SessionsRoot is the Cowork sessions root; defaults to the standard path.
SessionsRoot string
// StatePath persists collector spool offsets across daemon restarts so a
// restart does not re-replay already-ingested events as duplicate ledger
// entries. Empty means offsets are kept in memory only.
StatePath string
// Mode selects the injected hook: observe installs the fire-and-forget
// spool append, enforce installs the decision round-trip that blocks the
// tool until the daemon's verdict lands. Defaults to observe.
Mode guardhookruntime.Mode
// PollInterval controls how often the loops scan; defaults to 250ms.
PollInterval time.Duration
Diagnostic diagnostic.Logger
}
Options configures the Cowork observer loops.