Commit Graph

60 Commits

Author SHA1 Message Date
teernisse
ac629bd149 refactor(dashboard): remove unused Header and SessionGroup components
Remove components that are no longer part of the dashboard architecture:

Header.js:
- Original header component replaced by integrated spawn controls
- SpawnModal now handles project selection and agent spawning
- Sidebar provides navigation context previously in header

SessionGroup.js:
- Session grouping now handled directly in App.js
- Simplified rendering without intermediate wrapper component
- groupSessionsByProject utility provides the grouping logic

These components were bypassed during the spawn feature implementation
and maintaining them increases cognitive overhead without benefit.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-28 00:48:34 -05:00
teernisse
fa1fe8613a feat(dashboard): wire skills autocomplete configuration to session cards
Connect the skills enumeration API to session card input fields for
slash command autocomplete:

App.js:
- Add skillsConfig state for Claude and Codex skill configs
- Fetch skills for both agent types on mount using Promise.all
- Pass agent-appropriate autocompleteConfig to each SessionCard

SessionCard.js:
- Accept autocompleteConfig prop and forward to SimpleInput
- Move context usage display from header to footer status bar for
  better information hierarchy (activity indicator + context together)

SimpleInput.js:
- Fix autocomplete dropdown padding (py-2 -> py-1.5)
- Fix font inheritance (add font-mono to skill name)
- Fix description tooltip whitespace handling (add font-sans,
  whitespace-normal)

SpawnModal.js:
- Add SPAWN_TIMEOUT_MS (2x default) to handle pending spawn registry
  wait time plus session file confirmation polling

AgentActivityIndicator.js:
- Minor styling refinement for status display

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-28 00:48:26 -05:00
teernisse
d3c6af9b00 test(skills): add frontmatter name override tests
Add comprehensive test coverage for skill name resolution priority:

- test_frontmatter_name_overrides_dir_name: Verifies that the 'name'
  field in YAML frontmatter takes precedence over directory name,
  enabling namespaced skills like "doodle:bug-hunter"

- test_name_preserved_across_fallback_files: Confirms that when SKILL.md
  provides a name but README.md provides the description, the name from
  SKILL.md is preserved (important for multi-file skill definitions)

- test_no_frontmatter_name_uses_dir_name: Validates fallback behavior
  when no name is specified in frontmatter

This ensures the skill autocomplete system correctly handles both
simple directory-named skills and explicitly namespaced skills from
skill packs.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-28 00:47:49 -05:00
teernisse
0acc56417e test(spawn): add pending spawn registry and timestamp parsing tests
Extend spawn test coverage with verification of Codex session correlation
mechanisms:

Pending spawn registry tests:
- _write_pending_spawn creates JSON file with correct spawn_id,
  project_path, agent_type, and timestamp
- _cleanup_stale_pending_spawns removes files older than PENDING_SPAWN_TTL
  while preserving fresh files

Session timestamp parsing tests:
- Handles ISO 8601 with Z suffix (Zulu time)
- Handles ISO 8601 with explicit +00:00 offset
- Returns None for invalid format strings
- Returns None for empty string input

These tests ensure spawn_id correlation works correctly for Codex
sessions, which don't have direct environment variable injection like
Claude sessions do.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-28 00:47:40 -05:00
teernisse
19a31a4620 feat(state): add same-pane session deduplication
Handle edge case where Claude's --resume creates an orphan session file
before resuming the original session, leaving two session files pointing
to the same Zellij pane.

The deduplication algorithm (_dedupe_same_pane_sessions) resolves
conflicts by preferring:
1. Sessions with context_usage (indicates actual conversation occurred)
2. Higher conversation_mtime_ns (more recent file activity)

When an orphan is identified, its session file is deleted from disk to
prevent re-discovery on subsequent state collection cycles.

