v1.3.0: Bedrock translator refactor, usage metrics, session restore fixes, and predefined rules

Major changes:

- **Bedrock translator architecture**: Extract orchestration logic from `impl.rs` into
  a dedicated `translator.rs` module. Rename `convert_request.rs` → `request_translator.rs`
  and `stream.rs` → `response_translator.rs` for clarity. Remove `tool_docs.rs` (inlined).
  Remove `fallback_to_warp` setting and server fallback path — Bedrock is now the sole backend.

- **Unknown tool handling**: The response translator now detects hallucinated/unknown tool
  calls from the model and synthesizes error tool_results so the conversation doesn't
  deadlock waiting for a result that will never come.

- **Usage display overhaul**: Replace credit-based usage display with detailed token metrics
  showing context window %, cache hit rate (read/write/miss), and estimated cost in dollars.
  Add `total_input_tokens`, `total_cache_read_tokens`, `total_cache_write_tokens`, and
  `cache_miss_tokens` accessors to `AIConversation`.

- **Predefined rules system**: Add `predefined_rules.rs` with 11 system-defined behavioral
  rules that are auto-seeded on first launch. Add "Add Predefined Rules" button to the
  Rules UI for re-adding them later. Track seeding state via `has_seeded_predefined_rules`
  setting.

- **Session restore improvements**: Rename database file from `warp.sqlite` to
  `galaxy.sqlite` with automatic migration from both same-directory and state_dir legacy
  paths. Improve CWD persistence by falling back to `session_startup_path` for agent-mode
  and fresh tabs. Add extensive session-save/restore logging.

- **Shell bootstrap rebrand**: Rename `WARP_INITIAL_WORKING_DIR` environment variable to
  `GALAXY_INITIAL_WORKING_DIR` across bash, zsh, and fish bootstrap scripts.

- **Model defaults**: Change default Bedrock model from Opus 4.7 to Opus 4.6.
  Add `context_window_for_model()` helper with model-aware context sizes.
  Remove `is_bedrock_model()` (no longer needed without server fallback).

- **User query persistence**: The response translator now emits a `UserQuery` proto message
  at stream start so the user's prompt persists across sessions for conversation titles.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ryan Ward
2026-05-20 15:15:28 -05:00
co-authored by Claude Opus 4.6
parent ec99146ccc
commit eaa2ddc75e
38 changed files with 1687 additions and 1129 deletions
+15
View File
@@ -1547,11 +1547,26 @@ impl PaneGroup {
}
});
let raw_cwd = terminal_snapshot.cwd.clone();
let startup_directory = terminal_snapshot
.cwd
.map(PathBuf::from)
.filter(|path| path.is_dir());
log::info!(
"[session-restore] pane=terminal raw_cwd={raw_cwd:?} \
resolved_startup_dir={startup_directory:?} \
conversations_to_restore={} \
active_conversation_id={:?} \
has_shell_launch_data={} \
has_block_list={} is_active={}",
terminal_snapshot.conversation_ids_to_restore.len(),
terminal_snapshot.active_conversation_id,
terminal_snapshot.shell_launch_data.is_some(),
block_list.is_some(),
terminal_snapshot.is_active,
);
// Filter conversation IDs to only include those that have task messages
// and are not entirely passive (ignored suggestions).
// This prevents showing the "Previous session" banner when there's nothing to restore
+27 -4
View File
@@ -418,15 +418,22 @@ impl PaneContent for TerminalPane {
if ambient_model.is_ambient_agent() {
let task_id = ambient_model.task_id();
log::info!(
"[session-save] pane=viewer/ambient task_id={task_id:?}"
);
return LeafContents::AmbientAgent(AmbientAgentPaneSnapshot {
uuid: self.uuid.clone(),
task_id,
});
}
let cwd = view.pwd_if_local(app);
log::info!(
"[session-save] pane=viewer cwd={cwd:?} is_active={is_active}"
);
LeafContents::Terminal(TerminalPaneSnapshot {
uuid: self.uuid.clone(),
cwd: view.pwd_if_local(app),
cwd,
is_active,
is_read_only: false,
shell_launch_data: None,
@@ -441,14 +448,21 @@ impl PaneContent for TerminalPane {
// can be restored via the ambient agent task if one exists.
let task_id = view.model.lock().ambient_agent_task_id();
if task_id.is_some() {
log::info!(
"[session-save] pane=transcript/ambient task_id={task_id:?}"
);
LeafContents::AmbientAgent(AmbientAgentPaneSnapshot {
uuid: self.uuid.clone(),
task_id,
})
} else {
let cwd = view.pwd_if_local(app);
log::info!(
"[session-save] pane=transcript cwd={cwd:?} is_active={is_active}"
);
LeafContents::Terminal(TerminalPaneSnapshot {
uuid: self.uuid.clone(),
cwd: view.pwd_if_local(app),
cwd,
is_active,
is_read_only: false,
shell_launch_data: None,
@@ -468,7 +482,7 @@ impl PaneContent for TerminalPane {
.sync_id();
// Collect all conversation IDs for this terminal view
let conversation_ids_to_restore = BlocklistAIHistoryModel::as_ref(app)
let conversation_ids_to_restore: Vec<_> = BlocklistAIHistoryModel::as_ref(app)
.all_live_conversations_for_terminal_view(self.terminal_view(app).id())
.map(|conversation| conversation.id())
.collect();
@@ -487,9 +501,18 @@ impl PaneContent for TerminalPane {
.active_conversation_id()
});
let cwd = view.pwd_if_local(app);
log::info!(
"[session-save] pane=terminal cwd={cwd:?} is_active={is_active} \
conversations={} active_conversation={active_conversation_id:?} \
has_shell_launch_data={} has_input_config=true",
conversation_ids_to_restore.len(),
view.shell_launch_data_if_local(app).is_some(),
);
LeafContents::Terminal(TerminalPaneSnapshot {
uuid: self.uuid.clone(),
cwd: view.pwd_if_local(app),
cwd,
is_active,
is_read_only: view.model.lock().is_read_only(),
shell_launch_data: view.shell_launch_data_if_local(app),