feat: import GitHub Copilot CLI and Kimi Code session histories - #569
Open
Harry19081 wants to merge 1471 commits into
Open
feat: import GitHub Copilot CLI and Kimi Code session histories#569Harry19081 wants to merge 1471 commits into
Harry19081 wants to merge 1471 commits into
Conversation
Pre-commit hook ran. Total eslint: 0, total circular: 1
Pre-commit hook ran. Total eslint: 0, total circular: 1
WKWebView reuses pooled keep-alive sockets that the server may have silently closed, and CFNetwork never auto-retries a POST on a fresh connection — so the first cloud mutation after idle (create invite, change role, set floor) deterministically failed with a raw WebKit "Load failed". Retry such transport-level TypeErrors exactly once across all org2 cloud RPC clients and the GoTrue token refresh, and translate the residual failure instead of surfacing the raw message. Pre-commit hook ran. Total eslint: 0, total circular: 1
Pre-commit hook ran. Total eslint: 0, total circular: 1
Carry the turn source from every frontend dispatch boundary through the Rust agent wire so Send Now remains a fresh turn even when the cancelled provider turn is still draining. Only ordinary user submissions may enter mid-turn steering. Require and validate the source at the Tauri boundary, reject unknown persisted values instead of silently coercing them to user_submit, and add wire, steering, queue, and persistence regression coverage. Verification: - pnpm exec tsc --noEmit --pretty false - pnpm exec vitest run src/store/ui/__tests__/messageQueueAtom.test.ts src/engines/SessionCore/hooks/session/__tests__/useQueueDispatch.classify.test.ts src/engines/SessionCore/hooks/session/__tests__/useQueueDispatch.intervention.test.ts src/engines/SessionCore/sync/adapters/__tests__/rustAgentSendPayload.test.ts src/engines/ChatPanel/hooks/useWorkspaceChat/useUserIntentSubmit.intervention.test.ts - cargo check -p agent_core - cargo check -p session_persistence - cargo test -p agent_core force_send_never_enters_mid_turn_steering --lib - cargo test -p session_persistence --lib -- --test-threads=1 Pre-commit hook ran. Total eslint: 0, total circular: 1
Pre-commit hook ran. Total eslint: 0, total circular: 1
Pre-commit hook ran. Total eslint: 0, total circular: 2
Pre-commit hook ran. Total eslint: 0, total circular: 2
…-followups fix(workstation): finish session workspace isolation
Pre-commit hook ran. Total eslint: 0, total circular: 1
feat(sessions): add human work logs
docs(agent): add React performance guidance
Pre-commit hook ran. Total eslint: 0, total circular: 2
Pre-commit hook ran. Total eslint: 0, total circular: 2
Pre-commit hook ran. Total eslint: 0, total circular: 2
fix(worktree): unify session workspace lifecycle
fix(codex): add reconnect action to account cards
fix(cloud): retry RPC posts once on webview transport failures
…s-clean fix(session): align installing runtime status
Pre-commit hook ran. Total eslint: 0, total circular: 1
Pre-commit hook ran. Total eslint: 0, total circular: 2
Pre-commit hook ran. Total eslint: 3, total circular: 2
Pre-commit hook ran. Total eslint: 3, total circular: 2
Pre-commit hook ran. Total eslint: 0, total circular: 2
The panel body ran on the 900px content token while the tab header above it uses 932, so every section sat 16px narrower on each side. Both now use DETAIL_PANEL_TOKENS.headerWidth. Highlight tiles were a card grid inside a bordered SectionContainer — a card within a card. They now carry the Usage page's stat-tile surface and sit directly in the flow, which is how UsageStatCards is placed. Every section is collapsible. CollapsibleSection draws no surface of its own, so wrapping a SectionContainer in one still yields a single card layer, with the heading gaining a chevron. Highlights keeps its title but needs no inner container, since the tiles are already the card layer. Pre-commit hook ran. Total eslint: 2, total circular: 0
The Over time table showed a sessions column that read 400 on every row — the drift window is a fixed 400 sessions, so the count was a constant, not data. What varies is how long each window took to accumulate, so the row now shows that span; four days in July against eleven in June says something the constant never could. Work hour now renders as 12-hour AM/PM everywhere rather than deferring to the locale. `toLocaleTimeString` gave zh "15时", which reads as a timestamp rather than as a time of day, which is what the card is asking about. Card copy: trailing full stops removed from every detail line across all 13 locales (273 strings), the Highlights blurb is gone, and the deck now opens on the streak — the longest span it can show. Pre-commit hook ran. Total eslint: 2, total circular: 0
Pre-commit hook ran. Total eslint: 2, total circular: 0
fix(dev): Linux tauri:dev startup failures (WebKitGTK load + i18n mount race)
Resolve loaders conflict with human sessions, keep agent+os fetch for channel rows, and fix prettier import placement for the refresh poller. Pre-commit hook ran. Total eslint: 2, total circular: 0
fix(sidebar): refresh and load channel-created sessions
Clicking a Team Sessions card handed the replay to a Chat Pane session tab. That tab switch unmounts Work Management (UnifiedChatPanelTabContent mounts it only while its tab is active), which unmounts TaskKanban, which runs the cleanup in useCloudSessionActions that aborts the in-flight import. The click cancelled its own replay: the abort path returns "noop" without a toast, so the new tab sat on the empty-history placeholder forever. The sidebar never unmounts itself, which is why the identical call works there. Team sessions now replay into the board's own draggable preview window — the surface the board already uses for local cards — via a new `openSurface` option on `replaySession`. Callers a tab switch would unmount pass their own in-place surface; the sidebar and fork-header callers keep the default tab behavior. While the import runs, the preview shows the cloud card with the pending imported session id grafted on; once the local copy lands on the board its own task takes over (the cloud row is then dropped as a duplicate). Also: the provider mark now follows the model when a row carries no agent identity. ORG2-native sessions publish no cliAgentType — the Rust session categories store NULL — so a shared Claude or GPT session used to render under the generic ORG2 glyph. Only the icon follows the model; agentLabel (and with it the Agent filter) keeps naming the runtime. The replay hydration placeholder now shares the row's own projection, so legacy `*_cli` wire aliases stop falling through to the Bot mark. Pre-commit hook ran. Total eslint: 2, total circular: 0
Pre-commit hook ran. Total eslint: 2, total circular: 0
Real-machine testing of the reference chips surfaced three dead-click modes on the auto-replay consumer: - An own-session chip only wrote a sidebar reveal request, which highlights nothing unless the session is already active. Reveal-local now opens the live local transcript via the chat panel tab. - The reveal-local decision trusts persisted push markers, which can outlive the local session until the vanished sweep. Confirm via demand-hydration before repointing any tab; a stale marker gets the same not-found refusal a missing cloud row would. - Skip toasts (not-found / not-published) used the 1s default duration, which polling misses and users read as a broken chip. They now use the same extended duration as admission refusals, for the same reason. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Pre-commit hook ran. Total eslint: 20, total circular: 0
A dragged session lands in the issue comment box and create-issue body as a bare orgii://cloud/session/ref URL; the chip only appeared after publishing. A native textarea cannot render inline pills, so a preview strip under the editor now shows the same chips the rendered body will. - Extract the candidate scan out of the remark plugin into a shared scanCloudSessionReferences, so the preview and the rendered body can never disagree; code spans/fences are excluded to match the renderer, and a trailing backtick no longer leaks into the session id. - One walk per whitespace-free run makes the scan linear regardless of scheme-hit density (measured: adversarial 100KB pastes now cost the same as reference-free text; realistic typing shows zero long tasks). - Preview chips are passive and identity-stable across keystrokes; the chip resolves its local title through sessionByIdAtom instead of subscribing to the whole sessions array. - Point the create-issue flow at the extracted CreateIssueModal and drop the inline duplicate in GitHubWorkItemControls; the duplicate was the live one, so the reference drop target never worked there. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Pre-commit hook ran. Total eslint: 20, total circular: 0
Pre-commit hook ran. Total eslint: 2, total circular: 0
fix(key-vault): prevent CLI discovery from hanging packaged app
Pre-commit hook ran. Total eslint: 2, total circular: 0
Pre-commit hook ran. Total eslint: 51, total circular: 0
The chat pane could only render a session as its GUI transcript; the raw JSON was reachable only through a modal, and neither host could answer "where did the time go" or "what did this session touch". Adds a ghost select (Chat / Timeline / Changes / Raw) to the session published header, shared by the chat pane and the Workstation chat-session tab so both hosts render one identical control instead of duplicating the switcher. - Timeline: per-turn gantt over the session's own wall-clock span, with failed / interrupted turns toned apart. - Changes: modifiedFiles aggregated across turns, busiest first. A path created then modified stays "created"; a delete anywhere wins. - Raw: the existing transcript view, now embedded instead of modal. Both derived views read one loadTurnIndex call (metadata only, no event bodies), so a whole-session view costs a single read regardless of transcript size. Virtualization: the GUI subtree is hidden, never unmounted, so switching views cannot drop the chat list's measurement cache and force a re-measure of every turn on the way back. The alternate views mount only while active, and each windows its own rows. The raw transcript's merge and JSON.stringify are now gated on `enabled` so a session that loaded Raw once no longer re-serializes on every streaming event. Pre-commit hook ran. Total eslint: 2, total circular: 0
feat(cloud): session reference chips in issue text
feat(chat): add per-session timeline, changes, and raw views
Pre-commit hook ran. Total eslint: 2, total circular: 0
Pre-commit hook ran. Total eslint: 2, total circular: 0
style(profile): pair builder type avatars
New orgtrack_core::sources::cli_resume maps an imported session (claude_code / codex_app / cursor_cli) to the invocation its owning CLI accepts: claude --resume <uuid> (original cwd required — Claude keys session storage on the project path), codex resume <thread-uuid> (extracted from the rollout stem), cursor-agent --resume <chat-id>. Cursor IDE composers are deliberately unsupported: their ids share no space with cursor-agent's chat store. The desktop app exposes it as external_history_cli_resume_plan, folding in the host-only freshness checks (workspace dir still exists, source store still on disk).
Imported Claude Code / Codex / Cursor CLI sessions get a 'Continue in <CLI>' header action next to the fork button. It reuses the existing chat-panel TUI launch flow end to end: launch-profile command resolution, a managed code_sessions row (runner='tui') for ORGII_SESSION_ID lifecycle attribution and managed-mirror dedup, and the session's recorded workspace as cwd. Disabled states explain exactly why (CLI not installed, original folder gone for Claude's cwd-scoped resume, source store deleted). Sources with no CLI resume path (Cursor IDE composers and the rest) simply don't render the button — the backend plan is the single source of truth.
orgtrack resume <session-id> scans only the provider that owns the id, plans the invocation via core's cli_resume, and execs it from the session's recorded workspace (--print emits the cd-prefixed command line instead). Verified against real local history: claude/codex/cursor-agent command lines all resolve, and Claude's cwd requirement is enforced — resuming from another directory provably fails upstream.
Verified against the installed binaries and their own stores: - opencode --session <ses_id> — central db, ids match session.id exactly - mimo --session <ses_id> — OpenCode fork, identical flag surface - cline --id <id> — global sessions.db, ids match sessions.session_id - omp --session <transcript-path> — pi-family id lookup is scoped to the current project's session dir, so the plan addresses the session file by absolute path instead (works from anywhere) Still out, with reasons in the module doc: cursor_ide/windsurf/warp/trae/ qoder (app-bound, no CLI surface) and zcode/qoder_cli/workbuddy (no binary present to verify the flag shape).
…oser copy, source-model preselect
Three imported-session UX fixes around continuation:
- Sources no CLI can reopen (Cursor IDE, Windsurf, Trae, Warp, Qoder,
ZCode, WorkBuddy, Qoder CLI) no longer render the chat composer — they
are pure read-only replays. The capability lives on the source
descriptor (cliResume {agentType, displayName}) so the composer gate and
the header continue-button share one sync source of truth, and the
header skips the backend plan round-trip for never-resumable sources.
- The composer placeholder now says what typing actually does — fork into
a new ORGII session — and names the native alternative per source
('…or resume in Claude Code from the header'), in all 13 locales.
- The fork setup dialog preselects the imported session's own model: when
no agent was explicitly chosen (user pick or collab agent hint), the
source session's model outranks the fallback agent's configured model
whenever the selected account offers it. Extracted as a pure helper with
unit tests; collab forks with agent hints keep their existing order.
…ries
Two new imported-history sources, end to end (reader → registry → replay
router → app commands → sidebar descriptors → CLI resume):
- copilot — Copilot CLI 1.x stores one ~/.copilot/session-state/<uuid>/
per session (events.jsonl stream + workspace.yaml sidecar). The reader
walks those dirs (strict uuid gate skips optimistic-chat/pending-draft
junk and 1.0.8-era metadata-only shells), converts user/assistant/tool
events into chunks pairing results by toolCallId, and enriches
repository/branch/tokens from session-store.db best-effort — verified
against the live db: input_tokens is already cache-inclusive and
output_tokens already includes reasoning (documented in-module).
Resume: copilot --resume=<id> (attached form; the flag's value is
optional so a detached id would parse as a prompt).
- kimi — Kimi Code CLI stores $KIMI_CODE_HOME/sessions/<wd_key>/<id>/
with state.json + agents/main/wire.jsonl, a persisted op journal
(metadata first line, then {type, ...payload, time}). Transcript from
context.append_message (origin-filtered, think parts → thinking,
toolCalls paired with tool-role results), cache-aware usage from
usage.record, cwd from config.update. Format pinned against upstream
MoonshotAI/kimi-code (wire protocol 1.5); fixture-tested — no local
sessions exist on this machine (CLI not logged in). Torn final lines
and unknown ops are tolerated like the CLI itself does.
Resume: kimi --session <id> from the recorded workspace (sessions are
cwd-bucketed).
Both are detected as history-importable in the Data Sources panel, join
the kanban source filters, and the chat pane's Continue-in-CLI button.
Antigravity (agy) was probed too — conversations/ +
conversation_summaries.db exist but are empty and seeding needs an
interactive Google login, so its importer is deferred; findings are in
docs/orgtrack-cli.md.
Neonforge98
force-pushed
the
feat/external-session-cli-resume
branch
2 times, most recently
from
July 30, 2026 08:37
f6b45ab to
9880eb7
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Stacked on #560 (retarget to
developafter it merges). Adds two new imported-history sources, fully wired end to end — reader → registry → replay router → app commands → sidebar/kanban → Continue-in-CLI:copilot (GitHub Copilot CLI 1.x)
~/.copilot/session-state/<uuid>/per session —events.jsonlevent stream +workspace.yamlsidecar (hand-parsed, no yaml dep). Strict uuid-shape gate excludesoptimistic-chat-*/pending-session:draft:*junk and 1.0.8-era metadata-only shells.toolCallId(bash→ run_command_line,str_replace→ edit_file_by_replace feeding impact stats); unknown event types tolerated.session-store.db, best-effort with lock-safe degradation. Verified against the live db:input_tokensis already cache-inclusive (fresh + cache_read = column; cross-checked viatoken_details_json) andoutput_tokensalready includes reasoning — both documented in-module. Per-roundRoundUsagecarries fresh input.copilot --resume=<id>(attached form — the flag's value is optional, a detached id would parse as a prompt; Copilot itself prints this exact command).orgtrack resume --printemits the rightcd … && copilot --resume=….kimi (Kimi Code CLI)
$KIMI_CODE_HOME/sessions/<wd_key>/<sessionId>/withstate.json+agents/main/wire.jsonl— a persisted op journal (metadata first line, then{type, …payload, time}), format pinned against upstream MoonshotAI/kimi-code (wire protocol 1.5).context.append_message(origin-filtered;thinkparts → thinking chunks; toolCalls paired with tool-role results), cache-aware usage fromusage.record(inputOther + cacheRead + cacheCreation→ cache-inclusive input), cwd fromconfig.update. Torn final lines and unknown ops tolerated exactly like the CLI's own replay.kimi --session <id>from the recorded workspace (sessions are cwd-bucketed).Deferred: antigravity
agyresumes via--conversation <id>and keeps~/.gemini/antigravity-cli/(conversations/+conversation_summaries.db, schema recorded in docs), but both stores are empty here and seeding needs an interactive Google login — importer deferred, findings indocs/orgtrack-cli.md.Testing
cargo test -p orgtrack_core: 443 passed (13+1 copilot, 17 kimi new), clippy clean for new code, app lib compilespnpm typecheckclean; frontend suites 1063/1063 (source-registry, kanban filters, ChatPanel)orgtrack sourceslists 17 providers; copilot scan/list/show/resume verified against the real store; kimi scans an empty store cleanlycopilot/copilot,kimi_cli/kimi) so GUI-launched TUI sessions won't double-list