Test coverage includes:
- Keeping session with context_usage over one without
- Keeping higher mtime when both have context_usage
- Keeping higher mtime when neither has context_usage
- Preserving sessions on different panes (no false positives)
- Single session per pane unchanged
- Sessions without pane info unchanged
- Handling non-numeric mtime values defensively

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-28 00:47:28 -05:00
teernisse
1fb4a82b39 refactor(server): extract context.py into focused modules
Split the monolithic context.py (117 lines) into five purpose-specific
modules following single-responsibility principle:

- config.py: Server-level constants (DATA_DIR, SESSIONS_DIR, PORT,
  STALE_EVENT_AGE, _state_lock)
- agents.py: Agent-specific paths and caches (CLAUDE_PROJECTS_DIR,
  CODEX_SESSIONS_DIR, discovery caches)
- auth.py: Authentication token generation/validation for spawn endpoint
- spawn_config.py: Spawn feature configuration (PENDING_SPAWNS_DIR,
  rate limiting, projects watcher thread)
- zellij.py: Zellij binary resolution and session management constants

This refactoring improves:
- Code navigation: Find relevant constants by domain, not alphabetically
- Testing: Each module can be tested in isolation
- Import clarity: Mixins import only what they need
- Future maintenance: Changes to one domain don't risk breaking others

All mixins updated to import from new module locations. Tests updated
to use new import paths.

Includes PROPOSED_CODE_FILE_REORGANIZATION_PLAN.md documenting the
rationale and mapping from old to new locations.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-28 00:47:15 -05:00
teernisse
69175f08f9 fix(spawn): strip ANSI codes from zellij list-sessions output
Zellij outputs colored text with ANSI escape codes, which caused
session name parsing to fail. Now strips escape codes before parsing.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-26 17:27:56 -05:00
teernisse
f26e9acb34 chore(beads): close final spawn feature beads
All spawn feature beads completed:
- bd-zgt: Spawn Modal UI verification (dropdown implementation)
- bd-3g8: Polish and edge cases (validation, feedback, warnings)

Feature complete: agent spawning with autocomplete.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-26 17:18:37 -05:00
teernisse
baa712ba15 refactor(dashboard): change SpawnModal from overlay modal to dropdown
Position the spawn modal directly under the 'New Agent' button without a
blur overlay. Uses click-outside dismissal and absolute positioning.
Reduces visual disruption for quick agent spawning.
2026-02-26 17:15:22 -05:00
teernisse
7a9d290cb9 test(spawn): verify Zellij metadata for spawned agents 2026-02-26 17:10:41 -05:00
teernisse
1e21dd08b6 feat(dashboard): add visual feedback for spawned agents 2026-02-26 17:09:52 -05:00
teernisse
e3e42e53f2 fix(spawn): handle special characters in project names
- Reject null bytes and control characters (U+0000-U+001F, U+007F) in
  _validate_spawn_params with explicit INVALID_PROJECT error
- Reject whitespace-only project names as MISSING_PROJECT
- Reject non-string project names (int, list, etc.)
- Add _sanitize_pane_name() to clean Zellij pane names: replaces quotes,
  backticks, and control chars with underscores; collapses whitespace;
  truncates to 64 chars
