@getpaseo/server 0.1.91 → 0.1.93

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist/server/server/agent/agent-manager.d.ts +12 -4
  2. package/dist/server/server/agent/agent-manager.js +87 -26
  3. package/dist/server/server/agent/agent-projections.d.ts +4 -2
  4. package/dist/server/server/agent/agent-projections.js +8 -28
  5. package/dist/server/server/agent/agent-sdk-types.d.ts +30 -10
  6. package/dist/server/server/agent/import-sessions.d.ts +3 -2
  7. package/dist/server/server/agent/import-sessions.js +23 -55
  8. package/dist/server/server/agent/provider-registry.js +34 -13
  9. package/dist/server/server/agent/provider-session-import.d.ts +10 -0
  10. package/dist/server/server/agent/provider-session-import.js +49 -0
  11. package/dist/server/server/agent/providers/acp-agent.d.ts +12 -2
  12. package/dist/server/server/agent/providers/acp-agent.js +78 -36
  13. package/dist/server/server/agent/providers/claude/agent.d.ts +3 -2
  14. package/dist/server/server/agent/providers/claude/agent.js +28 -24
  15. package/dist/server/server/agent/providers/claude/models.js +15 -0
  16. package/dist/server/server/agent/providers/codex-app-server-agent.d.ts +3 -2
  17. package/dist/server/server/agent/providers/codex-app-server-agent.js +20 -21
  18. package/dist/server/server/agent/providers/cursor-acp-agent.d.ts +1 -0
  19. package/dist/server/server/agent/providers/cursor-acp-agent.js +1 -0
  20. package/dist/server/server/agent/providers/generic-acp-agent.d.ts +9 -0
  21. package/dist/server/server/agent/providers/generic-acp-agent.js +18 -1
  22. package/dist/server/server/agent/providers/mock-load-test-agent.d.ts +3 -2
  23. package/dist/server/server/agent/providers/mock-load-test-agent.js +11 -1
  24. package/dist/server/server/agent/providers/mock-slow-provider.d.ts +1 -2
  25. package/dist/server/server/agent/providers/mock-slow-provider.js +0 -3
  26. package/dist/server/server/agent/providers/opencode/test-utils/test-opencode-runtime.d.ts +2 -0
  27. package/dist/server/server/agent/providers/opencode/test-utils/test-opencode-runtime.js +8 -0
  28. package/dist/server/server/agent/providers/opencode-agent.d.ts +3 -2
  29. package/dist/server/server/agent/providers/opencode-agent.js +60 -32
  30. package/dist/server/server/agent/providers/pi/agent.d.ts +3 -2
  31. package/dist/server/server/agent/providers/pi/agent.js +28 -5
  32. package/dist/server/server/agent/providers/pi/session-descriptor.d.ts +3 -4
  33. package/dist/server/server/agent/providers/pi/session-descriptor.js +17 -37
  34. package/dist/server/server/session.js +0 -1
  35. package/package.json +5 -5
@@ -0,0 +1,10 @@
1
+ import type { AgentClient, AgentPersistenceHandle, AgentProvider, AgentSessionConfig, ImportedProviderSession, ImportProviderSessionContext, ImportProviderSessionInput } from "./agent-sdk-types.js";
2
+ export declare function importSessionFromPersistence(input: {
3
+ provider: AgentProvider;
4
+ request: ImportProviderSessionInput;
5
+ context: ImportProviderSessionContext;
6
+ resumeSession: AgentClient["resumeSession"];
7
+ config?: Partial<AgentSessionConfig>;
8
+ persistence?: AgentPersistenceHandle;
9
+ }): Promise<ImportedProviderSession>;
10
+ //# sourceMappingURL=provider-session-import.d.ts.map
@@ -0,0 +1,49 @@
1
+ export async function importSessionFromPersistence(input) {
2
+ const config = {
3
+ ...input.context.config,
4
+ ...input.config,
5
+ provider: input.provider,
6
+ cwd: input.request.cwd,
7
+ };
8
+ const storedConfig = {
9
+ ...input.context.storedConfig,
10
+ ...input.config,
11
+ provider: input.provider,
12
+ cwd: input.request.cwd,
13
+ };
14
+ const persistence = input.persistence ?? buildImportPersistenceHandle(input.provider, input.request, storedConfig);
15
+ const session = await input.resumeSession(persistence, config, input.context.launchContext);
16
+ const timeline = await collectImportedTimeline(session.streamHistory());
17
+ return {
18
+ session,
19
+ config: storedConfig,
20
+ persistence,
21
+ timeline,
22
+ };
23
+ }
24
+ function buildImportPersistenceHandle(provider, input, config) {
25
+ return {
26
+ provider,
27
+ sessionId: input.providerHandleId,
28
+ nativeHandle: input.providerHandleId,
29
+ metadata: {
30
+ ...config,
31
+ provider,
32
+ cwd: input.cwd,
33
+ },
34
+ };
35
+ }
36
+ async function collectImportedTimeline(events) {
37
+ const timeline = [];
38
+ for await (const event of events) {
39
+ if (event.type !== "timeline") {
40
+ continue;
41
+ }
42
+ timeline.push({
43
+ item: event.item,
44
+ ...(event.timestamp ? { timestamp: event.timestamp } : {}),
45
+ });
46
+ }
47
+ return timeline;
48
+ }
49
+ //# sourceMappingURL=provider-session-import.js.map
@@ -2,8 +2,14 @@ import { type ChildProcessWithoutNullStreams } from "node:child_process";
2
2
  import type { ReadableStream as NodeReadableStream, WritableStream as NodeWritableStream } from "node:stream/web";
