@getpaseo/server 0.1.99 → 0.1.101

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 (83) hide show
  1. package/dist/server/executable-resolution/windows.js +3 -0
  2. package/dist/server/server/agent/agent-manager.d.ts +10 -0
  3. package/dist/server/server/agent/agent-manager.js +65 -27
  4. package/dist/server/server/agent/agent-sdk-types.d.ts +8 -0
  5. package/dist/server/server/agent/mcp-server.d.ts +2 -45
  6. package/dist/server/server/agent/mcp-server.js +45 -1985
  7. package/dist/server/server/agent/prompt-attachments.js +6 -2
  8. package/dist/server/server/agent/provider-registry.js +1 -0
  9. package/dist/server/server/agent/provider-snapshot-manager.d.ts +4 -0
  10. package/dist/server/server/agent/provider-snapshot-manager.js +58 -13
  11. package/dist/server/server/agent/providers/acp-agent.d.ts +39 -2
  12. package/dist/server/server/agent/providers/acp-agent.js +281 -20
  13. package/dist/server/server/agent/providers/claude/agent.js +96 -62
  14. package/dist/server/server/agent/providers/codex-app-server-agent.js +6 -57
  15. package/dist/server/server/agent/providers/copilot-acp-agent.d.ts +2 -1
  16. package/dist/server/server/agent/providers/copilot-acp-agent.js +10 -0
  17. package/dist/server/server/agent/providers/diagnostic-utils.d.ts +1 -0
  18. package/dist/server/server/agent/providers/diagnostic-utils.js +1 -1
  19. package/dist/server/server/agent/providers/generic-acp-agent.d.ts +3 -0
  20. package/dist/server/server/agent/providers/generic-acp-agent.js +41 -23
  21. package/dist/server/server/agent/providers/mock-load-test-agent.js +4 -2
  22. package/dist/server/server/agent/providers/opencode/server-manager.d.ts +14 -11
  23. package/dist/server/server/agent/providers/opencode/server-manager.js +149 -91
  24. package/dist/server/server/agent/providers/opencode/test-server-manager.d.ts +6 -5
  25. package/dist/server/server/agent/providers/opencode/test-server-manager.js +13 -3
  26. package/dist/server/server/agent/providers/opencode/test-utils/{test-opencode-runtime.d.ts → test-opencode-harness.d.ts} +11 -11
  27. package/dist/server/server/agent/providers/opencode/test-utils/{test-opencode-runtime.js → test-opencode-harness.js} +23 -10
  28. package/dist/server/server/agent/providers/opencode-agent.d.ts +9 -3
  29. package/dist/server/server/agent/providers/opencode-agent.js +26 -38
  30. package/dist/server/server/agent/providers/pi/agent.d.ts +4 -2
  31. package/dist/server/server/agent/providers/pi/agent.js +8 -3
  32. package/dist/server/server/agent/providers/pi/cli-runtime.d.ts +3 -0
  33. package/dist/server/server/agent/providers/pi/cli-runtime.js +6 -3
  34. package/dist/server/server/agent/providers/pi/rpc-types.d.ts +2 -1
  35. package/dist/server/server/agent/providers/provider-image-output.d.ts +5 -0
  36. package/dist/server/server/agent/providers/provider-image-output.js +55 -0
  37. package/dist/server/server/agent/tools/paseo-tools.d.ts +48 -0
  38. package/dist/server/server/agent/tools/paseo-tools.js +2121 -0
  39. package/dist/server/server/agent/tools/types.d.ts +36 -0
  40. package/dist/server/server/agent/tools/types.js +2 -0
  41. package/dist/server/server/bootstrap.js +71 -62
  42. package/dist/server/server/persisted-config.d.ts +5 -0
  43. package/dist/server/server/persisted-config.js +10 -2
  44. package/dist/server/server/session/agent-updates/agent-updates-service.d.ts +59 -0
  45. package/dist/server/server/session/agent-updates/agent-updates-service.js +220 -0
  46. package/dist/server/server/session/checkout/checkout-session.d.ts +13 -15
  47. package/dist/server/server/session/checkout/checkout-session.js +18 -16
  48. package/dist/server/server/session/checkout/git-metadata-generator.d.ts +53 -0
  49. package/dist/server/server/session/checkout/git-metadata-generator.js +159 -0
  50. package/dist/server/server/session/daemon/daemon-session.d.ts +14 -0
  51. package/dist/server/server/session/daemon/daemon-session.js +38 -0
  52. package/dist/server/server/session/daemon/diagnostics.d.ts +41 -0
  53. package/dist/server/server/session/daemon/diagnostics.js +421 -0
  54. package/dist/server/server/session/git-mutation/git-mutation-service.d.ts +34 -0
  55. package/dist/server/server/session/git-mutation/git-mutation-service.js +71 -0
  56. package/dist/server/server/session/workspace-git-observer/workspace-git-observer-service.d.ts +36 -0
  57. package/dist/server/server/session/workspace-git-observer/workspace-git-observer-service.js +134 -0
  58. package/dist/server/server/session/workspace-provisioning/workspace-provisioning-service.d.ts +34 -0
  59. package/dist/server/server/session/workspace-provisioning/workspace-provisioning-service.js +190 -0
  60. package/dist/server/server/session/workspace-scripts/workspace-scripts-service.d.ts +41 -0
  61. package/dist/server/server/session/workspace-scripts/workspace-scripts-service.js +100 -0
  62. package/dist/server/server/session.d.ts +7 -51
  63. package/dist/server/server/session.js +113 -938
  64. package/dist/server/server/speech/providers/openai/config.d.ts +1 -2
  65. package/dist/server/server/speech/providers/openai/config.js +13 -9
  66. package/dist/server/server/speech/providers/openai/runtime.js +2 -16
  67. package/dist/server/server/speech/providers/openai/stt.d.ts +1 -0
  68. package/dist/server/server/speech/providers/openai/stt.js +4 -2
  69. package/dist/server/server/speech/providers/openai/tts.d.ts +1 -0
  70. package/dist/server/server/speech/providers/openai/tts.js +1 -0
  71. package/dist/server/server/websocket/runtime-metrics.d.ts +20 -0
  72. package/dist/server/server/websocket-server.d.ts +1 -2
  73. package/dist/server/server/websocket-server.js +26 -21
  74. package/dist/server/server/worktree-bootstrap.d.ts +1 -1
  75. package/dist/server/server/worktree-branch-name-generator.js +3 -1
  76. package/dist/server/utils/checkout-git.js +51 -26
  77. package/dist/src/executable-resolution/windows.js +3 -0
  78. package/dist/src/server/persisted-config.js +10 -2
  79. package/package.json +5 -5
  80. package/dist/server/server/agent/providers/opencode/runtime.d.ts +0 -28
  81. package/dist/server/server/agent/providers/opencode/runtime.js +0 -5
  82. package/dist/server/server/speech/providers/openai/realtime-transcription-session.d.ts +0 -42
  83. package/dist/server/server/speech/providers/openai/realtime-transcription-session.js +0 -168
