Files
oikos/migrations/021_session_blocker_and_closed_at.up.sql
dtoro e055a7c6ce
Some checks failed
ci / build-test (push) Has been cancelled
ci / docker-build (push) Has been cancelled
ci / web (push) Has been cancelled
Desktop App / Build Linux (amd64) (push) Has been cancelled
Desktop App / Attach to Release (push) Has been cancelled
feat(nomos): session-review improvements (P0/P1/P2 from 2026-07-20 audit)
Classifier now unwraps pct exec / qm guest exec / bash -c / sh -c / sudo
and env-var assignments before classification, so read-only inspection
wrapped in pct exec no longer escalates to config_mutation. curl GET
(default method, no -d/-F/-T/-o/>) is read-only. Eliminates the three
duplicate rclone sessions (a51e2086, 8acea2e3, cb8c8a4a) that bounced
off the classifier for the same goal.

New classify_command MCP tool: command-scoped preflight that returns the
exact risk class run would assign. Documented in SOUL.md with guidance
to pre-classify before run when the verdict is uncertain.

set_goal surfaces prior partial/failed sessions from the last 24h so the
agent picks up the thread instead of rediscovering it.

completeTask auto-closes in-flight plan steps (pending/running -> done
on success, skipped on partial/failure), so one-step plans no longer
need the per-step running->done dance right before completion.

Migration 021 adds blocker + closed_at to agent_sessions. completeTask
sets closed_at once and derives a structured blocker reason
(approval_timeout, user_abandoned, classifier_overreach, model_refusal,
tool_error, ...) from the last assistant message.

/sessions list now carries message_count, tool_call_count,
duration_seconds (server-side aggregates — no more N+1 transcript
fetches to audit a fleet). GET /sessions/{id} returns both metadata
and messages. New query params filter + paginate: outcome, status,
entity_id, blocker, since (RFC3339 or Go duration), cursor, limit.

Titles now prefer the goal when set; sessions without a goal fall back
to the first assistant text.

New GET /sessions/{id}/tool_calls flat view for audit scripts.

Plan: plans/2026-07-20-session-review-ten-sessions.md. VERSION 0.7.12 -> 0.7.13.
2026-07-20 11:32:31 +02:00

36 lines
1.8 KiB
SQL

-- 021_session_blocker_and_closed_at.up.sql
-- Track why a session ended partial/failed and when it actually closed.
-- See plans/2026-07-20-session-review-ten-sessions.md P1.5.
--
-- `blocker` is a short structured reason: "approval_timeout",
-- "classifier_overreach", "user_abandoned", "tool_error", "model_refusal",
-- etc. Set by complete_task when outcome is partial/failed, derived from the
-- last assistant message's text. Empty for success outcomes.
--
-- `closed_at` is when the session reached its terminal state. Distinct from
-- `last_active_at`, which is touched on any access (including the operator
-- just opening the transcript) — `closed_at` is set ONCE at completion.
-- Without it, "session duration" can only be computed as
-- `last_active - created`, which lies for reopened sessions (a51e2086
-- reported 4-day duration because the operator reopened it to close it).
ALTER TABLE agent_sessions ADD COLUMN IF NOT EXISTS blocker TEXT NOT NULL DEFAULT '';
ALTER TABLE agent_sessions ADD COLUMN IF NOT EXISTS closed_at TIMESTAMPTZ;
-- Backfill closed_at for already-terminal sessions so the new column isn't
-- NULL forever on existing rows. Use last_active_at as the best proxy — it's
-- the most recent touch, which is the closest we have to "when it ended"
-- for historical sessions. New sessions set closed_at explicitly on
-- complete_task.
UPDATE agent_sessions
SET closed_at = last_active_at
WHERE closed_at IS NULL
AND status IN ('done', 'failed');
-- Index for "show me partial sessions in the last N days" — the common
-- audit query. Covers the blocker column too so the planner can answer
-- "blocker breakdown over the last week" with an index-only scan.
CREATE INDEX IF NOT EXISTS idx_agent_sessions_closed
ON agent_sessions (closed_at DESC)
WHERE status IN ('done', 'failed');