@getpaseo/server 0.1.96 → 0.1.97-beta.1

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 (134) hide show
  1. package/dist/server/{utils/executable.d.ts → executable-resolution/executable-resolution.d.ts} +2 -2
  2. package/dist/server/{utils/executable.js → executable-resolution/executable-resolution.js} +16 -14
  3. package/dist/server/executable-resolution/windows.d.ts +18 -0
  4. package/dist/server/executable-resolution/windows.js +62 -0
  5. package/dist/server/server/agent/agent-loading.js +4 -1
  6. package/dist/server/server/agent/agent-manager.d.ts +10 -2
  7. package/dist/server/server/agent/agent-manager.js +34 -46
  8. package/dist/server/server/agent/agent-projections.js +3 -0
  9. package/dist/server/server/agent/agent-prompt.js +19 -1
  10. package/dist/server/server/agent/agent-response-loop.js +2 -4
  11. package/dist/server/server/agent/agent-storage.d.ts +18 -19
  12. package/dist/server/server/agent/agent-storage.js +6 -23
  13. package/dist/server/server/agent/create-agent/create.d.ts +2 -12
  14. package/dist/server/server/agent/create-agent/create.js +28 -30
  15. package/dist/server/server/agent/create-agent-lifecycle-dispatch.d.ts +4 -2
  16. package/dist/server/server/agent/create-agent-lifecycle-dispatch.js +31 -22
  17. package/dist/server/server/agent/import-sessions.d.ts +1 -10
  18. package/dist/server/server/agent/import-sessions.js +1 -53
  19. package/dist/server/server/agent/lifecycle-command.js +5 -4
  20. package/dist/server/server/agent/mcp-server.d.ts +8 -5
  21. package/dist/server/server/agent/mcp-server.js +41 -14
  22. package/dist/server/server/agent/mcp-shared.d.ts +6 -3
  23. package/dist/server/server/agent/mcp-shared.js +3 -0
  24. package/dist/server/server/agent/provider-launch-config.js +1 -1
  25. package/dist/server/server/agent/providers/acp-agent.d.ts +5 -0
  26. package/dist/server/server/agent/providers/acp-agent.js +31 -26
  27. package/dist/server/server/agent/providers/claude/agent.js +45 -6
  28. package/dist/server/server/agent/providers/codex-app-server-agent.js +1 -1
  29. package/dist/server/server/agent/providers/copilot-acp-agent.js +1 -0
  30. package/dist/server/server/agent/providers/cursor-acp-agent.d.ts +0 -7
  31. package/dist/server/server/agent/providers/cursor-acp-agent.js +0 -78
  32. package/dist/server/server/agent/providers/mock-load-test-agent.d.ts +2 -0
  33. package/dist/server/server/agent/providers/mock-load-test-agent.js +73 -1
  34. package/dist/server/server/agent/providers/opencode/server-manager.js +1 -1
  35. package/dist/server/server/agent/structured-generation-providers.js +45 -1
  36. package/dist/server/server/agent-attention-policy.d.ts +12 -3
  37. package/dist/server/server/agent-attention-policy.js +15 -3
  38. package/dist/server/server/auto-archive-on-merge/archive-if-safe.d.ts +7 -6
  39. package/dist/server/server/auto-archive-on-merge/archive-if-safe.js +21 -16
  40. package/dist/server/server/bootstrap.d.ts +3 -0
  41. package/dist/server/server/bootstrap.js +91 -12
  42. package/dist/server/server/config.js +1 -0
  43. package/dist/server/server/daemon-config-store.js +1 -0
  44. package/dist/server/server/exports.d.ts +1 -1
  45. package/dist/server/server/exports.js +1 -1
  46. package/dist/server/server/loop-service.d.ts +24 -24
  47. package/dist/server/server/migrations/backfill-workspace-id.migration.d.ts +9 -0
  48. package/dist/server/server/migrations/backfill-workspace-id.migration.js +60 -0
  49. package/dist/server/server/paseo-worktree-service.d.ts +9 -0
  50. package/dist/server/server/paseo-worktree-service.js +71 -12
  51. package/dist/server/server/path-utils.d.ts +1 -0
  52. package/dist/server/server/path-utils.js +6 -1
  53. package/dist/server/server/persisted-config.d.ts +7 -0
  54. package/dist/server/server/persisted-config.js +1 -0
  55. package/dist/server/server/persistence-hooks.d.ts +1 -0
  56. package/dist/server/server/persistence-hooks.js +13 -5
  57. package/dist/server/server/resolve-workspace-id-for-path.d.ts +3 -0
  58. package/dist/server/server/resolve-workspace-id-for-path.js +41 -0
  59. package/dist/server/server/script-proxy.d.ts +1 -1
  60. package/dist/server/server/script-proxy.js +1 -1
  61. package/dist/server/server/service-proxy.js +1 -1
  62. package/dist/server/server/session.d.ts +31 -6
  63. package/dist/server/server/session.js +640 -196
  64. package/dist/server/server/websocket-server.d.ts +5 -0
  65. package/dist/server/server/websocket-server.js +137 -3
  66. package/dist/server/server/workspace-archive-service.d.ts +60 -3
  67. package/dist/server/server/workspace-archive-service.js +217 -4
  68. package/dist/server/server/workspace-directory.d.ts +20 -2
  69. package/dist/server/server/workspace-directory.js +148 -70
  70. package/dist/server/server/workspace-git-service.js +21 -21
  71. package/dist/server/server/workspace-reconciliation-service.d.ts +1 -1
  72. package/dist/server/server/workspace-reconciliation-service.js +21 -22
  73. package/dist/server/server/workspace-registry-bootstrap.js +23 -10
  74. package/dist/server/server/workspace-registry-model.d.ts +3 -3
  75. package/dist/server/server/workspace-registry-model.js +9 -10
  76. package/dist/server/server/workspace-registry.d.ts +17 -4
  77. package/dist/server/server/workspace-registry.js +27 -0
  78. package/dist/server/server/worktree/commands.d.ts +7 -5
  79. package/dist/server/server/worktree/commands.js +38 -18
  80. package/dist/server/server/worktree-bootstrap.d.ts +1 -0
  81. package/dist/server/server/worktree-bootstrap.js +4 -1
  82. package/dist/server/server/worktree-branch-name-generator.d.ts +5 -1
  83. package/dist/server/server/worktree-branch-name-generator.js +8 -2
  84. package/dist/server/server/worktree-session.d.ts +4 -5
  85. package/dist/server/server/worktree-session.js +9 -3
  86. package/dist/server/services/github-service.js +1 -1
  87. package/dist/server/terminal/activity/terminal-activity-tracker.d.ts +20 -0
  88. package/dist/server/terminal/activity/terminal-activity-tracker.js +59 -0
  89. package/dist/server/terminal/agent-hooks/agent-hook-installer.d.ts +62 -0
  90. package/dist/server/terminal/agent-hooks/agent-hook-installer.js +117 -0
  91. package/dist/server/terminal/agent-hooks/claude/claude-settings.d.ts +7 -0
  92. package/dist/server/terminal/agent-hooks/claude/claude-settings.js +88 -0
  93. package/dist/server/terminal/agent-hooks/claude/claude.d.ts +4 -0
  94. package/dist/server/terminal/agent-hooks/claude/claude.js +47 -0
  95. package/dist/server/terminal/agent-hooks/codex/codex-settings.d.ts +7 -0
  96. package/dist/server/terminal/agent-hooks/codex/codex-settings.js +99 -0
  97. package/dist/server/terminal/agent-hooks/codex/codex.d.ts +4 -0
  98. package/dist/server/terminal/agent-hooks/codex/codex.js +30 -0
  99. package/dist/server/terminal/agent-hooks/opencode/opencode-plugin.d.ts +4 -0
  100. package/dist/server/terminal/agent-hooks/opencode/opencode-plugin.js +46 -0
  101. package/dist/server/terminal/agent-hooks/opencode/opencode.d.ts +3 -0
  102. package/dist/server/terminal/agent-hooks/opencode/opencode.js +23 -0
  103. package/dist/server/terminal/agent-hooks/provider-registry.d.ts +24 -0
  104. package/dist/server/terminal/agent-hooks/provider-registry.js +36 -0
  105. package/dist/server/terminal/agent-hooks/terminal-agent-hook-setting.d.ts +10 -0
  106. package/dist/server/terminal/agent-hooks/terminal-agent-hook-setting.js +26 -0
  107. package/dist/server/terminal/terminal-manager-factory.d.ts +4 -1
  108. package/dist/server/terminal/terminal-manager-factory.js +2 -2
  109. package/dist/server/terminal/terminal-manager.d.ts +33 -2
  110. package/dist/server/terminal/terminal-manager.js +144 -18
  111. package/dist/server/terminal/terminal-output-coalescer.d.ts +4 -0
  112. package/dist/server/terminal/terminal-output-coalescer.js +18 -0
  113. package/dist/server/terminal/terminal-restore.d.ts +1 -0
  114. package/dist/server/terminal/terminal-restore.js +6 -0
  115. package/dist/server/terminal/terminal-session-controller.d.ts +4 -2
  116. package/dist/server/terminal/terminal-session-controller.js +65 -24
  117. package/dist/server/terminal/terminal-worker-process.js +146 -63
  118. package/dist/server/terminal/terminal-worker-protocol.d.ts +19 -14
  119. package/dist/server/terminal/terminal.d.ts +42 -0
  120. package/dist/server/terminal/terminal.js +235 -16
  121. package/dist/server/terminal/worker-terminal-manager.d.ts +1 -0
  122. package/dist/server/terminal/worker-terminal-manager.js +220 -36
  123. package/dist/server/utils/build-metadata-prompt.d.ts +1 -1
  124. package/dist/server/utils/github-remote.js +1 -1
  125. package/dist/server/utils/tree-kill.d.ts +2 -2
  126. package/dist/src/{utils/executable.js → executable-resolution/executable-resolution.js} +16 -14
  127. package/dist/src/executable-resolution/windows.js +62 -0
  128. package/dist/src/server/agent/provider-launch-config.js +1 -1
  129. package/dist/src/server/persisted-config.js +1 -0
  130. package/package.json +10 -5
  131. package/dist/server/server/agent/agent-metadata-generator.d.ts +0 -36
  132. package/dist/server/server/agent/agent-metadata-generator.js +0 -112
  133. package/dist/server/server/paseo-worktree-archive-service.d.ts +0 -41
  134. package/dist/server/server/paseo-worktree-archive-service.js +0 -144
