feat(hub,web): cursor flavor for multi-agent session import (#732)#945
Draft
heavygee wants to merge 9 commits into
Draft
feat(hub,web): cursor flavor for multi-agent session import (#732)#945heavygee wants to merge 9 commits into
heavygee wants to merge 9 commits into
Conversation
Mirror the Codex session importer for Claude Code: scan ~/.claude/projects transcripts, map user/assistant(text/thinking/tool_use)/tool_result records to Hapi imported messages, persist via the shared store/sync engine, and expose /api/claude/status|sessions|sync-session plus a web import dialog. Extract the flavor-agnostic import engine into transcriptImport.ts so Codex and Claude share session-target selection, dedupe, persistence and event emission (single source of truth); each flavor keeps only its scanner, parser and adapter. Codex behavior and routes are unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
When resuming an imported Claude session still held open by a running background/interactive agent, 'claude --resume' is rejected (currently running as a background agent), the error was swallowed to {}, and the launcher retried forever. Now: detect liveness via 'claude agents --json' and branch a copy with --fork-session (recording forkedFrom) instead of taking over; logger serializes Error to {name,message,stack}; launcher classifies unrecoverable resume errors and stops retry (MAX_LAUNCH_RETRIES=3), surfacing the real reason. Dead sessions fall through to plain --resume (unchanged); codex and other flavors untouched.
via [HAPI](https://hapi.run)
Co-Authored-By: HAPI <noreply@hapi.run>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The direct-import path (importSingleSession) wrote rows via addMessage, stamping created_at/invoked_at with import time, so imported Claude/Codex sessions sorted as "active today" and lost their original message timeline (review finding on tiann#942). - Carry each JSONL record's top-level timestamp through import data (new ImportedMessage wrapper + shared parseImportedTimestamp) for both Claude and Codex parsers. - Insert via copyMessageToSession with createdAt/invokedAt = original timestamp (fallback to file mtime). - Back-fill the newly-created imported session's updatedAt to real last activity (setImportedSessionActivity, new-session path only); existing/online sessions keep forward-only activity semantics untouched. - New unit tests (Claude + Codex): timestamp preservation + mtime fallback. via [HAPI](https://hapi.run) Co-Authored-By: HAPI <noreply@hapi.run> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
getLiveAgentKind() runs 'claude agents --json', which honors CLAUDE_CONFIG_DIR / HAPI_CLAUDE_PATH. It ran before opts.claudeEnvVars were applied to process.env, so when a session supplies those overrides the liveness probe queried the default Claude config, missed the live agent, and the launch then resumed against the overridden config and hit the occupied-session path instead of forking (review finding on tiann#942). Move the env application above the fork decision so probe and launch share the same Claude env. Adds a regression test asserting the override is visible to getLiveAgentKind. via [HAPI](https://hapi.run) Co-Authored-By: HAPI <noreply@hapi.run> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Standalone bun script that walks every legacy chat at ~/.cursor/chats/<wsh>/<uuid>/store.db, stages an isolated $HOME/$HAPI_HOME, copies the store + synthesizes meta.json, and drives agent acp initialize + session/load. Surfaces a per-chat outcome class matching the upstream Cursor import refusal contract (verify_load_failed, verify_timeout, corrupted_store, ...). Doubles as a regression harness operators can re-run when cursor-agent updates its on-disk schema. Produces CSV at the configured path. Probe shape mirrors hub/src/cursor/acpVerifyProbe.ts (introduced by tiann#844). Script is intentionally self-contained so it remains runnable even before the import endpoints land. Co-authored-by: Cursor <cursoragent@cursor.com>
Adds /api/cursor/importable-sessions + /api/cursor/import endpoints mirroring the codex import shape from tiann#796, with strict ACP-only refusal contract enforced before any HAPI row is created. A maintainer-side audit ran 391/391 = 100% pass on a real chat library before this code shipped; the strictness is theoretical-cost-only in practice. Architecture: parallel /cursor/* endpoints alongside /codex/*, shared types under hub/src/web/routes/_agentImport/types.ts. The cursorImporter wraps the existing cursorLegacyMigrator verify-probe pattern. Legacy stream-json stores are transplanted to the ACP location only after agent acp initialize + session/load both succeed. ACP-format stores are imported as-is. Refusal cases (verify_load_failed, missing_on_disk_store, target_already_exists, already_imported, agent_binary_not_found, verify_timeout, corrupted_store, ambiguous_legacy_store, internal_error) leave disk and DB state untouched. 20 unit tests cover discovery, every refusal branch, both happy paths, and route shape. Real agent acp integration coverage lives in the existing cursorLegacyMigratorIntegration harness. Refs tiann#732 Co-authored-by: Cursor <cursoragent@cursor.com>
Single toolbar import affordance with Codex | Cursor | Claude tabs; remove separate Claude toolbar button and router wiring. Co-authored-by: Cursor <cursoragent@cursor.com>
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.
Summary
Adds the Cursor flavor to the multi-agent session import surface (
Fixes #732).Blocked on #942 (
feat/claude-session-import). Do not merge until #942 lands onmain; then rebase this branch ontomain(or retarget the PR) before merge.Review the cursor-only delta now:
heavygee/hapi@dzshzx:feat/claude-session-import...heavygee:feat/agent-session-import-picker
Cursor delta (3 commits on top of #942)
chore(scripts):cursor-acp verify-probe audit harnessfeat(hub):/api/cursor/*import routes + strict ACP verify-probefeat(web):AgentSessionImportDialogcodex|cursor tabs on sharedSessionImportPickerClaude import keeps its separate toolbar icon from #942.
Not in this PR: MCP stateful transport — #944 (land separately; more urgent for live
display_image).Test plan
bun typecheckAgentSessionImportDialog.test.tsxIssues
Fixes #732
Made with Cursor