@getpaseo/server 0.1.100 → 0.1.102-beta.2

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