Commit Graph

71 Commits

  • fix(coding-agent): handle empty session titles and session info metadata (#2304)
    * fix(coding-agent): handle empty session titles in tree
    
    * fix(coding-agent): ignore session info in summaries
    
    * docs: update changelog for PR #2304
  • feat(session-manager): allow supplying custom session ID in newSession() (#2130)
    * feat(session-manager): allow supplying custom session ID in newSession()
    
    Add optional `id` field to `NewSessionOptions`. When provided, this ID
    is used as the session ID instead of generating a random UUID. Existing
    callers are unaffected since the field is optional and falls back to
    `randomUUID()`.
    
    Closes #2097
    
    * test(session-manager): add tests for custom session ID in newSession()
    
    Verify that newSession() uses the provided id when supplied, falls back
    to randomUUID() when omitted, and includes the custom id in the session
    header.
  • fix(coding-agent): prevent duplicate session headers when forking from pre-assistant entry
    createBranchedSession() wrote the file and set flushed=true even when the
    branched path had no assistant message. The next _persist() call saw no
    assistant, reset flushed=false, and the subsequent flush appended all
    in-memory entries to the already-populated file, duplicating the header
    and entries.
    
    Fix: defer file creation when the branched path has no assistant message,
    matching the newSession() contract. _persist() creates the file on the
    first assistant response.
    
    closes #1672
  • fix(coding-agent): persist user message after fork
    When hasAssistant guard returns early, set flushed=false so
    the next persist (after assistant arrives) writes all entries
    including the user message.
  • fix(coding-agent): fork writes to new session file, not parent (fixes #1242)
    - Store previousSessionFile before creating branched session
    - Update sessionFile after writing new branch file
    - Pass parentSession when forking from first message
    - Add --local to git config in tests to prevent repo escape
  • feat(coding-agent): add threaded sort mode to /resume with tree display
    Adds 'Threaded' as a new sort mode (now default) that displays sessions
    in a tree structure based on parent-child relationships. The tree is
    sorted by root session's modified date. Other sort modes (Recent, Fuzzy)
    continue to show flat lists.
    
    Sort mode cycles: Threaded -> Recent -> Fuzzy -> Threaded
  • Rename session from /resume session list (#863)
    * Add session renaming in interactive mode resume picker
    
    Session list now displays last message timestamp as modified time
    instead of file mtime. Ctrl+N enters rename mode in the interactive
    resume picker, allowing quick session renaming without leaving the
    selector. Rename hint is shown only in interactive mode, not in the
    CLI --resume picker./
    
    * Add docs entry for renaming in picker
    
    * Update shortcut to ctrl+r for session renaming
  • fix(session): improve session ID resolution with global search and fork support (#785)
    When using `--session <UUID>`, the session lookup now:
    
    1. Searches locally first (current project's session directory)
    2. Falls back to global search across all projects
    3. If found in different project, prompts user to fork the session
    4. If not found anywhere, shows clear error instead of silently creating
       a broken session with malformed path
    
    Adds `SessionManager.forkFrom()` to create a forked session from another
    project, preserving full conversation history with updated cwd.
  • feat(coding-agent): add session naming via /name command and extension API
    - Add SessionInfoEntry type for session metadata
    - Add /name <name> command to set session display name
    - Add pi.setSessionName() and pi.getSessionName() extension API
    - Session selector shows name (in warning color) instead of first message when set
    - Session name included in fuzzy search
    - /session command displays name when set
    
    closes #650
  • feat(coding-agent): add resume scope toggle with async loading
    - /resume and --resume now toggle between Current Folder and All sessions with Tab
    - SessionManager.list() and listAll() are now async with optional progress callback
    - Shows loading progress (e.g. Loading 5/42) while scanning sessions
    - SessionInfo.cwd field shows session working directory in All view
    - Lazy loading: All sessions only loaded when user presses Tab
    
    closes #619
    
    Co-authored-by: Thomas Mustier <mustierthomas@gmail.com>
  • feat(coding-agent): add resume scope toggle
    refactor(coding-agent): refine session listing helpers
  • Merge hooks and custom-tools into unified extensions system (#454)
    Breaking changes:
    - Settings: 'hooks' and 'customTools' arrays replaced with 'extensions'
    - CLI: '--hook' and '--tool' flags replaced with '--extension' / '-e'
    - API: HookMessage renamed to CustomMessage, role 'hookMessage' to 'custom'
    - API: FileSlashCommand renamed to PromptTemplate
    - API: discoverSlashCommands() renamed to discoverPromptTemplates()
    - Directories: commands/ renamed to prompts/ for prompt templates
    
    Migration:
    - Session version bumped to 3 (auto-migrates v2 sessions)
    - Old 'hookMessage' role entries converted to 'custom'
    
    Structural changes:
    - src/core/hooks/ and src/core/custom-tools/ merged into src/core/extensions/
    - src/core/slash-commands.ts renamed to src/core/prompt-templates.ts
    - examples/hooks/ and examples/custom-tools/ merged into examples/extensions/
    - docs/hooks.md and docs/custom-tools.md merged into docs/extensions.md
    
    New test coverage:
    - test/extensions-runner.test.ts (10 tests)
    - test/extensions-discovery.test.ts (26 tests)
    - test/prompt-templates.test.ts
  • Consolidate session events: remove session_before_new/session_new, add reason field to switch events
    - Remove session_before_new and session_new hook events
    - Add reason: 'new' | 'resume' to session_before_switch and session_switch events
    - Remove 'new' reason from custom tool onSession (use 'switch' for both /new and /resume)
    - Rename reset() to newSession(options?) in AgentSession
    - Add NewSessionOptions with optional parentSession for lineage tracking
    - Rename branchedFrom to parentSession in SessionHeader
    - Rename RPC reset command to new_session with optional parentSession
    - Update example hooks to use new event structure
    - Update documentation and changelog
    
    Based on discussion in #293
  • Rework custom tools API with CustomToolContext
    - CustomAgentTool renamed to CustomTool
    - ToolAPI renamed to CustomToolAPI
    - ToolContext renamed to CustomToolContext
    - ToolSessionEvent renamed to CustomToolSessionEvent
    - Added CustomToolContext parameter to execute() and onSession()
    - CustomToolFactory now returns CustomTool<any, any> for type compatibility
    - dispose() replaced with onSession({ reason: 'shutdown' })
    - Added wrapCustomTool() to convert CustomTool to AgentTool
    - Session exposes setToolUIContext() instead of leaking internals
    - Fix ToolExecutionComponent to sync with toolOutputExpanded state
    - Update all custom tool examples for new API
  • Add fromHook field to CompactionEntry and BranchSummaryEntry
    - fromHook: true = hook generated, skip file extraction
    - fromHook: undefined/false = pi generated, extract files (backward compatible)
    - branchWithSummary now accepts fromHook parameter
    - File extraction only runs for !entry.fromHook entries
  • Store file lists in BranchSummaryEntry.details for cumulative tracking
    - BranchSummaryResult now returns readFiles and modifiedFiles separately
    - BranchSummaryDetails type for details: { readFiles, modifiedFiles }
    - branchWithSummary accepts optional details parameter
    - Collect files from existing branch_summary.details when preparing entries
    - Files accumulate across nested branch summaries
  • Add ReadonlySessionManager and refactor branch summarization
    - Add ReadonlySessionManager interface to session-manager.ts
    - Re-export from hooks/index.ts
    - Add collectEntriesForBranchSummary() to extract entries for summarization
    - Don't stop at compaction boundaries (include their summaries as context)
    - Add token budget support to prepareBranchEntries()
    - Walk entries newest-to-oldest to prioritize recent context
    - Use options object for generateBranchSummary()
    - Handle compaction entries as context summaries
    - Export new types: CollectEntriesResult, GenerateBranchSummaryOptions
  • Add tree navigation tests and shared test utilities
    - Add test/utilities.ts with shared helpers (API_KEY, userMsg, assistantMsg, createTestSession)
    - Add agent-session-tree-navigation.test.ts with e2e tests for tree navigation
    - Add getChildren() method to SessionManager
    - Add summaryEntry to navigateTree return type
    - Update existing tests to use shared utilities
  • Fix branch summarization abort handling and tree navigation
    - Check response.stopReason instead of catching errors for abort detection
    - Return result object from _generateBranchSummary instead of throwing
    - Fix summary attachment: attach to navigation target position, not old branch
    - Support root-level summaries (parentId=null) in branchWithSummary
    - Remove setTimeout hack for re-showing tree selector on abort
  • feat(coding-agent): implement /tree command for session tree navigation
    - Add TreeSelectorComponent with ASCII tree visualization
    - Add AgentSession.navigateTree() for switching branches
    - Add session_before_tree/session_tree hook events
    - Add SessionManager.resetLeaf() for navigating to root
    - Change leafId from string to string|null for consistency with parentId
    - Support optional branch summarization when switching
    - Update buildSessionContext() to handle null leafId
    - Add /tree to slash commands in interactive mode
  • refactor(hooks): split session events into individual typed events
    Major changes:
    - Replace monolithic SessionEvent with reason discriminator with individual
      event types: session_start, session_before_switch, session_switch,
      session_before_new, session_new, session_before_branch, session_branch,
      session_before_compact, session_compact, session_shutdown
    - Each event has dedicated result type (SessionBeforeSwitchResult, etc.)
    - HookHandler type now allows bare return statements (void in return type)
    - HookAPI.on() has proper overloads for each event with correct typing
    
    Additional fixes:
    - AgentSession now always subscribes to agent in constructor (was only
      subscribing when external subscribe() called, breaking internal handlers)
    - Standardize on undefined over null throughout codebase
    - HookUIContext methods return undefined instead of null
    - SessionManager methods return undefined instead of null
    - Simplify hook exports to 'export type * from types.js'
    - Add detailed JSDoc for skipConversationRestore vs cancel
    - Fix createBranchedSession to rebuild index in persist mode
    - newSession() now returns the session file path
    
    Updated all example hooks, tests, and emission sites to use new event types.
  • Use exhaustive switch on message.role throughout coding-agent
    - addMessageToChat: exhaustive switch for all AgentMessage roles
    - renderSessionContext: delegates to addMessageToChat, special handling for assistant tool calls and tool results
    - export-html formatMessage: exhaustive switch for all AgentMessage roles
    - Removed isHookMessage, isBashExecutionMessage type guards in favor of role checks
    - Fixed imports and removed unused getLatestCompactionEntry
  • Fix hook message duplication in TUI
    Two bugs:
    1. createCustomMessage was returning role: 'user' instead of preserving
       the hook message structure (role: 'hookMessage', customType, etc.)
    2. rebuildChatFromMessages wasn't clearing the container before rebuilding
  • WIP: Refactor agent package - not compiling
    - Renamed AppMessage to AgentMessage throughout
    - New agent-loop.ts with AgentLoopContext, AgentLoopConfig
    - Removed transport abstraction, Agent now takes streamFn directly
    - Extracted streamProxy to proxy.ts utility
    - Removed agent-loop from pi-ai (now in agent package)
    - Updated consumers (coding-agent, mom) for AgentMessage rename
    - Tests updated but some consumers still need migration
    
    Known issues:
    - AgentTool, AgentToolResult not exported from pi-ai
    - Attachment not exported from pi-agent-core
    - ProviderTransport removed but still referenced
    - messageTransformer -> convertToLlm migration incomplete
    - CustomMessages declaration merging not working properly
  • Cleanup: unify HookMessage naming and simplify SessionContext
    - Rename HookAppMessage to HookMessage, isHookAppMessage to isHookMessage
    - Remove entries array from SessionContext (use isHookMessage type guard instead)
    - HookMessage.content now accepts string directly (not just array)
    - Fix streamMessage type in AgentState (AppMessage, not Message)
    - Rename CustomMessageComponent to HookMessageComponent
    - Fix test hook to use pi.sendMessage
  • Add CustomMessageEntry rendering infrastructure
    - Add renderCustomMessage to HookAPI for registering custom renderers
    - Add CustomMessageRenderer type and CustomMessageRenderOptions
    - Store customMessageRenderers in LoadedHook
    - Add getCustomMessageRenderer(customType) to HookRunner
    - SessionContext.entries now aligned with messages (same length, corresponding indices)
    
    TUI can now correlate messages with their source entries to identify
    custom_message entries and use hook-provided renderers.
  • Fix CustomMessageEntry content type to match UserMessage
    content: string | (TextContent | ImageContent)[]
    
    This matches the UserMessage type from pi-ai, so content can be
    passed directly to AppMessage without conversion.
  • Add CustomMessageEntry for hook-injected messages in LLM context
    - CustomMessageEntry<T> type with customType, content, display, details
    - appendCustomMessageEntry() in SessionManager
    - buildSessionContext() includes custom_message entries as user messages
    - Exported CustomEntry and CustomMessageEntry from main index
    
    CustomEntry is for hook state (not in context).
    CustomMessageEntry is for hook-injected content (in context).
  • Refactor SessionEventBase to pass sessionManager and modelRegistry
    Breaking changes to hook types:
    - SessionEventBase now passes sessionManager and modelRegistry directly
    - before_compact: passes preparation, previousCompactions (newest first)
    - before_switch: has targetSessionFile; switch: has previousSessionFile
    - Removed resolveApiKey (use modelRegistry.getApiKey())
    - getSessionFile() returns string | undefined for in-memory sessions
    
    Updated:
    - All session event emissions in agent-session.ts
    - Hook examples (custom-compaction.ts, auto-commit-on-exit.ts, confirm-destructive.ts)
    - Tests (compaction-hooks.test.ts, compaction-hooks-example.test.ts)
    - export-html.ts guards for in-memory sessions
  • Make CompactionEntry and CompactionResult generic with details field
    - CompactionEntry<T> and CompactionResult<T> now have optional details?: T
    - appendCompaction() accepts optional details parameter
    - Hooks can return compaction.details to store custom data
    - Enables structured compaction with ArtifactIndex (see #314)
    - Fix CompactionResult export location (now from compaction.ts)
    - Update plan with remaining compaction refactor items
  • Improve CustomEntry docs and make it generic
    - Add detailed doc comment explaining purpose (hook state persistence)
    - Make CustomEntry<T = unknown> generic
    - Clarify difference from CustomMessageEntry in plan
    - Update changelog
  • Add label support for session entries
    - Add LabelEntry type with targetId and label (string | undefined)
    - Add labelsById map built on load via linear scan
    - Add getLabel(id) and appendLabelChange(targetId, label) methods
    - Add label field to SessionTreeNode, populated by getTree()
    - Update createBranchedSession to preserve labels for entries on path
    - Labels are ignored by buildSessionContext (not sent to LLM)
    - Add comprehensive tests for label functionality