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.
36 lines
1.8 KiB
SQL
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');
|