For a "resume session" or "attach to session" flow, don't start from the full session list. Use listConnectableSessions() to narrow the choices, then prepareSessionForRemoteAttach() to decide whether the selected session is live, wakeable, or already history-only.
These methods tell you whether a session can still be reconnected to or should fall back to replay. They don't open a controller socket for you.
The Pattern
Listing Reconnectable Sessions
Use listConnectableSessions() when you want to show only sessions that still have a reconnect story.
import { CoderClient } from '@agentuity/coder';
async function main(): Promise<void> {
const client = new CoderClient();
try {
// listConnectableSessions filters out history-only sessions so your picker starts from resumable candidates
const { sessions } = await client.listConnectableSessions({ limit: 20 });
console.log(
JSON.stringify(
sessions.map((session) => ({
sessionId: session.sessionId,
label: session.label,
status: session.status,
bucket: session.bucket,
runtimeAvailable: session.runtimeAvailable,
wakeAvailable: session.wakeAvailable,
})),
null,
2
)
);
} catch (error) {
const message = error instanceof Error ? error.stack ?? error.message : String(error);
console.error('Failed to list reconnectable sessions');
console.error(message);
process.exitCode = 1;
}
}
void main();An empty list is common. Sessions in bucket: 'provisioning' are intentionally excluded until they reach running or paused, so a session you just created won't show up yet. Others may be absent because they're already history-only or no longer in a resumable state.
Preparing One Session for Reconnect
Once a user picks a session, use prepareSessionForRemoteAttach() to check whether it is live, wakeable, or already read-only.
import { CoderClient } from '@agentuity/coder';
const sessionId = process.argv[2];
async function main(): Promise<void> {
const client = new CoderClient();
try {
if (!sessionId) {
throw new Error(
'Pass a session ID as the first argument, for example: bun example.ts codesess_123'
);
}
// Polls until the session is attachable, wakeable, or history-only
// The returned session carries the live flags your UI branches on below
const session = await client.prepareSessionForRemoteAttach(sessionId, {
timeoutMs: 30_000,
pollIntervalMs: 1_000,
});
if (session.historyOnly) {
// The live runtime is gone. Hydrate from replay or a read-only view instead
console.log('Session is history-only. Use replay or session detail instead of re-attaching.');
return;
}
if (session.runtimeAvailable) {
// Attaching before the runtime is up fails; this check confirms it's ready
console.log('Session is attachable.');
console.log(
JSON.stringify(
{
sessionId: session.sessionId,
status: session.status,
bucket: session.bucket,
runtimeAvailable: session.runtimeAvailable,
},
null,
2
)
);
return;
}
// wakeAvailable is false, so prepareSessionForRemoteAttach returned immediately
// without polling. Typical for provisioning-bucket sessions that aren't yet wakeable
console.log('Session is not attachable yet.');
console.log(
JSON.stringify(
{
sessionId: session.sessionId,
status: session.status,
bucket: session.bucket,
runtimeAvailable: session.runtimeAvailable,
wakeAvailable: session.wakeAvailable,
historyOnly: session.historyOnly,
},
null,
2
)
);
} catch (error) {
const message = error instanceof Error ? error.stack ?? error.message : String(error);
console.error('Failed to prepare the session for remote attach');
console.error(message);
process.exitCode = 1;
}
}
void main();What to Look For
The two methods solve different parts of the reconnect story:
listConnectableSessions()builds a clean picker or "resume recent work" listprepareSessionForRemoteAttach()runs the state check for one chosen sessionruntimeAvailable: truemeans the runtime is ready for a controller clienthistoryOnly: truemeans the live runtime is gone, so fall back to the replay hydration stream orlistEventHistory()wakeAvailable: truemeans the session may be resumable, even if it isn't live yet
Use listConnectableSessions() instead of listSessions() here. It already filters out history-only sessions, so your UI starts from sessions that still have a reconnect path.
In app code, call prepareSessionForRemoteAttach() right before your app opens a live connection to the session, whether that's to send messages to the agent or stream its events. It returns the current session detail so your app can branch on runtimeAvailable, wakeAvailable, and historyOnly. A thrown error usually means the underlying getSession() or resumeSession() call failed, so handle it like any other SDK error.
Example Output
An attachable session may look like this:
{
"sessionId": "codesess_abc123",
"status": "active",
"bucket": "running",
"runtimeAvailable": true
}A session that can no longer be re-attached falls back to session history or stored session data instead:
{
"sessionId": "codesess_def456",
"status": "archived",
"bucket": "history",
"runtimeAvailable": false,
"wakeAvailable": false,
"historyOnly": true
}Key Points
- Use
listConnectableSessions()for chooser UIs, resume commands, and recent-session pickers. - Call
prepareSessionForRemoteAttach()right before your app opens its live connection to the session, and branch onruntimeAvailable,wakeAvailable, andhistoryOnlyto decide the next step. - If the timeout expires, you still get the latest session detail, which is useful for your own retry logic.
- Once a session is
historyOnly, switch to the replay hydration stream orlistEventHistory()for a structured timeline. - For the CLI flow,
agentuity coder tui --remotebuilds on the same reconnect idea.
See Also
- Managing Coder Sessions with the SDK: Create sessions, read state, and manage lifecycle from code
- Creating Loop-Mode Coder Sessions: Create loop workflows and inspect loop state
- Coder Commands: Use
agentuity coder tui --remotewhen you want the CLI remote-attach flow