@@ -1,4 +1,5 @@
1
1
  import { type ChildProcessWithoutNullStreams } from "node:child_process";
2
+ import type { ProcessTerminator } from "../../../utils/tree-kill.js";
2
3
  import type { ReadableStream as NodeReadableStream, WritableStream as NodeWritableStream } from "node:stream/web";
3
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";
4
5
  import type { Logger } from "pino";
@@ -31,6 +32,7 @@ interface ACPAgentClientOptions {
31
32
  capabilities?: AgentCapabilityFlags;
32
33
  waitForInitialCommands?: boolean;
33
34
  initialCommandsWaitTimeoutMs?: number;
35
+ terminateProcess?: ProcessTerminator;
34
36
  }
35
37
  interface ACPAgentSessionOptions {
36
38
  provider: string;
@@ -52,6 +54,7 @@ interface ACPAgentSessionOptions {
52
54
  launchEnv?: Record<string, string>;
53
55
  waitForInitialCommands?: boolean;
54
56
  initialCommandsWaitTimeoutMs?: number;
57
+ terminateProcess?: ProcessTerminator;
55
58
  }
56
59
  export interface SpawnedACPProcess {
57
60
  child: ChildProcessWithoutNullStreams;
@@ -148,6 +151,7 @@ export declare class ACPAgentClient implements AgentClient {
148
151
  private readonly thinkingOptionWriter?;
149
152
  private readonly waitForInitialCommands;
150
153
  private readonly initialCommandsWaitTimeoutMs;
154
+ protected readonly terminateProcess: ProcessTerminator;
151
155
  constructor(options: ACPAgentClientOptions);
152
156
  createSession(config: AgentSessionConfig, launchContext?: AgentLaunchContext): Promise<AgentSession>;
153
157
  resumeSession(handle: AgentPersistenceHandle, overrides?: Partial<AgentSessionConfig>, launchContext?: AgentLaunchContext): Promise<AgentSession>;
@@ -218,6 +222,7 @@ export declare class ACPAgentSession implements AgentSession, ACPClient {
218
222
  private historyPending;
219
223
  private replayingHistory;
220
224
  private bootstrapThreadEventPending;
225
+ private readonly terminateProcess;
221
226
  constructor(config: AgentSessionConfig, options: ACPAgentSessionOptions);
222
227
  get id(): string | null;
223
228
  initializeNewSession(): Promise<void>;
@@ -2,6 +2,7 @@ import { randomUUID } from "node:crypto";
2
2
  import fs from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import { Readable, Writable } from "node:stream";
5
+ import { terminateWithTreeKill } from "../../../utils/tree-kill.js";
5
6
  import { ClientSideConnection, PROTOCOL_VERSION, } from "@agentclientprotocol/sdk";
6
7
  import { getAgentStreamEventTurnId, } from "../agent-sdk-types.js";
7
8
  import { importSessionFromPersistence } from "../provider-session-import.js";
@@ -72,6 +73,10 @@ function resolveTerminalCommand(command, args) {
72
73
  export const DEFAULT_ACP_CAPABILITIES = {
73
74
  supportsStreaming: true,
74
75
  supportsSessionPersistence: true,
76
+ // ACP agents can list prior sessions via `session/list`. The runtime probe in
77
+ // listImportableSessions returns nothing for agents that don't advertise the
78
+ // capability, so enabling this here only makes the daemon query them.
79
+ supportsSessionListing: true,
75
80
  supportsDynamicModes: true,
76
81
  supportsMcpServers: true,
77
82
  supportsReasoningStream: true,
@@ -255,6 +260,7 @@ export function deriveModelDefinitionsFromACP(provider, models, configOptions) {
255
260
  export class ACPAgentClient {
256
261
  constructor(options) {
257
262
  this.provider = options.provider;
263
+ this.terminateProcess = options.terminateProcess ?? terminateWithTreeKill;
258
264
  this.capabilities = options.capabilities ?? DEFAULT_ACP_CAPABILITIES;
259
265
  this.logger = options.logger.child({
260
266
  module: "agent",
@@ -379,7 +385,13 @@ export class ACPAgentClient {
379
385
  const sessions = [];
380
386
  let cursor;
381
387
  for (;;) {
382
- const page = await this.runACPRequest(() => probe.connection.listSessions(cursor ? { cursor } : {}));
388
+ const page = await this.runACPRequest(() => probe.connection.listSessions({
389
+ ...(cursor ? { cursor } : {}),
390
+ // Filter by working directory at the source. Without this the agent
391
+ // returns globally-recent sessions, which the `limit` below can
392
+ // truncate before the current directory's sessions are reached.
393
+ ...(options?.cwd ? { cwd: options.cwd } : {}),
394
+ }));
383
395
  for (const session of page.sessions) {
384
396
  sessions.push({
385
397
  providerHandleId: session.sessionId,
@@ -463,7 +475,7 @@ export class ACPAgentClient {
463
475
  ]));
464
476
  }
465
477
  catch (error) {
466
- await terminateChildProcess(child, 2000);
478
+ await terminateChildProcess(child, 2000, this.terminateProcess);
467
479
  throw error;
468
480
  }
469
481
  finally {
@@ -500,7 +512,7 @@ export class ACPAgentClient {
500
512
  }
501
513
  }
502
514
  finally {
503
- await terminateChildProcess(probe.child, 2000);
515
+ await terminateChildProcess(probe.child, 2000, this.terminateProcess);
504
516
  }
505
517
  }
506
518
  async runACPRequest(request) {
@@ -572,6 +584,7 @@ export class ACPAgentSession {
572
584
  this.replayingHistory = false;
573
585
  this.bootstrapThreadEventPending = false;
574
586
  this.provider = options.provider;
587
+ this.terminateProcess = options.terminateProcess ?? terminateWithTreeKill;
575
588
  this.capabilities = options.capabilities;
576
589
  this.logger = options.logger.child({ module: "agent", provider: options.provider });
577
590
  this.runtimeSettings = options.runtimeSettings;
@@ -1106,13 +1119,14 @@ export class ACPAgentSession {
1106
1119
  this.logger.debug({ err: error }, "ACP closeSession failed during shutdown");
1107
1120
  }
1108
1121
  }
1109
- for (const terminal of this.terminalEntries.values()) {
1110
- terminal.child.kill("SIGTERM");
1111
- }
1122
+ const terminalTerminations = Array.from(this.terminalEntries.values(), (terminal) => this.terminateProcess(terminal.child, {
1123
+ gracefulTimeoutMs: 2000,
1124
+ forceTimeoutMs: 2000,
1125
+ }));
1126
+ await Promise.all(terminalTerminations);
1112
1127
  this.terminalEntries.clear();
1113
1128
  if (this.child) {
1114
- this.child.kill("SIGTERM");
1115
- await waitForChildExit(this.child, 2000);
1129
+ await this.terminateProcess(this.child, { gracefulTimeoutMs: 2000, forceTimeoutMs: 2000 });
1116
1130
  }
1117
1131
  this.subscribers.clear();
1118
1132
  this.connection = null;
@@ -1260,14 +1274,14 @@ export class ACPAgentSession {
1260
1274
  async releaseTerminal(params) {
1261
1275
  const entry = this.getTerminalEntry(params.terminalId);
1262
1276
  if (!entry.exit) {
1263
- entry.child.kill("SIGTERM");
1277
+ await this.terminateProcess(entry.child, { gracefulTimeoutMs: 2000, forceTimeoutMs: 2000 });
1264
1278
  }
1265
1279
  this.terminalEntries.delete(params.terminalId);
1266
1280
  }
1267
1281
  async killTerminal(params) {
1268
1282
  const entry = this.getTerminalEntry(params.terminalId);
1269
1283
  if (!entry.exit) {
1270
- entry.child.kill("SIGTERM");
1284
+ await this.terminateProcess(entry.child, { gracefulTimeoutMs: 2000, forceTimeoutMs: 2000 });
1271
1285
  }
1272
1286
  return {};
1273
1287
  }
@@ -2099,23 +2113,14 @@ function coerceSessionConfigMetadata(metadata) {
2099
2113
  }
2100
2114
  return metadata;
2101
2115
  }
2102
- async function waitForChildExit(child, timeoutMs) {
2103
- if (child.exitCode !== null || child.signalCode !== null) {
2104
- return;
2116
+ async function terminateChildProcess(child, timeoutMs, terminate) {
2117
+ try {
2118
+ await terminate(child, { gracefulTimeoutMs: timeoutMs, forceTimeoutMs: timeoutMs });
2105
2119
  }
2106
- await Promise.race([
2107
- new Promise((resolve) => child.once("exit", () => resolve())),
2108
- new Promise((resolve) => setTimeout(resolve, timeoutMs)),
2109
- ]);
2110
- if (child.exitCode === null && child.signalCode === null) {
2111
- child.kill("SIGKILL");
2120
+ finally {
2121
+ child.stdin.destroy();
2122
+ child.stdout.destroy();
2123
+ child.stderr.destroy();
2112
2124
  }
2113
2125
  }
2114
- async function terminateChildProcess(child, timeoutMs) {
2115
- child.kill("SIGTERM");
2116
- child.stdin.destroy();
2117
- child.stdout.destroy();
2118
- child.stderr.destroy();
2119
- await waitForChildExit(child, timeoutMs);
2120
- }
2121
2126
  //# sourceMappingURL=acp-agent.js.map
@@ -381,6 +381,9 @@ function isClaudeNoResponsePlaceholderText(value) {
381
381
  return normalizeClaudeTranscriptText(value) === NO_RESPONSE_REQUESTED_PLACEHOLDER;
382
382
  }
383
383
  const LOCAL_COMMAND_STDOUT_PATTERN = /^\s*<local-command-stdout>[\s\S]*<\/local-command-stdout>\s*$/;
384
+ const CLAUDE_COMMAND_MESSAGE_PATTERN = /<command-message>([\s\S]*?)<\/command-message>/;
385
+ const CLAUDE_COMMAND_ARGS_PATTERN = /<command-args>([\s\S]*?)<\/command-args>/;
386
+ const CLAUDE_COMMAND_NAME_PATTERN = /<command-name>([\s\S]*?)<\/command-name>/;
384
387
  function isClaudeLocalCommandStdout(value) {
385
388
  const normalized = normalizeClaudeTranscriptText(value);
386
389
  return normalized !== null && LOCAL_COMMAND_STDOUT_PATTERN.test(normalized);
@@ -426,7 +429,7 @@ export function extractUserMessageText(content) {
426
429
  if (!normalized || isClaudeTranscriptNoiseText(normalized)) {
427
430
  return null;
428
431
  }
429
- return normalized;
432
+ return normalizeClaudeUserPromptText(normalized);
430
433
  }
431
434
  if (!Array.isArray(content)) {
432
435
  return null;
@@ -440,7 +443,10 @@ export function extractUserMessageText(content) {
440
443
  if (text && text.trim()) {
441
444
  const trimmed = text.trim();
442
445
  if (!isClaudeTranscriptNoiseText(trimmed)) {
443
- parts.push(trimmed);
446
+ const normalized = normalizeClaudeUserPromptText(trimmed);
447
+ if (normalized) {
448
+ parts.push(normalized);
449
+ }
444
450
  }
445
451
  continue;
446
452
  }
@@ -448,7 +454,10 @@ export function extractUserMessageText(content) {
448
454
  if (input && input.trim()) {
449
455
  const trimmed = input.trim();
450
456
  if (!isClaudeTranscriptNoiseText(trimmed)) {
451
- parts.push(trimmed);
457
+ const normalized = normalizeClaudeUserPromptText(trimmed);
458
+ if (normalized) {
459
+ parts.push(normalized);
460
+ }
452
461
  }
453
462
  }
454
463
  }
@@ -4074,6 +4083,32 @@ function normalizeImportablePromptPreview(text) {
4074
4083
  return null;
4075
4084
  return normalized.length > 160 ? normalized.slice(0, 160) : normalized;
4076
4085
  }
4086
+ function normalizeClaudeUserPromptText(text) {
4087
+ const normalized = text.trim();
4088
+ if (!CLAUDE_COMMAND_MESSAGE_PATTERN.test(normalized)) {
4089
+ return normalized || null;
4090
+ }
4091
+ const command = readClaudeCommandPromptName(normalized);
4092
+ if (!command) {
4093
+ return null;
4094
+ }
4095
+ const commandArgs = normalized.match(CLAUDE_COMMAND_ARGS_PATTERN)?.[1]?.trim();
4096
+ if (commandArgs) {
4097
+ return `${command} ${commandArgs}`;
4098
+ }
4099
+ return command;
4100
+ }
4101
+ function readClaudeCommandPromptName(text) {
4102
+ const commandName = text.match(CLAUDE_COMMAND_NAME_PATTERN)?.[1]?.trim();
4103
+ if (commandName) {
4104
+ return commandName.startsWith("/") ? commandName : `/${commandName}`;
4105
+ }
4106
+ const commandMessage = text.match(CLAUDE_COMMAND_MESSAGE_PATTERN)?.[1]?.trim();
4107
+ if (!commandMessage) {
4108
+ return null;
4109
+ }
4110
+ return commandMessage.startsWith("/") ? commandMessage : `/${commandMessage}`;
4111
+ }
4077
4112
  function extractClaudeUserText(messageRaw) {
4078
4113
  const message = toObjectRecord(messageRaw);
4079
4114
  if (!message) {
@@ -4081,11 +4116,15 @@ function extractClaudeUserText(messageRaw) {
4081
4116
  }
4082
4117
  if (typeof message.content === "string") {
4083
4118
  const normalized = message.content.trim();
4084
- return normalized && !isClaudeTranscriptNoiseText(normalized) ? normalized : null;
4119
+ if (!normalized || isClaudeTranscriptNoiseText(normalized))
4120
+ return null;
4121
+ return normalizeClaudeUserPromptText(normalized);
4085
4122
  }
4086
4123
  if (typeof message.text === "string") {
4087
4124
  const normalized = message.text.trim();
4088
- return normalized && !isClaudeTranscriptNoiseText(normalized) ? normalized : null;
4125
+ if (!normalized || isClaudeTranscriptNoiseText(normalized))
4126
+ return null;
4127
+ return normalizeClaudeUserPromptText(normalized);
4089
4128
  }
4090
4129
  if (isUnknownArray(message.content)) {
4091
4130
  for (const block of message.content) {
@@ -4093,7 +4132,7 @@ function extractClaudeUserText(messageRaw) {
4093
4132
  if (blockRecord && typeof blockRecord.text === "string") {
4094
4133
  const normalized = blockRecord.text.trim();
4095
4134
  if (normalized && !isClaudeTranscriptNoiseText(normalized)) {
4096
- return normalized;
4135
+ return normalizeClaudeUserPromptText(normalized);
4097
4136
  }
4098
4137
  }
4099
4138
  }
@@ -12,7 +12,7 @@ import { composeSystemPromptParts } from "../system-prompt.js";
12
12
  import { curateAgentActivity } from "../activity-curator.js";
13
13
  import { mapCodexToolCallEnvelope, mapCodexToolCallFromThreadItem, } from "./codex/tool-call-mapper.js";
14
14
  import { checkProviderLaunchAvailable, createProviderEnv, createProviderEnvSpec, resolveProviderLaunch, } from "../provider-launch-config.js";
15
- import { findExecutable, probeExecutable } from "../../../utils/executable.js";
15
+ import { findExecutable, probeExecutable, } from "../../../executable-resolution/executable-resolution.js";
16
16
  import { createPathEquivalenceMatcher } from "../../../utils/path.js";
17
17
  import { spawnProcess } from "../../../utils/spawn.js";
18
18
  import { extractCodexTerminalSessionId, nonEmptyString } from "./tool-call-mapper-utils.js";
@@ -5,6 +5,7 @@ import { formatDiagnosticStatus, formatProviderDiagnostic, formatProviderDiagnos
5
5
  const COPILOT_CAPABILITIES = {
6
6
  supportsStreaming: true,
7
7
  supportsSessionPersistence: true,
8
+ supportsSessionListing: true,
8
9
  supportsDynamicModes: true,
9
10
  supportsMcpServers: true,
10
11
  supportsReasoningStream: true,
@@ -1,5 +1,4 @@
1
1
  import type { Logger } from "pino";
2
- import type { AgentModelDefinition, ListModelsOptions } from "../agent-sdk-types.js";
3
2
  import { GenericACPAgentClient } from "./generic-acp-agent.js";
4
3
  interface CursorACPAgentClientOptions {
5
4
  logger: Logger;
@@ -10,13 +9,7 @@ interface CursorACPAgentClientOptions {
10
9
  providerParams?: unknown;
11
10
  }
12
11
  export declare class CursorACPAgentClient extends GenericACPAgentClient {
13
- private readonly cursorCommand;
14
- private readonly env?;
15
12
  constructor(options: CursorACPAgentClientOptions);
16
- listModels(options: ListModelsOptions): Promise<AgentModelDefinition[]>;
17
- private canUseCursorModelsFallback;
18
- private listCursorModelsFallback;
19
13
  }
20
- export declare function parseCursorAgentModelsOutput(output: string): AgentModelDefinition[];
21
14
  export {};
22
15
  //# sourceMappingURL=cursor-acp-agent.d.ts.map
@@ -1,9 +1,5 @@
1
- import { basename } from "node:path";
2
- import * as spawnUtils from "../../../utils/spawn.js";
3
1
  import { GenericACPAgentClient } from "./generic-acp-agent.js";
4
- const CURSOR_MODELS_TIMEOUT_MS = 10000;
5
2
  const CURSOR_INITIAL_COMMANDS_WAIT_TIMEOUT_MS = 10000;
6
- const CURSOR_MODEL_MARKER_PATTERN = /\s+\((?:default|current)\)$/;
7
3
  export class CursorACPAgentClient extends GenericACPAgentClient {
8
4
  constructor(options) {
9
5
  super({
@@ -17,80 +13,6 @@ export class CursorACPAgentClient extends GenericACPAgentClient {
17
13
  waitForInitialCommands: true,
18
14
  initialCommandsWaitTimeoutMs: CURSOR_INITIAL_COMMANDS_WAIT_TIMEOUT_MS,
19
15
  });
20
- this.cursorCommand = options.command;
21
- this.env = options.env;
22
16
  }
23
- async listModels(options) {
24
- const acpModels = await super.listModels(options);
25
- if (acpModels.length > 0) {
26
- return acpModels;
27
- }
28
- if (this.canUseCursorModelsFallback()) {
29
- return this.listCursorModelsFallback(acpModels);
30
- }
31
- return acpModels;
32
- }
33
- canUseCursorModelsFallback() {
34
- return basename(this.cursorCommand[0]) === "cursor-agent";
35
- }
36
- async listCursorModelsFallback(acpModels) {
37
- try {
38
- const { stdout } = await spawnUtils.execCommand(this.cursorCommand[0], ["models"], {
39
- envOverlay: this.env,
40
- timeout: CURSOR_MODELS_TIMEOUT_MS,
41
- maxBuffer: 1024 * 1024,
42
- });
43
- const fallbackModels = parseCursorAgentModelsOutput(stdout);
44
- if (fallbackModels.length === 0) {
45
- this.logger.warn({ provider: "cursor" }, "Cursor ACP model fallback returned no parseable models");
46
- return acpModels;
47
- }
48
- return fallbackModels;
49
- }
50
- catch (error) {
51
- this.logger.warn({ err: error, provider: "cursor" }, "Failed to list Cursor models via cursor-agent models fallback");
52
- return acpModels;
53
- }
54
- }
55
- }
56
- export function parseCursorAgentModelsOutput(output) {
57
- const parsed = output
58
- .split(/\r?\n/)
59
- .map((line) => line.trim())
60
- .filter((line) => line && line !== "Available models" && !line.startsWith("Tip:"))
61
- .map((line) => {
62
- const separatorIndex = line.indexOf(" - ");
63
- if (separatorIndex <= 0) {
64
- return null;
65
- }
66
- const id = line.slice(0, separatorIndex).trim();
67
- const rawLabel = line.slice(separatorIndex + 3).trim();
68
- if (!id || !rawLabel) {
69
- return null;
70
- }
71
- let marker = null;
72
- if (rawLabel.endsWith(" (default)")) {
73
- marker = "default";
74
- }
75
- else if (rawLabel.endsWith(" (current)")) {
76
- marker = "current";
77
- }
78
- return {
79
- provider: "acp",
80
- id,
81
- label: rawLabel.replace(CURSOR_MODEL_MARKER_PATTERN, ""),
82
- marker,
83
- };
84
- })
85
- .filter((model) => model !== null);
86
- const defaultModelId = parsed.find((model) => model.marker === "default")?.id ??
87
- parsed.find((model) => model.marker === "current")?.id ??
88
- parsed[0]?.id;
89
- return parsed.map((model) => ({
90
- provider: model.provider,
91
- id: model.id,
92
- label: model.label,
93
- isDefault: model.id === defaultModelId,
94
- }));
95
17
  }
96
18
  //# sourceMappingURL=cursor-acp-agent.js.map
@@ -67,6 +67,8 @@ export declare class MockLoadTestAgentSession implements AgentSession {
67
67
  private scheduleStressTurn;
68
68
  private schedulePlanApprovalTurn;
69
69
  private scheduleQuestionPromptTurn;
70
+ private scheduleStructuredJsonTurn;
71
+ private emitStructuredJsonTurn;
70
72
  private emitPlanApprovalTurn;
71
73
  private emitQuestionPromptTurn;
72
74
  private emitStressTurn;
@@ -165,6 +165,35 @@ function parseAgentStreamStressPrompt(prompt) {
165
165
  coalesced: Boolean(match[2]),
166
166
  };
167
167
  }
168
+ function parseStructuredBranchNamePrompt(prompt) {
169
+ const text = promptToText(prompt);
170
+ const hasBranchNamePrompt = text.includes("Generate a git branch name for a coding agent") &&
171
+ (text.includes("Return JSON only with fields 'title' and 'branch'.") ||
172
+ text.includes('"title"') ||
173
+ text.includes('"branch"'));
174
+ if (!hasBranchNamePrompt &&
175
+ !(text.includes("You must respond with JSON only that matches this JSON Schema") &&
176
+ text.includes('"title"') &&
177
+ text.includes('"branch"'))) {
178
+ return null;
179
+ }
180
+ const seed = text.split("User context:\n").at(-1)?.trim() ?? "";
181
+ const firstLine = seed
182
+ .split("\n")
183
+ .find((line) => line.trim().length > 0)
184
+ ?.trim() ?? "Mock task";
185
+ const title = firstLine
186
+ .replace(/^["'`]+|["'`]+$/g, "")
187
+ .replace(/\s+/g, " ")
188
+ .slice(0, 80)
189
+ .trim();
190
+ const branch = title
191
+ .toLowerCase()
192
+ .replace(/[^a-z0-9]+/g, "-")
193
+ .replace(/^-+|-+$/g, "")
194
+ .slice(0, 100) || "mock-task";
195
+ return { title: title || "Mock task", branch };
196
+ }
168
197
  function buildRepeatedPayload(bytes, prefix) {
169
198
  const line = `${prefix} ${"x".repeat(96)}\n`;
170
199
  let output = "";
@@ -446,7 +475,11 @@ export class MockLoadTestAgentSession {
446
475
  const largePayload = parseLargeAgentStreamPayloadPrompt(prompt);
447
476
  const stress = parseAgentStreamStressPrompt(prompt);
448
477
  const questionPrompt = parseMockQuestionPrompt(prompt);
449
- if (shouldEmitPlanApprovalPrompt(prompt)) {
478
+ const structuredBranchName = parseStructuredBranchNamePrompt(prompt);
479
+ if (structuredBranchName) {
480
+ this.scheduleStructuredJsonTurn(turn, structuredBranchName);
481
+ }
482
+ else if (shouldEmitPlanApprovalPrompt(prompt)) {
450
483
  this.schedulePlanApprovalTurn(turn);
451
484
  }
452
485
  else if (questionPrompt) {
@@ -600,6 +633,45 @@ export class MockLoadTestAgentSession {
600
633
  }, 0);
601
634
  turn.timer.unref?.();
602
635
  }
636
+ scheduleStructuredJsonTurn(turn, result) {
637
+ turn.timer = setTimeout(() => {
638
+ this.emitStructuredJsonTurn(turn, result);
639
+ }, 0);
640
+ turn.timer.unref?.();
641
+ }
642
+ emitStructuredJsonTurn(turn, result) {
643
+ if (this.activeTurn !== turn) {
644
+ return;
645
+ }
646
+ this.clearTurnTimer(turn);
647
+ this.emit({
648
+ type: "turn_started",
649
+ provider: this.provider,
650
+ turnId: turn.turnId,
651
+ });
652
+ const finalText = JSON.stringify(result);
653
+ this.emitTimeline(turn.turnId, {
654
+ type: "assistant_message",
655
+ text: finalText,
656
+ });
657
+ this.activeTurn = null;
658
+ this.emit({
659
+ type: "turn_completed",
660
+ provider: this.provider,
661
+ turnId: turn.turnId,
662
+ });
663
+ turn.resolve({
664
+ sessionId: this.id,
665
+ finalText,
666
+ timeline: [
667
+ {
668
+ type: "assistant_message",
669
+ text: finalText,
670
+ },
671
+ ],
672
+ canceled: false,
673
+ });
674
+ }
603
675
  emitPlanApprovalTurn(turn) {
604
676
  if (this.activeTurn !== turn) {
605
677
  return;
@@ -1,5 +1,5 @@
1
1
  import net from "node:net";
2
- import { findExecutable } from "../../../../utils/executable.js";
2
+ import { findExecutable } from "../../../../executable-resolution/executable-resolution.js";
3
3
  import { spawnProcess } from "../../../../utils/spawn.js";
4
4
  import { terminateWithTreeKill } from "../../../../utils/tree-kill.js";
5
5
  import { createProviderEnvSpec, resolveProviderCommandPrefix, } from "../../provider-launch-config.js";
@@ -5,6 +5,21 @@ export const DEFAULT_STRUCTURED_GENERATION_PROVIDERS = [
5
5
  { modelSubstring: "nemotron-3-super" },
6
6
  ];
7
7
  export async function resolveStructuredGenerationProviders(options) {
8
+ const configuredProviders = readConfiguredProviders(options.daemonConfig);
9
+ if (configuredProviders.length > 0) {
10
+ const explicitProviders = resolveExplicitConfiguredProviders(configuredProviders);
11
+ if (explicitProviders.length === configuredProviders.length) {
12
+ return dedupeProviders(explicitProviders);
13
+ }
14
+ const providerEntries = await options.providerSnapshotManager.listProviders({
15
+ cwd: options.cwd,
16
+ wait: false,
17
+ });
18
+ const providers = resolveConfiguredProviders(configuredProviders, providerEntries);
19
+ if (providers.length > 0) {
20
+ return dedupeProviders(providers);
21
+ }
22
+ }
8
23
  const providerEntries = await options.providerSnapshotManager.listProviders({
9
24
  cwd: options.cwd,
10
25
  wait: true,
@@ -13,7 +28,7 @@ export async function resolveStructuredGenerationProviders(options) {
13
28
  const modelEntries = enabledEntries.filter((entry) => (entry.models?.length ?? 0) > 0);
14
29
  const entriesByProvider = new Map(enabledEntries.map((entry) => [entry.provider, entry]));
15
30
  const providers = [];
16
- for (const configured of readConfiguredProviders(options.daemonConfig)) {
31
+ for (const configured of configuredProviders) {
17
32
  const resolvedConfigured = resolveConfiguredCandidate(configured, modelEntries, entriesByProvider);
18
33
  if (!resolvedConfigured) {
19
34
  continue;
@@ -32,6 +47,35 @@ export async function resolveStructuredGenerationProviders(options) {
32
47
  }
33
48
  return dedupeProviders(providers);
34
49
  }
50
+ function resolveConfiguredProviders(configuredProviders, providerEntries) {
51
+ const enabledEntries = providerEntries.filter((entry) => entry.enabled);
52
+ const modelEntries = enabledEntries.filter((entry) => (entry.models?.length ?? 0) > 0);
53
+ const entriesByProvider = new Map(enabledEntries.map((entry) => [entry.provider, entry]));
54
+ const providers = [];
55
+ for (const configured of configuredProviders) {
56
+ const resolved = resolveConfiguredCandidate(configured, modelEntries, entriesByProvider);
57
+ if (resolved) {
58
+ providers.push(resolved);
59
+ }
60
+ }
61
+ return providers;
62
+ }
63
+ function resolveExplicitConfiguredProviders(configuredProviders) {
64
+ const providers = [];
65
+ for (const configured of configuredProviders) {
66
+ const provider = configured.provider.trim();
67
+ const model = configured.model?.trim();
68
+ if (!provider || !model) {
69
+ continue;
70
+ }
71
+ providers.push({
72
+ provider,
73
+ model,
74
+ ...(configured.thinkingOptionId ? { thinkingOptionId: configured.thinkingOptionId } : {}),
75
+ });
76
+ }
77
+ return providers;
78
+ }
35
79
  function resolveCurrentSelection(selection, readyEntries, entriesByProvider) {
36
80
  if (!selection) {
37
81
  return null;
@@ -4,17 +4,26 @@ export interface ClientPresenceState {
4
4
  appVisible: boolean;
5
5
  lastActivityAtMs: number | null;
6
6
  focusedAgentId: string | null;
7
+ focusedTerminalId: string | null;
7
8
  }
9
+ export type AttentionFocusTarget = {
10
+ kind: "agent";
11
+ id: string;
12
+ } | {
13
+ kind: "terminal";
14
+ id: string;
15
+ };
8
16
  export interface NotificationPlan {
9
17
  inAppRecipientIndex: number | null;
10
18
  shouldPush: boolean;
11
19
  }
12
20
  interface ComputeNotificationPlanInput {
13
21
  allStates: ClientPresenceState[];
14
- agentId: string;
15
- reason: AgentAttentionReason;
22
+ focusTarget: AttentionFocusTarget | null;
23
+ pushEligible: boolean;
16
24
  nowMs: number;
17
25
  }
18
- export declare function computeNotificationPlan({ allStates, agentId, reason, nowMs, }: ComputeNotificationPlanInput): NotificationPlan;
26
+ export declare function computeNotificationPlan({ allStates, focusTarget, pushEligible, nowMs, }: ComputeNotificationPlanInput): NotificationPlan;
27
+ export declare function isPushEligibleAttentionReason(reason: AgentAttentionReason): boolean;
19
28
  export {};
20
29
  //# sourceMappingURL=agent-attention-policy.d.ts.map
@@ -1,5 +1,14 @@
1
1
  export const PRESENCE_THRESHOLD_MS = 180000;
2
- export function computeNotificationPlan({ allStates, agentId, reason, nowMs, }) {
2
+ function isFocusedOnTarget(state, target) {
3
+ if (target === null) {
4
+ return false;
5
+ }
6
+ if (target.kind === "agent") {
7
+ return state.focusedAgentId === target.id;
8
+ }
9
+ return state.focusedTerminalId === target.id;
10
+ }
11
+ export function computeNotificationPlan({ allStates, focusTarget, pushEligible, nowMs, }) {
3
12
  let mostRecentPresentIndex = null;
4
13
  let mostRecentPresentAtMs = Number.NEGATIVE_INFINITY;
5
14
  for (const [clientIndex, state] of allStates.entries()) {
@@ -8,7 +17,7 @@ export function computeNotificationPlan({ allStates, agentId, reason, nowMs, })
8
17
  if (!isPresent) {
9
18
  continue;
10
19
  }
11
- if (state.appVisible && state.focusedAgentId === agentId) {
20
+ if (state.appVisible && isFocusedOnTarget(state, focusTarget)) {
12
21
  return { inAppRecipientIndex: null, shouldPush: false };
13
22
  }
14
23
  if (clampedActivityAtMs > mostRecentPresentAtMs) {
@@ -19,6 +28,9 @@ export function computeNotificationPlan({ allStates, agentId, reason, nowMs, })
19
28
  if (mostRecentPresentIndex !== null) {
20
29
  return { inAppRecipientIndex: mostRecentPresentIndex, shouldPush: false };
21
30
  }
22
- return { inAppRecipientIndex: null, shouldPush: reason !== "error" };
31
+ return { inAppRecipientIndex: null, shouldPush: pushEligible };
32
+ }
33
+ export function isPushEligibleAttentionReason(reason) {
34
+ return reason !== "error";
23
35
  }
24
36
  //# sourceMappingURL=agent-attention-policy.js.map