tooling: hapi-resurrect-session + auto-migrate chain-forward#36
tooling: hapi-resurrect-session + auto-migrate chain-forward#36heavygee wants to merge 2 commits into
Conversation
Operator hand-built this 361-line script on 2026-06-06 to recover a HAPI session whose `cursorSessionId` was set to NULL when the hub auto-archived on agent crash. The procedure (path-hash discovery against ~/.cursor/chats/<md5(path)>/, sqlite metadata patch, hapi-restart-hub, POST /api/sessions/<id>/resume) was used twice in the same session and works; committing before it gets clobbered by a `git clean` the way the attach scripts did last week. Why this matters for the ACP migration (PR #34 / upstream tiann#824): the migrator refuses sessions in `lifecycleState='running'` and is not designed to recover crashed sessions with `cursorSessionId=NULL`. A crashed legacy session has to be resurrected (back to `lifecycleState='inactive'` with `cursorSessionProtocol='stream-json'`) BEFORE `hapi cursor migrate <id>` becomes applicable. This script closes that gap. Surface: hapi-resurrect-session <hapi-session-id-or-prefix> [--cursor-uuid <uuid>] # skip auto-discovery [--symlink-old-path <old> --as <new>] # moved worktrees [--name '<friendly label>'] [--dry-run] [--no-restart-hub] Exit codes: 0 ok, 1 discovery failed, 2 sqlite patch failed, 3 resume call failed, 4 cursor-agent crashed within 8s of spawn. No new dependencies (Python 3 stdlib + sqlite3 + curl). Co-authored-by: Cursor <cursoragent@cursor.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
After successful resurrection the session is back to inactive + cursorSessionProtocol=stream-json by design (legacy launcher knows how to dispatch the stream-json store). Once #34's invisible auto- migrate ships, SyncEngine.resumeSession will transplant this to ACP transparently on the operator's next Reopen — no manual migrate command required. This adds three terminal-stdout lines at the end of the script so the operator sees the next step without having to remember #34 exists: [hapi-resurrect-session] session is on legacy stream-json. [hapi-resurrect-session] next: open this session in the HAPI web UI and click Reopen. [hapi-resurrect-session] once #34 (tiann#824) auto-migrate is live, that [hapi-resurrect-session] Reopen will transparently transplant this session to ACP [hapi-resurrect-session] (cp store.db, verify session/load, flip protocol; ~15-20s banner). [hapi-resurrect-session] kill-switch: HAPI_CURSOR_LEGACY_AUTO_MIGRATE=0 in hub env. If LIVE_PROTO is already 'acp' (e.g. operator re-ran resurrect on a session that auto-migrate had already handled), the script just prints "session is already on ACP. No further action needed." instead. Pure UX polish. No code-path change. Replaces the earlier attach-agent-chat.ts post-attach hint that I dropped — that approach was obsoleted by #34 moving migration from operator-driven CLI to invisible auto-fire inside resumeSession. Co-authored-by: Cursor <cursoragent@cursor.com>
c143c0e to
228757c
Compare
|
Closing without merging. The chain-forward polish commit (228757c) was self-contradicting under the broader strategic call: cursor-agent will deprecate legacy stream-json eventually, so any HAPI tool that creates or shepherds a stream-json row is building tech debt. The right next move is a larger upstream PR for Cursor import that mirrors codex (tiann#796) but produces ACP sessions directly via #34's cursorLegacyMigrator as a transplant primitive - never touching stream-json at any point. The branch `tooling/legacy-chat-acp-alignment` will be kept alive (not deleted) as the durable location for `hapi-resurrect-session.sh` so the operator capability survives. The script will be retired in favor of the hub-side import primitive once the bigger PR lands. Plan doc: `docs/plans/2026-06-08-upstream-cursor-import-acp-only.md` (in fork). |
Summary
Force-pushed 2026-06-08 to drop the obsolete post-attach hint commit. PR #34 evolved between 2026-06-06 and 2026-06-08 from "operator-driven CLI/button migrator" to "invisible auto-migrate inside
SyncEngine.resumeSession". The post-attach hint's reference tohapi cursor migrate <id>was already wrong by the time #34 settled, so dropping it is the honest fix.Two-commit branch now:
tooling(resurrect)(2ed1bb30) — commitsscripts/tooling/hapi-resurrect-session.sh(361 lines, operator-authored 2026-06-06). Recovers HAPI sessions auto-archived on agent crash withcursorSessionId=NULL(the failure mode wherePOST /api/sessions/:id/resumereturns 500 "Resume session ID unavailable").tooling(resurrect) chain-forward(228757ce) — adds 18 lines of stdout polish at the end of the script that points the operator atReopen→ auto-migrate (feat(cursor): operator-driven legacy stream-json -> ACP session migrator (transplant) #34) as the next step. Pure UX polish; no code-path change.Why this matters relative to #34
#34's
maybeAutoMigrateLegacyCursorSessionfires insideSyncEngine.resumeSession. ButresumeSessionnever reaches the auto-migrate call if it errors out at thecursorSessionIdlookup first. A crashed-archived session withcursorSessionId=NULLis permanently stranded as un-resumable — auto-migrate has nothing to migrate because there's no resume token to look up the legacy store with.hapi-resurrect-session.shfixes the null first (path-hash discovery against~/.cursor/chats/<md5(metadata.path)>/, sqlite metadata patch, hub restart,POST /resume). After this script succeeds, the session is back tolifecycleState=inactivewithcursorSessionProtocol=stream-json. The operator clicks Reopen → #34's auto-migrate fires transparently → session ends up on ACP.The chain is: resurrect → Reopen → auto-migrate. The polish commit's stdout lines literally walk the operator through that chain so they don't have to remember step 3 exists.
End-to-end flow (crash branch, after #34 lands)
Refusal contract for
hapi-resurrect-session.sh/api/sessions/<id>/resumereturned non-2xxIdempotent: re-running on an already-resurrected session is safe.
--dry-runprints the discovery + planned patch without writing.Why this stays as a script (not a hub endpoint)
cursorSessionId=NULLrecovery (verified viagh search issues --repo tiann/hapi).POST /api/sessions/:id/resurrectendpoint, or better, as prevention: capturecursorSessionIdto a session-events log BEFORE archive so the token is never lost in the first place. Either of those is a separate upstream issue/PR.Test plan
bash -n scripts/tooling/hapi-resurrect-session.shsyntax-checks--helpprints the documented usage blockCompanion branch
tooling/backfill-truncation-warnings(#37) — raises backfillMAX_MESSAGEScap from 2000 to 50_000 and surfaces aBACKFILL TRUNCATEDwarning when transcripts exceed the cap. Independent of migration concerns; should land on its own merits.