activity: follow the agent's background work live + chat-event retention

"Activity" (placeholder name — jobs/tasks were taken) = watch background tasks
scroll in parallel with chat. Built DB-free; NOT restarted — deploy + test in
the morning.

- NDJSON progress contract (activity/progress.ts): capabilities append
  {job,cap,phase,status,pct,detail,ts,...} lines; tolerant parser treats any
  JSON object with phase/status as structured progress, else a raw log line.
- Backend (activity/router.ts, owner-only, path-guarded):
  - GET /api/activity/tasks — registry by scanning /tmp/claude-*/<cwd>/tasks/
    *.output (harness run_in_background) + announced detached jobs.
  - POST /api/activity/announce {name,path} — register a detached (setsid) job's
    log so it's followable too (the setsid case is on the critical path, since
    the warm worker now makes plain run_in_background the default for heavy jobs).
  - GET /api/activity/stream?task=<id>|path=<abs> — SSE tail (poll + offset),
    emitting {kind:'line'|'progress'} with NDJSON parsed.
- Frontend /activity screen + Radio nav item: task list (active dot) → live tail
  with a phase/pct progress header + raw log, following the /system-monitor pattern.
- Retention: startChatEventRetention() prunes chat_session_events >7d every 6h
  (wired in bootstrap) so the durable queue stays bounded.

Verified headlessly (no restart): parseTailLine classification, and the scan
finds 53 real task output files. Endpoints + UI untested until deploy.

Deferred (see handoff): cross-device sync + OpenCode parity (both touch the
now-stable chat path — won't ship un-restart-tested); task:progress into chat.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-27 00:56:10 +00:00
co-authored by Claude Opus 4.8
parent 6b3eb247a3
commit 35973a5505
10 changed files with 382 additions and 0 deletions
+3
View File
@@ -5,6 +5,7 @@ import { ensureToolLoader } from './ensure-tool-loader';
import { startDiscordBotIfConfigured } from './channels/discord/bot';
import { startTelegramBotIfConfigured } from './channels/telegram/bot';
import { startWhatsAppBotIfConfigured } from './channels/whatsapp/bot';
import { startChatEventRetention } from './api/chat/retention';
mkdirSync(DATA_PATH, { recursive: true });
ensureItemDirs();
@@ -13,6 +14,8 @@ ensureItemDirs();
ensureToolLoader();
// Queue is initialized by the sidecar process
startChatEventRetention();
await startDiscordBotIfConfigured().catch((err) => {
console.error('[channels] Failed to start Discord bot:', err);
});