Skip to content

Spike: suspend can preserve agent identity without vendoring hub-agent - #291

Closed
TheGreatAxios wants to merge 1 commit into
mainfrom
cl-suspend-spike
Closed

Spike: suspend can preserve agent identity without vendoring hub-agent#291
TheGreatAxios wants to merge 1 commit into
mainfrom
cl-suspend-spike

Conversation

@TheGreatAxios

Copy link
Copy Markdown
Contributor

Summary

Go/no-go spike for CL-6581 (blocked on: CL-6239 — upstream ask to make
@intx/hub-agent's undeploy path non-destructive so idle-sleep can suspend
an agent instead of terminate-and-relaunch).

Finding: no upstream ask, no vendoring required. A sidecar-side fix is
sufficient.

  • @intx/hub-agent is consumed as a published npm package (vendor/intx/hub-agent
    does not exist — only 7 other @intx/* trees are vendored, per VENDORED.md).
    Its AgentKeyStore persists each agent's reconnect-challenge Ed25519 keypair
    under agentDir(dataDir, address)/keys/, and handleAgentUndeploy
    (ws/hub-link.js) unconditionally calls sessions.deleteAgentDir
    repoStore.removefsp.rm(agentDir, { recursive: true }) on every
    sendAgentUndeploy, hibernate reason or not — that whole call chain lives
    inside the published package, unreachable from this repo without vendoring.
  • Workbench already has a hibernate-aware flag at this exact seam:
    apps/sidecar/src/workflow-host-wiring/index.ts's undeploy(frame) computes
    reclaimDirs = frame.reason !== IDLE_HIBERNATE_UNDEPLOY_REASON and uses it to
    skip deleting the workflow-deployment record / releasing the slug — but it has
    no power over the key directory, since that deletion happens in hub-link.js
    after this hook returns, regardless of what the hook did.
  • agentDir(dataDir, address) is a stable public export of @intx/hub-agent.
    This PoC proves, against the real unmodified package, that a caller can
    snapshot the whole directory before the destructive remove() and restore it
    before the next deploy, and loadOrGenerateKey comes back isNew: false
    with the identical keypair. No fork, no vendor, no upstream ask needed —
    extend the existing reclaimDirs-gated hook to snapshot/restore agentDir.

What this PR is (and isn't)

This is a standalone proof-of-concept test only — it exercises
@intx/hub-agent's real APIs against a tmp dir to establish the technique
works. It does not wire the snapshot/restore into the actual
workflow-host-wiring undeploy/deploy hooks, build idle-sleep, or touch
lifecycle/packages/folded-runs — that's the real feature build, owned by a
separate lane, once this go/no-go lands.

Test plan

  • bun test apps/sidecar/test/suspend-key-preservation.poc.test.ts — 2/2 pass
  • Baseline test confirms the real destructive bug (fresh keypair minted post-remove)
  • Snapshot/restore test confirms isNew: false + identical keypair bytes post-restore
  • tsc --noEmit and eslint clean on the new file

Linear: CL-6581 (spike ticket, links CL-6239)

…b-agent

CL-6581 asks whether the idle-sleep epic is really blocked on an upstream
ask (CL-6239) to make @intx/hub-agent's undeploy non-destructive. It's
consumed as a published package here (no vendor/intx/hub-agent tree), but
the destructive fsp.rm(agentDir) lives entirely inside it. This PoC proves
against the real, unmodified package that a caller can snapshot agentDir
(a stable public export) before the delete and restore it before the next
deploy, and loadOrGenerateKey comes back isNew: false with the same
keypair -- so the fix is a sidecar-side snapshot/restore, not a vendor or
an upstream ask.

Standalone proof only; wiring this into workflow-host-wiring's existing
reclaimDirs-aware undeploy/deploy hooks is the actual feature build.
@TheGreatAxios

Copy link
Copy Markdown
Contributor Author

Superseded by #293, which wires this into the real undeploy/redeploy hooks and carries equivalent tests against the unmodified @intx/hub-agent (hibernate preserves the keypair; a non-hibernate undeploy still destroys it). The go/no-go finding this PR established is recorded in CL-6585. Closing to keep the queue clean — reopen if the baseline test proves something #293's suite doesn't.

TheGreatAxios added a commit that referenced this pull request Aug 22, 2026
CL-6581: the published @intx/hub-agent package destroys an agent's
reconnect-challenge keypair on every undeploy, hibernate or not, which is
the documented root cause of CL-6203/CL-6044. These tests exercise a
snapshot-before-delete/restore-before-redeploy technique (proved viable in
PR #291) against the real, unmodified @intx/hub-agent key/repo stores, and
end-to-end through createSidecarDeployRouter's actual undeploy/deploy
hooks: a hibernate teardown must preserve the identity across a real
redeploy, a reclaiming teardown must still destroy it, a broken snapshot
must report loudly instead of failing silent, and an orphaned snapshot
must be reaped past its retention window.

The shared lifecycle fixture gains an optional real-key-store override
(needed because its default fake never touches disk) and its default fake
now creates a real agentDir so an unrelated hibernate-teardown test does
not trip the vault's ordering-broke detector.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant