diff --git a/CHANGELOG.md b/CHANGELOG.md index a48ac7a..793464c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,15 @@ new version heading in the same commit. ## [Unreleased] +## [0.325.1] — 2026-08-08 +### Changed +- **Session status glyphs: `ready` takes the check, `done` takes a dim dot.** The dashed circle read as + visual noise rather than a state. More importantly the swap fixes a claim the old set made: a check on + `done` reads "succeeded", and a finished run may well have failed — the verdict belongs to the Result + column, which has the outcome to say it with. On a LIVE session the check is a plain fact: the turn + landed, your move. So `done` goes back to the quiet dot it had before icons and claims nothing, and the + chain rail's `no report` fallback follows it. + ## [0.325.0] — 2026-08-08 ### Fixed - **An INTERRUPTED turn (Esc / Ctrl-C) kept reading "working".** Claude Code fires **nothing** on a user diff --git a/package-lock.json b/package-lock.json index 4f00390..ae6783d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "agent-os", - "version": "0.325.0", + "version": "0.325.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "agent-os", - "version": "0.325.0", + "version": "0.325.1", "license": "MIT", "bin": { "agent-os": "bin/agent-os" diff --git a/package.json b/package.json index d3b06de..1d7c9d8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "agent-os", - "version": "0.325.0", + "version": "0.325.1", "description": "A generic, governed operating system for running autonomous agents safely across brands. Ships with a local web console.", "license": "MIT", "type": "commonjs", diff --git a/web/src/App.tsx b/web/src/App.tsx index b6e1d4c..822b53e 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -1,7 +1,7 @@ import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, type ReactNode, type DragEvent as ReactDragEvent, type MouseEvent as ReactMouseEvent, type KeyboardEvent as ReactKeyboardEvent, type ChangeEvent as ReactChangeEvent } from 'react' import { Inbox as InboxIcon, TerminalSquare, Play, Plus, Check, X, Square, Rocket, Plug, Trash2, Users, User, LogOut, Copy, Zap, Brain, Building2, ChevronDown, SlidersHorizontal, Pencil, FileText, HelpCircle, CheckCircle2, XCircle, Clock, Send, LayoutGrid, List, ArrowLeft, Bot, FolderTree, Folder, File as FileIcon, FileCode, Save, ChevronRight, Sparkles, Package, Image as ImageIcon, Film, Download, Search, BookText, BookOpen, History as HistoryIcon, ScrollText, Bell, AlertTriangle, Activity, Lightbulb, Moon, Upload, FolderPlus, ListChecks, PanelLeftClose, PanelLeftOpen, RefreshCw, ThumbsUp, ThumbsDown, Target, ExternalLink, Paperclip, KeyRound, Blocks, FilePlus, Maximize2, Minimize2, Filter, Share2, Lock, Gauge } from 'lucide-react' // The session-status glyph set (see STATE_META) — one icon per state, plus the chain rail's verdict icons. -import { LoaderCircle, CircleDashed, CircleStop, CircleCheck, CircleX, CircleSlash, Copy as CopyIcon } from 'lucide-react' +import { LoaderCircle, CircleSmall, CircleStop, CircleCheck, CircleX, CircleSlash, Copy as CopyIcon } from 'lucide-react' import { Wrench, Code2, Bug, MessageSquare, Mail, Megaphone, PenTool, Database, Server, Cloud, Shield, Calendar, LineChart, BarChart3, DollarSign, ShoppingCart, Headphones, Cog, Compass, Flag, Heart, Star, Globe, GitBranch, Palette, Camera, Music, Feather, Wand2, Boxes, Terminal, Webhook, CalendarClock, Hash, Cpu, MoreHorizontal, Power, PowerOff, Pin, PinOff, type LucideIcon } from 'lucide-react' import ReactMarkdown from 'react-markdown' import remarkGfm from 'remark-gfm' @@ -81,8 +81,14 @@ const sessionState = (s: Session, waiting = false): SessionState => /** Each state is an ICON, not a coloured dot — six dots differing only in hue and fill asked the eye to * learn a colour key, and colour alone can't carry "a turn is running right now" (the thing people - * actually scan for). The glyph says it: a spinner spins, a bell rings, a check is finished. Colour and - * motion stay as reinforcement, never as the only channel. + * actually scan for). The glyph says it: a spinner spins, a bell rings, a check means the turn landed + * and it is your move, a dim dot means the run is closed. Colour and motion stay as reinforcement, + * never as the only channel. + * + * Note which state gets the CHECK. It is `idle`, not `done`: a check on a finished run reads + * "succeeded", and a finished run may well have failed — the verdict belongs to the Result column, which + * has the outcome to say it with. On a live session the check is a plain fact: the turn landed, your + * move. So `done` keeps the dim dot it had before icons, and claims nothing. * * `dot` survives for the ROLL-UP badges (the Chain toggle), where the signal is a count of sessions in a * state rather than one session's status — a 8px pip is right there, an icon isn't. @@ -91,10 +97,10 @@ const sessionState = (s: Session, waiting = false): SessionState => const STATE_META: Record = { waiting: { label: 'needs you', icon: Bell, anim: 'motion-safe:animate-pulse', dot: 'bg-amber-400 motion-safe:animate-pulse', tone: 'text-amber-500', toneDark: 'text-amber-300', tip: 'blocked on you — a question or an approval is waiting' }, working: { label: 'working', icon: LoaderCircle, anim: 'motion-safe:animate-spin', dot: 'bg-emerald-500 motion-safe:animate-pulse', tone: 'text-emerald-600', toneDark: 'text-emerald-300', tip: 'a turn is running right now' }, - idle: { label: 'ready', icon: CircleDashed, anim: '', dot: 'border border-emerald-500 bg-emerald-500/20', tone: 'text-emerald-600/90', toneDark: 'text-emerald-400', tip: 'live session, nothing running — open it and type to continue' }, + idle: { label: 'ready', icon: Check, anim: '', dot: 'border border-emerald-500 bg-emerald-500/20', tone: 'text-emerald-600/90', toneDark: 'text-emerald-400', tip: 'live session, nothing running — the turn finished, your move' }, stopped: { label: 'stopped', icon: CircleStop, anim: '', dot: 'bg-amber-500', tone: 'text-amber-600', toneDark: 'text-amber-400/80', tip: 'halted by a human or the idle reaper' }, crashed: { label: 'crashed', icon: AlertTriangle, anim: '', dot: 'bg-red-500', tone: 'text-red-600', toneDark: 'text-red-400', tip: 'the pane died without an end signal' }, - done: { label: 'done', icon: Check, anim: '', dot: 'bg-muted-foreground/40', tone: 'text-muted-foreground/70', toneDark: 'text-neutral-400', tip: 'the run ended' }, + done: { label: 'done', icon: CircleSmall, anim: '', dot: 'bg-muted-foreground/40', tone: 'text-muted-foreground/70', toneDark: 'text-neutral-400', tip: 'the run ended' }, } /** The state word's colour for a given surface. Pair it with {@link statusLabel} wherever the word is * rendered away from the dot. */ @@ -3394,7 +3400,7 @@ const nodeState = (n: ChainNode): { label: string; tone: string; icon: LucideIco if (n.outcome === 'failure' || n.outcome === 'error') return { label: 'failed', tone: 'text-red-600', icon: CircleX, anim: '' } if (n.outcome === 'partial') return { label: 'partial', tone: 'text-amber-600', icon: CircleSlash, anim: '' } if (n.status === 'stopped') return shared('stopped') - return { label: n.outcome === 'unknown' ? 'no report' : n.status, tone: 'text-muted-foreground/70', icon: Check, anim: '' } + return { label: n.outcome === 'unknown' ? 'no report' : n.status, tone: 'text-muted-foreground/70', icon: CircleSmall, anim: '' } } /** Conversations with a live pane — what the Chain toggle badges when nothing needs a human and nothing