diff --git a/agent-tracker/cmd/tracker-server/main.go b/agent-tracker/cmd/tracker-server/main.go index 97529c0..cb3aa90 100644 --- a/agent-tracker/cmd/tracker-server/main.go +++ b/agent-tracker/cmd/tracker-server/main.go @@ -1234,9 +1234,8 @@ func (s *server) buildStateEnvelope() *ipc.Envelope { now := time.Now() tasks := make([]ipc.Task, 0, len(copies)) - var staleKeys []string nameCache := make(map[string][2]string) - for i, t := range copies { + for _, t := range copies { started := "" if !t.StartedAt.IsZero() { started = t.StartedAt.Format(time.RFC3339) @@ -1252,15 +1251,9 @@ func (s *server) buildStateEnvelope() *ipc.Envelope { if duration < 0 { duration = 0 } - // Auto-timeout: in_progress tasks older than 30 minutes - if t.Status == statusInProgress && duration > 30*time.Minute { - staleKeys = append(staleKeys, taskKeys[i]) - continue - } var names [2]string if cached, ok := nameCache[t.WindowID]; ok { if cached[0] == "" && cached[1] == "" { - staleKeys = append(staleKeys, taskKeys[i]) continue } names = cached @@ -1268,7 +1261,6 @@ func (s *server) buildStateEnvelope() *ipc.Envelope { sessName, winName, err := tmuxNamesForWindow(t.WindowID) if err != nil || (sessName == "" && winName == "") { nameCache[t.WindowID] = [2]string{"", ""} - staleKeys = append(staleKeys, taskKeys[i]) continue } names = [2]string{sessName, winName} @@ -1291,15 +1283,6 @@ func (s *server) buildStateEnvelope() *ipc.Envelope { }) } - // Clean up stale tasks (windows that no longer exist) - if len(staleKeys) > 0 { - s.mu.Lock() - for _, key := range staleKeys { - delete(s.tasks, key) - } - s.mu.Unlock() - } - activeNotes, archived := s.notesForState() s.mu.Lock() diff --git a/opencode/plugin/tracker-notify.js b/opencode/plugins/tracker-notify.ts similarity index 85% rename from opencode/plugin/tracker-notify.js rename to opencode/plugins/tracker-notify.ts index 5ccb43d..415d573 100644 --- a/opencode/plugin/tracker-notify.js +++ b/opencode/plugins/tracker-notify.ts @@ -1,12 +1,27 @@ +import { appendFileSync } from "fs"; + const NOTIFY_BIN = "/usr/bin/python3"; const NOTIFY_SCRIPT = "/Users/david/.config/codex/notify.py"; const MAX_SUMMARY_CHARS = 600; const TRACKER_BIN = "/Users/david/.config/agent-tracker/bin/tracker-client"; +const LOG_FILE = "/tmp/tracker-notify-debug.log"; + +const log = (msg: string, data?: any) => { + const timestamp = new Date().toISOString(); + const logMsg = `[${timestamp}] ${msg}${data ? " " + JSON.stringify(data) : ""}\n`; + try { + appendFileSync(LOG_FILE, logMsg); + } catch (e) { + // ignore + } +}; export const TrackerNotifyPlugin = async ({ client, directory, $ }) => { // Only run within tmux (TMUX_PANE must be set) const TMUX_PANE = process.env.TMUX_PANE; + log("Plugin loading, TMUX_PANE:", TMUX_PANE); if (!TMUX_PANE) { + log("Not in tmux, plugin disabled"); return {}; } @@ -49,14 +64,6 @@ export const TrackerNotifyPlugin = async ({ client, directory, $ }) => { return args; }; - // On init: finish any stale task for this pane - const finishStaleTask = async () => { - if (!(await trackerReady())) return; - const args = buildTrackerArgs(); - await $`${TRACKER_BIN} command ${args} -summary "stale" finish_task`.nothrow(); - }; - finishStaleTask(); - const summarizeText = (parts = []) => { const text = parts .filter((p) => p?.type === "text" && !p.ignored) @@ -155,6 +162,7 @@ export const TrackerNotifyPlugin = async ({ client, directory, $ }) => { return { event: async ({ event }) => { + // Track message roles from message.updated events if (event?.type === "message.updated") { const info = event?.properties?.info; @@ -178,13 +186,22 @@ export const TrackerNotifyPlugin = async ({ client, directory, $ }) => { } } - if (event?.type !== "session.status") return; + if (event?.type !== "session.status") return; - const sessionID = event?.properties?.sessionID; - const status = event?.properties?.status; - if (!sessionID || !status) return; + const sessionID = event?.properties?.sessionID; + const status = event?.properties?.status; + if (!sessionID || !status) return; - if (status.type === "busy" && !taskActive) { + // Check if this is a subagent session + const session = await client.session.get({ path: { id: sessionID } }).catch(() => null); + const parentID = session?.data?.parentID; + + // Skip subagent sessions (they have a parentID) + if (parentID) { + return; + } + + if (status.type === "busy" && !taskActive) { // Use captured message first, then fall back to API let text = lastUserMessage; if (!text) {