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
+76 -2
View File
@@ -141,7 +141,8 @@ const COMMANDS_COUNT_LIMIT: i64 = 10000;
use galaxy_server_client::persistence::{upsert_cloud_object, CloudObjectId};
const WARP_SQLITE_FILE_NAME: &str = "warp.sqlite";
const WARP_SQLITE_FILE_NAME: &str = "galaxy.sqlite";
const LEGACY_SQLITE_FILE_NAME: &str = "warp.sqlite";
/// When delete a cloud object, this callback is used to delete the cloud
/// object. It takes the id of the cloud object to delete as a parameter.
@@ -162,7 +163,14 @@ pub fn initialize(ctx: &mut AppContext) -> (Option<PersistedData>, Option<Writer
Ok(mut conn) => {
let user_uid = AuthStateProvider::as_ref(ctx).get().user_id();
let app_state = match read_sqlite_data(&mut conn, user_uid) {
Ok(app_state) => Some(app_state),
Ok(app_state) => {
log::info!(
"[session-restore] read_sqlite_data: windows={} conversations={}",
app_state.app_state.windows.len(),
app_state.multi_agent_conversations.len(),
);
Some(app_state)
}
Err(err) => {
send_telemetry_from_app_ctx!(
TelemetryEvent::DatabaseReadError(err.to_string()),
@@ -335,6 +343,11 @@ pub(super) fn init_db() -> Result<SqliteConnection> {
// First, make sure the parent directory of the file exists, otherwise
// we'll get an error if the file doesn't already exist.
let db_path = database_file_path();
log::info!(
"[session-restore] init_db: target={} exists={}",
db_path.display(),
db_path.exists()
);
// If we fail to create the necessary directories, log a warning and
// continue; we'll return a sqlite error if it actually fails to initialize
// a database connection.
@@ -348,6 +361,55 @@ pub(super) fn init_db() -> Result<SqliteConnection> {
);
}
// Migrate from legacy "warp.sqlite" filename to "galaxy.sqlite" (same directory).
if !db_path.exists() {
let legacy_same_dir = db_path
.parent()
.expect("database file path should be absolute")
.join(LEGACY_SQLITE_FILE_NAME);
if legacy_same_dir.exists() {
match std::fs::rename(&legacy_same_dir, &db_path) {
Ok(_) => {
log::info!("Migrated legacy warp.sqlite to galaxy.sqlite");
let old_wal = legacy_same_dir.with_extension("sqlite-wal");
let old_shm = legacy_same_dir.with_extension("sqlite-shm");
let new_wal = db_path.with_extension("sqlite-wal");
let new_shm = db_path.with_extension("sqlite-shm");
let _ = std::fs::rename(&old_wal, &new_wal);
let _ = std::fs::rename(&old_shm, &new_shm);
}
Err(err) => {
log::warn!("Failed to migrate legacy warp.sqlite: {err:#}");
}
}
}
}
// Migrate from legacy "warp.sqlite" in state_dir (handles users who never
// got the in-place rename above because they were on the old app ID path).
if !db_path.exists() {
let legacy_state_dir = galaxy_core::paths::state_dir().join(LEGACY_SQLITE_FILE_NAME);
if legacy_state_dir.exists() {
match std::fs::rename(&legacy_state_dir, &db_path) {
Ok(_) => {
log::info!(
"Migrated legacy warp.sqlite from state_dir to {}",
db_path.display()
);
let old_wal = legacy_state_dir.with_extension("sqlite-wal");
let old_shm = legacy_state_dir.with_extension("sqlite-shm");
let new_wal = db_path.with_extension("sqlite-wal");
let new_shm = db_path.with_extension("sqlite-shm");
let _ = std::fs::rename(&old_wal, &new_wal);
let _ = std::fs::rename(&old_shm, &new_shm);
}
Err(err) => {
log::warn!("Failed to migrate legacy warp.sqlite from state_dir: {err:#}");
}
}
}
}
// Migrate old SQLite files into the secure application container.
let old_db_path = galaxy_core::paths::state_dir().join(WARP_SQLITE_FILE_NAME);
if old_db_path != db_path && old_db_path.exists() && !db_path.exists() {
@@ -2409,6 +2471,18 @@ fn read_node(conn: &mut SqliteConnection, node: model::PaneNode) -> Result<PaneN
.active_conversation_id
.and_then(|id_str| AIConversationId::try_from(id_str).ok());
log::info!(
"[session-db-read] terminal_pane: cwd={:?} is_active={} \
conversations={} active_conversation={:?} \
has_shell_launch_data={} has_input_config={}",
terminal_pane.cwd,
terminal_pane.is_active,
conversation_ids_to_restore.len(),
active_conversation_id,
shell_launch_data.is_some(),
input_config.is_some(),
);
LeafContents::Terminal(TerminalPaneSnapshot {
uuid: terminal_pane.uuid,
cwd: terminal_pane.cwd,