3
3
  import { ClientSideConnection, type Client as ACPClient, type CreateTerminalRequest, type InitializeResponse, type KillTerminalRequest, type LoadSessionResponse, type NewSessionResponse, type ReadTextFileRequest, type RequestPermissionRequest, type RequestPermissionResponse, type ResumeSessionResponse, type SessionConfigOption, type SessionMode, type SessionModelState, type SessionNotification, type TerminalOutputRequest, type TerminalOutputResponse, type ToolCallContent, type ToolCallLocation, type ToolCallStatus, type ToolKind, type Usage, type WaitForTerminalExitRequest, type WriteTextFileRequest, type Stream as ACPStream } from "@agentclientprotocol/sdk";
4
4
  import type { Logger } from "pino";
5
- import { type AgentCapabilityFlags, type AgentClient, type AgentLaunchContext, type AgentMode, type AgentModelDefinition, type AgentPermissionRequest, type AgentPermissionResponse, type AgentPersistenceHandle, type AgentPromptInput, type AgentRunOptions, type AgentRunResult, type AgentRuntimeInfo, type AgentSession, type AgentSessionConfig, type AgentSlashCommand, type AgentStreamEvent, type AgentUsage, type ListModesOptions, type ListModelsOptions, type ListPersistedAgentsOptions, type PersistedAgentDescriptor } from "../agent-sdk-types.js";
5
+ import { type AgentCapabilityFlags, type AgentClient, type AgentLaunchContext, type AgentMode, type AgentModelDefinition, type AgentPermissionRequest, type AgentPermissionResponse, type AgentPersistenceHandle, type AgentPromptInput, type AgentRunOptions, type AgentRunResult, type AgentRuntimeInfo, type AgentSession, type AgentSessionConfig, type AgentSlashCommand, type AgentStreamEvent, type AgentUsage, type ImportableProviderSession, type ImportProviderSessionContext, type ImportProviderSessionInput, type ListImportableSessionsOptions, type ListModesOptions, type ListModelsOptions } from "../agent-sdk-types.js";
6
6
  import { type ProviderRuntimeSettings } from "../provider-launch-config.js";