- Add 44 new tests: safe chars (hyphens, spaces, dots, @, +, #),
  dangerous chars (null byte, newline, tab, ESC, DEL), shell
  metacharacters ($, ;, backtick, |), pane name sanitization, and
  spawn command construction with special char names

Closes bd-14p
2026-02-26 17:09:51 -05:00
teernisse
99a55472a5 test(e2e): add spawn workflow end-to-end tests 2026-02-26 17:09:49 -05:00
teernisse
5494d76a98 feat(dashboard): add Zellij unavailable warning banner 2026-02-26 17:09:30 -05:00
teernisse
c0ee053d50 chore(beads): close bd-3c7 empty projects edge case 2026-02-26 17:08:37 -05:00
teernisse
8070c4132a fix(spawn): handle empty or missing projects directory
- Add empty-state message in SpawnModal when no projects found
- Spawn button stays disabled when projects list is empty
- Server already handled OSError/missing dir gracefully (returns [])
- Add tests: missing directory, only-hidden-dirs, empty API responses

Closes: bd-3c7
2026-02-26 17:08:33 -05:00
teernisse
2d65d8f95b feat(server): integrate SpawnMixin into AMCHandler 2026-02-26 17:07:35 -05:00
teernisse
37748cb99c test(e2e): add autocomplete workflow tests 2026-02-26 17:04:11 -05:00
teernisse
9695e9b08a feat(server): add spawn feature startup initialization
- Import and call load_projects_cache() to populate cache before requests
- Import and call generate_auth_token() to create one-time auth token
- Import and call start_projects_watcher() for background cache refresh
- Inject auth token into dashboard HTML via placeholder replacement
- Add AMC_AUTH_TOKEN placeholder in index.html head
2026-02-26 17:03:20 -05:00
teernisse
58f0befe72 feat(dashboard): integrate SpawnModal with header button 2026-02-26 17:03:14 -05:00
teernisse
62d23793c4 feat(server): add spawn API HTTP routes
Add routing for spawn-related endpoints to HttpMixin:
- GET /api/projects -> _handle_projects
- GET /api/health -> _handle_health
- POST /api/spawn -> _handle_spawn
- POST /api/projects/refresh -> _handle_projects_refresh

Update CORS preflight (AC-39) to include GET in allowed methods
and Authorization in allowed headers.

Closes bd-2al
2026-02-26 17:03:00 -05:00
teernisse
7b1e47adc0 feat(dashboard): implement SpawnModal component 2026-02-26 17:00:51 -05:00
teernisse
a7a9ebbf2b feat(server): implement SpawnMixin for agent spawning
Implement amc_server/mixins/spawn.py with:
- _handle_spawn: POST /api/spawn handler with auth, validation, rate limiting
- _validate_spawn_params: path traversal/symlink escape protection
- _check_zellij_session_exists: session availability check
- _wait_for_session_file: poll for spawn correlation via spawn_id
- _spawn_agent_in_project_tab: Zellij tab creation + pane spawn
- _handle_projects: GET cached project list
- _handle_projects_refresh: POST to refresh cache
- _handle_health: Zellij availability check
- load_projects_cache: module-level project directory scanner

Closes bd-5m4
2026-02-26 17:00:49 -05:00
teernisse
48c3ddce90 test(dashboard): add autocomplete trigger/filter tests 2026-02-26 17:00:19 -05:00
teernisse
7059dea3f8 test(skills): add comprehensive SkillsMixin tests 2026-02-26 16:59:34 -05:00
teernisse
e4a0631fd7 feat(hook): add spawn_id correlation to amc-hook
Read AMC_SPAWN_ID env var and include spawn_id in session JSON when
present. This enables deterministic spawn correlation: the server
generates a UUID, passes it via env to the spawned agent, and then
polls for a session file containing that specific spawn_id.

Closes: bd-1zy
2026-02-26 16:58:20 -05:00
teernisse
1bece476a4 feat(server): add spawn constants to context.py 2026-02-26 16:58:02 -05:00
teernisse
e99ae2ed89 feat(dashboard): add spawn API constants 2026-02-26 16:57:56 -05:00
teernisse
49a57b5364 feat(dashboard): scroll selected autocomplete item into view
Closes bd-4lc. When navigating with arrow keys, the selected item now
scrolls into view using scrollIntoView({ block: 'nearest' }) to prevent
jarring scrolls when item is already visible.
2026-02-26 16:55:34 -05:00
teernisse
db3d2a2e31 feat(dashboard): add click-outside dismissal for autocomplete dropdown
Closes bd-3ny. Added mousedown listener that dismisses the dropdown when
clicking outside both the dropdown and textarea. Uses early return to avoid
registering listeners when dropdown is already closed.
2026-02-26 16:54:40 -05:00
teernisse
ba16daac2a feat(dashboard): complete autocomplete UI with dropdown and keyboard navigation
- Add triggerInfo state and filteredSkills useMemo
- Add showAutocomplete, selectedIndex state management
- Implement insertSkill callback for skill insertion
- Add full keyboard navigation (ArrowUp/Down, Enter/Tab, Escape)
- Wrap textarea in relative container for dropdown positioning
- Add autocomplete dropdown UI with empty states and mouse interaction

Closes: bd-29o, bd-1y3, bd-3vd, bd-2uj, bd-3us, bd-253

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-26 16:52:36 -05:00
teernisse
c7db46191c feat(dashboard): add skill autocomplete server-side enumeration and client wiring
- Add SkillsMixin with _enumerate_claude_skills and _enumerate_codex_skills
- Claude: reads ~/.claude/skills/, parses YAML frontmatter for descriptions
- Codex: reads curated cache + ~/.codex/skills/ user directory
- Add /api/skills?agent= endpoint to HttpMixin
- Add fetchSkills() API helper in dashboard
- Wire autocomplete config through Modal -> SessionCard -> SimpleInput
- Add getTriggerInfo() for detecting trigger at valid positions

Closes: bd-3q1, bd-sv1, bd-3eu, bd-g9t, bd-30p, bd-1ba, bd-2n7, bd-3s3

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-26 16:48:23 -05:00
teernisse
2926645b10 docs: add implementation plans for upcoming features
Planning documents for future AMC features:

PLAN-slash-autocomplete.md:
- Slash-command autocomplete for SimpleInput
- Skills API endpoint, SlashMenu dropdown, keyboard navigation
- 8 implementation steps with file locations and dependencies

plans/agent-spawning.md:
- Agent spawning acceptance criteria documentation
- Spawn command integration, status tracking, error handling
- Written as testable acceptance criteria (AC-1 through AC-10)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-26 15:25:09 -05:00
teernisse
6e566cfe82 chore: add .gitignore for bv cache directory
Ignore .bv/ directory which contains local cache and configuration
for the bv (beads viewer) graph-aware triage tool.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-26 15:25:02 -05:00
teernisse
117784f8ef chore: initialize beads project tracking
Add .beads/ directory for dependency-aware issue tracking with the
beads (br) CLI. This provides lightweight task management for the
AMC project.

Files:
- config.yaml: Project configuration (prefix, priorities, types)
- issues.jsonl: Issue database in append-only JSONL format
- metadata.json: Project metadata and statistics
- .gitignore: Ignore database files, locks, and temporaries

Current issues track slash-command autocomplete feature planning
(fetchSkills API, SlashMenu component, hook integration, etc.)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-26 15:24:57 -05:00
teernisse
de994bb837 feat(dashboard): add markdown preview for AskUserQuestion options
Implement side-by-side preview layout when AskUserQuestion options
include markdown content, matching the Claude Code tool spec.

Hook changes (bin/amc-hook):
- Extract optional 'markdown' field from question options
- Only include when present (maintains backward compatibility)

QuestionBlock layout:
- Detect hasMarkdownPreviews via options.some(opt => opt.markdown)
- Standard layout: vertical option list (unchanged)
- Preview layout: 40% options left, 60% preview pane right
- Fixed 400px preview height prevents layout thrashing on hover
- Track previewIndex state, update on mouseEnter/focus

Content rendering (smart detection):
- Code fences (starts with ```): renderContent() for syntax highlighting
- Everything else: raw <pre> to preserve ASCII diagrams exactly
- "No preview for this option" when hovered option lacks markdown

OptionButton enhancements:
- Accept selected, onMouseEnter, onFocus props
- Visual selected state: brighter border/bg with subtle shadow
- Compact padding (py-2 vs py-3.5) for preview layout density
- Graceful undefined handling for standard layout usage

Bug fixes during development:
- Layout thrashing: Fixed height (not max-height) on preview pane
- ASCII corruption: Detect code fences vs plain text for render path
- Horizontal overflow: Use overflow-auto (not just overflow-y-auto)
- Data pipeline: Hook was stripping markdown field (root cause)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-26 15:24:35 -05:00
teernisse
b9c1bd6ff1 feat(dashboard): improve real-time updates and scroll behavior
Major UX improvements to conversation display and state management.

Scroll behavior (SessionCard.js):
- Replace scroll-position tracking with wheel-event intent detection
- Accumulate scroll-up distance before disabling sticky mode (50px threshold)
- Re-enable sticky on scroll-down when near bottom (100px threshold)
- Always scroll to bottom on first load or user's own message submission
- Use requestAnimationFrame for smooth scroll positioning

Optimistic updates (App.js):
- Immediately show user messages before API confirmation
- Remove optimistic message on send failure
- Eliminates perceived latency when sending responses

Error tracking integration (App.js):
- Wire up trackError/clearErrorCount for API operations
- Track: state fetch, conversation fetch, respond, dismiss, SSE init/parse
- Clear error counts on successful operations
- Clear SSE event cache on reconnect to force refresh

Conversation management (App.js):
- Use mtime_ns (preferred) or last_event_at for change detection
- Clean up conversation cache when sessions are dismissed
- Add modalSessionRef for stable reference across renders

Message stability (ChatMessages.js):
- Prefer server-assigned message IDs for React keys
- Fallback to role+timestamp+index for legacy messages

Input UX (SimpleInput.js):
- Auto-refocus textarea after successful submission
- Use setTimeout to ensure React has re-rendered first

Sorting simplification (status.js):
- Remove status-based group/session reordering
- Return groups in API order (server handles sorting)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-26 15:24:21 -05:00
teernisse
3dc10aa060 feat(dashboard): add toast notification system with error tracking
Add lightweight toast notification infrastructure that surfaces
repeated errors to users while avoiding alert fatigue.

Components:
- ToastContainer: Renders toast notifications with auto-dismiss
- showToast(): Singleton function to display messages from anywhere
- trackError(): Counts errors by key, surfaces toast after threshold

Error tracking strategy:
- Errors logged immediately (console.error)
- Toast shown only after 3+ occurrences within 30-second window
- Prevents spam from transient network issues
- Reset counter on success (clearErrorCount)

Toast styling:
- Fixed position bottom-right with z-index 100
- Type-aware colors (error=red, success=green, info=yellow)
- Manual dismiss button with auto-dismiss after 5 seconds
- Backdrop blur and slide-up animation

This prepares the dashboard to gracefully handle API failures
without overwhelming users with error popups for transient issues.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-26 15:24:06 -05:00
teernisse
0d15787c7a test(server): add unit tests for conversation mtime tracking
Comprehensive test coverage for _get_conversation_mtime() and its
integration with _collect_sessions().

Test cases:
- Claude sessions: file exists, file missing, OSError on stat,
  missing project_dir, missing session_id
- Codex sessions: transcript_path exists, transcript_path missing,
  discovery fallback, discovery returns None, OSError on stat
- Edge cases: unknown agent type, missing agent key
- Integration: mtime included when file exists, omitted when missing
- Change detection: mtime changes trigger payload hash changes

Uses mock patching to isolate file system interactions and test
error handling paths without requiring actual conversation files.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-26 15:23:55 -05:00
teernisse
dcbaf12f07 feat(server): add conversation mtime tracking for real-time updates
Add conversation_mtime_ns field to session state that tracks the actual
modification time of conversation files. This enables more responsive
dashboard updates by detecting changes that occur between hook events
(e.g., during streaming tool execution).

Changes:
- state.py: Add _get_conversation_mtime() to stat conversation files
  and include mtime_ns in session payloads when available
- conversation.py: Add stable message IDs (claude-{session}-{n} format)
  for React key stability and message deduplication
- control.py: Fix FIFO eviction for dismissed_codex_ids - set.pop()
  removes arbitrary element, now uses dict with insertion-order iteration
- context.py: Update dismissed_codex_ids type from set to dict

The mtime approach complements existing last_event_at tracking:
- last_event_at: Changes on hook events (session boundaries)
- conversation_mtime_ns: Changes on every file write (real-time)

Dashboard can now detect mid-session conversation updates without
waiting for the next hook event.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-26 15:23:42 -05:00
teernisse
fa1ad4b22b unify card/modal 2026-02-26 11:39:39 -05:00
teernisse
31862f3a40 perf(dashboard): optimize CSS transitions and add entrance animations
Performance and polish improvements across dashboard components:

Transition optimizations (reduces reflow/repaint overhead):
- OptionButton: transition-all → transition-[transform,border-color,
  background-color,box-shadow]
- QuestionBlock: Add transition-colors to textarea, transition-all →
  transition-[transform,filter] on send button
- SimpleInput: Same pattern as QuestionBlock
- Sidebar: transition-all → transition-colors for project buttons

Animation additions:
- App: Add animate-fade-in-up to loading and error state containers
- MessageBubble: Make fade-in-up animation conditional on non-compact
  mode to avoid animation spam in card preview

Using specific transition properties instead of transition-all tells
the browser exactly which properties to watch, avoiding unnecessary
style recalculation on unrelated property changes.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-25 16:32:28 -05:00
teernisse
4740922b8d refactor(dashboard): simplify chat rendering and add scroll behavior
Removes unnecessary complexity from ChatMessages while adding proper
scroll management to SessionCard:

ChatMessages.js:
- Remove scroll position tracking refs and effects (wasAtBottomRef,
  prevMessagesLenRef, containerRef)
- Remove spinner display logic (moved to parent components)
- Simplify to pure message filtering and rendering
- Add display limit (last 20 messages) with offset tracking for keys

SessionCard.js:
- Add chatPaneRef for scroll container
- Add useEffect to scroll to bottom when conversation updates
- Provides natural "follow" behavior for new messages

The refactor moves scroll responsibility to the component that owns
the scroll container, reducing prop drilling and effect complexity.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-25 16:32:08 -05:00
teernisse
8578a19330 feat(dashboard): add smooth modal entrance/exit animations
Implements animated modal open/close with accessibility support:

- Add closing state with 200ms exit animation before unmount
- Refactor to React hooks-compliant structure (guards after hooks)
- Add CSS keyframes for backdrop fade and panel scale+translate
- Include prefers-reduced-motion media query to disable animations
  for users with vestibular sensitivities
- Use handleClose callback wrapper for consistent animation behavior
  across Escape key, backdrop click, and close button

The animations provide visual continuity without being distracting,
and gracefully degrade to instant transitions when reduced motion
is preferred.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-25 16:32:00 -05:00
teernisse
754e85445a test(server): add unit tests for context, control, and state mixins
Adds comprehensive test coverage for the amc_server package:

- test_context.py: Tests _resolve_zellij_bin preference order (which
  first, then fallback to bare name)
- test_control.py: Tests SessionControlMixin including two-step Enter
  injection with configurable delay, freeform response handling,
  plugin inject with explicit session/pane targeting, and unsafe
  fallback behavior
- test_state.py: Tests StateMixin zellij session parsing with the
  resolved binary path

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-25 16:31:51 -05:00
teernisse
183942fbaa fix(dashboard): spinner animation polish and accessibility
Polish the working indicator animations:

- Use cubic-bezier easing for smoother spinner rotation
- Add will-change hints for GPU acceleration
- Add display: inline-block to bounce-dot spans (required for transform)
- Add prefers-reduced-motion media query to disable animations for
  motion-sensitive users (accessibility)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-25 15:36:36 -05:00
teernisse
2f80995f8d fix(dashboard): robust tool call display and filter logic
Two fixes for tool call display in the dashboard:

1. **filterDisplayMessages includes tool_calls** (MessageBubble.js)
   Previously filtered out messages with only tool_calls (no content/thinking).
   Now correctly keeps messages that have tool_calls.

2. **Type-safe getToolSummary** (markdown.js)
   The heuristic tool summary extractor was calling .slice() without
   type checks. If a tool input had a non-string value (e.g., number),
   it would throw TypeError. Now uses a helper function to safely
   check types before calling string methods.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-25 15:36:26 -05:00
teernisse
8224acbba7 feat(dashboard): add working indicator for active sessions
Visual feedback when an agent is actively processing:

1. **Spinner on status dots** (SessionCard.js, Modal.js)
   - Status dot gets a spinning ring animation when session is active/starting
   - Uses CSS border trick with transparent borders except top

2. **Working indicator in chat** (ChatMessages.js, Modal.js)
   - Shows at bottom of conversation when agent is working
   - Bouncing dots animation ("...") next to "Agent is working" text
   - Only visible for active/starting statuses

3. **CSS animations** (styles.css)
   - spin-ring: 0.8s rotation for the status dot border
   - bounce-dot: staggered vertical bounce for the working dots

4. **Status metadata** (status.js)
   - Added `spinning: true` flag for active and starting statuses
   - Used by components to conditionally render spinner elements

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-25 15:20:26 -05:00
teernisse
7cf51427b7 fix(codex): parse tool calls and reasoning in conversation API
The Codex conversation parser was only handling "message" payload types,
missing tool calls entirely. Codex uses separate response_items:

  - function_call: tool invocations with name, arguments, call_id
  - reasoning: thinking summaries (encrypted content, visible summary)
  - message: user/assistant text (previously the only type handled)

Changes:
- Parse function_call payloads and accumulate as tool_calls array
- Attach tool_calls to the next assistant message, or flush standalone
- Parse reasoning payloads and extract summary text as thinking
- Add _parse_codex_arguments() helper to handle JSON string arguments

This fixes the dashboard not showing Codex tool calls like exec_command,
read_file, etc.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-25 15:20:18 -05:00
teernisse
be2dd6a4fb fix(zellij): robust binary resolution and two-step Enter injection
Two reliability fixes for response injection:

1. **Zellij binary resolution** (context.py, state.py, control.py)
   
   When AMC is started via macOS launchctl, PATH is minimal and may not
   include Homebrew's bin directory. The new `_resolve_zellij_bin()`
   function tries `shutil.which("zellij")` first, then falls back to
   common installation paths:
   - /opt/homebrew/bin/zellij (Apple Silicon Homebrew)
   - /usr/local/bin/zellij (Intel Homebrew)
   - /usr/bin/zellij
   
   All subprocess calls now use ZELLIJ_BIN instead of hardcoded "zellij".

2. **Two-step Enter injection** (control.py)
   
   Previously, text and Enter were sent together, causing race conditions
   where Claude Code would receive only the Enter key (blank submit).
   Now uses `_inject_text_then_enter()`:
   - Send text (without Enter)
   - Wait for configurable delay (default 200ms)
   - Send Enter separately
   
   Delay is configurable via AMC_SUBMIT_ENTER_DELAY_MS env var (0-2000ms).

3. **Documentation updates** (README.md)
   
   - Update file table: dashboard-preact.html → dashboard/
   - Clarify plugin is required (not optional) for pane-targeted injection
   - Document AMC_ALLOW_UNSAFE_WRITE_CHARS_FALLBACK env var
   - Note about Zellij resolution for launchctl compatibility

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-25 15:20:08 -05:00