@getpaseo/server 0.1.98 → 0.1.100

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 (59) hide show
  1. package/dist/server/server/agent/agent-manager.js +2 -2
  2. package/dist/server/server/agent/agent-sdk-types.d.ts +11 -6
  3. package/dist/server/server/agent/provider-registry.d.ts +6 -3
  4. package/dist/server/server/agent/provider-registry.js +49 -22
  5. package/dist/server/server/agent/provider-snapshot-manager.js +26 -14
  6. package/dist/server/server/agent/providers/acp-agent.d.ts +23 -3
  7. package/dist/server/server/agent/providers/acp-agent.js +139 -9
  8. package/dist/server/server/agent/providers/claude/agent.d.ts +2 -2
  9. package/dist/server/server/agent/providers/claude/agent.js +41 -77
  10. package/dist/server/server/agent/providers/codex-app-server-agent.d.ts +3 -2
  11. package/dist/server/server/agent/providers/codex-app-server-agent.js +6 -25
  12. package/dist/server/server/agent/providers/copilot-acp-agent.d.ts +2 -1
  13. package/dist/server/server/agent/providers/copilot-acp-agent.js +11 -31
  14. package/dist/server/server/agent/providers/generic-acp-agent.d.ts +0 -1
  15. package/dist/server/server/agent/providers/generic-acp-agent.js +2 -108
  16. package/dist/server/server/agent/providers/mock-load-test-agent.d.ts +2 -3
  17. package/dist/server/server/agent/providers/mock-load-test-agent.js +5 -5
  18. package/dist/server/server/agent/providers/mock-slow-provider.d.ts +2 -3
  19. package/dist/server/server/agent/providers/mock-slow-provider.js +2 -5
  20. package/dist/server/server/agent/providers/opencode/server-manager.d.ts +14 -11
  21. package/dist/server/server/agent/providers/opencode/server-manager.js +149 -91
  22. package/dist/server/server/agent/providers/opencode/test-server-manager.d.ts +6 -5
  23. package/dist/server/server/agent/providers/opencode/test-server-manager.js +13 -3
  24. package/dist/server/server/agent/providers/opencode/test-utils/{test-opencode-runtime.d.ts → test-opencode-harness.d.ts} +11 -11
  25. package/dist/server/server/agent/providers/opencode/test-utils/{test-opencode-runtime.js → test-opencode-harness.js} +23 -10
  26. package/dist/server/server/agent/providers/opencode-agent.d.ts +13 -6
  27. package/dist/server/server/agent/providers/opencode-agent.js +74 -137
  28. package/dist/server/server/agent/providers/pi/agent.d.ts +4 -4
  29. package/dist/server/server/agent/providers/pi/agent.js +13 -76
  30. package/dist/server/server/agent/providers/pi/cli-runtime.d.ts +3 -0
  31. package/dist/server/server/agent/providers/pi/cli-runtime.js +8 -5
  32. package/dist/server/server/agent/providers/pi/rpc-types.d.ts +2 -1
  33. package/dist/server/server/agent/providers/pi/runtime.d.ts +1 -1
  34. package/dist/server/server/agent/providers/pi/test-utils/fake-pi.d.ts +1 -1
  35. package/dist/server/server/agent/providers/pi/test-utils/fake-pi.js +1 -1
  36. package/dist/server/server/session/agent-config/agent-config-session.d.ts +50 -0
  37. package/dist/server/server/session/agent-config/agent-config-session.js +98 -0
  38. package/dist/server/server/session/chat/chat-schedule-loop-session.d.ts +120 -0
  39. package/dist/server/server/session/chat/chat-schedule-loop-session.js +489 -0
  40. package/dist/server/server/session/checkout/checkout-session.d.ts +142 -0
  41. package/dist/server/server/session/checkout/checkout-session.js +925 -0
  42. package/dist/server/server/session/daemon/daemon-session.d.ts +50 -0
  43. package/dist/server/server/session/daemon/daemon-session.js +98 -0
  44. package/dist/server/server/session/files/workspace-files-session.d.ts +43 -0
  45. package/dist/server/server/session/files/workspace-files-session.js +218 -0
  46. package/dist/server/server/session/project-config/project-config-session.d.ts +34 -0
  47. package/dist/server/server/session/project-config/project-config-session.js +125 -0
  48. package/dist/server/server/session/provider/provider-catalog-session.d.ts +74 -0
  49. package/dist/server/server/session/provider/provider-catalog-session.js +339 -0
  50. package/dist/server/server/session/voice/voice-session.d.ts +166 -0
  51. package/dist/server/server/session/voice/voice-session.js +893 -0
  52. package/dist/server/server/{voice → session/voice}/voice-turn-controller.d.ts +2 -2
  53. package/dist/server/server/{voice → session/voice}/voice-turn-controller.js +2 -2
  54. package/dist/server/server/session.d.ts +13 -208
  55. package/dist/server/server/session.js +2132 -5105
  56. package/dist/server/utils/checkout-git.d.ts +6 -0
  57. package/package.json +5 -5
  58. package/dist/server/server/agent/providers/opencode/runtime.d.ts +0 -28
  59. package/dist/server/server/agent/providers/opencode/runtime.js +0 -5