@@ -1,36 +1,27 @@
1
1
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  import { z } from "zod";
3
- import { ensureValidJson } from "../json-utils.js";
4
- import { AgentFeatureSchema, AgentPermissionRequestPayloadSchema, AgentListItemPayloadSchema, AgentPermissionResponseSchema, AgentSnapshotPayloadSchema, } from "../messages.js";
5
- import { buildStoredAgentPayload, toAgentListItemPayload, toAgentPayload, } from "./agent-projections.js";
6
- import { curateAgentActivity } from "./activity-curator.js";
7
- import { selectItemsByProjectedLimit } from "./timeline-projection.js";
8
- import { ensureAgentLoaded } from "./agent-loading.js";
9
- import { isStoredAgentProviderAvailable } from "../persistence-hooks.js";
10
- import { killTerminalsForWorkspace, } from "../workspace-archive-service.js";
11
- import { WaitForAgentTracker } from "./wait-for-agent-tracker.js";
12
- import { createAgentCommand } from "./create-agent/create.js";
13
- import { expandUserPath, isSameOrDescendantPath, resolvePathFromBase } from "../path-utils.js";
14
- import { ScheduleRunSchema, ScheduleSummarySchema, StoredScheduleSchema, } from "@getpaseo/protocol/schedule/types";
15
- import { resolveSnapshotCwd } from "./provider-snapshot-manager.js";
16
- import { AgentModelSchema, AgentProviderEnum, AgentStatusEnum, ProviderModeSchema, ProviderSummarySchema, parseDurationString, resolveRequiredProviderModel, sanitizePermissionRequest, serializeSnapshotWithMetadata, toScheduleSummary, waitForAgentWithTimeout, } from "./mcp-shared.js";
17
- import { sendPromptToAgent, setupFinishNotification } from "./agent-prompt.js";
18
- import { respondToAgentPermission } from "./permission-response.js";
19
- import { archiveAgentCommand, cancelAgentRunCommand, closeAgentCommand, setAgentModeCommand, updateAgentCommand, } from "./lifecycle-command.js";
20
- import { WorktreeRequestError } from "../worktree-errors.js";
21
- import { archiveCommand, createPaseoWorktreeCommand, listPaseoWorktreesCommand, } from "../worktree/commands.js";
22
- function addModelVisibleStructuredContent(result) {
23
- if (result.structuredContent === undefined || result.content.length > 0) {
24
- return result;
3
+ import { createPaseoToolCatalog } from "./tools/paseo-tools.js";
4
+ function isZodSchema(value) {
5
+ return (typeof value === "object" &&
6
+ value !== null &&
7
+ typeof value.safeParseAsync === "function");
8
+ }
9
+ function relaxMcpOutputSchema(outputSchema) {
10
+ if (!outputSchema) {
11
+ return outputSchema;
12
+ }
13
+ if (isZodSchema(outputSchema)) {
14
+ return outputSchema instanceof z.ZodObject ? outputSchema.passthrough() : outputSchema;
15
+ }
16
+ return z.object(outputSchema).passthrough();
17
+ }
18
+ function relaxMcpToolOutputSchema(config) {
19
+ if (config.outputSchema === undefined) {
20
+ return config;
25
21
  }
26
22
  return {
27
- ...result,
28
- content: [
29
- {
30
- type: "text",
31
- text: formatStructuredContentForModel(result.structuredContent),
32
- },
33
- ],
23
+ ...config,
24
+ outputSchema: relaxMcpOutputSchema(config.outputSchema),
34
25
  };
35
26
  }
36
27
  function formatStructuredContentForModel(structuredContent) {
@@ -58,1974 +49,43 @@ function formatStructuredContentForModel(structuredContent) {
58
49
  const json = JSON.stringify(structuredContent, null, 2);
59
50
  return summary.length > 0 ? `${summary.join("\n")}\n\n${json}` : json;
60
51
  }
61
- function isZodSchema(value) {
62
- return (typeof value === "object" &&
63
- value !== null &&
64
- typeof value.safeParseAsync === "function");
65
- }
66
- function relaxMcpOutputSchema(outputSchema) {
67
- if (!outputSchema) {
68
- return outputSchema;
69
- }
70
- if (isZodSchema(outputSchema)) {
71
- return outputSchema instanceof z.ZodObject ? outputSchema.passthrough() : outputSchema;
72
- }
73
- return z.object(outputSchema).passthrough();
74
- }
75
- function relaxMcpToolOutputSchema(config) {
76
- if (config.outputSchema === undefined) {
77
- return config;
78
- }
79
- return {
80
- ...config,
81
- outputSchema: relaxMcpOutputSchema(config.outputSchema),
82
- };
83
- }
84
- function parseTimestamp(value) {
85
- if (!value) {
86
- return 0;
87
- }
88
- const parsed = Date.parse(value);
89
- return Number.isNaN(parsed) ? 0 : parsed;
90
- }
91
- function resolveAgentListActivityTime(agent) {
92
- return Math.max(parseTimestamp(agent.updatedAt), parseTimestamp(agent.lastUserMessageAt), parseTimestamp(agent.attentionTimestamp), parseTimestamp(agent.archivedAt), parseTimestamp(agent.createdAt));
93
- }
94
- function toProviderSummary(entry) {
95
- return {
96
- id: entry.provider,
97
- label: entry.label ?? entry.provider,
98
- description: entry.description ?? "",
99
- enabled: entry.enabled,
100
- modes: entry.modes ?? [],
101
- status: entry.status === "ready" ? "available" : entry.status,
102
- ...(entry.error ? { error: entry.error } : {}),
103
- };
104
- }
105
- function compareAgentListItems(a, b) {
106
- const attentionDelta = Number(b.requiresAttention ?? false) - Number(a.requiresAttention ?? false);
107
- if (attentionDelta !== 0) {
108
- return attentionDelta;
109
- }
110
- const statusOrder = {
111
- running: 0,
112
- initializing: 1,
113
- idle: 2,
114
- error: 3,
115
- closed: 4,
116
- };
117
- const statusDelta = (statusOrder[a.status] ?? 999) - (statusOrder[b.status] ?? 999);
118
- if (statusDelta !== 0) {
119
- return statusDelta;
120
- }
121
- return resolveAgentListActivityTime(b) - resolveAgentListActivityTime(a);
122
- }
123
- function resolveScheduleProviderAndModel(params) {
124
- const providerInput = params.provider?.trim() || params.defaultProvider;
125
- const slashIndex = providerInput.indexOf("/");
126
- if (slashIndex === -1) {
127
- return { provider: providerInput };
128
- }
129
- const provider = providerInput.slice(0, slashIndex).trim();
130
- const model = providerInput.slice(slashIndex + 1).trim();
131
- if (!provider || !model) {
132
- throw new Error("provider must be <provider> or <provider>/<model>");
133
- }
134
- return {
135
- provider: provider,
136
- model,
137
- };
138
- }
139
- function resolveScheduleUpdateProviderAndModel(params) {
140
- const providerInput = params.provider?.trim();
141
- const modelInput = typeof params.model === "string" ? params.model.trim() : params.model;
142
- if (params.model !== undefined && modelInput === "") {
143
- throw new Error("model cannot be empty");
144
- }
145
- if (!providerInput) {
146
- return params.model !== undefined ? { model: modelInput } : {};
147
- }
148
- const slashIndex = providerInput.indexOf("/");
149
- if (slashIndex === -1) {
150
- return {
151
- provider: providerInput,
152
- ...(params.model !== undefined ? { model: modelInput } : {}),
153
- };
154
- }
155
- const provider = providerInput.slice(0, slashIndex).trim();
156
- const modelFromProvider = providerInput.slice(slashIndex + 1).trim();
157
- if (!provider || !modelFromProvider) {
158
- throw new Error("provider must be <provider> or <provider>/<model>");
159
- }
160
- if (params.model === null) {
161
- throw new Error("provider specifies a model but model is null");
162
- }
163
- if (typeof modelInput === "string" && modelInput !== modelFromProvider) {
164
- throw new Error("Conflicting model values provided");
52
+ function addModelVisibleStructuredContent(result) {
53
+ if (result.structuredContent === undefined || result.content.length > 0) {
54
+ return result;
165
55
  }
166
56
  return {
167
- provider,
168
- model: modelInput ?? modelFromProvider,
57
+ ...result,
58
+ content: [
59
+ {
60
+ type: "text",
61
+ text: formatStructuredContentForModel(result.structuredContent),
62
+ },
63
+ ],
169
64
  };
170
65
  }
171
- function normalizeScheduleCadenceArg(value) {
172
- if (value === undefined) {
173
- return undefined;
174
- }
175
- const trimmed = value.trim();
176
- if (!trimmed) {
177
- return undefined;
178
- }
179
- return trimmed;
180
- }
181
- function normalizeScheduleTimeZoneArg(value) {
182
- return normalizeScheduleCadenceArg(value);
183
- }
184
- function resolveScheduleUpdateCadence(input) {
185
- const every = normalizeScheduleCadenceArg(input.every);
186
- const cron = normalizeScheduleCadenceArg(input.cron);
187
- const timeZone = normalizeScheduleTimeZoneArg(input.timezone);
188
- if (every !== undefined && cron !== undefined) {
189
- throw new Error("Specify at most one of every or cron");
190
- }
191
- if (timeZone !== undefined && cron === undefined) {
192
- throw new Error("timezone can only be used with cron");
193
- }
194
- if (every !== undefined) {
195
- return { type: "every", everyMs: parseDurationString(every) };
196
- }
197
- if (cron !== undefined) {
198
- return {
199
- type: "cron",
200
- expression: cron,
201
- ...(timeZone !== undefined ? { timezone: timeZone } : {}),
202
- };
203
- }
204
- return undefined;
205
- }
206
- function resolveScheduleUpdateExpiresAt(input) {
207
- if (input.expiresIn !== undefined && input.clearExpires) {
208
- throw new Error("Specify at most one of expiresIn or clearExpires");
209
- }
210
- if (input.expiresIn !== undefined) {
211
- return new Date(Date.now() + parseDurationString(input.expiresIn)).toISOString();
212
- }
213
- if (input.clearExpires) {
214
- return null;
215
- }
216
- return undefined;
217
- }
218
- function buildScheduleUpdateInput(input) {
219
- const cadence = resolveScheduleUpdateCadence(input);
220
- const expiresAt = resolveScheduleUpdateExpiresAt(input);
221
- const providerModelPatch = resolveScheduleUpdateProviderAndModel({
222
- provider: input.provider,
223
- model: input.model,
66
+ function toMcpToolResult(result) {
67
+ return addModelVisibleStructuredContent({
68
+ content: result.content,
69
+ ...(result.structuredContent !== undefined
70
+ ? { structuredContent: result.structuredContent }
71
+ : {}),
72
+ ...(result.isError !== undefined ? { isError: result.isError } : {}),
224
73
  });
225
- const newAgentConfig = {
226
- ...(providerModelPatch.provider !== undefined ? { provider: providerModelPatch.provider } : {}),
227
- ...(providerModelPatch.model !== undefined ? { model: providerModelPatch.model } : {}),
228
- ...(input.mode !== undefined ? { modeId: input.mode } : {}),
229
- ...(input.cwd !== undefined ? { cwd: input.cwd } : {}),
230
- };
231
- return {
232
- id: input.id,
233
- ...(input.name !== undefined ? { name: input.name } : {}),
234
- ...(input.prompt !== undefined ? { prompt: input.prompt } : {}),
235
- ...(cadence !== undefined ? { cadence } : {}),
236
- ...(input.maxRuns !== undefined ? { maxRuns: input.maxRuns } : {}),
237
- ...(expiresAt !== undefined ? { expiresAt } : {}),
238
- ...(Object.keys(newAgentConfig).length > 0 ? { newAgentConfig } : {}),
239
- };
240
- }
241
- function resolveChildAgentCwd(params) {
242
- const lockedCwd = params.lockedCwd?.trim();
243
- if (lockedCwd) {
244
- return expandUserPath(lockedCwd);
245
- }
246
- const requestedCwd = params.requestedCwd?.trim();
247
- if (!requestedCwd || !params.allowCustomCwd) {
248
- return params.parentCwd;
249
- }
250
- return resolvePathFromBase(params.parentCwd, requestedCwd);
251
- }
252
- const TerminalSummarySchema = z.object({
253
- id: z.string(),
254
- name: z.string(),
255
- cwd: z.string(),
256
- });
257
- const WorktreeSummarySchema = z.object({
258
- path: z.string(),
259
- createdAt: z.string(),
260
- branchName: z.string().optional(),
261
- head: z.string().optional(),
262
- });
263
- function resolveTerminalKeyToken(key, literal) {
264
- if (literal) {
265
- return key;
266
- }
267
- switch (key) {
268
- case "Enter":
269
- return "\r";
270
- case "Tab":
271
- return "\t";
272
- case "Escape":
273
- return "\u001b";
274
- case "Space":
275
- return " ";
276
- case "BSpace":
277
- return "\u007f";
278
- case "C-c":
279
- return "\u0003";
280
- case "C-d":
281
- return "\u0004";
282
- case "C-z":
283
- return "\u001a";
284
- case "C-l":
285
- return "\u000c";
286
- case "C-a":
287
- return "\u0001";
288
- case "C-e":
289
- return "\u0005";
290
- default:
291
- return key;
292
- }
293
74
  }
294
75
  export async function createAgentMcpServer(options) {
295
- const { agentManager, agentStorage, terminalManager, scheduleService, providerSnapshotManager, callerAgentId, resolveSpeakHandler, resolveCallerContext, logger, } = options;
296
- const childLogger = logger.child({ module: "agent", component: "mcp-server" });
297
- const waitTracker = new WaitForAgentTracker(logger);
298
- const callerContext = callerAgentId ? (resolveCallerContext?.(callerAgentId) ?? null) : null;
76
+ const catalog = await createPaseoToolCatalog(options);
299
77
  const server = new McpServer({
300
78
  name: "agent-mcp",
301
79
  version: "2.0.0",
302
80
  });
303
- const registerRawTool = server.registerTool.bind(server);
304
- const registerTool = (name, config, handler) => registerRawTool(name, relaxMcpToolOutputSchema(config), (async (args, extra) => addModelVisibleStructuredContent(await handler(args, extra))));
305
- const buildCronScheduleCadence = (input) => {
306
- const expression = input.cron?.trim() ?? "";
307
- if (!expression) {
308
- throw new Error("cron is required");
309
- }
310
- const timezone = normalizeScheduleTimeZoneArg(input.timezone);
311
- return {
312
- type: "cron",
313
- expression,
314
- ...(timezone !== undefined ? { timezone } : {}),
315
- };
316
- };
317
- const buildScheduleExpiry = (expiresIn) => {
318
- return expiresIn === undefined
319
- ? undefined
320
- : new Date(Date.now() + parseDurationString(expiresIn)).toISOString();
321
- };
322
- const resolveCallerAgent = () => {
323
- if (!callerAgentId) {
324
- return null;
325
- }
326
- const parentAgent = agentManager.getAgent(callerAgentId);
327
- if (!parentAgent) {
328
- throw new Error(`Parent agent ${callerAgentId} not found`);
329
- }
330
- return parentAgent;
331
- };
332
- const resolveScopedCwd = (requestedCwd, opts) => {
333
- const callerAgent = resolveCallerAgent();
334
- if (callerAgent) {
335
- return resolveChildAgentCwd({
336
- parentCwd: callerAgent.cwd,
337
- requestedCwd,
338
- lockedCwd: callerContext?.lockedCwd,
339
- allowCustomCwd: callerContext?.allowCustomCwd ?? true,
340
- });
341
- }
342
- const trimmedCwd = requestedCwd?.trim();
343
- if (!trimmedCwd) {
344
- if (opts?.required) {
345
- throw new Error("cwd is required");
346
- }
347
- throw new Error("cwd is required outside an agent-scoped session");
348
- }
349
- return expandUserPath(trimmedCwd);
350
- };
351
- async function resolveTerminalWorkspaceId(resolvedCwd) {
352
- // An MCP-spawned terminal belongs to the caller agent's workspace. Only if
353
- // the caller has no workspace do we mint one for the cwd.
354
- const callerAgent = callerAgentId ? agentManager.getAgent(callerAgentId) : null;
355
- if (callerAgent?.workspaceId) {
356
- return callerAgent.workspaceId;
357
- }
358
- if (!options.ensureWorkspaceForCreate) {
359
- throw new Error(callerAgentId
360
- ? `Caller agent ${callerAgentId} has no workspace and workspace minting is not configured`
361
- : "workspaceId is required outside an agent-scoped session");
362
- }
363
- return options.ensureWorkspaceForCreate(resolvedCwd);
364
- }
365
- const buildCallerAgentScheduleConfigExtras = (callerAgent) => {
366
- return {
367
- ...(callerAgent.config.thinkingOptionId
368
- ? { thinkingOptionId: callerAgent.config.thinkingOptionId }
369
- : {}),
370
- ...(callerAgent.config.approvalPolicy
371
- ? { approvalPolicy: callerAgent.config.approvalPolicy }
372
- : {}),
373
- ...(callerAgent.config.sandboxMode ? { sandboxMode: callerAgent.config.sandboxMode } : {}),
374
- ...(typeof callerAgent.config.networkAccess === "boolean"
375
- ? { networkAccess: callerAgent.config.networkAccess }
376
- : {}),
377
- ...(typeof callerAgent.config.webSearch === "boolean"
378
- ? { webSearch: callerAgent.config.webSearch }
379
- : {}),
380
- ...(callerAgent.config.title ? { title: callerAgent.config.title } : {}),
381
- ...(callerAgent.config.extra ? { extra: callerAgent.config.extra } : {}),
382
- ...(callerAgent.config.featureValues
383
- ? { featureValues: callerAgent.config.featureValues }
384
- : {}),
385
- ...(callerAgent.config.systemPrompt ? { systemPrompt: callerAgent.config.systemPrompt } : {}),
386
- ...(callerAgent.config.mcpServers ? { mcpServers: callerAgent.config.mcpServers } : {}),
387
- };
388
- };
389
- const buildCallerAgentScheduleConfig = (callerAgent, params) => {
390
- const hasProviderOverride = params?.provider !== undefined;
391
- const resolvedProviderModel = hasProviderOverride
392
- ? resolveScheduleProviderAndModel({
393
- provider: params?.provider,
394
- defaultProvider: callerAgent.provider,
395
- })
396
- : null;
397
- const resolvedProvider = resolvedProviderModel?.provider ?? callerAgent.provider;
398
- let resolvedModel;
399
- if (resolvedProviderModel?.model) {
400
- resolvedModel = resolvedProviderModel.model;
401
- }
402
- else if (!hasProviderOverride && callerAgent.config.model) {
403
- resolvedModel = callerAgent.config.model;
404
- }
405
- return {
406
- provider: resolvedProvider,
407
- cwd: params?.cwd?.trim() ? expandUserPath(params.cwd) : callerAgent.cwd,
408
- ...(callerAgent.currentModeId && callerAgent.provider === resolvedProvider
409
- ? {
410
- modeId: callerAgent.currentModeId,
411
- }
412
- : {}),
413
- ...(resolvedModel ? { model: resolvedModel } : {}),
414
- ...buildCallerAgentScheduleConfigExtras(callerAgent),
415
- };
416
- };
417
- const resolveNewAgentScheduleTarget = (params) => {
418
- if (!params?.provider?.trim()) {
419
- throw new Error("provider is required when target is new-agent");
420
- }
421
- const callerAgent = resolveCallerAgent();
422
- if (callerAgent) {
423
- return {
424
- type: "new-agent",
425
- config: buildCallerAgentScheduleConfig(callerAgent, params),
426
- };
427
- }
428
- const resolvedProviderModel = resolveScheduleProviderAndModel({
429
- provider: params?.provider,
430
- defaultProvider: params.provider,
431
- });
432
- return {
433
- type: "new-agent",
434
- config: {
435
- provider: resolvedProviderModel.provider,
436
- cwd: params?.cwd?.trim() ? expandUserPath(params.cwd) : process.cwd(),
437
- ...(resolvedProviderModel.model ? { model: resolvedProviderModel.model } : {}),
438
- },
439
- };
440
- };
441
- const ProviderModelInputSchema = AgentProviderEnum.trim()
442
- .refine((value) => value.includes("/"), {
443
- message: "provider must be provider/model, for example codex/gpt-5.4",
444
- })
445
- .refine((value) => {
446
- try {
447
- resolveRequiredProviderModel(value);
448
- return true;
449
- }
450
- catch {
451
- return false;
452
- }
453
- }, { message: "provider must be provider/model, for example codex/gpt-5.4" });
454
- const ProviderOrProviderModelInputSchema = AgentProviderEnum.trim()
455
- .min(1, "provider is required")
456
- .refine((value) => {
457
- if (!value.includes("/")) {
458
- return true;
459
- }
460
- try {
461
- resolveRequiredProviderModel(value);
462
- return true;
463
- }
464
- catch {
465
- return false;
466
- }
467
- }, { message: "provider must be provider or provider/model, for example codex/gpt-5.4" });
468
- const CreateAgentSettingsInputSchema = z
469
- .object({
470
- modeId: z.string().optional().describe("Session mode to configure before the first run."),
471
- thinkingOptionId: z.string().optional().describe("Thinking option ID."),
472
- features: z
473
- .record(z.string(), z.unknown())
474
- .optional()
475
- .describe("Provider-specific feature values, for example { fast_mode: true } for Codex."),
476
- })
477
- .strict();
478
- const UpdateAgentSettingsInputSchema = z
479
- .object({
480
- modeId: z.string().optional().describe("Session mode ID."),
481
- model: z.string().nullable().optional().describe("Model ID. Pass null to clear."),
482
- thinkingOptionId: z
483
- .string()
484
- .nullable()
485
- .optional()
486
- .describe("Thinking option ID. Pass null to clear."),
487
- features: z
488
- .record(z.string(), z.unknown())
489
- .optional()
490
- .describe("Provider-specific feature values, for example { fast_mode: true } for Codex."),
491
- })
492
- .strict();
493
- const InspectProviderSettingsInputSchema = z
494
- .object({
495
- modeId: z.string().optional().describe("Draft session mode ID."),
496
- model: z.string().optional().describe("Draft model ID."),
497
- thinkingOptionId: z.string().optional().describe("Draft thinking option ID."),
498
- features: z
499
- .record(z.string(), z.unknown())
500
- .optional()
501
- .describe("Draft provider feature values."),
502
- })
503
- .strict();
504
- const AgentRelationshipInputSchema = z.discriminatedUnion("kind", [
505
- z
506
- .object({ kind: z.literal("subagent") })
507
- .strict()
508
- .describe("Create a child agent under this agent's subagent track."),
509
- z
510
- .object({ kind: z.literal("detached") })
511
- .strict()
512
- .describe("Create a root agent that does not appear in this agent's subagent track."),
513
- ]);
514
- const AgentCreateWorktreeTargetInputSchema = z.discriminatedUnion("kind", [
515
- z
516
- .object({
517
- kind: z.literal("branch-off"),
518
- worktreeSlug: z
519
- .string()
520
- .min(1)
521
- .optional()
522
- .describe("Optional worktree slug/path label. Omit to let Paseo generate one."),
523
- branchName: z
524
- .string()
525
- .min(1)
526
- .optional()
527
- .describe("Optional git branch name. Defaults to the worktree slug."),
528
- baseBranch: z
529
- .string()
530
- .min(1)
531
- .optional()
532
- .describe("Optional base branch. Defaults to the repository default branch."),
533
- })
534
- .strict()
535
- .describe("Create a new branch in a new Paseo worktree."),
536
- z
537
- .object({
538
- kind: z.literal("checkout-branch"),
539
- branch: z.string().min(1).describe("Existing branch to check out."),
540
- })
541
- .strict()
542
- .describe("Check out an existing branch in a new Paseo worktree."),
543
- z
544
- .object({
545
- kind: z.literal("checkout-pr"),
546
- githubPrNumber: z.number().int().positive().describe("GitHub pull request number."),
547
- })
548
- .strict()
549
- .describe("Check out a GitHub pull request in a new Paseo worktree."),
550
- ]);
551
- const AgentWorkspaceInputSchema = z.discriminatedUnion("kind", [
552
- z
553
- .object({
554
- kind: z.literal("current"),
555
- cwd: z.string().optional().describe("Optional runtime cwd. Defaults to the caller's cwd."),
556
- })
557
- .strict()
558
- .describe("Use the caller's current workspace."),
559
- z
560
- .object({
561
- kind: z.literal("existing"),
562
- workspaceId: z.string().min(1).describe("Existing workspace id to attach the agent to."),
563
- cwd: z
564
- .string()
565
- .optional()
566
- .describe("Optional runtime cwd. Defaults to the existing workspace cwd."),
567
- })
568
- .strict()
569
- .describe("Attach the agent to an existing workspace."),
570
- z
571
- .object({
572
- kind: z.literal("create"),
573
- source: z.discriminatedUnion("kind", [
574
- z
575
- .object({
576
- kind: z.literal("directory"),
577
- path: z
578
- .string()
579
- .optional()
580
- .describe("Optional directory path. Defaults to the caller's cwd."),
581
- })
582
- .strict(),
583
- z
584
- .object({
585
- kind: z.literal("worktree"),
586
- cwd: z
587
- .string()
588
- .optional()
589
- .describe("Optional source repository. Defaults to the caller's cwd."),
590
- target: AgentCreateWorktreeTargetInputSchema,
591
- })
592
- .strict(),
593
- ]),
594
- })
595
- .strict()
596
- .describe("Create a new workspace for the agent."),
597
- ]);
598
- const commonCreateAgentInputSchema = {
599
- relationship: AgentRelationshipInputSchema.describe("Whether the created agent is a subagent under you or a detached root agent."),
600
- workspace: AgentWorkspaceInputSchema.describe("Workspace ownership/location for the created agent."),
601
- title: z
602
- .string()
603
- .trim()
604
- .min(1, "Title is required")
605
- .max(60, "Title must be 60 characters or fewer")
606
- .describe("Short descriptive title (<= 60 chars) summarizing the agent's focus."),
607
- provider: ProviderModelInputSchema.describe("Required provider/model pair, for example codex/gpt-5.4."),
608
- labels: z.record(z.string(), z.string()).optional().describe("Labels to set on the agent"),
609
- settings: CreateAgentSettingsInputSchema.optional().describe("Initial runtime settings for the new agent."),
610
- initialPrompt: z
611
- .string()
612
- .trim()
613
- .min(1, "initialPrompt is required")
614
- .describe("Required first task to run immediately after creation."),
615
- };
616
- const agentToAgentInputSchema = {
617
- ...commonCreateAgentInputSchema,
618
- notifyOnFinish: z
619
- .boolean()
620
- .optional()
621
- .default(true)
622
- .describe("Get notified when the created agent finishes, errors, or needs permission. Set false only for truly fire-and-forget agents."),
623
- };
624
- const topLevelInputSchema = {
625
- ...commonCreateAgentInputSchema,
626
- background: z
627
- .boolean()
628
- .optional()
629
- .default(false)
630
- .describe("Run agent in background. If false (default), waits for completion or permission request. If true, returns immediately."),
631
- notifyOnFinish: z
632
- .boolean()
633
- .optional()
634
- .default(false)
635
- .describe("Agent-scoped only: get notified when the created agent finishes, errors, or needs permission."),
636
- };
637
- const createAgentInputSchema = callerAgentId ? agentToAgentInputSchema : topLevelInputSchema;
638
- const agentToAgentCreateAgentArgsSchema = z.object(agentToAgentInputSchema).strict();
639
- const topLevelCreateAgentArgsSchema = z.object(topLevelInputSchema).strict();
640
- const commonSendAgentPromptInputSchema = {
641
- agentId: z.string(),
642
- prompt: z.string(),
643
- sessionMode: z.string().optional().describe("Optional mode to set before running the prompt."),
644
- };
645
- const agentToAgentSendAgentPromptInputSchema = {
646
- ...commonSendAgentPromptInputSchema,
647
- background: z
648
- .boolean()
649
- .optional()
650
- .default(true)
651
- .describe("Run agent in background. Agent-scoped default is true so you can continue until the finish notification arrives. Set false only when you need a blocking response."),
652
- notifyOnFinish: z
653
- .boolean()
654
- .optional()
655
- .default(true)
656
- .describe("Get notified when the prompted agent finishes, errors, or needs permission. Set false only for truly fire-and-forget prompts."),
657
- };
658
- const topLevelSendAgentPromptInputSchema = {
659
- ...commonSendAgentPromptInputSchema,
660
- background: z
661
- .boolean()
662
- .optional()
663
- .default(false)
664
- .describe("Run agent in background. If false (default), waits for completion or permission request. If true, returns immediately."),
665
- notifyOnFinish: z
666
- .boolean()
667
- .optional()
668
- .default(false)
669
- .describe("Agent-scoped only: get notified when the prompted agent finishes, errors, or needs permission."),
670
- };
671
- const sendAgentPromptInputSchema = callerAgentId
672
- ? agentToAgentSendAgentPromptInputSchema
673
- : topLevelSendAgentPromptInputSchema;
674
- const inspectProviderInputSchema = {
675
- provider: ProviderOrProviderModelInputSchema.describe("Provider ID, optionally with a model ID (for example codex or codex/gpt-5.4)."),
676
- cwd: z
677
- .string()
678
- .optional()
679
- .describe("Working directory used to resolve provider feature availability."),
680
- settings: InspectProviderSettingsInputSchema.optional().describe("Draft provider settings used to compute available features."),
681
- };
682
- if (options.voiceOnly || options.enableVoiceTools || callerContext?.enableVoiceTools) {
683
- registerTool("speak", {
684
- title: "Speak",
685
- description: "Speak text to the user via daemon-managed voice output. Blocks until playback completes.",
686
- inputSchema: {
687
- text: z
688
- .string()
689
- .trim()
690
- .min(1, "text is required")
691
- .max(4000, "text must be 4000 characters or fewer"),
692
- },
693
- outputSchema: {
694
- ok: z.boolean(),
695
- },
696
- }, async (args, context) => {
697
- if (!callerAgentId) {
698
- throw new Error("speak is only available to agent-scoped MCP sessions");
699
- }
700
- const handler = resolveSpeakHandler?.(callerAgentId) ?? null;
701
- if (!handler) {
702
- throw new Error(`No speak handler registered for your session '${callerAgentId}'`);
703
- }
704
- await handler({
705
- text: args.text,
706
- callerAgentId,
707
- signal: context?.signal,
708
- });
709
- return {
710
- content: [],
711
- structuredContent: ensureValidJson({ ok: true }),
712
- };
713
- });
714
- }
715
- if (options.voiceOnly) {
716
- return server;
717
- }
718
- registerTool("create_agent", {
719
- title: "Create agent",
720
- description: "Create an agent. Requires relationship, workspace, provider/model (for example codex/gpt-5.4), and an initial prompt. Do not guess; call list_providers and list_models first if uncertain.",
721
- inputSchema: createAgentInputSchema,
722
- outputSchema: {
723
- agentId: z.string(),
724
- type: AgentProviderEnum,
725
- status: AgentStatusEnum,
726
- cwd: z.string(),
727
- workspaceId: z.string().optional(),
728
- currentModeId: z.string().nullable(),
729
- availableModes: z.array(ProviderModeSchema),
730
- lastMessage: z.string().nullable().optional(),
731
- permission: AgentPermissionRequestPayloadSchema.nullable().optional(),
732
- guidance: z.string().optional(),
733
- },
734
- }, async (args) => {
735
- const resolvedArgs = await resolveCreateAgentToolArgs(args);
736
- const { parsedArgs, worktree } = resolvedArgs;
737
- let requestedBackground;
738
- let notifyOnFinish;
739
- let detached;
740
- if (resolvedArgs.kind === "agent-scoped") {
741
- requestedBackground = true;
742
- notifyOnFinish = parsedArgs.notifyOnFinish;
743
- detached = resolvedArgs.relationship.kind === "detached";
744
- }
745
- else {
746
- requestedBackground = resolvedArgs.parsedArgs.background;
747
- notifyOnFinish = resolvedArgs.parsedArgs.notifyOnFinish ?? false;
748
- detached = resolvedArgs.parsedArgs.relationship.kind === "detached";
749
- }
750
- const { snapshot, background: createdInBackground, initialPromptStarted, } = await createAgentCommand({
751
- agentManager,
752
- agentStorage,
753
- logger: childLogger,
754
- paseoHome: options.paseoHome,
755
- worktreesRoot: options.worktreesRoot,
756
- terminalManager,
757
- providerSnapshotManager,
758
- createPaseoWorktree: options.createPaseoWorktree,
759
- ...(options.ensureWorkspaceForCreate
760
- ? { ensureWorkspaceForCreate: options.ensureWorkspaceForCreate }
761
- : {}),
762
- }, {
763
- kind: "mcp",
764
- provider: parsedArgs.provider,
765
- title: parsedArgs.title,
766
- initialPrompt: parsedArgs.initialPrompt,
767
- cwd: resolvedArgs.cwd,
768
- workspaceId: resolvedArgs.workspaceId,
769
- thinking: parsedArgs.settings?.thinkingOptionId,
770
- features: parsedArgs.settings?.features,
771
- labels: parsedArgs.labels,
772
- mode: parsedArgs.settings?.modeId,
773
- background: requestedBackground,
774
- notifyOnFinish,
775
- detached,
776
- callerAgentId,
777
- callerContext,
778
- worktree,
779
- });
780
- try {
781
- if (!createdInBackground && initialPromptStarted) {
782
- const result = await waitForAgentWithTimeout(agentManager, snapshot.id, {
783
- waitForActive: true,
784
- });
785
- const liveSnapshot = agentManager.getAgent(snapshot.id) ?? snapshot;
786
- const responseData = {
787
- agentId: snapshot.id,
788
- type: snapshot.provider,
789
- status: result.status,
790
- cwd: liveSnapshot.cwd,
791
- ...(liveSnapshot.workspaceId ? { workspaceId: liveSnapshot.workspaceId } : {}),
792
- currentModeId: liveSnapshot.currentModeId,
793
- availableModes: liveSnapshot.availableModes,
794
- lastMessage: result.lastMessage,
795
- permission: sanitizePermissionRequest(result.permission),
796
- };
797
- const validJson = ensureValidJson(responseData);
798
- const response = {
799
- content: [],
800
- structuredContent: validJson,
801
- };
802
- return response;
803
- }
804
- }
805
- catch (error) {
806
- childLogger.error({ err: error, agentId: snapshot.id }, "Failed to run initial prompt");
807
- throw error;
808
- }
809
- // Return immediately for async creation.
810
- const currentSnapshot = agentManager.getAgent(snapshot.id) ?? snapshot;
811
- const guidance = callerAgentId && notifyOnFinish && initialPromptStarted
812
- ? "You will get notified when the created agent finishes, errors, or needs permission. Do not call wait_for_agent or poll for status; continue with other work until the notification arrives."
813
- : undefined;
814
- const response = {
815
- content: [],
816
- structuredContent: ensureValidJson({
817
- agentId: currentSnapshot.id,
818
- type: snapshot.provider,
819
- status: currentSnapshot.lifecycle,
820
- cwd: currentSnapshot.cwd,
821
- ...(currentSnapshot.workspaceId ? { workspaceId: currentSnapshot.workspaceId } : {}),
822
- currentModeId: currentSnapshot.currentModeId,
823
- availableModes: currentSnapshot.availableModes,
824
- lastMessage: null,
825
- permission: null,
826
- ...(guidance ? { guidance } : {}),
827
- }),
828
- };
829
- return response;
830
- });
831
- async function resolveCreateAgentToolArgs(args) {
832
- if (callerAgentId) {
833
- const parsed = agentToAgentCreateAgentArgsSchema.parse(args);
834
- const { cwd, workspaceId, worktree } = await resolveCreateAgentWorkspace(parsed.workspace);
835
- return {
836
- kind: "agent-scoped",
837
- parsedArgs: parsed,
838
- relationship: parsed.relationship,
839
- cwd,
840
- workspaceId,
841
- worktree,
842
- };
843
- }
844
- const parsedArgs = topLevelCreateAgentArgsSchema.parse(args);
845
- if (parsedArgs.relationship.kind === "subagent") {
846
- throw new Error("relationship subagent requires an agent-scoped MCP session");
847
- }
848
- const { cwd, workspaceId, worktree } = await resolveCreateAgentWorkspace(parsedArgs.workspace);
849
- return {
850
- kind: "top-level",
851
- parsedArgs,
852
- cwd,
853
- workspaceId,
854
- worktree,
855
- };
81
+ for (const tool of catalog.tools.values()) {
82
+ server.registerTool(tool.name, relaxMcpToolOutputSchema({
83
+ title: tool.title,
84
+ description: tool.description,
85
+ inputSchema: tool.inputSchema,
86
+ outputSchema: tool.outputSchema,
87
+ }), async (args, context) => toMcpToolResult(await catalog.executeTool(tool.name, args, { signal: context?.signal })));
856
88
  }
857
- async function resolveCreateAgentWorkspace(workspace) {
858
- if (workspace.kind === "current") {
859
- if (!callerAgentId) {
860
- throw new Error("workspace current requires an agent-scoped MCP session");
861
- }
862
- const callerAgent = resolveCallerAgent();
863
- if (!callerAgent?.workspaceId) {
864
- throw new Error(`Caller agent ${callerAgentId} has no current workspace`);
865
- }
866
- return {
867
- cwd: workspace.cwd,
868
- workspaceId: callerAgent.workspaceId,
869
- worktree: undefined,
870
- };
871
- }
872
- if (workspace.kind === "existing") {
873
- if (!options.listActiveWorkspaces) {
874
- throw new Error("Workspace lookup is not configured");
875
- }
876
- const existingWorkspace = (await options.listActiveWorkspaces()).find((candidate) => candidate.workspaceId === workspace.workspaceId);
877
- if (!existingWorkspace) {
878
- throw new Error(`Workspace ${workspace.workspaceId} not found`);
879
- }
880
- const cwd = workspace.cwd
881
- ? resolveScopedCwd(workspace.cwd, { required: true })
882
- : existingWorkspace.cwd;
883
- const lockedCwd = callerContext?.lockedCwd?.trim();
884
- if (lockedCwd && !isSameOrDescendantPath(expandUserPath(lockedCwd), cwd)) {
885
- throw new Error(`Workspace ${workspace.workspaceId} is outside the allowed cwd`);
886
- }
887
- return {
888
- cwd,
889
- workspaceId: workspace.workspaceId,
890
- worktree: undefined,
891
- };
892
- }
893
- if (workspace.source.kind === "directory") {
894
- const cwd = resolveScopedCwd(workspace.source.path, { required: true });
895
- if (!options.ensureWorkspaceForCreate) {
896
- throw new Error("Workspace creation is not configured");
897
- }
898
- return {
899
- cwd,
900
- workspaceId: await options.ensureWorkspaceForCreate(cwd),
901
- worktree: undefined,
902
- };
903
- }
904
- const cwd = resolveScopedCwd(workspace.source.cwd, { required: true });
905
- return {
906
- cwd,
907
- workspaceId: undefined,
908
- worktree: resolveCreateAgentWorktree(workspace.source.target),
909
- };
910
- }
911
- function resolveCreateAgentWorktree(target) {
912
- switch (target.kind) {
913
- case "branch-off":
914
- return {
915
- action: "branch-off",
916
- worktreeName: target.worktreeSlug,
917
- branchName: target.branchName,
918
- baseBranch: target.baseBranch,
919
- };
920
- case "checkout-branch":
921
- return {
922
- action: "checkout",
923
- refName: target.branch,
924
- };
925
- case "checkout-pr":
926
- return {
927
- action: "checkout",
928
- githubPrNumber: target.githubPrNumber,
929
- };
930
- default:
931
- throw new Error("unreachable");
932
- }
933
- }
934
- registerTool("wait_for_agent", {
935
- title: "Wait for agent",
936
- description: "Block until the agent requests permission or the current run completes. Returns the pending permission (if any) and recent activity summary.",
937
- inputSchema: {
938
- agentId: z.string().describe("Agent identifier returned by the create_agent tool"),
939
- },
940
- outputSchema: {
941
- agentId: z.string(),
942
- status: AgentStatusEnum,
943
- permission: AgentPermissionRequestPayloadSchema.nullable(),
944
- lastMessage: z.string().nullable(),
945
- },
946
- }, async ({ agentId }, { signal }) => {
947
- const abortController = new AbortController();
948
- const cleanupFns = [];
949
- const cleanup = () => {
950
- while (cleanupFns.length) {
951
- const fn = cleanupFns.pop();
952
- try {
953
- fn?.();
954
- }
955
- catch {
956
- // ignore cleanup errors
957
- }
958
- }
959
- };
960
- const forwardExternalAbort = () => {
961
- if (!abortController.signal.aborted) {
962
- const reason = signal?.reason ?? new Error("wait_for_agent aborted");
963
- abortController.abort(reason);
964
- }
965
- };
966
- if (signal) {
967
- if (signal.aborted) {
968
- forwardExternalAbort();
969
- }
970
- else {
971
- signal.addEventListener("abort", forwardExternalAbort, { once: true });
972
- cleanupFns.push(() => signal.removeEventListener("abort", forwardExternalAbort));
973
- }
974
- }
975
- const unregister = waitTracker.register(agentId, (reason) => {
976
- if (!abortController.signal.aborted) {
977
- abortController.abort(new Error(reason ?? "wait_for_agent cancelled"));
978
- }
979
- });
980
- cleanupFns.push(unregister);
981
- try {
982
- const result = await waitForAgentWithTimeout(agentManager, agentId, {
983
- signal: abortController.signal,
984
- });
985
- const validJson = ensureValidJson({
986
- agentId,
987
- status: result.status,
988
- permission: sanitizePermissionRequest(result.permission),
989
- lastMessage: result.lastMessage,
990
- });
991
- const response = {
992
- content: [],
993
- structuredContent: validJson,
994
- };
995
- return response;
996
- }
997
- finally {
998
- cleanup();
999
- }
1000
- });
1001
- registerTool("send_agent_prompt", {
1002
- title: "Send agent prompt",
1003
- description: "Send a task to a running agent. Agent-scoped callers run in background by default; top-level callers wait by default.",
1004
- inputSchema: sendAgentPromptInputSchema,
1005
- outputSchema: {
1006
- success: z.boolean(),
1007
- status: AgentStatusEnum,
1008
- lastMessage: z.string().nullable().optional(),
1009
- permission: AgentPermissionRequestPayloadSchema.nullable().optional(),
1010
- guidance: z.string().optional(),
1011
- },
1012
- }, async ({ agentId, prompt, sessionMode, background = Boolean(callerAgentId), notifyOnFinish = Boolean(callerAgentId), }) => {
1013
- if (agentManager.hasInFlightRun(agentId)) {
1014
- waitTracker.cancel(agentId, "Agent run interrupted by new prompt");
1015
- }
1016
- const shouldNotifyOnFinish = Boolean(callerAgentId && notifyOnFinish && background);
1017
- await sendPromptToAgent({
1018
- agentManager,
1019
- agentStorage,
1020
- agentId,
1021
- prompt,
1022
- sessionMode,
1023
- logger: childLogger,
1024
- });
1025
- if (shouldNotifyOnFinish && callerAgentId) {
1026
- setupFinishNotification({
1027
- agentManager,
1028
- agentStorage,
1029
- childAgentId: agentId,
1030
- callerAgentId,
1031
- logger: childLogger,
1032
- });
1033
- }
1034
- // If not running in background, wait for completion
1035
- if (!background) {
1036
- const result = await waitForAgentWithTimeout(agentManager, agentId, {
1037
- waitForActive: true,
1038
- });
1039
- const responseData = {
1040
- success: true,
1041
- status: result.status,
1042
- lastMessage: result.lastMessage,
1043
- permission: sanitizePermissionRequest(result.permission),
1044
- };
1045
- const validJson = ensureValidJson(responseData);
1046
- const response = {
1047
- content: [],
1048
- structuredContent: validJson,
1049
- };
1050
- return response;
1051
- }
1052
- // Return immediately if background=true
1053
- // Re-fetch snapshot since the state may have changed
1054
- const currentSnapshot = agentManager.getAgent(agentId);
1055
- const responseData = {
1056
- success: true,
1057
- status: currentSnapshot?.lifecycle ?? "idle",
1058
- lastMessage: null,
1059
- permission: null,
1060
- ...(shouldNotifyOnFinish
1061
- ? {
1062
- guidance: "You will get notified when the prompted agent finishes, errors, or needs permission. Do not call wait_for_agent or poll for status; continue with other work until the notification arrives.",
1063
- }
1064
- : {}),
1065
- };
1066
- const validJson = ensureValidJson(responseData);
1067
- const response = {
1068
- content: [],
1069
- structuredContent: validJson,
1070
- };
1071
- return response;
1072
- });
1073
- registerTool("get_agent_status", {
1074
- title: "Get agent status",
1075
- description: "Return the latest snapshot for an agent, including lifecycle state, capabilities, and pending permissions.",
1076
- inputSchema: {
1077
- agentId: z.string(),
1078
- },
1079
- outputSchema: {
1080
- status: AgentStatusEnum,
1081
- snapshot: AgentSnapshotPayloadSchema,
1082
- },
1083
- }, async ({ agentId }) => {
1084
- const snapshot = agentManager.getAgent(agentId);
1085
- if (snapshot) {
1086
- const structuredSnapshot = await serializeSnapshotWithMetadata(agentStorage, snapshot, childLogger);
1087
- return {
1088
- content: [],
1089
- structuredContent: ensureValidJson({
1090
- status: snapshot.lifecycle,
1091
- snapshot: structuredSnapshot,
1092
- }),
1093
- };
1094
- }
1095
- const record = await agentStorage.get(agentId);
1096
- if (!record || record.internal) {
1097
- throw new Error(`Agent ${agentId} not found`);
1098
- }
1099
- const structuredSnapshot = buildStoredAgentPayload(record, new Set(providerSnapshotManager.listRegisteredProviderIds()));
1100
- return {
1101
- content: [],
1102
- structuredContent: ensureValidJson({
1103
- status: structuredSnapshot.status,
1104
- snapshot: structuredSnapshot,
1105
- }),
1106
- };
1107
- });
1108
- registerTool("list_agents", {
1109
- title: "List agents",
1110
- description: "List recent agents as compact metadata.",
1111
- inputSchema: {
1112
- includeArchived: z.boolean().optional().default(false),
1113
- cwd: z.string().optional(),
1114
- sinceHours: z
1115
- .number()
1116
- .int()
1117
- .positive()
1118
- .max(24 * 30)
1119
- .optional()
1120
- .default(48),
1121
- statuses: z.array(AgentStatusEnum).optional(),
1122
- limit: z.number().int().positive().max(200).optional().default(50),
1123
- },
1124
- outputSchema: {
1125
- agents: z.array(AgentListItemPayloadSchema),
1126
- },
1127
- }, async ({ includeArchived = false, cwd, sinceHours = 48, statuses, limit = 50 }) => {
1128
- const callerCwd = callerAgentId ? resolveCallerAgent()?.cwd : undefined;
1129
- const requestedCwd = cwd?.trim() ? expandUserPath(cwd) : callerCwd;
1130
- const statusFilter = statuses && statuses.length > 0 ? new Set(statuses) : null;
1131
- const sinceMs = Date.now() - sinceHours * 60 * 60 * 1000;
1132
- const liveSnapshots = agentManager.listAgents();
1133
- const liveAgents = await Promise.all(liveSnapshots.map((snapshot) => serializeSnapshotWithMetadata(agentStorage, snapshot, childLogger)));
1134
- const liveIds = new Set(liveSnapshots.map((snapshot) => snapshot.id));
1135
- const storedRecords = await agentStorage.list();
1136
- const registeredProviderIds = new Set(providerSnapshotManager.listRegisteredProviderIds());
1137
- const storedAgents = storedRecords
1138
- .filter((record) => !record.internal && !liveIds.has(record.id))
1139
- .filter((record) => includeArchived || !record.archivedAt)
1140
- .filter((record) => includeArchived || isStoredAgentProviderAvailable(record, registeredProviderIds))
1141
- .map((record) => buildStoredAgentPayload(record, registeredProviderIds));
1142
- const agents = [...liveAgents, ...storedAgents]
1143
- .map(toAgentListItemPayload)
1144
- .filter((agent) => !requestedCwd || isSameOrDescendantPath(requestedCwd, agent.cwd))
1145
- .filter((agent) => !statusFilter || statusFilter.has(agent.status))
1146
- .filter((agent) => !agent.archivedAt || resolveAgentListActivityTime(agent) >= sinceMs)
1147
- .sort(compareAgentListItems)
1148
- .slice(0, limit);
1149
- return {
1150
- content: [],
1151
- structuredContent: ensureValidJson({ agents }),
1152
- };
1153
- });
1154
- registerTool("cancel_agent", {
1155
- title: "Cancel agent run",
1156
- description: "Abort the agent's current run but keep the agent alive for future tasks.",
1157
- inputSchema: {
1158
- agentId: z.string(),
1159
- },
1160
- outputSchema: {
1161
- success: z.boolean(),
1162
- },
1163
- }, async ({ agentId }) => {
1164
- const { cancelled } = await cancelAgentRunCommand({ agentManager, logger: childLogger }, agentId);
1165
- if (cancelled) {
1166
- waitTracker.cancel(agentId, "Agent run cancelled");
1167
- }
1168
- return {
1169
- content: [],
1170
- structuredContent: ensureValidJson({ success: cancelled }),
1171
- };
1172
- });
1173
- registerTool("archive_agent", {
1174
- title: "Archive agent",
1175
- description: "Archive an agent (soft-delete). The agent is interrupted if running and removed from the active list.",
1176
- inputSchema: {
1177
- agentId: z.string(),
1178
- },
1179
- outputSchema: {
1180
- success: z.boolean(),
1181
- },
1182
- }, async ({ agentId }) => {
1183
- await archiveAgentCommand({
1184
- agentManager,
1185
- agentStorage,
1186
- logger: childLogger,
1187
- }, agentId);
1188
- waitTracker.cancel(agentId, "Agent archived");
1189
- return {
1190
- content: [],
1191
- structuredContent: ensureValidJson({ success: true }),
1192
- };
1193
- });
1194
- registerTool("kill_agent", {
1195
- title: "Kill agent",
1196
- description: "Terminate an agent session permanently.",
1197
- inputSchema: {
1198
- agentId: z.string(),
1199
- },
1200
- outputSchema: {
1201
- success: z.boolean(),
1202
- },
1203
- }, async ({ agentId }) => {
1204
- await closeAgentCommand({ agentManager }, agentId);
1205
- waitTracker.cancel(agentId, "Agent terminated");
1206
- return {
1207
- content: [],
1208
- structuredContent: ensureValidJson({ success: true }),
1209
- };
1210
- });
1211
- registerTool("update_agent", {
1212
- title: "Update agent",
1213
- description: "Update an agent name, labels, and/or runtime settings.",
1214
- inputSchema: {
1215
- agentId: z.string(),
1216
- name: z.string().optional(),
1217
- labels: z.record(z.string(), z.string()).optional().describe("Labels to set on the agent"),
1218
- settings: UpdateAgentSettingsInputSchema.optional().describe("Runtime settings to apply to the agent."),
1219
- },
1220
- outputSchema: {
1221
- success: z.boolean(),
1222
- },
1223
- }, async ({ agentId, name, labels, settings }) => {
1224
- if (settings?.modeId !== undefined) {
1225
- await agentManager.setAgentMode(agentId, settings.modeId);
1226
- }
1227
- if (settings?.model !== undefined) {
1228
- await agentManager.setAgentModel(agentId, settings.model);
1229
- }
1230
- if (settings?.thinkingOptionId !== undefined) {
1231
- await agentManager.setAgentThinkingOption(agentId, settings.thinkingOptionId);
1232
- }
1233
- if (settings?.features) {
1234
- for (const [featureId, value] of Object.entries(settings.features)) {
1235
- await agentManager.setAgentFeature(agentId, featureId, value);
1236
- }
1237
- }
1238
- await updateAgentCommand({ agentManager }, { agentId, name, labels });
1239
- return {
1240
- content: [],
1241
- structuredContent: ensureValidJson({ success: true }),
1242
- };
1243
- });
1244
- registerTool("list_terminals", {
1245
- title: "List terminals",
1246
- description: "List terminals for a working directory or across all working directories.",
1247
- inputSchema: {
1248
- cwd: z
1249
- .string()
1250
- .optional()
1251
- .describe("Optional working directory. Defaults to your current working directory."),
1252
- all: z.boolean().optional().describe("List terminals across all working directories."),
1253
- },
1254
- outputSchema: {
1255
- terminals: z.array(TerminalSummarySchema),
1256
- },
1257
- }, async ({ cwd, all }) => {
1258
- if (!terminalManager) {
1259
- throw new Error("Terminal manager is not configured");
1260
- }
1261
- const terminals = all
1262
- ? (await Promise.all(terminalManager.listDirectories().map(async (directory) => (await terminalManager.getTerminals(directory)).map((terminal) => ({
1263
- id: terminal.id,
1264
- name: terminal.name,
1265
- cwd: terminal.cwd,
1266
- }))))).flat()
1267
- : (await terminalManager.getTerminals(resolveScopedCwd(cwd, { required: true }))).map((terminal) => ({
1268
- id: terminal.id,
1269
- name: terminal.name,
1270
- cwd: terminal.cwd,
1271
- }));
1272
- return {
1273
- content: [],
1274
- structuredContent: ensureValidJson({ terminals }),
1275
- };
1276
- });
1277
- registerTool("create_terminal", {
1278
- title: "Create terminal",
1279
- description: "Create a terminal session for a working directory.",
1280
- inputSchema: {
1281
- cwd: z
1282
- .string()
1283
- .optional()
1284
- .describe("Optional working directory. Defaults to your current working directory."),
1285
- name: z.string().optional().describe("Optional terminal name."),
1286
- },
1287
- outputSchema: TerminalSummarySchema.shape,
1288
- }, async ({ cwd, name }) => {
1289
- if (!terminalManager) {
1290
- throw new Error("Terminal manager is not configured");
1291
- }
1292
- const resolvedCwd = resolveScopedCwd(cwd, { required: true });
1293
- const workspaceId = await resolveTerminalWorkspaceId(resolvedCwd);
1294
- const terminal = await terminalManager.createTerminal({
1295
- cwd: resolvedCwd,
1296
- workspaceId,
1297
- ...(name?.trim() ? { name: name.trim() } : {}),
1298
- });
1299
- return {
1300
- content: [],
1301
- structuredContent: ensureValidJson({
1302
- id: terminal.id,
1303
- name: terminal.name,
1304
- cwd: terminal.cwd,
1305
- }),
1306
- };
1307
- });
1308
- registerTool("kill_terminal", {
1309
- title: "Kill terminal",
1310
- description: "Kill an existing terminal session.",
1311
- inputSchema: {
1312
- terminalId: z.string(),
1313
- },
1314
- outputSchema: {
1315
- success: z.boolean(),
1316
- },
1317
- }, async ({ terminalId }) => {
1318
- if (!terminalManager) {
1319
- throw new Error("Terminal manager is not configured");
1320
- }
1321
- const terminal = terminalManager.getTerminal(terminalId);
1322
- if (!terminal) {
1323
- throw new Error(`Terminal ${terminalId} not found`);
1324
- }
1325
- terminal.kill();
1326
- return {
1327
- content: [],
1328
- structuredContent: ensureValidJson({ success: true }),
1329
- };
1330
- });
1331
- registerTool("capture_terminal", {
1332
- title: "Capture terminal",
1333
- description: "Capture plain-text terminal output lines from a terminal session.",
1334
- inputSchema: {
1335
- terminalId: z.string(),
1336
- start: z.number().optional(),
1337
- end: z.number().optional(),
1338
- scrollback: z.boolean().optional(),
1339
- stripAnsi: z.boolean().optional().default(true),
1340
- },
1341
- outputSchema: {
1342
- terminalId: z.string(),
1343
- lines: z.array(z.string()),
1344
- totalLines: z.number().int().nonnegative(),
1345
- },
1346
- }, async ({ terminalId, start, end, scrollback, stripAnsi = true }) => {
1347
- if (!terminalManager) {
1348
- throw new Error("Terminal manager is not configured");
1349
- }
1350
- if (!terminalManager.getTerminal(terminalId)) {
1351
- throw new Error(`Terminal ${terminalId} not found`);
1352
- }
1353
- const capture = await terminalManager.captureTerminal(terminalId, {
1354
- start: scrollback ? 0 : start,
1355
- end,
1356
- stripAnsi,
1357
- });
1358
- return {
1359
- content: [],
1360
- structuredContent: ensureValidJson({
1361
- terminalId,
1362
- lines: capture.lines,
1363
- totalLines: capture.totalLines,
1364
- }),
1365
- };
1366
- });
1367
- registerTool("send_terminal_keys", {
1368
- title: "Send terminal keys",
1369
- description: "Send literal text or special key tokens to a terminal session.",
1370
- inputSchema: {
1371
- terminalId: z.string(),
1372
- keys: z.string(),
1373
- literal: z.boolean().optional(),
1374
- },
1375
- outputSchema: {
1376
- success: z.boolean(),
1377
- },
1378
- }, async ({ terminalId, keys, literal = false }) => {
1379
- if (!terminalManager) {
1380
- throw new Error("Terminal manager is not configured");
1381
- }
1382
- const terminal = terminalManager.getTerminal(terminalId);
1383
- if (!terminal) {
1384
- throw new Error(`Terminal ${terminalId} not found`);
1385
- }
1386
- terminal.send({
1387
- type: "input",
1388
- data: resolveTerminalKeyToken(keys, literal),
1389
- });
1390
- return {
1391
- content: [],
1392
- structuredContent: ensureValidJson({ success: true }),
1393
- };
1394
- });
1395
- registerTool("create_schedule", {
1396
- title: "Create schedule",
1397
- description: "Create a recurring schedule that starts a new agent on a cron cadence.",
1398
- inputSchema: {
1399
- prompt: z.string().trim().min(1, "prompt is required"),
1400
- cron: z.string().trim().min(1, "cron is required"),
1401
- timezone: z
1402
- .string()
1403
- .trim()
1404
- .min(1)
1405
- .optional()
1406
- .describe("IANA time zone for the cron cadence. For example: America/New_York."),
1407
- name: z.string().optional(),
1408
- provider: AgentProviderEnum.describe("Provider, or provider/model (for example: codex or codex/gpt-5.4)."),
1409
- cwd: z.string().optional(),
1410
- maxRuns: z.number().int().positive().optional(),
1411
- expiresIn: z.string().optional(),
1412
- },
1413
- outputSchema: ScheduleSummarySchema.shape,
1414
- }, async ({ prompt, cron, timezone, name, provider, cwd, maxRuns, expiresIn }) => {
1415
- if (!scheduleService) {
1416
- throw new Error("Schedule service is not configured");
1417
- }
1418
- const expiresAt = buildScheduleExpiry(expiresIn);
1419
- const schedule = await scheduleService.create({
1420
- prompt: prompt.trim(),
1421
- cadence: buildCronScheduleCadence({
1422
- cron,
1423
- ...(timezone !== undefined ? { timezone } : {}),
1424
- }),
1425
- target: resolveNewAgentScheduleTarget({ provider, cwd }),
1426
- ...(name?.trim() ? { name: name.trim() } : {}),
1427
- ...(maxRuns === undefined ? {} : { maxRuns }),
1428
- ...(expiresAt === undefined ? {} : { expiresAt }),
1429
- });
1430
- return {
1431
- content: [],
1432
- structuredContent: ensureValidJson(toScheduleSummary(schedule)),
1433
- };
1434
- });
1435
- registerTool("create_heartbeat", {
1436
- title: "Create heartbeat",
1437
- description: "Create a recurring heartbeat that sends you a prompt on a cron cadence.",
1438
- inputSchema: {
1439
- prompt: z.string().trim().min(1, "prompt is required"),
1440
- cron: z.string().trim().min(1, "cron is required"),
1441
- timezone: z
1442
- .string()
1443
- .trim()
1444
- .min(1)
1445
- .optional()
1446
- .describe("IANA time zone for the cron cadence. For example: America/New_York."),
1447
- name: z.string().optional(),
1448
- maxRuns: z.number().int().positive().optional(),
1449
- expiresIn: z.string().optional(),
1450
- },
1451
- outputSchema: ScheduleSummarySchema.shape,
1452
- }, async ({ prompt, cron, timezone, name, maxRuns, expiresIn }) => {
1453
- if (!scheduleService) {
1454
- throw new Error("Schedule service is not configured");
1455
- }
1456
- if (!callerAgentId) {
1457
- throw new Error("create_heartbeat requires an agent-scoped session");
1458
- }
1459
- resolveCallerAgent();
1460
- const expiresAt = buildScheduleExpiry(expiresIn);
1461
- const schedule = await scheduleService.create({
1462
- prompt: prompt.trim(),
1463
- cadence: buildCronScheduleCadence({
1464
- cron,
1465
- ...(timezone !== undefined ? { timezone } : {}),
1466
- }),
1467
- target: { type: "agent", agentId: callerAgentId },
1468
- ...(name?.trim() ? { name: name.trim() } : {}),
1469
- ...(maxRuns === undefined ? {} : { maxRuns }),
1470
- ...(expiresAt === undefined ? {} : { expiresAt }),
1471
- });
1472
- return {
1473
- content: [],
1474
- structuredContent: ensureValidJson(toScheduleSummary(schedule)),
1475
- };
1476
- });
1477
- registerTool("list_schedules", {
1478
- title: "List schedules",
1479
- description: "List all schedules managed by the daemon.",
1480
- inputSchema: {},
1481
- outputSchema: {
1482
- schedules: z.array(ScheduleSummarySchema),
1483
- },
1484
- }, async () => {
1485
- if (!scheduleService) {
1486
- throw new Error("Schedule service is not configured");
1487
- }
1488
- const schedules = (await scheduleService.list()).map((schedule) => toScheduleSummary(schedule));
1489
- return {
1490
- content: [],
1491
- structuredContent: ensureValidJson({ schedules }),
1492
- };
1493
- });
1494
- registerTool("inspect_schedule", {
1495
- title: "Inspect schedule",
1496
- description: "Inspect a schedule and its run history.",
1497
- inputSchema: {
1498
- id: z.string(),
1499
- },
1500
- outputSchema: StoredScheduleSchema.shape,
1501
- }, async ({ id }) => {
1502
- if (!scheduleService) {
1503
- throw new Error("Schedule service is not configured");
1504
- }
1505
- const schedule = await scheduleService.inspect(id);
1506
- return {
1507
- content: [],
1508
- structuredContent: ensureValidJson(schedule),
1509
- };
1510
- });
1511
- registerTool("pause_schedule", {
1512
- title: "Pause schedule",
1513
- description: "Pause an active schedule.",
1514
- inputSchema: {
1515
- id: z.string(),
1516
- },
1517
- outputSchema: {
1518
- success: z.boolean(),
1519
- },
1520
- }, async ({ id }) => {
1521
- if (!scheduleService) {
1522
- throw new Error("Schedule service is not configured");
1523
- }
1524
- await scheduleService.pause(id);
1525
- return {
1526
- content: [],
1527
- structuredContent: ensureValidJson({ success: true }),
1528
- };
1529
- });
1530
- registerTool("resume_schedule", {
1531
- title: "Resume schedule",
1532
- description: "Resume a paused schedule.",
1533
- inputSchema: {
1534
- id: z.string(),
1535
- },
1536
- outputSchema: {
1537
- success: z.boolean(),
1538
- },
1539
- }, async ({ id }) => {
1540
- if (!scheduleService) {
1541
- throw new Error("Schedule service is not configured");
1542
- }
1543
- await scheduleService.resume(id);
1544
- return {
1545
- content: [],
1546
- structuredContent: ensureValidJson({ success: true }),
1547
- };
1548
- });
1549
- registerTool("delete_schedule", {
1550
- title: "Delete schedule",
1551
- description: "Delete a schedule permanently.",
1552
- inputSchema: {
1553
- id: z.string(),
1554
- },
1555
- outputSchema: {
1556
- success: z.boolean(),
1557
- },
1558
- }, async ({ id }) => {
1559
- if (!scheduleService) {
1560
- throw new Error("Schedule service is not configured");
1561
- }
1562
- await scheduleService.delete(id);
1563
- return {
1564
- content: [],
1565
- structuredContent: ensureValidJson({ success: true }),
1566
- };
1567
- });
1568
- registerTool("update_schedule", {
1569
- title: "Update schedule",
1570
- description: "Update an existing schedule. Only provided fields are changed; omitted fields remain unchanged.",
1571
- inputSchema: {
1572
- id: z.string(),
1573
- every: z.string().optional().describe("New interval duration string (e.g. 5m, 1h)."),
1574
- cron: z.string().optional().describe("New cron expression."),
1575
- timezone: z
1576
- .string()
1577
- .trim()
1578
- .min(1)
1579
- .optional()
1580
- .describe("IANA time zone for cron cadence; requires cron. For example: America/New_York."),
1581
- name: z.string().nullable().optional().describe("New name (null to clear)."),
1582
- prompt: z.string().trim().min(1).optional().describe("New prompt text."),
1583
- maxRuns: z
1584
- .number()
1585
- .int()
1586
- .positive()
1587
- .nullable()
1588
- .optional()
1589
- .describe("New max runs limit (null to clear)."),
1590
- provider: z
1591
- .string()
1592
- .trim()
1593
- .min(1)
1594
- .optional()
1595
- .describe("New provider for new-agent target."),
1596
- model: z
1597
- .string()
1598
- .trim()
1599
- .min(1)
1600
- .nullable()
1601
- .optional()
1602
- .describe("New model for new-agent target (null to clear)."),
1603
- mode: z
1604
- .string()
1605
- .trim()
1606
- .min(1)
1607
- .nullable()
1608
- .optional()
1609
- .describe("New mode for new-agent target (null to clear)."),
1610
- cwd: z.string().trim().min(1).optional().describe("New cwd for new-agent target."),
1611
- expiresIn: z
1612
- .string()
1613
- .optional()
1614
- .describe("New relative expiry duration (for example: 1h, 2d)."),
1615
- clearExpires: z.boolean().optional().describe("Clear any schedule expiry."),
1616
- },
1617
- outputSchema: StoredScheduleSchema.shape,
1618
- }, async (input) => {
1619
- if (!scheduleService) {
1620
- throw new Error("Schedule service is not configured");
1621
- }
1622
- const schedule = await scheduleService.update(buildScheduleUpdateInput(input));
1623
- return {
1624
- content: [],
1625
- structuredContent: ensureValidJson(schedule),
1626
- };
1627
- });
1628
- registerTool("schedule_logs", {
1629
- title: "Schedule logs",
1630
- description: "Get the run history (logs) for a schedule.",
1631
- inputSchema: {
1632
- id: z.string(),
1633
- },
1634
- outputSchema: {
1635
- runs: z.array(ScheduleRunSchema),
1636
- },
1637
- }, async ({ id }) => {
1638
- if (!scheduleService) {
1639
- throw new Error("Schedule service is not configured");
1640
- }
1641
- const runs = await scheduleService.logs(id);
1642
- return {
1643
- content: [],
1644
- structuredContent: ensureValidJson({ runs }),
1645
- };
1646
- });
1647
- registerTool("list_providers", {
1648
- title: "List providers",
1649
- description: "List configured agent providers, availability, and their modes.",
1650
- inputSchema: {},
1651
- outputSchema: {
1652
- providers: z.array(ProviderSummarySchema),
1653
- },
1654
- }, async () => {
1655
- const providers = (await providerSnapshotManager.listProviders({ wait: true })).map(toProviderSummary);
1656
- return {
1657
- content: [],
1658
- structuredContent: ensureValidJson({ providers }),
1659
- };
1660
- });
1661
- registerTool("list_models", {
1662
- title: "List models",
1663
- description: "List models for an agent provider.",
1664
- inputSchema: {
1665
- provider: AgentProviderEnum,
1666
- },
1667
- outputSchema: {
1668
- provider: z.string(),
1669
- models: z.array(AgentModelSchema),
1670
- },
1671
- }, async ({ provider }) => {
1672
- const models = await providerSnapshotManager.listModels({
1673
- cwd: resolveSnapshotCwd(),
1674
- provider,
1675
- wait: true,
1676
- });
1677
- return {
1678
- content: [],
1679
- structuredContent: ensureValidJson({
1680
- provider,
1681
- models,
1682
- }),
1683
- };
1684
- });
1685
- registerTool("inspect_provider", {
1686
- title: "Inspect provider",
1687
- description: "Inspect compact provider capabilities for orchestration, including modes and draft feature settings. Use list_models for the full model list.",
1688
- inputSchema: inspectProviderInputSchema,
1689
- outputSchema: {
1690
- provider: AgentProviderEnum,
1691
- label: z.string().nullable().optional(),
1692
- description: z.string().nullable().optional(),
1693
- enabled: z.boolean(),
1694
- status: z.string(),
1695
- modes: z.array(ProviderModeSchema).nullish(),
1696
- selectedModel: z.string().nullable(),
1697
- features: z.array(AgentFeatureSchema),
1698
- },
1699
- }, async ({ provider, cwd, settings }) => {
1700
- const resolvedProviderModel = resolveScheduleProviderAndModel({
1701
- provider,
1702
- defaultProvider: provider,
1703
- });
1704
- const providerId = resolvedProviderModel.provider;
1705
- const resolvedCwd = resolveScopedCwd(cwd, { required: true });
1706
- const entry = await providerSnapshotManager.getProvider({
1707
- cwd: resolvedCwd,
1708
- provider: providerId,
1709
- wait: true,
1710
- });
1711
- const summary = toProviderSummary(entry);
1712
- if (!entry.enabled) {
1713
- throw new Error(`Provider '${providerId}' is disabled`);
1714
- }
1715
- if (entry.status !== "ready") {
1716
- throw new Error(entry.error ?? `Provider '${providerId}' is unavailable`);
1717
- }
1718
- const selectedModel = settings?.model ?? resolvedProviderModel.model;
1719
- const features = await agentManager.listDraftFeatures({
1720
- provider: providerId,
1721
- cwd: resolvedCwd,
1722
- ...(settings?.modeId ? { modeId: settings.modeId } : {}),
1723
- ...(selectedModel ? { model: selectedModel } : {}),
1724
- ...(settings?.thinkingOptionId ? { thinkingOptionId: settings.thinkingOptionId } : {}),
1725
- ...(settings?.features ? { featureValues: settings.features } : {}),
1726
- });
1727
- return {
1728
- content: [],
1729
- structuredContent: ensureValidJson({
1730
- provider: providerId,
1731
- label: summary.label,
1732
- description: summary.description,
1733
- enabled: summary.enabled,
1734
- status: summary.status,
1735
- modes: summary.modes,
1736
- selectedModel: selectedModel ?? null,
1737
- features,
1738
- }),
1739
- };
1740
- });
1741
- registerTool("list_worktrees", {
1742
- title: "List worktrees",
1743
- description: "List Paseo-managed git worktrees for a repository.",
1744
- inputSchema: {
1745
- cwd: z
1746
- .string()
1747
- .optional()
1748
- .describe("Optional repository cwd. Defaults to your current working directory."),
1749
- },
1750
- outputSchema: {
1751
- worktrees: z.array(WorktreeSummarySchema),
1752
- },
1753
- }, async ({ cwd }) => {
1754
- const resolvedCwd = resolveScopedCwd(cwd, { required: true });
1755
- if (!options.workspaceGitService) {
1756
- throw new Error("WorkspaceGitService is required to list worktrees");
1757
- }
1758
- const worktrees = await listPaseoWorktreesCommand({ workspaceGitService: options.workspaceGitService }, {
1759
- cwd: resolvedCwd,
1760
- reason: "mcp:list-worktrees",
1761
- });
1762
- return {
1763
- content: [],
1764
- structuredContent: ensureValidJson({ worktrees }),
1765
- };
1766
- });
1767
- registerTool("create_worktree", {
1768
- title: "Create worktree",
1769
- description: "Create a Paseo-managed git worktree. Branch off a new branch, check out an existing branch, or check out a GitHub PR.",
1770
- inputSchema: {
1771
- cwd: z.string().optional().describe("Repository directory. Defaults to the agent's cwd."),
1772
- target: AgentCreateWorktreeTargetInputSchema.describe("What the worktree should contain."),
1773
- },
1774
- outputSchema: {
1775
- branchName: z.string(),
1776
- worktreePath: z.string(),
1777
- workspaceId: z.string(),
1778
- },
1779
- }, async ({ cwd, target }) => {
1780
- const repoRoot = resolveScopedCwd(cwd, { required: true });
1781
- const commandResult = await createPaseoWorktreeCommand({
1782
- paseoHome: options.paseoHome,
1783
- worktreesRoot: options.worktreesRoot,
1784
- createPaseoWorktreeWorkflow: options.createPaseoWorktree,
1785
- }, createMcpWorktreeCommandInput(repoRoot, target));
1786
- if (!commandResult.ok) {
1787
- throw new WorktreeRequestError(commandResult.error);
1788
- }
1789
- const { worktree, workspace } = commandResult.createdWorktree;
1790
- await options.workspaceGitService?.listWorktrees?.(repoRoot, {
1791
- force: true,
1792
- reason: "mcp:create-worktree",
1793
- });
1794
- return {
1795
- content: [],
1796
- structuredContent: ensureValidJson({
1797
- branchName: worktree.branchName,
1798
- worktreePath: worktree.worktreePath,
1799
- workspaceId: workspace.workspaceId,
1800
- }),
1801
- };
1802
- });
1803
- registerTool("archive_worktree", {
1804
- title: "Archive worktree",
1805
- description: "Delete a Paseo-managed git worktree.",
1806
- inputSchema: {
1807
- cwd: z
1808
- .string()
1809
- .optional()
1810
- .describe("Optional repository cwd. Defaults to your current working directory."),
1811
- worktreePath: z.string().optional(),
1812
- worktreeSlug: z.string().optional(),
1813
- },
1814
- outputSchema: {
1815
- success: z.boolean(),
1816
- },
1817
- }, async ({ cwd, worktreePath, worktreeSlug }) => {
1818
- const resolvedCwd = resolveScopedCwd(cwd, { required: true });
1819
- if (!worktreePath && !worktreeSlug) {
1820
- throw new Error("worktreePath or worktreeSlug is required");
1821
- }
1822
- if (!options.workspaceGitService) {
1823
- throw new Error("WorkspaceGitService is required to archive worktrees");
1824
- }
1825
- const repoRoot = await options.workspaceGitService.resolveRepoRoot(resolvedCwd);
1826
- const result = await archiveCommand(archiveWorktreeDependencies(options, {
1827
- agentManager,
1828
- agentStorage,
1829
- terminalManager: terminalManager ?? null,
1830
- logger: childLogger,
1831
- }), {
1832
- requestId: "mcp:archive_worktree",
1833
- repoRoot,
1834
- worktreePath,
1835
- worktreeSlug,
1836
- // This tool archives every workspace on the directory, then removes the
1837
- // directory. Disk removal is derived from scope + last-reference.
1838
- scope: "worktree",
1839
- });
1840
- if (!result.ok) {
1841
- throw new Error(result.message);
1842
- }
1843
- await options.workspaceGitService.listWorktrees(repoRoot, {
1844
- force: true,
1845
- reason: "mcp:archive-worktree",
1846
- });
1847
- return {
1848
- content: [],
1849
- structuredContent: ensureValidJson({ success: true }),
1850
- };
1851
- });
1852
- registerTool("get_agent_activity", {
1853
- title: "Get agent activity",
1854
- description: "Return recent agent timeline entries as a curated summary.",
1855
- inputSchema: {
1856
- agentId: z.string(),
1857
- limit: z
1858
- .number()
1859
- .optional()
1860
- .describe("Optional limit for number of activities to include (most recent first)."),
1861
- },
1862
- outputSchema: {
1863
- agentId: z.string(),
1864
- updateCount: z.number(),
1865
- currentModeId: z.string().nullable(),
1866
- content: z.string(),
1867
- },
1868
- }, async ({ agentId, limit }) => {
1869
- await ensureAgentLoaded(agentId, {
1870
- agentManager,
1871
- agentStorage,
1872
- logger: childLogger,
1873
- });
1874
- const timeline = agentManager.getTimeline(agentId);
1875
- const snapshot = agentManager.getAgent(agentId);
1876
- const selection = selectItemsByProjectedLimit({
1877
- items: timeline,
1878
- direction: "tail",
1879
- limit: limit ?? 0,
1880
- });
1881
- const curatedContent = curateAgentActivity(selection.items);
1882
- const { totalProjected, shownProjected } = selection;
1883
- const noun = totalProjected === 1 ? "activity" : "activities";
1884
- const countHeader = limit && shownProjected < totalProjected
1885
- ? `Showing ${shownProjected} of ${totalProjected} ${noun} (limited to ${limit})`
1886
- : `Showing all ${totalProjected} ${noun}`;
1887
- const contentWithCount = `${countHeader}\n\n${curatedContent}`;
1888
- return {
1889
- content: [],
1890
- structuredContent: ensureValidJson({
1891
- agentId,
1892
- updateCount: timeline.length,
1893
- currentModeId: snapshot?.currentModeId ?? null,
1894
- content: contentWithCount,
1895
- }),
1896
- };
1897
- });
1898
- registerTool("set_agent_mode", {
1899
- title: "Set agent session mode",
1900
- description: "Switch the agent's session mode (plan, bypassPermissions, read-only, auto, etc.).",
1901
- inputSchema: {
1902
- agentId: z.string(),
1903
- modeId: z.string(),
1904
- },
1905
- outputSchema: {
1906
- success: z.boolean(),
1907
- newMode: z.string(),
1908
- },
1909
- }, async ({ agentId, modeId }) => {
1910
- const result = await setAgentModeCommand({ agentManager }, { agentId, modeId });
1911
- return {
1912
- content: [],
1913
- structuredContent: ensureValidJson({ success: true, newMode: result.modeId }),
1914
- };
1915
- });
1916
- registerTool("list_pending_permissions", {
1917
- title: "List pending permissions",
1918
- description: "Return all pending permission requests across all agents with the normalized payloads.",
1919
- inputSchema: {},
1920
- outputSchema: {
1921
- permissions: z.array(z.object({
1922
- agentId: z.string(),
1923
- status: AgentStatusEnum,
1924
- request: AgentPermissionRequestPayloadSchema,
1925
- })),
1926
- },
1927
- }, async () => {
1928
- const permissions = agentManager.listAgents().flatMap((agent) => {
1929
- const payload = toAgentPayload(agent);
1930
- return payload.pendingPermissions.map((request) => ({
1931
- agentId: agent.id,
1932
- status: payload.status,
1933
- request: sanitizePermissionRequest(request),
1934
- }));
1935
- });
1936
- return {
1937
- content: [],
1938
- structuredContent: ensureValidJson({ permissions }),
1939
- };
1940
- });
1941
- registerTool("respond_to_permission", {
1942
- title: "Respond to permission",
1943
- description: "Approve or deny a pending permission request with an AgentManager-compatible response payload.",
1944
- inputSchema: {
1945
- agentId: z.string(),
1946
- requestId: z.string(),
1947
- response: AgentPermissionResponseSchema,
1948
- },
1949
- outputSchema: {
1950
- success: z.boolean(),
1951
- },
1952
- }, async ({ agentId, requestId, response }) => {
1953
- await respondToAgentPermission({
1954
- agentManager,
1955
- agentId,
1956
- requestId,
1957
- response,
1958
- logger: childLogger,
1959
- });
1960
- return {
1961
- content: [],
1962
- structuredContent: ensureValidJson({ success: true }),
1963
- };
1964
- });
1965
89
  return server;
1966
90
  }
1967
- function archiveWorktreeDependencies(options, context) {
1968
- if (!options.github) {
1969
- throw new Error("GitHub service is required to archive worktrees");
1970
- }
1971
- if (!options.workspaceGitService) {
1972
- throw new Error("WorkspaceGitService is required to archive worktrees");
1973
- }
1974
- if (!options.archiveWorkspaceRecord) {
1975
- throw new Error("Workspace registry archiver is required to archive worktrees");
1976
- }
1977
- if (!options.findWorkspaceIdForCwd) {
1978
- throw new Error("Workspace resolver is required to archive worktrees");
1979
- }
1980
- if (!options.listActiveWorkspaces) {
1981
- throw new Error("Active workspace lister is required to archive worktrees");
1982
- }
1983
- if (!options.emitWorkspaceUpdatesForWorkspaceIds) {
1984
- throw new Error("Workspace update emitter is required to archive worktrees");
1985
- }
1986
- if (!options.markWorkspaceArchiving) {
1987
- throw new Error("Workspace archiving marker is required to archive worktrees");
1988
- }
1989
- if (!options.clearWorkspaceArchiving) {
1990
- throw new Error("Workspace archiving clearer is required to archive worktrees");
1991
- }
1992
- return {
1993
- paseoHome: options.paseoHome,
1994
- paseoWorktreesBaseRoot: options.worktreesRoot,
1995
- github: options.github,
1996
- workspaceGitService: options.workspaceGitService,
1997
- agentManager: context.agentManager,
1998
- agentStorage: context.agentStorage,
1999
- findWorkspaceIdForCwd: options.findWorkspaceIdForCwd,
2000
- listActiveWorkspaces: options.listActiveWorkspaces,
2001
- archiveWorkspaceRecord: options.archiveWorkspaceRecord,
2002
- emitWorkspaceUpdatesForWorkspaceIds: options.emitWorkspaceUpdatesForWorkspaceIds,
2003
- markWorkspaceArchiving: options.markWorkspaceArchiving,
2004
- clearWorkspaceArchiving: options.clearWorkspaceArchiving,
2005
- killTerminalsForWorkspace: (workspaceId) => killTerminalsForWorkspace({
2006
- terminalManager: context.terminalManager,
2007
- sessionLogger: context.logger,
2008
- }, workspaceId),
2009
- sessionLogger: context.logger,
2010
- };
2011
- }
2012
- function createMcpWorktreeCommandInput(repoRoot, target) {
2013
- const base = { cwd: repoRoot };
2014
- switch (target.kind) {
2015
- case "branch-off":
2016
- return {
2017
- ...base,
2018
- worktreeSlug: target.worktreeSlug,
2019
- branchName: target.branchName,
2020
- action: "branch-off",
2021
- ...(target.baseBranch ? { refName: target.baseBranch } : {}),
2022
- };
2023
- case "checkout-branch":
2024
- return { ...base, action: "checkout", refName: target.branch };
2025
- case "checkout-pr":
2026
- return { ...base, action: "checkout", githubPrNumber: target.githubPrNumber };
2027
- default:
2028
- throw new Error("unreachable");
2029
- }
2030
- }
2031
91
  //# sourceMappingURL=mcp-server.js.map