Standardize project name to lowercase 'g3' throughout documentation,
comments, and configuration files. Environment variables (G3_*) are
unchanged as they follow the uppercase convention.
Improve readability of stream_completion_with_tools (~1000 line function):
- Add deduplicate_tool_calls() helper with closure for previous-message check
- Add should_auto_continue() with AutoContinueReason enum for clearer control flow
- Replace inline deduplication loop with helper call (-19 lines)
- Replace complex auto-continue conditional with match on reason enum (-13 lines)
- Add section comments for major phases (State Init, Pre-loop, Main Loop, Auto-Continue, Post-Loop)
- Add comprehensive tests for new helpers
Net reduction: 82 deletions, behavior unchanged (172+ tests pass)
Agent: carmack
When the streaming parser encountered fragments of JSON that looked like
partial tool calls (e.g., {"tool":) embedded in inline text (like code
examples or prose), it would incorrectly enter JSON parsing mode and
poison the parser state, causing control to be returned to the user
mid-task.
This fix:
- Adds sanitize_inline_tool_patterns() to detect tool-call patterns that
are NOT on their own line and replace the opening brace with a Unicode
homoglyph (fullwidth left curly bracket U+FF5B)
- Integrates sanitization into process_chunk() before text is buffered
- Updates system prompts to instruct LLMs to use homoglyphs when showing
example tool call JSON in prose
- Adds comprehensive tests for the sanitization logic
Real tool calls from LLMs always appear on their own line, so those are
left untouched. Only inline patterns (with non-whitespace before them)
are sanitized.
When the LLM reads the same file multiple times in sequence (scrolling
through a large file), instead of showing each as a separate line:
● read_file | path [0..2000] | 50 lines | 100 ◉ 5ms
● read_file | path [2000..4000] | 50 lines | 100 ◉ 5ms
● read_file | path [4000..6000] | 50 lines | 100 ◉ 5ms
Now shows a cleaner continuation format:
● read_file | path [0..2000] | 50 lines | 100 ◉ 5ms
└─ reading further [2000..4000] | 50 lines | 100 ◉ 5ms
└─ reading further [4000..6000] | 50 lines | 100 ◉ 5ms
This makes it visually clear that the agent is scrolling through
a single file rather than reading multiple different files.
Implementation:
- Added last_read_file_path field to ConsoleUiWriter
- Detect when consecutive read_file calls target the same file
- Print continuation format for subsequent reads
- Reset tracking when:
- A different tool is executed (shell, write_file, etc.)
- A different file is read
- Text is output between tool calls
Agent: hopper
Adds 56 new integration tests covering the observable end-of-turn
behaviors in the streaming module:
- Timing footer formatting (5 tests): verifies user-facing timing display
with various durations, token counts, and context percentages
- Tool call duplicate detection (6 tests): ensures identical sequential
tool calls are detected while different tools/args are not
- Empty response detection (9 tests): validates detection of empty,
whitespace-only, and timing-only responses that trigger auto-continue
- Connection error classification (5 tests): verifies EOF, connection,
chunk, and body errors are correctly identified for graceful recovery
- Tool output summary formatting (17 tests): covers read_file, write_file,
str_replace, remember, screenshot, coverage, and rehydrate summaries
- Duration formatting (4 tests): milliseconds, seconds, minutes, zero
- Text truncation (4 tests): short/long strings, multiline, flag behavior
- LLM token cleaning (3 tests): removal of stop tokens like <|im_end|>
- Edge cases (4 tests): empty inputs, unicode handling, large numbers
All tests are blackbox/characterization style - they test observable
outputs through stable public interfaces without encoding internal
implementation details. Tests remain stable under refactoring that
preserves behavior.
The truncate_for_display() function now takes only the first line
of input before truncating. This prevents multi-line error messages
(like str_replace failures) from breaking the compact single-line
format.
Added tests for multi-line input handling.
1. architecture.md: Fixed diagram to show 'studio' instead of 'g3-console'
(the crate was renamed during development)
2. analysis/memory.md: Removed reference to non-existent machine_ui_writer.rs
3. theme.rs: Clarified that 'retro' is a theme option (the default theme),
not a separate TUI mode. No --retro CLI flag exists.
Agent: lamport
Changes:
- docs/architecture.md: Replace non-existent g3-console with studio crate,
remove references to non-existent retro_tui.rs, update g3-cli module list
to reflect actual source files, fix execution modes list
- docs/tools.md: Add missing Research & Memory Tools section documenting
research, remember, and rehydrate tools with examples and notes
- AGENTS.md: Fix error logs path from logs/errors/ to .g3/errors/
- README.md: Remove references to non-existent CONTRIBUTING.md and LICENSE
All documentation links verified working.
The write_file compact display was showing 1 line because it was
counting lines in the success message, not the actual written content.
Now parses the tool result (e.g. '✅ wrote 150 lines | 4.2k chars')
to extract and display the correct counts.
Added format_write_file_result() to parse the tool output.
Adds blank line separation between text and tool calls for better readability:
- Text → Tool: blank line before tool call
- Tool → Text: blank line before text
- Tool → Tool: no gap (stays tight)
Implemented via two state tracking flags in ConsoleUiWriter:
- last_output_was_text
- last_output_was_tool
Updated print_tool_output_header(), print_tool_compact(), and
print_agent_response() to check and set these flags appropriately.
- Move run_flock_mode() to autonomous.rs (parallel execution mode belongs with autonomous code)
- Move initialize_logging() to utils.rs (utility function with simple bool parameter)
- lib.rs reduced from 274 to 216 lines
No behavior changes. All 28 unit tests pass.
Agent: fowler
When compact tools (read_file, write_file, str_replace, etc.) failed,
they would fall through to the non-compact output path, causing:
- Missing or incorrect headers
- Stray footers with wrong formatting
- State leakage (is_shell_compact) between tool calls
Now failed compact tools display in the same single-line format as
successful ones, just with a truncated error message instead of the
success summary:
● read_file | path/to/file.txt | ❌ Failed to read file... | 123 ◉ 0ms
This keeps the UI consistent and avoids the "stray footer" bug.
The LLM often hallucinates incorrect paths like /Users/jnbrymn/GitHub/g3
when the actual working directory is different. This causes wasted tokens
and failed commands as the LLM tries cd commands that fail.
Fix: Add the current working directory to the combined project content
that gets injected into the context at startup. This appears as:
📂 Working Directory: /actual/path/to/workspace
This is prepended before AGENTS.md, README.md, and project memory,
so the LLM knows the correct path from the start.
This change removes the legacy logs/ directory and consolidates all
session data, error logs, and discovery files under the .g3/ directory.
New directory structure:
- .g3/sessions/<session_id>/session.json - session logs
- .g3/errors/ - error logs (was logs/errors/)
- .g3/background_processes/ - background process logs
- .g3/discovery/ - planner discovery files (was workspace/logs/)
Changes:
- paths.rs: Remove get_logs_dir()/logs_dir(), add get_errors_dir(),
get_background_processes_dir(), get_discovery_dir()
- session.rs: Anonymous sessions now use .g3/sessions/anonymous_<ts>/
- error_handling.rs: Errors now saved to .g3/errors/
- project.rs: Remove logs_dir() and ensure_logs_dir() methods
- feedback_extraction.rs: Remove logs_dir field and fallback logic
- planner: Use .g3/ for workspace data and .g3/discovery/ for reports
- flock.rs: Look for session metrics in .g3/sessions/
- coach_feedback.rs: Remove fallback to logs/ path
- Update all tests to use new paths
- Update README.md and .gitignore
Implement compact display format for read_file, write_file, str_replace, and shell:
- read_file/write_file/str_replace: Single line with dimmed summary and timing
Format: ● tool_name | path [range] | summary | tokens ◉ time
- shell: Two-line format with command header and dimmed output
Format: ● shell | command
└─ output (N lines) | tokens ◉ time
Changes:
- Add print_tool_compact() method to UiWriter trait
- Add is_shell_compact state tracking in ConsoleUiWriter
- Add format_write_file_summary() and format_str_replace_summary() helpers
- Fix duplicate response output by checking if response is empty before printing
- Add finish_streaming_markdown() call before return to flush markdown buffer
Agent: hopper
Added 4 new test files with blackbox/characterization-style integration tests:
- compaction_behavior_test.rs (14 tests): Token cap calculation, thinking mode
disable logic, summary message building, CompactionResult behavior
- retry_behavior_test.rs (17 tests): RetryConfig presets and customization,
RetryResult state handling, retry_operation behavior with simulated errors
- tool_execution_roundtrip_test.rs (16 tests): End-to-end tool execution through
Agent interface for read_file, write_file, shell, str_replace, and TODO tools
- error_classification_test.rs (25 tests): Recoverable vs non-recoverable error
classification, retry delay calculation, edge cases and priority handling
All tests follow integration-first philosophy:
- Test through stable public interfaces
- Assert observable behavior, not implementation details
- Use characterization style to document current behavior
- Enable refactoring by not encoding internal structure
Add ANSI clear-to-end-of-line escape sequence (\x1b[K]) after the
reset code in the context thinning animation. This prevents leftover
background color artifacts when the carriage return overwrites the
line during the flash animation.
Project memory is now stored at analysis/memory.md instead of .g3/memory.md.
This change enables:
- Shared memory across git worktrees (studio agent sessions)
- Version-controlled memory that persists across clones
- Memory changes tracked in git history and reviewable in PRs
Changes:
- crates/g3-core/src/tools/memory.rs: Update get_memory_path() to use analysis/
- crates/g3-cli/src/project_files.rs: Update read_project_memory() path
- crates/g3-core/src/prompts.rs: Update documentation references (2 occurrences)
- analysis/memory.md: Add memory file (copied from .g3/memory.md)
Changes:
- Fix JSON path for session logs: now reads from context_window.conversation_history
(with fallback to messages for backwards compatibility)
- Remove 500-character truncation to show full summary
- Add termimad dependency for terminal markdown rendering
- Display summary with proper markdown formatting (headers, bold, code, lists)
The extract_session_summary() function was looking for messages at the wrong
JSON path. Session logs store conversation history at context_window.conversation_history,
not at the top-level messages key.
Consolidate duplicated logic into canonical shared functions:
- Extract load_config_with_cli_overrides() to utils.rs
- Was duplicated in lib.rs and accumulative.rs with subtle differences
- lib.rs version had Chrome diagnostics + provider validation
- accumulative.rs version was missing both
- Now all callers use the complete canonical implementation
- Extract combine_project_content() to project_files.rs
- Was duplicated inline in lib.rs and agent_mode.rs
- Simplified implementation using iterator flatten
- Added unit tests for all cases
This eliminates drift risk where the duplicated implementations
could diverge over time (accumulative.rs was already missing
Chrome diagnostics and provider validation).
Agent: fowler
Studio enables running multiple g3 agents concurrently without conflicts
by using git worktrees for isolation.
Features:
- studio run --agent <name> [args...]: Create worktree, spawn g3, tail output
- studio list: Show all active sessions
- studio status <id>: Show session details and summary
- studio accept <id>: Merge session branch to main and cleanup
- studio discard <id>: Delete session without merging
Each session gets:
- Isolated worktree at .worktrees/sessions/<agent>/<session-id>
- Dedicated branch: sessions/<agent>/<session-id>
- Short UUID (8 chars) for easy reference
- Automatic --workspace and --agent flags passed to g3
Added first_user_message field to Fragment struct that captures the
full first user message (task) from the dehydrated conversation.
This is now displayed at the top of the stub with a 📋 Task: prefix.
Removed the Topics section from the stub since the full task provides
better context for forensics and debugging.
Agent: g3
The UTF-8 string slicing pattern is better suited as a remembered
pattern in project memory rather than a static AGENTS.md section.
This keeps AGENTS.md focused on codebase-specific invariants while
the pattern remains accessible for reference.
Agent: g3
ACD (Aggressive Context Dehydration) fixes:
- Fixed dehydrate_context() to extract turn summary from context window
instead of using the passed-in final_response (which contained only
the timing footer, not the actual LLM response)
- Removed final_response parameter from dehydrate_context() since it
now self-extracts the last assistant message as the summary
- This ensures the actual turn summary is preserved after dehydration,
not just the timing footer
New /dump command:
- Added /dump command to dump entire context window to tmp/ for debugging
- Shows message index, role, kind, content length, and full content
- Available in both console and machine modes
UTF-8 safety:
- Fixed truncate_to_word_boundary() to use character indices instead of
byte indices, preventing panics on multi-byte UTF-8 characters
- Added UTF-8 string slicing guidance to AGENTS.md
Agent: g3
When read_file is called with an end position beyond the file length,
instead of returning an error that forces a retry, now clamps to the
actual file length and returns the content with an informative message.
This eliminates wasteful retry cycles where the LLM had to make a
second request with the corrected end position.
Change read_file output format so the "🔍 N lines read" appears as
the last line after the file content, not before it. This keeps the
output cleaner with just one metadata line at the end.