@@ -2580,8 +2580,8 @@ export class AgentManager {
2580
2580
  const client = this.clients.get(normalized.provider);
2581
2581
  if (client) {
2582
2582
  try {
2583
- const models = await client.listModels({ cwd: normalized.cwd, force: false });
2584
- const defaultModel = models.find((model) => model.isDefault) ?? models[0];
2583
+ const catalog = await client.fetchCatalog({ cwd: normalized.cwd, force: false });
2584
+ const defaultModel = catalog.models.find((model) => model.isDefault) ?? catalog.models[0];
2585
2585
  if (defaultModel) {
2586
2586
  normalized.model = defaultModel.id;
2587
2587
  }
@@ -578,21 +578,26 @@ export interface AgentSession {
578
578
  }): Promise<void>;
579
579
  } | null;
580
580
  }
581
- export interface ListModelsOptions {
581
+ export interface FetchCatalogOptions {
582
582
  cwd: string;
583
583
  force: boolean;
584
584
  }
585
- export interface ListModesOptions {
586
- cwd: string;
587
- force: boolean;
585
+ export interface ProviderCatalog {
586
+ models: AgentModelDefinition[];
587
+ modes: AgentMode[];
588
588
  }
589
589
  export interface AgentClient {
590
590
  readonly provider: AgentProvider;
591
591
  readonly capabilities: AgentCapabilityFlags;
592
592
  createSession(config: AgentSessionConfig, launchContext?: AgentLaunchContext, options?: AgentCreateSessionOptions): Promise<AgentSession>;
593
593
  resumeSession(handle: AgentPersistenceHandle, overrides?: Partial<AgentSessionConfig>, launchContext?: AgentLaunchContext): Promise<AgentSession>;
594
- listModels(options: ListModelsOptions): Promise<AgentModelDefinition[]>;
595
- listModes?(options: ListModesOptions): Promise<AgentMode[]>;
594
+ /**
595
+ * Discover models and modes together. Implementations may use one upstream
596
+ * process, separate upstream calls, static modes, or private helpers; callers
597
+ * outside the provider do not get separate runtime model/mode probes.
598
+ * The registry is responsible for merging configured model overrides.
599
+ */
600
+ fetchCatalog(options: FetchCatalogOptions): Promise<ProviderCatalog>;
596
601
  resolveCreateConfig?(input: ResolveAgentCreateConfigInput): ResolveAgentCreateConfigResult;
597
602
  isCreateConfigUnattended?(input: AgentCreateConfigUnattendedInput): boolean;
598
603
  listCommands?(config: AgentSessionConfig): Promise<AgentSlashCommand[]>;
@@ -1,5 +1,5 @@
1
1
  import type { Logger } from "pino";
2
- import type { AgentClient, AgentCreateConfigUnattendedInput, AgentMode, AgentModelDefinition, AgentProvider, AgentSession, ListModelsOptions, ListModesOptions, ResolveAgentCreateConfigInput, ResolveAgentCreateConfigResult } from "./agent-sdk-types.js";
2
+ import type { AgentClient, AgentCreateConfigUnattendedInput, AgentProvider, AgentSession, FetchCatalogOptions, ProviderCatalog, ResolveAgentCreateConfigInput, ResolveAgentCreateConfigResult } from "./agent-sdk-types.js";
3
3
  import type { WorkspaceGitService } from "../workspace-git-service.js";
4
4
  import type { ManagedProcessRegistry } from "../managed-processes/managed-processes.js";
5
5
  import type { AgentProviderRuntimeSettingsMap, ProviderOverride } from "./provider-launch-config.js";
@@ -17,8 +17,11 @@ export interface ProviderDefinition extends AgentProviderDefinition {
17
17
  createClient: (logger: Logger) => AgentClient;
18
18
  resolveCreateConfig: (input: ResolveAgentCreateConfigInput) => ResolveAgentCreateConfigResult;
19
19
  isCreateConfigUnattended: (input: AgentCreateConfigUnattendedInput) => boolean;
20
- fetchModels: (options: ListModelsOptions) => Promise<AgentModelDefinition[]>;
21
- fetchModes: (options: ListModesOptions) => Promise<AgentMode[]>;
20
+ /**
21
+ * Single catalog discovery call used by ProviderSnapshotManager. Should spawn
22
+ * at most one provider runtime process and return both models and modes.
23
+ */
24
+ fetchCatalog: (options: FetchCatalogOptions, client?: AgentClient) => Promise<ProviderCatalog>;
22
25
  }
23
26
  export interface BuildProviderRegistryOptions {
24
27
  runtimeSettings?: AgentProviderRuntimeSettingsMap;
@@ -51,6 +51,7 @@ const PROVIDER_CLIENT_FACTORIES = {
51
51
  providerParams: options?.providerParams ?? {
52
52
  sessionDir: "~/.omp/agent/sessions",
53
53
  },
54
+ commandsRpcType: "get_available_commands",
54
55
  }),
55
56
  mock: (logger) => new MockLoadTestAgentClient(logger),
56
57
  "mock-slow": () => new MockSlowProviderClient(),
@@ -232,10 +233,15 @@ function wrapClientProvider(provider, inner, profileModels, additionalModels, pr
232
233
  provider: inner.provider,
233
234
  }
234
235
  : undefined, launchContext)),
235
- listModels: async (options) => mergeModels(provider, profileModels, additionalModels, await inner.listModels(options), {
236
- profileModelsAreAdditive,
237
- }),
238
- listModes: inner.listModes?.bind(inner),
236
+ fetchCatalog: async (options) => {
237
+ const catalog = await inner.fetchCatalog(options);
238
+ return {
239
+ models: mergeModels(provider, profileModels, additionalModels, catalog.models, {
240
+ profileModelsAreAdditive,
241
+ }),
242
+ modes: catalog.modes,
243
+ };
244
+ },
239
245
  resolveCreateConfig: inner.resolveCreateConfig?.bind(inner),
240
246
  isCreateConfigUnattended: inner.isCreateConfigUnattended?.bind(inner),
241
247
  listImportableSessions: listImportableSessions
@@ -275,6 +281,22 @@ function wrapClientProvider(provider, inner, profileModels, additionalModels, pr
275
281
  }
276
282
  function createRegistryEntry(logger, provider, resolved) {
277
283
  const modelClient = resolved.createBaseClient(logger);
284
+ const hasReplacementModels = resolved.profileModels.length > 0 && !resolved.profileModelsAreAdditive;
285
+ const replacementModels = hasReplacementModels
286
+ ? resolved.profileModels.map((model) => mapModel(provider, model))
287
+ : [];
288
+ const decorateModes = (modes) => modes.map((mode) => {
289
+ if (mode.icon && mode.colorTier)
290
+ return mode;
291
+ const definitionMode = resolved.definition.modes.find((d) => d.id === mode.id);
292
+ if (!definitionMode)
293
+ return mode;
294
+ return Object.assign({}, mode, {
295
+ icon: mode.icon ?? definitionMode.icon,
296
+ colorTier: mode.colorTier ?? definitionMode.colorTier,
297
+ });
298
+ });
299
+ const hasStaticModes = resolved.definition.modes.length > 0;
278
300
  return {
279
301
  ...resolved.definition,
280
302
  enabled: resolved.enabled,
@@ -282,24 +304,29 @@ function createRegistryEntry(logger, provider, resolved) {
282
304
  createClient: (providerLogger) => createResolvedProviderClient(providerLogger, provider, resolved),
283
305
  resolveCreateConfig: modelClient.resolveCreateConfig ?? resolveDefaultAgentCreateConfig,
284
306
  isCreateConfigUnattended: modelClient.isCreateConfigUnattended ?? isDefaultAgentCreateConfigUnattended,
285
- fetchModels: async (options) => mergeModels(provider, resolved.profileModels, resolved.additionalModels, await modelClient.listModels(options), {
286
- profileModelsAreAdditive: resolved.profileModelsAreAdditive,
287
- }),
288
- fetchModes: async (options) => {
289
- const modes = modelClient.listModes
290
- ? await modelClient.listModes(options)
291
- : resolved.definition.modes;
292
- return modes.map((mode) => {
293
- if (mode.icon && mode.colorTier)
294
- return mode;
295
- const definitionMode = resolved.definition.modes.find((d) => d.id === mode.id);
296
- if (!definitionMode)
297
- return mode;
298
- return Object.assign({}, mode, {
299
- icon: mode.icon ?? definitionMode.icon,
300
- colorTier: mode.colorTier ?? definitionMode.colorTier,
301
- });
302
- });
307
+ fetchCatalog: async (options, client) => {
308
+ const catalogClient = client ?? modelClient;
309
+ if (hasReplacementModels) {
310
+ // Replacement models skip runtime model discovery, but additionalModels
311
+ // must still be merged on top. If modes are dynamic, probe for modes via
312
+ // the single catalog API; otherwise use static/empty modes with no runtime.
313
+ const models = mergeModelAdditions(provider, replacementModels, resolved.additionalModels);
314
+ if (hasStaticModes) {
315
+ return {
316
+ models,
317
+ modes: decorateModes(resolved.definition.modes),
318
+ };
319
+ }
320
+ const catalog = await catalogClient.fetchCatalog(options);
321
+ return { models, modes: decorateModes(catalog.modes) };
322
+ }
323
+ const catalog = await catalogClient.fetchCatalog(options);
324
+ return {
325
+ models: mergeModels(provider, resolved.profileModels, resolved.additionalModels, catalog.models, {
326
+ profileModelsAreAdditive: resolved.profileModelsAreAdditive,
327
+ }),
328
+ modes: decorateModes(catalog.modes),
329
+ };
303
330
  },
304
331
  };
305
332
  }
@@ -5,6 +5,7 @@ import { expandTilde } from "../../utils/path.js";
5
5
  import { withTimeout } from "../../utils/promise-timeout.js";
6
6
  import { buildProviderRegistry, shutdownAgentClients, } from "./provider-registry.js";
7
7
  import { applyMutableProviderConfigToOverrides } from "../daemon-config-store.js";
8
+ import { formatProviderDiagnostic } from "./providers/diagnostic-utils.js";
8
9
  const DEFAULT_REFRESH_TIMEOUT_MS = 30000;
9
10
  const REFRESH_TIMEOUT_ENV_VAR = "PASEO_PROVIDER_REFRESH_TIMEOUT_MS";
10
11
  // Provider refresh probes can be slow on cold starts (e.g. Copilot's first
@@ -179,13 +180,19 @@ export class ProviderSnapshotManager {
179
180
  });
180
181
  }
181
182
  async getProviderDiagnostic(provider) {
182
- const client = this.providerClients[provider];
183
- if (!client) {
184
- throw new Error(`Provider ${provider} is not configured`);
185
- }
186
- const diagnostic = client.getDiagnostic
183
+ const definition = this.requireProvider(provider);
184
+ const client = this.ensureClient(provider, definition);
185
+ // Force-refresh the snapshot so Models/Status come from the single catalog authority.
186
+ await this.refreshSnapshotForCwd({ cwd: homedir(), providers: [provider] });
187
+ const entry = await this.getProvider({ cwd: homedir(), provider, wait: true });
188
+ const modelCount = entry.status === "ready" ? String(entry.models?.length ?? 0) : "—";
189
+ const status = formatProviderStatus(entry);
190
+ const baseDiagnostic = client.getDiagnostic
187
191
  ? (await client.getDiagnostic()).diagnostic
188
- : "No diagnostic available for this provider.";
192
+ : formatProviderDiagnostic(definition.label ?? provider, [
193
+ { label: "Diagnostic", value: "No diagnostic available" },
194
+ ]);
195
+ const diagnostic = `${baseDiagnostic}\n Models: ${modelCount}\n Status: ${status}`;
189
196
  return { provider, diagnostic };
190
197
  }
191
198
  applyMutableProviderConfig(mutableProviders) {
@@ -238,8 +245,7 @@ export class ProviderSnapshotManager {
238
245
  createClient: () => client,
239
246
  resolveCreateConfig: client.resolveCreateConfig?.bind(client) ?? definition.resolveCreateConfig,
240
247
  isCreateConfigUnattended: client.isCreateConfigUnattended?.bind(client) ?? definition.isCreateConfigUnattended,
241
- fetchModels: client.listModels.bind(client),
242
- fetchModes: client.listModes?.bind(client) ?? definition.fetchModes,
248
+ fetchCatalog: client.fetchCatalog.bind(client),
243
249
  };
244
250
  }
245
251
  return registry;
@@ -448,16 +454,13 @@ export class ProviderSnapshotManager {
448
454
  setEntry({ ...base, status: "unavailable", enabled: true });
449
455
  return;
450
456
  }
451
- const [models, modes] = await withTimeout(Promise.all([
452
- definition.fetchModels({ cwd, force }),
453
- definition.fetchModes({ cwd, force }),
454
- ]), this.refreshTimeoutMs, `Timed out refreshing ${definition.label} after ${this.refreshTimeoutMs}ms`);
457
+ const catalog = await withTimeout(definition.fetchCatalog({ cwd, force }, client), this.refreshTimeoutMs, `Timed out refreshing ${definition.label} after ${this.refreshTimeoutMs}ms`);
455
458
  setEntry({
456
459
  ...base,
457
460
  status: "ready",
458
461
  enabled: true,
459
- models,
460
- modes,
462
+ models: catalog.models,
463
+ modes: catalog.modes,
461
464
  fetchedAt: new Date().toISOString(),
462
465
  });
463
466
  }
