From 75014f70dd74302865607a63256444c13c1cc9b9 Mon Sep 17 00:00:00 2001
From: hanafish <1106510024@qq.com>
Date: Tue, 11 Aug 2026 15:30:31 +0800
Subject: [PATCH] refactor(orgtrack): remove unused imported history modules
---
.../src/sources/imported_history/chunks.rs | 141 -----------
.../src/sources/imported_history/impact.rs | 232 ------------------
.../src/sources/imported_history/rows.rs | 227 -----------------
3 files changed, 600 deletions(-)
delete mode 100644 src-tauri/crates/orgtrack-core/src/sources/imported_history/chunks.rs
delete mode 100644 src-tauri/crates/orgtrack-core/src/sources/imported_history/impact.rs
delete mode 100644 src-tauri/crates/orgtrack-core/src/sources/imported_history/rows.rs
diff --git a/src-tauri/crates/orgtrack-core/src/sources/imported_history/chunks.rs b/src-tauri/crates/orgtrack-core/src/sources/imported_history/chunks.rs
deleted file mode 100644
index 3643636a9a..0000000000
--- a/src-tauri/crates/orgtrack-core/src/sources/imported_history/chunks.rs
+++ /dev/null
@@ -1,141 +0,0 @@
-use core_types::activity::ActivityChunk;
-use serde_json::json;
-
-use super::{
- ImportedToolCall, ACTION_TYPE_ASSISTANT, ACTION_TYPE_RAW, ACTION_TYPE_THINKING,
- ACTION_TYPE_TOOL_CALL, FUNCTION_ASSISTANT, FUNCTION_THINKING, FUNCTION_USER_MESSAGE,
- IMPORTED_STATUS_COMPLETED,
-};
-
-/// Internal wrapper blocks ORGII prepends to the prompt it hands the CLI:
-/// the GUI exec-mode briefing and the IDE-context injection
-/// (`inject_ide_context_into_prompt`). The CLI's native transcript stores
-/// the full prompt verbatim, so replay readers must strip these to recover
-/// what the user actually typed.
-const INTERNAL_CONTEXT_BLOCKS: &[(&str, &str)] = &[
- ("", ""),
- ("", ""),
-];
-
-/// Repeatedly strip LEADING internal wrapper blocks (exec-mode briefing,
-/// IDE context) from `text`, in any order.
-///
-/// If a known tag opens but never closes (e.g. a truncated title), the whole
-/// remainder is treated as internal and `""` is returned — an unclosed
-/// internal block never carries user-authored text after it.
-pub fn strip_internal_context_blocks(text: &str) -> &str {
- let mut remaining = text;
- let mut stripped = false;
- 'outer: loop {
- let candidate = remaining.trim_start();
- for (open, close) in INTERNAL_CONTEXT_BLOCKS {
- if let Some(rest) = candidate.strip_prefix(open) {
- match rest.find(close) {
- Some(end) => {
- remaining = &rest[end + close.len()..];
- stripped = true;
- continue 'outer;
- }
- None => return "",
- }
- }
- }
- break;
- }
- if stripped {
- remaining.trim_start()
- } else {
- text
- }
-}
-
-/// GUI-launched runs prefix the task with an internal exec-mode briefing;
-/// strip it so titles/replay show only what the user typed.
-///
-/// Back-compat name: now also strips the `` injection via
-/// [`strip_internal_context_blocks`].
-pub fn strip_orgii_exec_mode_bridge(text: &str) -> &str {
- strip_internal_context_blocks(text)
-}
-
-pub fn user_message_chunk(
- session_id: &str,
- provider_slug: &str,
- sequence: usize,
- created_at: &str,
- message: &str,
-) -> ActivityChunk {
- // Single funnel for every imported reader's user bubbles: strip the
- // GUI exec-mode briefing and IDE-context injection here so no source
- // can leak them into replay.
- let message = strip_internal_context_blocks(message);
- let mut chunk = ActivityChunk::new(session_id, ACTION_TYPE_RAW, FUNCTION_USER_MESSAGE);
- chunk.chunk_id = format!("{provider_slug}-user-{sequence}");
- chunk.created_at = created_at.to_string();
- chunk.result = json!({
- "type": "user",
- "message": { "content": message, "role": "user" },
- });
- chunk
-}
-
-pub fn assistant_message_chunk(
- session_id: &str,
- provider_slug: &str,
- sequence: usize,
- created_at: &str,
- message: &str,
-) -> ActivityChunk {
- let mut chunk = ActivityChunk::new(session_id, ACTION_TYPE_ASSISTANT, FUNCTION_ASSISTANT);
- chunk.chunk_id = format!("{provider_slug}-asst-{sequence}");
- chunk.created_at = created_at.to_string();
- chunk.result = json!({
- "observation": message,
- "content": message,
- "role": "assistant",
- "is_delta": false,
- "is_full_content": true,
- });
- chunk
-}
-
-pub fn thinking_chunk(
- session_id: &str,
- provider_slug: &str,
- sequence: usize,
- created_at: &str,
- thought: &str,
-) -> ActivityChunk {
- let mut chunk = ActivityChunk::new(session_id, ACTION_TYPE_THINKING, FUNCTION_THINKING);
- chunk.chunk_id = format!("{provider_slug}-thinking-{sequence}");
- chunk.created_at = created_at.to_string();
- chunk.result = json!({
- "thought": thought,
- "content": thought,
- "observation": thought,
- "is_delta": false,
- });
- chunk
-}
-
-pub fn tool_call_chunk(
- session_id: &str,
- provider_slug: &str,
- sequence: usize,
- call: &ImportedToolCall,
- output: &str,
-) -> ActivityChunk {
- let mut chunk = ActivityChunk::new(session_id, ACTION_TYPE_TOOL_CALL, &call.canonical_name);
- chunk.chunk_id = format!("{provider_slug}-tool-{sequence}-{}", call.call_id);
- chunk.created_at = call.created_at.clone();
- chunk.args = call.args.clone();
- chunk.result = json!({
- "success": true,
- "status": IMPORTED_STATUS_COMPLETED,
- "call_id": call.call_id,
- "output": output,
- "observation": output,
- "raw_tool_name": call.raw_name,
- });
- chunk
-}
diff --git a/src-tauri/crates/orgtrack-core/src/sources/imported_history/impact.rs b/src-tauri/crates/orgtrack-core/src/sources/imported_history/impact.rs
deleted file mode 100644
index 580899d6bb..0000000000
--- a/src-tauri/crates/orgtrack-core/src/sources/imported_history/impact.rs
+++ /dev/null
@@ -1,232 +0,0 @@
-use std::collections::BTreeSet;
-
-use core_types::activity::ActivityChunk;
-use serde_json::Value;
-
-use super::metadata::ImportedHistoryImpactStats;
-use super::{ACTION_TYPE_TOOL_CALL, FUNCTION_EDIT_FILE};
-
-/// Derive conservative file-impact metadata from normalized edit tool calls.
-///
-/// Source loaders remain responsible for recognizing their native tool names and
-/// reshaping them to [`FUNCTION_EDIT_FILE`]. This collector intentionally ignores
-/// failed edits and only counts line changes when the source exposes a diff or
-/// before/after text.
-pub fn impact_from_edit_chunks(chunks: &[ActivityChunk]) -> ImportedHistoryImpactStats {
- let mut touched_files = BTreeSet::new();
- let mut lines_added = 0_i64;
- let mut lines_removed = 0_i64;
-
- for chunk in chunks {
- if chunk.action_type != ACTION_TYPE_TOOL_CALL
- || chunk.function != FUNCTION_EDIT_FILE
- || edit_chunk_failed(chunk)
- {
- continue;
- }
-
- collect_edit_paths(&chunk.args, &mut touched_files);
-
- if let Some(patch) = find_string(&chunk.args, &["patch", "diff"]) {
- collect_patch_paths(patch, &mut touched_files);
- let (added, removed) = count_patch_lines(patch);
- lines_added += added;
- lines_removed += removed;
- continue;
- }
-
- let old = find_string(
- &chunk.args,
- &[
- "old_string",
- "oldString",
- "old_text",
- "oldText",
- "old_content",
- "oldContent",
- ],
- )
- .or_else(|| {
- find_string(
- &chunk.result,
- &[
- "old_content",
- "oldContent",
- "before_content",
- "beforeContent",
- ],
- )
- });
- let new = find_string(
- &chunk.args,
- &[
- "new_string",
- "newString",
- "new_text",
- "newText",
- "new_content",
- "newContent",
- "content",
- ],
- )
- .or_else(|| {
- find_string(
- &chunk.result,
- &["new_content", "newContent", "after_content", "afterContent"],
- )
- });
-
- if old.is_some() || new.is_some() {
- lines_removed += old.map(nonempty_line_count).unwrap_or_default();
- lines_added += new.map(nonempty_line_count).unwrap_or_default();
- }
- }
-
- let touched_files = touched_files.into_iter().collect::>();
- ImportedHistoryImpactStats {
- files_changed: touched_files.len() as i64,
- lines_added,
- lines_removed,
- touched_files,
- }
-}
-
-fn edit_chunk_failed(chunk: &ActivityChunk) -> bool {
- if chunk.result.get("success").and_then(Value::as_bool) == Some(false) {
- return true;
- }
- chunk
- .result
- .get("status")
- .and_then(Value::as_str)
- .is_some_and(|status| {
- matches!(
- status.trim().to_ascii_lowercase().as_str(),
- "failed" | "error" | "cancelled" | "canceled" | "rejected"
- )
- })
-}
-
-fn collect_edit_paths(value: &Value, paths: &mut BTreeSet) {
- const PATH_KEYS: &[&str] = &[
- "file_path",
- "filePath",
- "path",
- "targetFile",
- "relativeWorkspacePath",
- ];
- let Some(object) = value.as_object() else {
- return;
- };
- for key in PATH_KEYS {
- if let Some(path) = object.get(*key).and_then(Value::as_str) {
- insert_touched_path(path, paths);
- }
- }
- if let Some(payload) = object.get("payload") {
- collect_edit_paths(payload, paths);
- }
-}
-
-fn find_string<'a>(value: &'a Value, keys: &[&str]) -> Option<&'a str> {
- let object = value.as_object()?;
- for key in keys {
- if let Some(text) = object.get(*key).and_then(Value::as_str) {
- return Some(text);
- }
- }
- object
- .get("payload")
- .and_then(|payload| find_string(payload, keys))
-}
-
-fn collect_patch_paths(patch: &str, paths: &mut BTreeSet) {
- for line in patch.lines() {
- let candidate = line
- .strip_prefix("*** Add File: ")
- .or_else(|| line.strip_prefix("*** Update File: "))
- .or_else(|| line.strip_prefix("*** Delete File: "))
- .or_else(|| line.strip_prefix("*** Move to: "))
- .or_else(|| line.strip_prefix("rename from "))
- .or_else(|| line.strip_prefix("rename to "))
- .or_else(|| line.strip_prefix("+++ "))
- .or_else(|| line.strip_prefix("--- "));
- if let Some(candidate) = candidate {
- insert_touched_path(candidate, paths);
- }
- }
-}
-
-fn insert_touched_path(path: &str, paths: &mut BTreeSet) {
- let path = path.trim().trim_matches('"');
- let path = path
- .strip_prefix("a/")
- .or_else(|| path.strip_prefix("b/"))
- .unwrap_or(path);
- if !path.is_empty() && path != "/dev/null" {
- paths.insert(path.to_string());
- }
-}
-
-fn count_patch_lines(patch: &str) -> (i64, i64) {
- patch.lines().fold((0, 0), |(added, removed), line| {
- if line.starts_with("+++") || line.starts_with("---") {
- (added, removed)
- } else if line.starts_with('+') {
- (added + 1, removed)
- } else if line.starts_with('-') {
- (added, removed + 1)
- } else {
- (added, removed)
- }
- })
-}
-
-fn nonempty_line_count(text: &str) -> i64 {
- if text.is_empty() {
- 0
- } else {
- text.lines().count() as i64
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use serde_json::json;
-
- #[test]
- fn impact_collector_counts_normalized_edit_and_patch_paths() {
- let edit = ActivityChunk::new("session", ACTION_TYPE_TOOL_CALL, FUNCTION_EDIT_FILE)
- .with_args(json!({
- "file_path": "src/main.rs",
- "old_string": "old\nline",
- "new_string": "new\nline\nadded"
- }))
- .with_result(json!({"success": true, "status": "completed"}));
- let patch = ActivityChunk::new("session", ACTION_TYPE_TOOL_CALL, FUNCTION_EDIT_FILE)
- .with_args(json!({
- "payload": {"patch": "*** Update File: src/lib.rs\n*** Move to: src/moved.rs\n-old\n+new\n+extra"}
- }))
- .with_result(json!({"success": true}));
-
- let impact = impact_from_edit_chunks(&[edit, patch]);
-
- assert_eq!(
- impact.touched_files,
- vec!["src/lib.rs", "src/main.rs", "src/moved.rs"]
- );
- assert_eq!(impact.files_changed, 3);
- assert_eq!(impact.lines_added, 5);
- assert_eq!(impact.lines_removed, 3);
- }
-
- #[test]
- fn impact_collector_ignores_failed_edits() {
- let failed = ActivityChunk::new("session", ACTION_TYPE_TOOL_CALL, FUNCTION_EDIT_FILE)
- .with_args(json!({"file_path": "src/failed.rs", "new_string": "new"}))
- .with_result(json!({"success": true, "status": "failed"}));
-
- assert_eq!(impact_from_edit_chunks(&[failed]).files_changed, 0);
- }
-}
diff --git a/src-tauri/crates/orgtrack-core/src/sources/imported_history/rows.rs b/src-tauri/crates/orgtrack-core/src/sources/imported_history/rows.rs
deleted file mode 100644
index b86d7630bb..0000000000
--- a/src-tauri/crates/orgtrack-core/src/sources/imported_history/rows.rs
+++ /dev/null
@@ -1,227 +0,0 @@
-use std::collections::HashMap;
-
-use serde::Serialize;
-use serde_json::Value;
-
-use super::{
- epoch_ms_to_iso, repo_name_from_path, DEFAULT_LIST_LIMIT, IMPORTED_HISTORY_CATEGORY,
- IMPORTED_STATUS_COMPLETED,
-};
-
-#[derive(Debug, Clone, Serialize)]
-#[serde(rename_all = "camelCase")]
-pub struct ImportedHistorySessionRow {
- pub session_id: String,
- pub name: String,
- pub status: String,
- pub created_at: String,
- pub updated_at: String,
- pub category: &'static str,
- pub read_only: bool,
- pub model: Option,
- pub total_tokens: i64,
- pub background: bool,
- pub is_active: bool,
- pub repo_path: Option,
- pub storage_path: Option,
- pub repo_name: Option,
- pub branch: Option,
- pub files_changed: i64,
- pub lines_added: i64,
- pub lines_removed: i64,
- pub touched_files: Vec,
- pub parent_session_id: Option,
-}
-
-#[derive(Debug, Clone, Serialize)]
-#[serde(rename_all = "camelCase")]
-pub struct ImportedHistorySessionPage {
- pub sessions: Vec,
- pub has_more: bool,
-}
-
-/// Lightweight cached row for list-only surfaces such as the session sidebar.
-/// Carries the impact/model fields that card surfaces (e.g. the Kanban board)
-/// render inline; the heavier source metadata stays in SQLite until requested.
-#[derive(Debug, Clone, Serialize)]
-#[serde(rename_all = "camelCase")]
-pub struct ImportedHistorySidebarRow {
- pub session_id: String,
- pub name: String,
- pub created_at: String,
- pub updated_at: String,
- /// Live status override (`running`, `waiting_for_user`, `failed`)
- /// decorated by the desktop layer from lifecycle-hook signals or the
- /// transcript-mtime fallback. Absent means the frontend's historical
- /// default ("completed") applies. The core query never sets these.
- #[serde(skip_serializing_if = "Option::is_none")]
- pub status: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub is_active: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub repo_path: Option,
- /// The source app's own transcript file — the store of record for an
- /// imported session, which never has a `sessions.db` copy. Absent for
- /// rows cached before the path was recorded.
- #[serde(skip_serializing_if = "Option::is_none")]
- pub storage_path: Option,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub model: Option,
- pub total_tokens: i64,
- pub files_changed: i64,
- pub lines_added: i64,
- pub lines_removed: i64,
- pub touched_files: Vec,
-}
-
-#[derive(Debug, Clone, Serialize)]
-#[serde(rename_all = "camelCase")]
-pub struct ImportedHistorySidebarPage {
- pub sessions: Vec,
- pub has_more: bool,
-}
-
-#[derive(Debug, Clone, Serialize)]
-#[serde(rename_all = "camelCase")]
-pub struct ImportedHistoryRecentPath {
- pub path: String,
- pub name: Option,
- pub last_used_at: String,
- pub session_count: usize,
-}
-
-pub struct ImportedHistoryRowInput {
- pub session_id: String,
- pub name: String,
- pub created_at_ms: i64,
- pub updated_at_ms: i64,
- pub model: Option,
- pub input_tokens: i64,
- pub output_tokens: i64,
- pub repo_path: Option,
- pub storage_path: Option,
- pub branch: Option,
- pub files_changed: i64,
- pub lines_added: i64,
- pub lines_removed: i64,
- pub touched_files: Vec,
- pub parent_session_id: Option,
-}
-
-#[derive(Debug, Clone)]
-pub struct ImportedToolCall {
- pub call_id: String,
- pub raw_name: String,
- pub canonical_name: String,
- pub args: Value,
- pub created_at: String,
-}
-
-pub fn effective_limit(limit: usize) -> usize {
- if limit == 0 {
- DEFAULT_LIST_LIMIT
- } else {
- limit
- }
-}
-
-pub fn page_from_rows(
- mut rows: Vec,
- limit: usize,
- offset: usize,
-) -> ImportedHistorySessionPage {
- rows.sort_by(|session_a, session_b| session_b.updated_at.cmp(&session_a.updated_at));
- let limit = effective_limit(limit);
- let has_more = rows.len() > offset.saturating_add(limit);
- let sessions = rows.into_iter().skip(offset).take(limit).collect();
- ImportedHistorySessionPage { sessions, has_more }
-}
-
-pub fn row_from_input(input: ImportedHistoryRowInput) -> ImportedHistorySessionRow {
- let repo_name = input.repo_path.as_deref().and_then(repo_name_from_path);
- ImportedHistorySessionRow {
- session_id: input.session_id,
- name: input.name,
- status: IMPORTED_STATUS_COMPLETED.to_string(),
- created_at: epoch_ms_to_iso(input.created_at_ms),
- updated_at: epoch_ms_to_iso(input.updated_at_ms),
- category: IMPORTED_HISTORY_CATEGORY,
- read_only: true,
- model: input.model,
- total_tokens: input.input_tokens + input.output_tokens,
- background: false,
- is_active: false,
- repo_path: input.repo_path,
- storage_path: input.storage_path,
- repo_name,
- branch: input.branch,
- files_changed: input.files_changed,
- lines_added: input.lines_added,
- lines_removed: input.lines_removed,
- touched_files: input.touched_files,
- parent_session_id: input.parent_session_id,
- }
-}
-
-pub fn recent_paths_from_rows(
- rows: &[ImportedHistorySessionRow],
-) -> Vec {
- let paths = rows
- .iter()
- .filter_map(|row| {
- let path = row.repo_path.as_deref()?.trim();
- if path.is_empty() {
- return None;
- }
- Some(ImportedHistoryRecentPath {
- path: path.to_string(),
- name: repo_name_from_path(path),
- last_used_at: row.updated_at.clone(),
- session_count: 1,
- })
- })
- .collect::>();
- recent_paths_from_paths(&paths)
-}
-
-pub fn recent_paths_from_paths(
- paths: &[ImportedHistoryRecentPath],
-) -> Vec {
- let mut path_stats: HashMap, String, usize)> = HashMap::new();
-
- for recent_path in paths {
- let path = recent_path.path.trim();
- if path.is_empty() {
- continue;
- }
-
- let entry = path_stats.entry(path.to_string()).or_insert_with(|| {
- (
- recent_path
- .name
- .clone()
- .or_else(|| repo_name_from_path(path)),
- recent_path.last_used_at.clone(),
- 0,
- )
- });
- if recent_path.last_used_at > entry.1 {
- entry.1 = recent_path.last_used_at.clone();
- }
- entry.2 += recent_path.session_count;
- }
-
- let mut recent_paths = path_stats
- .into_iter()
- .map(
- |(path, (name, last_used_at, session_count))| ImportedHistoryRecentPath {
- name,
- path,
- last_used_at,
- session_count,
- },
- )
- .collect::>();
- recent_paths.sort_by(|path_a, path_b| path_b.last_used_at.cmp(&path_a.last_used_at));
- recent_paths
-}