7
+ export declare function summarizeACPRequestError(error: unknown): {
8
+ message: string;
9
+ code?: string;
10
+ diagnostic?: string;
11
+ };
12
+ export declare const DEFAULT_ACP_CAPABILITIES: AgentCapabilityFlags;
7
13
  export declare function createLoggedNdJsonStream(output: NodeWritableStream, input: NodeReadableStream, options: {
8
14
  logger: Logger;
9
15
  provider: string;
@@ -147,13 +153,15 @@ export declare class ACPAgentClient implements AgentClient {
147
153
  resumeSession(handle: AgentPersistenceHandle, overrides?: Partial<AgentSessionConfig>, launchContext?: AgentLaunchContext): Promise<AgentSession>;
148
154
  listModels(options: ListModelsOptions): Promise<AgentModelDefinition[]>;
149
155
  listModes(options: ListModesOptions): Promise<AgentMode[]>;
150
- listPersistedAgents(options?: ListPersistedAgentsOptions): Promise<PersistedAgentDescriptor[]>;
156
+ listImportableSessions(options?: ListImportableSessionsOptions): Promise<ImportableProviderSession[]>;
157
+ importSession(input: ImportProviderSessionInput, context: ImportProviderSessionContext): Promise<import("../agent-sdk-types.js").ImportedProviderSession>;
151
158
  isAvailable(): Promise<boolean>;
152
159
  protected spawnProcess(launchEnv?: Record<string, string>, options?: {
153
160
  initializeTimeoutMs?: number;
154
161
  }): Promise<SpawnedACPProcess>;
155
162
  protected buildProbeClient(): ACPClient;
156
163
  protected closeProbe(probe: SpawnedACPProcess): Promise<void>;
164
+ protected runACPRequest<T>(request: () => Promise<T>): Promise<T>;
157
165
  protected resolveLaunchCommand(): Promise<{
158
166
  command: string;
159
167
  args: string[];
@@ -257,6 +265,8 @@ export declare class ACPAgentSession implements AgentSession, ACPClient {
257
265
  }): Promise<void>;
258
266
  killTerminal(params: KillTerminalRequest): Promise<Record<string, never>>;
259
267
  private spawnProcess;
268
+ private runACPRequest;
269
+ private acpMcpServers;
260
270
  private applySessionState;
261
271
  private transformConfigOptions;
262
272
  private transformModeId;
@@ -4,6 +4,7 @@ import path from "node:path";
4
4
  import { Readable, Writable } from "node:stream";
5
5
  import { ClientSideConnection, PROTOCOL_VERSION, } from "@agentclientprotocol/sdk";
6
6
  import { getAgentStreamEventTurnId, } from "../agent-sdk-types.js";
7
+ import { importSessionFromPersistence } from "../provider-session-import.js";
7
8
  import { checkProviderLaunchAvailable, createProviderEnvSpec, resolveProviderLaunch, } from "../provider-launch-config.js";
8
9
  import { renderPromptAttachmentAsText } from "../prompt-attachments.js";
9
10
  import { appendOrReplaceGrowingAssistantMessage, runProviderTurn } from "./provider-runner.js";
@@ -19,15 +20,29 @@ function isRecord(value) {
19
20
  function isACPError(value) {
20
21
  return isRecord(value) && typeof value.message === "string" && typeof value.code === "number";
21
22
  }
22
- function summarizeACPRequestError(error) {
23
+ function extractACPErrorDataMessage(data) {
24
+ if (!isRecord(data)) {
25
+ return null;
26
+ }
27
+ for (const key of ["details", "errorMessage", "message", "detail", "title"]) {
28
+ const value = data[key];
29
+ if (typeof value === "string" && value.trim()) {
30
+ return value.trim();
31
+ }
32
+ }
33
+ return extractACPErrorDataMessage(data.error);
34
+ }
35
+ export function summarizeACPRequestError(error) {
23
36
  // Promise rejections are untyped, but the ACP SDK rejects JSON-RPC failures as response.error.
24
37
  if (isACPError(error)) {
25
38
  const code = String(error.code);
39
+ const detail = extractACPErrorDataMessage(error.data);
40
+ const message = detail && detail !== error.message ? `${error.message}: ${detail}` : error.message;
26
41
  const data = error.data === undefined ? "" : ` | data=${JSON.stringify(error.data)}`;
27
42
  return {
28
- message: error.message,
43
+ message,
29
44
  code,
30
- diagnostic: `${error.message} | code=${code}${data}`,
45
+ diagnostic: `${message} | code=${code}${data}`,
31
46
  };
32
47
  }
33
48
  if (error instanceof Error) {
@@ -35,6 +50,15 @@ function summarizeACPRequestError(error) {
35
50
  }
36
51
  return { message: String(error) };
37
52
  }
53
+ function toACPRequestError(error) {
54
+ if (!isACPError(error)) {
55
+ return error instanceof Error ? error : new Error(String(error));
56
+ }
57
+ const summary = summarizeACPRequestError(error);
58
+ const next = new Error(summary.message);
59
+ next.name = "ACPRequestError";
60
+ return next;
61
+ }
38
62
  function resolveTerminalCommand(command, args) {
39
63
  if (args && args.length > 0) {
40
64
  return { command, args };
@@ -45,7 +69,7 @@ function resolveTerminalCommand(command, args) {
45
69
  const shell = platformShell();
46
70
  return { command: shell.command, args: [...shell.flag, command] };
47
71
  }
48
- const DEFAULT_ACP_CAPABILITIES = {
72
+ export const DEFAULT_ACP_CAPABILITIES = {
49
73
  supportsStreaming: true,
50
74
  supportsSessionPersistence: true,
51
75
  supportsDynamicModes: true,
@@ -318,10 +342,10 @@ export class ACPAgentClient {
318
342
  const { cwd } = options;
319
343
  const probe = await this.spawnProcess(PROBE_ENV);
320
344
  try {
321
- const response = await probe.connection.newSession({
345
+ const response = await this.runACPRequest(() => probe.connection.newSession({
322
346
  cwd,
323
347
  mcpServers: [],
324
- });
348
+ }));
325
349
  const transformed = this.transformSessionResponse(response);
326
350
  const models = deriveModelDefinitionsFromACP(this.provider, transformed.models, transformed.configOptions);
327
351
  return this.modelTransformer ? this.modelTransformer(models) : models;
@@ -334,10 +358,10 @@ export class ACPAgentClient {
334
358
  const { cwd } = options;
335
359
  const probe = await this.spawnProcess(PROBE_ENV);
336
360
  try {
337
- const response = await probe.connection.newSession({
361
+ const response = await this.runACPRequest(() => probe.connection.newSession({
338
362
  cwd,
339
363
  mcpServers: [],
340
- });
364
+ }));
341
365
  const transformed = this.transformSessionResponse(response);
342
366
  const modeInfo = deriveModesFromACP(this.defaultModes, transformed.modes, transformed.configOptions);
343
367
  return modeInfo.modes;
@@ -346,7 +370,7 @@ export class ACPAgentClient {
346
370
  await this.closeProbe(probe);
347
371
  }
348
372
  }
349
- async listPersistedAgents(options) {
373
+ async listImportableSessions(options) {
350
374
  const probe = await this.spawnProcess(PROBE_ENV);
351
375
  try {
352
376
  if (!probe.initialize.agentCapabilities?.sessionCapabilities?.list) {
@@ -355,25 +379,15 @@ export class ACPAgentClient {
355
379
  const sessions = [];
356
380
  let cursor;
357
381
  for (;;) {
358
- const page = await probe.connection.listSessions(cursor ? { cursor } : {});
382
+ const page = await this.runACPRequest(() => probe.connection.listSessions(cursor ? { cursor } : {}));
359
383
  for (const session of page.sessions) {
360
384
  sessions.push({
361
- provider: this.provider,
362
- sessionId: session.sessionId,
385
+ providerHandleId: session.sessionId,
363
386
  cwd: session.cwd,
364
387
  title: session.title ?? null,
388
+ firstPromptPreview: null,
389
+ lastPromptPreview: null,
365
390
  lastActivityAt: session.updatedAt ? new Date(session.updatedAt) : new Date(0),
366
- persistence: {
367
- provider: this.provider,
368
- sessionId: session.sessionId,
369
- nativeHandle: session.sessionId,
370
- metadata: {
371
- provider: this.provider,
372
- cwd: session.cwd,
373
- title: session.title ?? null,
374
- },
375
- },
376
- timeline: [],
377
391
  });
378
392
  }
379
393
  cursor = page.nextCursor ?? null;
@@ -388,6 +402,14 @@ export class ACPAgentClient {
388
402
  await this.closeProbe(probe);
389
403
  }
390
404
  }
405
+ async importSession(input, context) {
406
+ return importSessionFromPersistence({
407
+ provider: this.provider,
408
+ request: input,
409
+ context,
410
+ resumeSession: this.resumeSession.bind(this),
411
+ });
412
+ }
391
413
  async isAvailable() {
392
414
  try {
393
415
  await this.resolveLaunchCommand();
@@ -430,7 +452,7 @@ export class ACPAgentClient {
430
452
  : null;
431
453
  let initialize;
432
454
  try {
433
- initialize = await Promise.race([
455
+ initialize = await this.runACPRequest(() => Promise.race([
434
456
  connection.initialize({
435
457
  protocolVersion: PROTOCOL_VERSION,
436
458
  clientCapabilities: ACP_CLIENT_CAPABILITIES,
@@ -438,7 +460,7 @@ export class ACPAgentClient {
438
460
  }),
439
461
  spawnErrorPromise,
440
462
  ...(initializeTimeoutPromise ? [initializeTimeoutPromise] : []),
441
- ]);
463
+ ]));
442
464
  }
443
465
  catch (error) {
444
466
  await terminateChildProcess(child, 2000);
@@ -481,6 +503,14 @@ export class ACPAgentClient {
481
503
  await terminateChildProcess(probe.child, 2000);
482
504
  }
483
505
  }
506
+ async runACPRequest(request) {
507
+ try {
508
+ return await request();
509
+ }
510
+ catch (error) {
511
+ throw toACPRequestError(error);
512
+ }
513
+ }
484
514
  async resolveLaunchCommand() {
485
515
  const prefix = await resolveProviderLaunch({
486
516
  commandConfig: this.runtimeSettings?.command,
@@ -575,10 +605,10 @@ export class ACPAgentSession {
575
605
  this.child = spawned.child;
576
606
  this.connection = spawned.connection;
577
607
  this.agentCapabilities = spawned.initialize.agentCapabilities ?? null;
578
- const response = await this.connection.newSession({
608
+ const response = await this.runACPRequest(() => this.connection.newSession({
579
609
  cwd: this.config.cwd,
580
- mcpServers: normalizeMcpServers(this.config.mcpServers),
581
- });
610
+ mcpServers: this.acpMcpServers(),
611
+ }));
582
612
  this.sessionId = response.sessionId;
583
613
  this.bootstrapThreadEventPending = true;
584
614
  this.applySessionState(response);
@@ -598,21 +628,21 @@ export class ACPAgentSession {
598
628
  const sessionCapabilities = this.agentCapabilities?.sessionCapabilities;
599
629
  if (this.agentCapabilities?.loadSession) {
600
630
  this.replayingHistory = true;
601
- const response = await this.connection.loadSession({
631
+ const response = await this.runACPRequest(() => this.connection.loadSession({
602
632
  sessionId: handle.sessionId,
603
633
  cwd: this.config.cwd,
604
- mcpServers: normalizeMcpServers(this.config.mcpServers),
605
- });
634
+ mcpServers: this.acpMcpServers(),
635
+ }));
606
636
  this.replayingHistory = false;
607
637
  this.historyPending = this.persistedHistory.length > 0;
608
638
  this.applySessionState(response);
609
639
  }
610
640
  else if (sessionCapabilities?.resume) {
611
- const response = await this.connection.unstable_resumeSession({
641
+ const response = await this.runACPRequest(() => this.connection.unstable_resumeSession({
612
642
  sessionId: handle.sessionId,
613
643
  cwd: this.config.cwd,
614
- mcpServers: normalizeMcpServers(this.config.mcpServers),
615
- });
644
+ mcpServers: this.acpMcpServers(),
645
+ }));
616
646
  this.applySessionState(response);
617
647
  }
618
648
  else {
@@ -1282,13 +1312,24 @@ export class ACPAgentSession {
1282
1312
  });
1283
1313
  const stream = createLoggedNdJsonStream(Writable.toWeb(child.stdin), Readable.toWeb(child.stdout), { logger: this.logger, provider: this.provider });
1284
1314
  const connection = new ClientSideConnection(() => this, stream);
1285
- const initialize = await connection.initialize({
1315
+ const initialize = await this.runACPRequest(() => connection.initialize({
1286
1316
  protocolVersion: PROTOCOL_VERSION,
1287
1317
  clientCapabilities: ACP_CLIENT_CAPABILITIES,
1288
1318
  clientInfo: { name: "Paseo", version: "dev" },
1289
- });
1319
+ }));
1290
1320
  return { child, connection, initialize };
1291
1321
  }
1322
+ async runACPRequest(request) {
1323
+ try {
1324
+ return await request();
1325
+ }
1326
+ catch (error) {
1327
+ throw toACPRequestError(error);
1328
+ }
1329
+ }
1330
+ acpMcpServers() {
1331
+ return this.capabilities.supportsMcpServers ? normalizeMcpServers(this.config.mcpServers) : [];
1332
+ }
1292
1333
  applySessionState(response) {
1293
1334
  const transformed = this.sessionResponseTransformer
1294
1335
  ? this.sessionResponseTransformer(response)
@@ -1400,6 +1441,7 @@ export class ACPAgentSession {
1400
1441
  name: command.name,
1401
1442
  description: command.description,
1402
1443
  argumentHint: "",
1444
+ kind: "command",
1403
1445
  }));
1404
1446
  this.settleCommandsReady();
1405
1447
  return [];
@@ -1,7 +1,7 @@
1
1
  import { type AgentDefinition, type McpServerConfig as ClaudeSdkMcpServerConfig, type SDKMessage } from "@anthropic-ai/claude-agent-sdk";
2
2
  import type { Logger } from "pino";
3
3
  import { type ClaudeQueryFactory } from "./query.js";
4
- import { type AgentCapabilityFlags, type AgentClient, type AgentCreateSessionOptions, type AgentFeature, type AgentLaunchContext, type AgentMetadata, type AgentModelDefinition, type AgentPersistenceHandle, type AgentSession, type AgentSessionConfig, type AgentTimelineItem, type ListModelsOptions, type ListPersistedAgentsOptions, type McpServerConfig, type PersistedAgentDescriptor } from "../../agent-sdk-types.js";
4
+ import { type AgentCapabilityFlags, type AgentClient, type AgentCreateSessionOptions, type AgentFeature, type AgentLaunchContext, type AgentMetadata, type AgentModelDefinition, type AgentPersistenceHandle, type AgentSession, type AgentSessionConfig, type AgentTimelineItem, type ImportableProviderSession, type ImportProviderSessionContext, type ImportProviderSessionInput, type ListImportableSessionsOptions, type ListModelsOptions, type McpServerConfig } from "../../agent-sdk-types.js";
5
5
  import { type ProviderRuntimeSettings } from "../../provider-launch-config.js";
6
6
  export declare function normalizeClaudeAskUserQuestionUpdatedInput(updatedInput: AgentMetadata | undefined, fallbackInput: AgentMetadata | undefined): AgentMetadata;
7
7
  interface EventIdentifiers {
@@ -40,7 +40,8 @@ export declare class ClaudeAgentClient implements AgentClient {
40
40
  resumeSession(handle: AgentPersistenceHandle, overrides?: Partial<AgentSessionConfig>, launchContext?: AgentLaunchContext): Promise<AgentSession>;
41
41
  listModels(_options: ListModelsOptions): Promise<AgentModelDefinition[]>;
42
42
  listFeatures(config: AgentSessionConfig): Promise<AgentFeature[]>;
43
- listPersistedAgents(options?: ListPersistedAgentsOptions): Promise<PersistedAgentDescriptor[]>;
43
+ listImportableSessions(options?: ListImportableSessionsOptions): Promise<ImportableProviderSession[]>;
44
+ importSession(input: ImportProviderSessionInput, context: ImportProviderSessionContext): Promise<import("../../agent-sdk-types.js").ImportedProviderSession>;
44
45
  isAvailable(): Promise<boolean>;
45
46
  getDiagnostic(): Promise<{
46
47
  diagnostic: string;
@@ -17,6 +17,7 @@ import { realClaudeRewindSdk, revertClaudeConversation, revertClaudeFiles } from
17
17
  import { normalizeProviderReplayTimestamp } from "../../provider-history-timestamps.js";
18
18
  import { claudeProjectDirSync } from "./project-dir.js";
19
19
  import { getAgentStreamEventTurnId, } from "../../agent-sdk-types.js";
20
+ import { importSessionFromPersistence } from "../../provider-session-import.js";
20
21
  import { checkProviderLaunchAvailable, createProviderEnv, createProviderEnvSpec, resolveProviderLaunch, } from "../../provider-launch-config.js";
21
22
  import { withTimeout } from "../../../../utils/promise-timeout.js";
22
23
  import { execCommand } from "../../../../utils/spawn.js";
@@ -87,6 +88,7 @@ function isImageMimeType(value) {
87
88
  const CLAUDE_CAPABILITIES = {
88
89
  supportsStreaming: true,
89
90
  supportsSessionPersistence: true,
91
+ supportsSessionListing: true,
90
92
  supportsDynamicModes: true,
91
93
  supportsMcpServers: true,
92
94
  supportsReasoningStream: true,
@@ -974,7 +976,7 @@ export class ClaudeAgentClient {
974
976
  fastModeEnabled: claudeConfig.featureValues?.fast_mode === true,
975
977
  });
976
978
  }
977
- async listPersistedAgents(options) {
979
+ async listImportableSessions(options) {
978
980
  const configDir = process.env.CLAUDE_CONFIG_DIR ?? path.join(os.homedir(), ".claude");
979
981
  const projectsRoot = path.join(configDir, "projects");
980
982
  if (!(await pathExists(projectsRoot))) {
@@ -984,9 +986,17 @@ export class ClaudeAgentClient {
984
986
  const candidates = await collectRecentClaudeSessions(projectsRoot, limit * 3);
985
987
  const parsed = await Promise.all(candidates.map((candidate) => parseClaudeSessionDescriptor(candidate.path, candidate.mtime)));
986
988
  return parsed
987
- .filter((descriptor) => descriptor !== null)
989
+ .filter((session) => session !== null)
988
990
  .slice(0, limit);
989
991
  }
992
+ async importSession(input, context) {
993
+ return importSessionFromPersistence({
994
+ provider: "claude",
995
+ request: input,
996
+ context,
997
+ resumeSession: this.resumeSession.bind(this),
998
+ });
999
+ }
990
1000
  async isAvailable() {
991
1001
  const launch = await resolveProviderLaunch({
992
1002
  commandConfig: this.runtimeSettings?.command,
@@ -1655,6 +1665,7 @@ class ClaudeAgentSession {
1655
1665
  name: cmd.name,
1656
1666
  description: cmd.description,
1657
1667
  argumentHint: cmd.argumentHint,
1668
+ kind: "command",
1658
1669
  });
1659
1670
  }
1660
1671
  }
@@ -3951,16 +3962,12 @@ function applyClaudeSessionEntryToAccumulator(entryRaw, acc) {
3951
3962
  if (!acc.title) {
3952
3963
  acc.title = text;
3953
3964
  }
3954
- acc.timeline.push({ type: "user_message", text });
3965
+ const preview = normalizeImportablePromptPreview(text);
3966
+ acc.firstPromptPreview ?? (acc.firstPromptPreview = preview);
3967
+ acc.lastPromptPreview = preview;
3955
3968
  }
3956
3969
  return;
3957
3970
  }
3958
- if (entry.type === "assistant" && entry.message) {
3959
- const text = extractClaudeUserText(entry.message);
3960
- if (text) {
3961
- acc.timeline.push({ type: "assistant_message", text });
3962
- }
3963
- }
3964
3971
  }
3965
3972
  async function parseClaudeSessionDescriptor(filePath, mtime) {
3966
3973
  let content;
@@ -3974,7 +3981,8 @@ async function parseClaudeSessionDescriptor(filePath, mtime) {
3974
3981
  sessionId: null,
3975
3982
  cwd: null,
3976
3983
  title: null,
3977
- timeline: [],
3984
+ firstPromptPreview: null,
3985
+ lastPromptPreview: null,
3978
3986
  };
3979
3987
  for (const rawLine of content.split(/\r?\n/)) {
3980
3988
  const line = rawLine.trim();
@@ -3992,29 +4000,25 @@ async function parseClaudeSessionDescriptor(filePath, mtime) {
3992
4000
  break;
3993
4001
  }
3994
4002
  }
3995
- const { sessionId, cwd, title, timeline } = acc;
4003
+ const { sessionId, cwd, title } = acc;
3996
4004
  if (!sessionId || !cwd) {
3997
4005
  return null;
3998
4006
  }
3999
- const persistence = {
4000
- provider: "claude",
4001
- sessionId,
4002
- nativeHandle: sessionId,
4003
- metadata: {
4004
- provider: "claude",
4005
- cwd,
4006
- },
4007
- };
4008
4007
  return {
4009
- provider: "claude",
4010
- sessionId,
4008
+ providerHandleId: sessionId,
4011
4009
  cwd,
4012
4010
  title: (title ?? "").trim() || `Claude session ${sessionId.slice(0, 8)}`,
4011
+ firstPromptPreview: acc.firstPromptPreview,
4012
+ lastPromptPreview: acc.lastPromptPreview,
4013
4013
  lastActivityAt: mtime,
4014
- persistence,
4015
- timeline,
4016
4014
  };
4017
4015
  }
4016
+ function normalizeImportablePromptPreview(text) {
4017
+ const normalized = text.trim().replace(/\s+/g, " ");
4018
+ if (!normalized)
4019
+ return null;
4020
+ return normalized.length > 160 ? normalized.slice(0, 160) : normalized;
4021
+ }
4018
4022
  function extractClaudeUserText(messageRaw) {
4019
4023
  const message = toObjectRecord(messageRaw);
4020
4024
  if (!message) {
@@ -15,6 +15,13 @@ const CLAUDE_OPUS_EXTENDED_THINKING_OPTIONS = [
15
15
  { id: "max", label: "Max" },
16
16
  ];
17
17
  const CLAUDE_MODELS = [
18
+ {
19
+ provider: "claude",
20
+ id: "claude-fable-5",
21
+ label: "Fable 5",
22
+ description: "Fable 5 · Most powerful model",
23
+ thinkingOptions: [...CLAUDE_OPUS_EXTENDED_THINKING_OPTIONS],
24
+ },
18
25
  {
19
26
  provider: "claude",
20
27
  id: "claude-opus-4-8[1m]",
@@ -170,6 +177,14 @@ export function normalizeClaudeRuntimeModelId(value) {
170
177
  if (CLAUDE_MODELS.some((model) => model.id === trimmed)) {
171
178
  return trimmed;
172
179
  }
180
+ // Fable uses a single-segment version (claude-fable-5), not the {major}-{minor}
181
+ // scheme of opus/sonnet/haiku, so match it separately. This maps dated runtime
182
+ // strings (e.g. claude-fable-5-20260301) back to the catalog ID. No [1m] variant:
183
+ // Fable 5 is natively 1M, so there is no 200K-default model to opt into 1M.
184
+ const fableMatch = trimmed.match(/(?:claude-)?fable[-_ ]+(\d+)/i);
185
+ if (fableMatch) {
186
+ return `claude-fable-${fableMatch[1]}`;
187
+ }
173
188
  // Match: claude-{family}-{major}-{minor}[1m]? possibly followed by a date suffix
174
189
  const runtimeMatch = trimmed.match(/(?:claude-)?(opus|sonnet|haiku)[-_ ]+(\d+)[-.](\d+)(\[1m\])?/i);
175
190
  if (!runtimeMatch) {
@@ -1,4 +1,4 @@
1
- import { type AgentCapabilityFlags, type AgentClient, type AgentCreateSessionOptions, type AgentFeature, type AgentLaunchContext, type AgentMode, type AgentModelDefinition, type AgentPersistenceHandle, type AgentPermissionRequest, type AgentPermissionResponse, type AgentPermissionResult, type AgentPromptContentBlock, type AgentPromptInput, type AgentRunOptions, type AgentRunResult, type AgentRuntimeInfo, type AgentSession, type AgentSessionConfig, type AgentSlashCommand, type AgentStreamEvent, type AgentTimelineItem, type ToolCallTimelineItem, type AgentUsage, type ListModelsOptions, type ListPersistedAgentsOptions, type PersistedAgentDescriptor } from "../agent-sdk-types.js";
1
+ import { type AgentCapabilityFlags, type AgentClient, type AgentCreateSessionOptions, type AgentFeature, type AgentLaunchContext, type AgentMode, type AgentModelDefinition, type AgentPersistenceHandle, type AgentPermissionRequest, type AgentPermissionResponse, type AgentPermissionResult, type AgentPromptContentBlock, type AgentPromptInput, type AgentRunOptions, type AgentRunResult, type AgentRuntimeInfo, type AgentSession, type AgentSessionConfig, type AgentSlashCommand, type AgentStreamEvent, type AgentTimelineItem, type ToolCallTimelineItem, type AgentUsage, type ImportableProviderSession, type ImportProviderSessionContext, type ImportProviderSessionInput, type ListImportableSessionsOptions, type ListModelsOptions } from "../agent-sdk-types.js";
2
2
  import type { Logger } from "pino";
3
3
  import type { ChildProcessWithoutNullStreams } from "node:child_process";
4
4
  import { type ProviderRuntimeSettings } from "../provider-launch-config.js";
@@ -293,7 +293,8 @@ export declare class CodexAppServerAgentClient implements AgentClient {
293
293
  sessionId: string;
294
294
  metadata?: Record<string, unknown>;
295
295
  }, overrides?: Partial<AgentSessionConfig>, launchContext?: AgentLaunchContext): Promise<AgentSession>;
296
- listPersistedAgents(options?: ListPersistedAgentsOptions): Promise<PersistedAgentDescriptor[]>;
296
+ listImportableSessions(options?: ListImportableSessionsOptions): Promise<ImportableProviderSession[]>;
297
+ importSession(input: ImportProviderSessionInput, context: ImportProviderSessionContext): Promise<import("../agent-sdk-types.js").ImportedProviderSession>;
297
298
  listModels(_options: ListModelsOptions): Promise<AgentModelDefinition[]>;
298
299
  archiveNativeSession(handle: AgentPersistenceHandle): Promise<void>;
299
300
  isAvailable(): Promise<boolean>;
@@ -1,4 +1,5 @@
1
1
  import { getAgentStreamEventTurnId, } from "../agent-sdk-types.js";
2
+ import { importSessionFromPersistence } from "../provider-session-import.js";
2
3
  import { homedir } from "node:os";
3
4
  import { randomUUID } from "node:crypto";
4
5
  import * as fsSync from "node:fs";
@@ -94,6 +95,7 @@ function formatOutOfBandStatusMessage(text) {
94
95
  const CODEX_APP_SERVER_CAPABILITIES = {
95
96
  supportsStreaming: true,
96
97
  supportsSessionPersistence: true,
98
+ supportsSessionListing: true,
97
99
  supportsDynamicModes: false,
98
100
  supportsMcpServers: true,
99
101
  supportsReasoningStream: true,
@@ -431,6 +433,7 @@ async function listCodexCustomPrompts() {
431
433
  name: `prompts:${name}`,
432
434
  description,
433
435
  argumentHint,
436
+ kind: "command",
434
437
  };
435
438
  }));
436
439
  const commands = parsedCommands.filter((cmd) => cmd !== null);
@@ -482,6 +485,7 @@ export async function listCodexSkills(cwd, workspaceGitService) {
482
485
  name,
483
486
  description,
484
487
  argumentHint: "",
488
+ kind: "skill",
485
489
  });
486
490
  }
487
491
  }
@@ -580,10 +584,6 @@ function filterCodexThreadsByCwd(threads, cwd) {
580
584
  const matchesCwd = createPathEquivalenceMatcher(cwd);
581
585
  return threads.filter((thread) => typeof thread.cwd === "string" && matchesCwd(thread.cwd));
582
586
  }
583
- function buildCodexThreadListTimeline(thread) {
584
- const preview = typeof thread.preview === "string" ? thread.preview.trim() : "";
585
- return preview ? [{ type: "user_message", text: preview }] : [];
586
- }
587
587
  export function toAgentUsage(tokenUsage) {
588
588
  const usage = toObjectRecord(tokenUsage);
589
589
  if (!usage)
@@ -3006,6 +3006,7 @@ export class CodexAppServerAgentSession {
3006
3006
  name: skill.name,
3007
3007
  description: skill.description,
3008
3008
  argumentHint: "",
3009
+ kind: "skill",
3009
3010
  }));
3010
3011
  const fallbackSkills = appServerSkills.length === 0
3011
3012
  ? await listCodexSkills(this.config.cwd, this.deps.workspaceGitService)
@@ -3015,6 +3016,7 @@ export class CodexAppServerAgentSession {
3015
3016
  name: "compact",
3016
3017
  description: "Summarize conversation to prevent hitting the context limit",
3017
3018
  argumentHint: "",
3019
+ kind: "command",
3018
3020
  },
3019
3021
  ];
3020
3022
  if (this.goalsEnabled) {
@@ -3022,6 +3024,7 @@ export class CodexAppServerAgentSession {
3022
3024
  name: "goal",
3023
3025
  description: "Set, pause, resume, or clear the agent's goal",
3024
3026
  argumentHint: "[<objective>|pause|resume|clear]",
3027
+ kind: "command",
3025
3028
  });
3026
3029
  }
3027
3030
  return [...builtin, ...appServerSkills, ...fallbackSkills, ...prompts].sort((a, b) => a.name.localeCompare(b.name));
@@ -4315,7 +4318,7 @@ export class CodexAppServerAgentClient {
4315
4318
  await session.connect();
4316
4319
  return session;
4317
4320
  }
4318
- async listPersistedAgents(options) {
4321
+ async listImportableSessions(options) {
4319
4322
  const child = await this.spawnAppServer();
4320
4323
  const client = this.deps._createCodexClient?.(child, this.logger, () => ({})) ??
4321
4324
  new CodexAppServerClient(child, this.logger);
@@ -4333,39 +4336,35 @@ export class CodexAppServerAgentClient {
4333
4336
  }));
4334
4337
  const allThreads = Array.isArray(response?.data) ? response.data.filter(isRecord) : [];
4335
4338
  const threads = filterCodexThreadsByCwd(allThreads, options?.cwd);
4336
- const descriptors = threads.slice(0, limit).map((thread) => {
4339
+ return threads.slice(0, limit).map((thread) => {
4337
4340
  const threadId = typeof thread.id === "string" ? thread.id : "";
4338
4341
  const cwd = typeof thread.cwd === "string" ? thread.cwd : process.cwd();
4339
4342
  const preview = typeof thread.preview === "string" ? thread.preview : null;
4340
4343
  const title = typeof thread.name === "string" && thread.name.trim() ? thread.name : preview;
4341
4344
  return {
4342
- provider: CODEX_PROVIDER,
4343
- sessionId: threadId,
4345
+ providerHandleId: threadId,
4344
4346
  cwd,
4345
4347
  title,
4348
+ firstPromptPreview: preview,
4349
+ lastPromptPreview: preview,
4346
4350
  lastActivityAt: new Date(((typeof thread.updatedAt === "number" ? thread.updatedAt : undefined) ??
4347
4351
  (typeof thread.createdAt === "number" ? thread.createdAt : undefined) ??
4348
4352
  0) * 1000),
4349
- persistence: {
4350
- provider: CODEX_PROVIDER,
4351
- sessionId: threadId,
4352
- nativeHandle: threadId,
4353
- metadata: {
4354
- provider: CODEX_PROVIDER,
4355
- cwd,
4356
- title,
4357
- threadId,
4358
- },
4359
- },
4360
- timeline: buildCodexThreadListTimeline(thread),
4361
4353
  };
4362
4354
  });
4363
- return descriptors;
4364
4355
  }
4365
4356
  finally {
4366
4357
  await client.dispose();
4367
4358
  }
4368
4359
  }
4360
+ async importSession(input, context) {
4361
+ return importSessionFromPersistence({
4362
+ provider: CODEX_PROVIDER,
4363
+ request: input,
4364
+ context,
4365
+ resumeSession: this.resumeSession.bind(this),
4366
+ });
4367
+ }
4369
4368
  async listModels(_options) {
4370
4369
  // Codex model/list is global to the app server in this flow; cwd/force are intentionally ignored.
4371
4370
  const child = await this.spawnAppServer();