@@ -580,4 +583,13 @@ function toErrorMessage(error) {
580
583
  }
581
584
  return "Unknown error";
582
585
  }
586
+ function formatProviderStatus(entry) {
587
+ if (entry.status === "ready")
588
+ return "Ready";
589
+ if (entry.status === "error")
590
+ return `Error: ${entry.error ?? "Unknown error"}`;
591
+ if (entry.status === "unavailable")
592
+ return "Unavailable";
593
+ return "Loading";
594
+ }
583
595
  //# sourceMappingURL=provider-snapshot-manager.js.map
@@ -3,7 +3,7 @@ import type { ProcessTerminator } from "../../../utils/tree-kill.js";
3
3
  import type { ReadableStream as NodeReadableStream, WritableStream as NodeWritableStream } from "node:stream/web";
4
4
  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";
5
5
  import type { Logger } from "pino";
6
- 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
+ import { type AgentCapabilityFlags, type AgentClient, type AgentFeature, 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 FetchCatalogOptions, type ImportableProviderSession, type ImportProviderSessionContext, type ImportProviderSessionInput, type ListImportableSessionsOptions, type ProviderCatalog } from "../agent-sdk-types.js";
7
7
  import { type ProviderRuntimeSettings } from "../provider-launch-config.js";
8
8
  export declare function summarizeACPRequestError(error: unknown): {
9
9
  message: string;
@@ -24,6 +24,7 @@ interface ACPAgentClientOptions {
24
24
  modelTransformer?: (models: AgentModelDefinition[]) => AgentModelDefinition[];
25
25
  sessionResponseTransformer?: (response: SessionStateResponse) => SessionStateResponse;
26
26
  configOptionsTransformer?: (configOptions: SessionConfigOption[]) => SessionConfigOption[];
27
+ configFeatureOptions?: ACPConfigFeatureOption[];
27
28
  modeIdTransformer?: (modeId: string) => string | null;
28
29
  toolSnapshotTransformer?: (snapshot: ACPToolSnapshot) => ACPToolSnapshot;
29
30
  providerModeWriter?: (context: ACPProviderModeWriterContext) => Promise<ACPProviderModeWriteResult>;
@@ -43,6 +44,7 @@ interface ACPAgentSessionOptions {
43
44
  modelTransformer?: (models: AgentModelDefinition[]) => AgentModelDefinition[];
44
45
  sessionResponseTransformer?: (response: SessionStateResponse) => SessionStateResponse;
45
46
  configOptionsTransformer?: (configOptions: SessionConfigOption[]) => SessionConfigOption[];
47
+ configFeatureOptions?: ACPConfigFeatureOption[];
46
48
  modeIdTransformer?: (modeId: string) => string | null;
47
49
  toolSnapshotTransformer?: (snapshot: ACPToolSnapshot) => ACPToolSnapshot;
48
50
  providerModeWriter?: (context: ACPProviderModeWriterContext) => Promise<ACPProviderModeWriteResult>;
@@ -76,6 +78,16 @@ interface TerminalExit {
76
78
  exitCode?: number | null;
77
79
  signal?: string | null;
78
80
  }
81
+ export interface ACPConfigFeatureOption {
82
+ id: string;
83
+ configId: string;
84
+ category: string;
85
+ label: string;
86
+ description?: string;
87
+ tooltip?: string;
88
+ icon?: string;
89
+ emptyOptionLabel?: string;
90
+ }
79
91
  type SelectConfigOption = Extract<SessionConfigOption, {
80
92
  type: "select";
81
93
  }>;
@@ -134,6 +146,7 @@ export declare function deriveModesFromACP(fallbackModes: AgentMode[], modeState
134
146
  currentModeId: string | null;
135
147
  };
136
148
  export declare function deriveModelDefinitionsFromACP(provider: string, models: SessionModelState | null | undefined, configOptions?: SessionConfigOption[] | null): AgentModelDefinition[];
149
+ export declare function deriveFeaturesFromACP(configOptions: SessionConfigOption[] | null | undefined, featureOptions: ACPConfigFeatureOption[]): AgentFeature[];
137
150
  export declare class ACPAgentClient implements AgentClient {
138
151
  readonly provider: string;
139
152
  readonly capabilities: AgentCapabilityFlags;
@@ -144,6 +157,7 @@ export declare class ACPAgentClient implements AgentClient {
144
157
  private readonly modelTransformer?;
145
158
  private readonly sessionResponseTransformer?;
146
159
  private readonly configOptionsTransformer?;
160
+ private readonly configFeatureOptions;
147
161
  private readonly modeIdTransformer?;
148
162
  private readonly toolSnapshotTransformer?;
149
163
  private readonly providerModeWriter?;
@@ -155,8 +169,8 @@ export declare class ACPAgentClient implements AgentClient {
155
169
  constructor(options: ACPAgentClientOptions);
156
170
  createSession(config: AgentSessionConfig, launchContext?: AgentLaunchContext): Promise<AgentSession>;
157
171
  resumeSession(handle: AgentPersistenceHandle, overrides?: Partial<AgentSessionConfig>, launchContext?: AgentLaunchContext): Promise<AgentSession>;
158
- listModels(options: ListModelsOptions): Promise<AgentModelDefinition[]>;
159
- listModes(options: ListModesOptions): Promise<AgentMode[]>;
172
+ fetchCatalog(options: FetchCatalogOptions): Promise<ProviderCatalog>;
173
+ listFeatures(config: AgentSessionConfig): Promise<AgentFeature[]>;
160
174
  listImportableSessions(options?: ListImportableSessionsOptions): Promise<ImportableProviderSession[]>;
161
175
  importSession(input: ImportProviderSessionInput, context: ImportProviderSessionContext): Promise<import("../agent-sdk-types.js").ImportedProviderSession>;
162
176
  isAvailable(): Promise<boolean>;
@@ -183,6 +197,7 @@ export declare class ACPAgentSession implements AgentSession, ACPClient {
183
197
  protected readonly modelTransformer?: (models: AgentModelDefinition[]) => AgentModelDefinition[];
184
198
  private readonly sessionResponseTransformer?;
185
199
  private readonly configOptionsTransformer?;
200
+ private readonly configFeatureOptions;
186
201
  private readonly modeIdTransformer?;
187
202
  private readonly toolSnapshotTransformer?;
188
203
  private readonly providerModeWriter?;
@@ -194,6 +209,7 @@ export declare class ACPAgentSession implements AgentSession, ACPClient {
194
209
  private readonly pendingPermissions;
195
210
  private readonly messageAssemblies;
196
211
  private readonly submittedUserMessageIds;
212
+ private activeSubmittedUserMessage;
197
213
  private readonly toolCalls;
198
214
  private readonly terminalEntries;
199
215
  private readonly persistedHistory;
@@ -236,6 +252,7 @@ export declare class ACPAgentSession implements AgentSession, ACPClient {
236
252
  getRuntimeInfo(): Promise<AgentRuntimeInfo>;
237
253
  getAvailableModes(): Promise<AgentMode[]>;
238
254
  getCurrentMode(): Promise<string | null>;
255
+ get features(): AgentFeature[];
239
256
  private ensureCommandsReadyDeferred;
240
257
  private settleCommandsReady;
241
258
  private waitForCommandsReady;
@@ -246,6 +263,7 @@ export declare class ACPAgentSession implements AgentSession, ACPClient {
246
263
  setModel(modelId: string | null): Promise<void>;
247
264
  private setModelWithSelection;
248
265
  setThinkingOption(thinkingOptionId: string | null): Promise<void>;
266
+ setFeature(featureId: string, value: unknown): Promise<void>;
249
267
  private applyConfigOptionResponse;
250
268
  getPendingPermissions(): AgentPermissionRequest[];
251
269
  respondToPermission(requestId: string, response: AgentPermissionResponse): Promise<void>;
@@ -282,6 +300,7 @@ export declare class ACPAgentSession implements AgentSession, ACPClient {
282
300
  private translateSessionUpdate;
283
301
  private handleToolCallUpdate;
284
302
  private createMessageTimelineItem;
303
+ private messageAssemblyKey;
285
304
  private handleCurrentModeUpdate;
286
305
  private handleConfigOptionUpdate;
287
306
  private handleSessionInfoUpdate;
@@ -292,6 +311,7 @@ export declare class ACPAgentSession implements AgentSession, ACPClient {
292
311
  private emitSubmittedUserMessage;
293
312
  private runtimeInfo;
294
313
  private finishTurn;
314
+ private isSubmittedUserMessageEcho;
295
315
  private emitBootstrapThreadEvent;
296
316
  private synthesizeCanceledToolCalls;
297
317
  private collectDiagnostic;
@@ -257,6 +257,26 @@ export function deriveModelDefinitionsFromACP(provider, models, configOptions) {
257
257
  metadata: option.metadata,
258
258
  }));
259
259
  }
260
+ export function deriveFeaturesFromACP(configOptions, featureOptions) {
261
+ return featureOptions.flatMap((featureOption) => {
262
+ const option = findSelectConfigFeatureOption(configOptions, featureOption);
263
+ if (!option) {
264
+ return [];
265
+ }
266
+ return [
267
+ {
268
+ type: "select",
269
+ id: featureOption.id,
270
+ label: featureOption.label,
271
+ description: featureOption.description,
272
+ tooltip: featureOption.tooltip,
273
+ icon: featureOption.icon,
274
+ value: option.currentValue ?? null,
275
+ options: deriveConfigFeatureSelectOptions(option, featureOption),
276
+ },
277
+ ];
278
+ });
279
+ }
260
280
  export class ACPAgentClient {
261
281
  constructor(options) {
262
282
  this.provider = options.provider;
@@ -272,6 +292,7 @@ export class ACPAgentClient {
272
292
  this.modelTransformer = options.modelTransformer;
273
293
  this.sessionResponseTransformer = options.sessionResponseTransformer;
274
294
  this.configOptionsTransformer = options.configOptionsTransformer;
295
+ this.configFeatureOptions = options.configFeatureOptions ?? [];
275
296
  this.modeIdTransformer = options.modeIdTransformer;
276
297
  this.toolSnapshotTransformer = options.toolSnapshotTransformer;
277
298
  this.providerModeWriter = options.providerModeWriter;
@@ -291,6 +312,7 @@ export class ACPAgentClient {
291
312
  modelTransformer: this.modelTransformer,
292
313
  sessionResponseTransformer: this.sessionResponseTransformer,
293
314
  configOptionsTransformer: this.configOptionsTransformer,
315
+ configFeatureOptions: this.configFeatureOptions,
294
316
  modeIdTransformer: this.modeIdTransformer,
295
317
  toolSnapshotTransformer: this.toolSnapshotTransformer,
296
318
  providerModeWriter: this.providerModeWriter,
@@ -329,6 +351,7 @@ export class ACPAgentClient {
329
351
  modelTransformer: this.modelTransformer,
330
352
  sessionResponseTransformer: this.sessionResponseTransformer,
331
353
  configOptionsTransformer: this.configOptionsTransformer,
354
+ configFeatureOptions: this.configFeatureOptions,
332
355
  modeIdTransformer: this.modeIdTransformer,
333
356
  toolSnapshotTransformer: this.toolSnapshotTransformer,
334
357
  providerModeWriter: this.providerModeWriter,
@@ -344,7 +367,7 @@ export class ACPAgentClient {
344
367
  await session.initializeResumedSession();
345
368
  return session;
346
369
  }
347
- async listModels(options) {
370
+ async fetchCatalog(options) {
348
371
  const { cwd } = options;
349
372
  const probe = await this.spawnProcess(PROBE_ENV);
350
373
  try {
@@ -354,23 +377,29 @@ export class ACPAgentClient {
354
377
  }));
355
378
  const transformed = this.transformSessionResponse(response);
356
379
  const models = deriveModelDefinitionsFromACP(this.provider, transformed.models, transformed.configOptions);
357
- return this.modelTransformer ? this.modelTransformer(models) : models;
380
+ const modeInfo = deriveModesFromACP(this.defaultModes, transformed.modes, transformed.configOptions);
381
+ return {
382
+ models: this.modelTransformer ? this.modelTransformer(models) : models,
383
+ modes: modeInfo.modes,
384
+ };
358
385
  }
359
386
  finally {
360
387
  await this.closeProbe(probe);
361
388
  }
362
389
  }
363
- async listModes(options) {
364
- const { cwd } = options;
390
+ async listFeatures(config) {
391
+ if (this.configFeatureOptions.length === 0) {
392
+ return [];
393
+ }
394
+ this.assertProvider(config);
365
395
  const probe = await this.spawnProcess(PROBE_ENV);
366
396
  try {
367
397
  const response = await this.runACPRequest(() => probe.connection.newSession({
368
- cwd,
398
+ cwd: config.cwd,
369
399
  mcpServers: [],
370
400
  }));
371
401
  const transformed = this.transformSessionResponse(response);
372
- const modeInfo = deriveModesFromACP(this.defaultModes, transformed.modes, transformed.configOptions);
373
- return modeInfo.modes;
402
+ return deriveFeaturesFromACP(transformed.configOptions, this.configFeatureOptions);
374
403
  }
375
404
  finally {
376
405
  await this.closeProbe(probe);
@@ -561,6 +590,7 @@ export class ACPAgentSession {
561
590
  this.pendingPermissions = new Map();
562
591
  this.messageAssemblies = new Map();
563
592
  this.submittedUserMessageIds = new Set();
593
+ this.activeSubmittedUserMessage = null;
564
594
  this.toolCalls = new Map();
565
595
  this.terminalEntries = new Map();
566
596
  this.persistedHistory = [];
@@ -593,6 +623,7 @@ export class ACPAgentSession {
593
623
  this.modelTransformer = options.modelTransformer;
594
624
  this.sessionResponseTransformer = options.sessionResponseTransformer;
595
625
  this.configOptionsTransformer = options.configOptionsTransformer;
626
+ this.configFeatureOptions = options.configFeatureOptions ?? [];
596
627
  this.modeIdTransformer = options.modeIdTransformer;
597
628
  this.toolSnapshotTransformer = options.toolSnapshotTransformer;
598
629
  this.providerModeWriter = options.providerModeWriter;
@@ -690,6 +721,7 @@ export class ACPAgentSession {
690
721
  const turnId = randomUUID();
691
722
  const messageId = options?.messageId ?? randomUUID();
692
723
  this.activeForegroundTurnId = turnId;
724
+ this.activeSubmittedUserMessage = null;
693
725
  this.emitBootstrapThreadEvent();
694
726
  this.pushEvent({ type: "turn_started", provider: this.provider, turnId });
695
727
  this.emitSubmittedUserMessage(prompt, messageId, turnId);
@@ -749,6 +781,9 @@ export class ACPAgentSession {
749
781
  async getCurrentMode() {
750
782
  return this.currentMode;
751
783
  }
784
+ get features() {
785
+ return deriveFeaturesFromACP(this.configOptions, this.configFeatureOptions);
786
+ }
752
787
  ensureCommandsReadyDeferred() {
753
788
  if (this.commandsReadyDeferred || this.commandsReadySettled || this.cachedCommands.length > 0) {
754
789
  return;
@@ -1025,6 +1060,37 @@ export class ACPAgentSession {
1025
1060
  thinkingOptionId: this.thinkingOptionId,
1026
1061
  });
1027
1062
  }
1063
+ async setFeature(featureId, value) {
1064
+ if (!this.connection || !this.sessionId) {
1065
+ throw new Error("ACP session not initialized");
1066
+ }
1067
+ const featureOption = this.configFeatureOptions.find((option) => option.id === featureId);
1068
+ if (!featureOption) {
1069
+ throw new Error(`Unknown ${this.provider} feature: ${featureId}`);
1070
+ }
1071
+ const option = findSelectConfigFeatureOption(this.configOptions, featureOption);
1072
+ if (!option) {
1073
+ throw new Error(`${this.provider} does not expose ACP feature '${featureId}'`);
1074
+ }
1075
+ const requestedValue = normalizeConfigFeatureValue(value);
1076
+ const choice = findSelectConfigChoice({ option, value: requestedValue });
1077
+ if (!choice) {
1078
+ throw new Error(`${this.provider} feature '${featureId}' does not include option '${requestedValue}'`);
1079
+ }
1080
+ const response = await this.connection.setSessionConfigOption({
1081
+ sessionId: this.sessionId,
1082
+ configId: option.id,
1083
+ value: requestedValue,
1084
+ });
1085
+ const currentValue = this.applyConfigOptionResponse({
1086
+ response,
1087
+ configId: option.id,
1088
+ category: featureOption.category,
1089
+ requestedValue,
1090
+ label: featureOption.label,
1091
+ });
1092
+ this.config.featureValues = { ...this.config.featureValues, [featureId]: currentValue };
1093
+ }
1028
1094
  applyConfigOptionResponse({ response, configId, category, requestedValue, label, }) {
1029
1095
  this.configOptions = this.transformConfigOptions(response.configOptions);
1030
1096
  const responseOption = findSelectConfigOption({
@@ -1396,6 +1462,13 @@ export class ACPAgentSession {
1396
1462
  if (this.config.thinkingOptionId && this.config.thinkingOptionId !== this.thinkingOptionId) {
1397
1463
  await this.setThinkingOption(this.config.thinkingOptionId);
1398
1464
  }
1465
+ const configuredFeatureValues = this.config.featureValues ?? {};
1466
+ for (const featureOption of this.configFeatureOptions) {
1467
+ if (!Object.prototype.hasOwnProperty.call(configuredFeatureValues, featureOption.id)) {
1468
+ continue;
1469
+ }
1470
+ await this.setFeature(featureOption.id, configuredFeatureValues[featureOption.id]);
1471
+ }
1399
1472
  }
1400
1473
  warnInvalidSelection(value, message) {
1401
1474
  this.logger.warn({ value }, message);
@@ -1413,7 +1486,10 @@ export class ACPAgentSession {
1413
1486
  if (!item) {
1414
1487
  return [];
1415
1488
  }
1416
- if (update.messageId && this.submittedUserMessageIds.has(update.messageId)) {
1489
+ if (item.type !== "user_message") {
1490
+ return [this.wrapTimeline(item)];
1491
+ }
1492
+ if (this.isSubmittedUserMessageEcho(item)) {
1417
1493
  return [];
1418
1494
  }
1419
1495
  return [this.wrapTimeline(item)];
@@ -1476,7 +1552,7 @@ export class ACPAgentSession {
1476
1552
  if (!chunkText) {
1477
1553
  return null;
1478
1554
  }
1479
- const key = `${type}:${update.messageId ?? "default"}`;
1555
+ const key = this.messageAssemblyKey(type, update.messageId);
1480
1556
  const state = this.messageAssemblies.get(key) ?? { text: "" };
1481
1557
  state.text += chunkText;
1482
1558
  this.messageAssemblies.set(key, state);
@@ -1488,6 +1564,10 @@ export class ACPAgentSession {
1488
1564
  }
1489
1565
  return { type: "reasoning", text: chunkText };
1490
1566
  }
1567
+ messageAssemblyKey(type, messageId) {
1568
+ const fallbackId = type === "user_message" ? (this.activeForegroundTurnId ?? "default") : "default";
1569
+ return `${type}:${messageId ?? fallbackId}`;
1570
+ }
1491
1571
  handleCurrentModeUpdate(update) {
1492
1572
  this.currentMode = this.transformModeId(update.currentModeId);
1493
1573
  }
@@ -1589,6 +1669,7 @@ export class ACPAgentSession {
1589
1669
  return;
1590
1670
  }
1591
1671
  this.submittedUserMessageIds.add(messageId);
1672
+ this.activeSubmittedUserMessage = { messageId, text, turnId };
1592
1673
  this.pushEvent({
1593
1674
  type: "timeline",
1594
1675
  provider: this.provider,
@@ -1611,8 +1692,23 @@ export class ACPAgentSession {
1611
1692
  }
1612
1693
  finishTurn(event) {
1613
1694
  this.activeForegroundTurnId = null;
1695
+ if (this.activeSubmittedUserMessage?.turnId === event.turnId) {
1696
+ this.activeSubmittedUserMessage = null;
1697
+ }
1614
1698
  this.pushEvent(event);
1615
1699
  }
1700
+ isSubmittedUserMessageEcho(item) {
1701
+ const active = this.activeSubmittedUserMessage;
1702
+ if (!active || active.turnId !== this.activeForegroundTurnId) {
1703
+ return false;
1704
+ }
1705
+ if (item.messageId) {
1706
+ if (this.submittedUserMessageIds.has(item.messageId)) {
1707
+ return true;
1708
+ }
1709
+ }
1710
+ return active.text.startsWith(item.text);
1711
+ }
1616
1712
  emitBootstrapThreadEvent() {
1617
1713
  if (!this.bootstrapThreadEventPending || !this.sessionId) {
1618
1714
  return;
@@ -1658,6 +1754,12 @@ function findSelectConfigOption({ configOptions, category, id, }) {
1658
1754
  const option = configOptions?.find((entry) => entry.type === "select" && entry.category === category && (!id || entry.id === id));
1659
1755
  return option ?? null;
1660
1756
  }
1757
+ function findSelectConfigFeatureOption(configOptions, featureOption) {
1758
+ const option = configOptions?.find((entry) => entry.type === "select" &&
1759
+ entry.id === featureOption.configId &&
1760
+ entry.category === featureOption.category);
1761
+ return option ?? null;
1762
+ }
1661
1763
  function findSelectConfigChoice({ option, value, }) {
1662
1764
  if (!option) {
1663
1765
  return null;
@@ -1677,6 +1779,34 @@ function flattenSelectOptions(options) {
1677
1779
  }
1678
1780
  return flattened;
1679
1781
  }
1782
+ function deriveConfigFeatureSelectOptions(option, featureOption) {
1783
+ return flattenSelectOptions(option.options).map((choice) => ({
1784
+ id: choice.value,
1785
+ label: normalizeConfigFeatureOptionLabel(choice, featureOption),
1786
+ description: choice.description ?? undefined,
1787
+ isDefault: choice.value === option.currentValue,
1788
+ metadata: choice.group ? { group: choice.group } : undefined,
1789
+ }));
1790
+ }
1791
+ function normalizeConfigFeatureOptionLabel(choice, featureOption) {
1792
+ const name = choice.name.trim();
1793
+ if (name) {
1794
+ return name;
1795
+ }
1796
+ if (choice.value === "" && featureOption.emptyOptionLabel) {
1797
+ return featureOption.emptyOptionLabel;
1798
+ }
1799
+ return choice.value;
1800
+ }
1801
+ function normalizeConfigFeatureValue(value) {
1802
+ if (typeof value === "string") {
1803
+ return value;
1804
+ }
1805
+ if (value === null) {
1806
+ return "";
1807
+ }
1808
+ throw new Error(`ACP feature value must be a string`);
1809
+ }
1680
1810
  function deriveSelectorOptions(configOptions, category) {
1681
1811
  const option = findSelectConfigOption({ configOptions, category });
1682
1812
  if (!option) {