@getpaseo/server 0.1.87 → 0.1.89

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 (69) hide show
  1. package/dist/server/server/agent/agent-manager.js +4 -1
  2. package/dist/server/server/agent/agent-storage.d.ts +22 -22
  3. package/dist/server/server/agent/create-agent/create.d.ts +2 -0
  4. package/dist/server/server/agent/create-agent/create.js +16 -5
  5. package/dist/server/server/agent/create-agent-lifecycle-dispatch.d.ts +1 -0
  6. package/dist/server/server/agent/create-agent-lifecycle-dispatch.js +4 -0
  7. package/dist/server/server/agent/mcp-server.d.ts +1 -0
  8. package/dist/server/server/agent/mcp-server.js +137 -63
  9. package/dist/server/server/agent/mcp-shared.d.ts +1 -0
  10. package/dist/server/server/agent/providers/pi/agent.js +13 -0
  11. package/dist/server/server/agent/providers/pi/rpc-types.d.ts +3 -0
  12. package/dist/server/server/agent/timeline-projection.d.ts +17 -1
  13. package/dist/server/server/agent/timeline-projection.js +82 -17
  14. package/dist/server/server/auto-archive-on-merge/archive-if-safe.d.ts +1 -0
  15. package/dist/server/server/auto-archive-on-merge/archive-if-safe.js +6 -1
  16. package/dist/server/server/bootstrap.d.ts +7 -2
  17. package/dist/server/server/bootstrap.js +152 -115
  18. package/dist/server/server/config.js +41 -0
  19. package/dist/server/server/loop-service.d.ts +22 -22
  20. package/dist/server/server/package-version.d.ts +2 -2
  21. package/dist/server/server/paseo-worktree-archive-service.d.ts +2 -0
  22. package/dist/server/server/paseo-worktree-archive-service.js +28 -9
  23. package/dist/server/server/persisted-config.d.ts +89 -33
  24. package/dist/server/server/persisted-config.js +17 -0
  25. package/dist/server/server/pid-lock.d.ts +2 -2
  26. package/dist/server/server/schedule/cron.js +52 -5
  27. package/dist/server/server/script-health-monitor.d.ts +4 -4
  28. package/dist/server/server/script-health-monitor.js +6 -6
  29. package/dist/server/server/script-proxy.d.ts +2 -39
  30. package/dist/server/server/script-proxy.js +1 -244
  31. package/dist/server/server/script-route-branch-handler.d.ts +2 -2
  32. package/dist/server/server/script-route-branch-handler.js +3 -37
  33. package/dist/server/server/script-status-projection.d.ts +6 -4
  34. package/dist/server/server/script-status-projection.js +85 -37
  35. package/dist/server/server/service-proxy.d.ts +237 -0
  36. package/dist/server/server/service-proxy.js +714 -0
  37. package/dist/server/server/session.d.ts +11 -4
  38. package/dist/server/server/session.js +96 -99
  39. package/dist/server/server/websocket-server.d.ts +7 -4
  40. package/dist/server/server/websocket-server.js +9 -4
  41. package/dist/server/server/workspace-directory.js +4 -0
  42. package/dist/server/server/workspace-git-service.d.ts +3 -0
  43. package/dist/server/server/workspace-git-service.js +53 -12
  44. package/dist/server/server/workspace-registry.d.ts +2 -2
  45. package/dist/server/server/workspace-service-env.d.ts +1 -0
  46. package/dist/server/server/workspace-service-env.js +23 -18
  47. package/dist/server/server/worktree/commands.d.ts +2 -0
  48. package/dist/server/server/worktree/commands.js +4 -1
  49. package/dist/server/server/worktree-bootstrap.d.ts +4 -3
  50. package/dist/server/server/worktree-bootstrap.js +14 -13
  51. package/dist/server/server/worktree-core.d.ts +1 -0
  52. package/dist/server/server/worktree-core.js +2 -0
  53. package/dist/server/server/worktree-session.d.ts +6 -2
  54. package/dist/server/server/worktree-session.js +3 -0
  55. package/dist/server/services/github-service.d.ts +1 -0
  56. package/dist/server/services/github-service.js +7 -1
  57. package/dist/server/terminal/terminal-manager.js +11 -1
  58. package/dist/server/terminal/terminal-session-controller.d.ts +3 -1
  59. package/dist/server/terminal/terminal-session-controller.js +22 -12
  60. package/dist/server/terminal/terminal.d.ts +1 -0
  61. package/dist/server/terminal/terminal.js +34 -0
  62. package/dist/server/utils/checkout-git.d.ts +6 -2
  63. package/dist/server/utils/checkout-git.js +136 -54
  64. package/dist/server/utils/worktree.d.ts +17 -12
  65. package/dist/server/utils/worktree.js +39 -22
  66. package/dist/src/server/persisted-config.js +17 -0
  67. package/package.json +5 -5
  68. package/dist/server/utils/script-hostname.d.ts +0 -8
  69. package/dist/server/utils/script-hostname.js +0 -14
@@ -2,7 +2,7 @@ import { randomUUID } from "node:crypto";
2
2
  import { resolve } from "node:path";
3
3
  import { stat } from "node:fs/promises";
4
4
  import { AGENT_LIFECYCLE_STATUSES, } from "@getpaseo/protocol/agent-lifecycle";
5
- import { PARENT_AGENT_ID_LABEL } from "@getpaseo/protocol/agent-labels";
5
+ import { isDelegatedAgent, PARENT_AGENT_ID_LABEL } from "@getpaseo/protocol/agent-labels";
6
6
  import { z } from "zod";
7
7
  import { getAgentStreamEventTurnId, } from "./agent-sdk-types.js";
8
8
  import { buildArchivedAgentRecord } from "./agent-archive.js";
@@ -2402,6 +2402,9 @@ export class AgentManager {
2402
2402
  }
2403
2403
  }
2404
2404
  broadcastAgentAttention(agent, reason) {
2405
+ if (isDelegatedAgent(agent)) {
2406
+ return;
2407
+ }
2405
2408
  this.onAgentAttention?.({
2406
2409
  agentId: agent.id,
2407
2410
  provider: agent.provider,
@@ -167,22 +167,12 @@ declare const STORED_AGENT_SCHEMA: z.ZodObject<{
167
167
  archivedAt: z.ZodOptional<z.ZodNullable<z.ZodString>>;
168
168
  }, "strip", z.ZodTypeAny, {
169
169
  cwd: string;
170
- createdAt: string;
171
170
  id: string;
172
171
  provider: string;
172
+ createdAt: string;
173
173
  updatedAt: string;
174
174
  labels: Record<string, string>;
175
175
  lastStatus: "error" | "running" | "initializing" | "idle" | "closed";
176
- config?: {
177
- modeId?: string | null | undefined;
178
- model?: string | null | undefined;
179
- thinkingOptionId?: string | null | undefined;
180
- featureValues?: Record<string, unknown> | null | undefined;
181
- extra?: Record<string, any> | null | undefined;
182
- systemPrompt?: string | null | undefined;
183
- mcpServers?: Record<string, any> | null | undefined;
184
- } | null | undefined;
185
- internal?: boolean | undefined;
186
176
  title?: string | null | undefined;
187
177
  persistence?: {
188
178
  provider: string;
@@ -193,6 +183,15 @@ declare const STORED_AGENT_SCHEMA: z.ZodObject<{
193
183
  lastActivityAt?: string | undefined;
194
184
  lastUserMessageAt?: string | null | undefined;
195
185
  lastModeId?: string | null | undefined;
186
+ config?: {
187
+ modeId?: string | null | undefined;
188
+ model?: string | null | undefined;
189
+ thinkingOptionId?: string | null | undefined;
190
+ featureValues?: Record<string, unknown> | null | undefined;
191
+ extra?: Record<string, any> | null | undefined;
192
+ systemPrompt?: string | null | undefined;
193
+ mcpServers?: Record<string, any> | null | undefined;
194
+ } | null | undefined;
196
195
  runtimeInfo?: {
197
196
  provider: string;
198
197
  sessionId: string | null;
@@ -229,23 +228,14 @@ declare const STORED_AGENT_SCHEMA: z.ZodObject<{
229
228
  requiresAttention?: boolean | undefined;
230
229
  attentionReason?: "finished" | "error" | "permission" | null | undefined;
231
230
  attentionTimestamp?: string | null | undefined;
231
+ internal?: boolean | undefined;
232
232
  archivedAt?: string | null | undefined;
233
233
  }, {
234
234
  cwd: string;
235
- createdAt: string;
236
235
  id: string;
237
236
  provider: string;
237
+ createdAt: string;
238
238
  updatedAt: string;
239
- config?: {
240
- modeId?: string | null | undefined;
241
- model?: string | null | undefined;
242
- thinkingOptionId?: string | null | undefined;
243
- featureValues?: Record<string, unknown> | null | undefined;
244
- extra?: Record<string, any> | null | undefined;
245
- systemPrompt?: string | null | undefined;
246
- mcpServers?: Record<string, any> | null | undefined;
247
- } | null | undefined;
248
- internal?: boolean | undefined;
249
239
  title?: string | null | undefined;
250
240
  persistence?: {
251
241
  provider: string;
@@ -258,6 +248,15 @@ declare const STORED_AGENT_SCHEMA: z.ZodObject<{
258
248
  labels?: Record<string, string> | undefined;
259
249
  lastStatus?: "error" | "running" | "initializing" | "idle" | "closed" | undefined;
260
250
  lastModeId?: string | null | undefined;
251
+ config?: {
252
+ modeId?: string | null | undefined;
253
+ model?: string | null | undefined;
254
+ thinkingOptionId?: string | null | undefined;
255
+ featureValues?: Record<string, unknown> | null | undefined;
256
+ extra?: Record<string, any> | null | undefined;
257
+ systemPrompt?: string | null | undefined;
258
+ mcpServers?: Record<string, any> | null | undefined;
259
+ } | null | undefined;
261
260
  runtimeInfo?: {
262
261
  provider: string;
263
262
  sessionId: string | null;
@@ -294,6 +293,7 @@ declare const STORED_AGENT_SCHEMA: z.ZodObject<{
294
293
  requiresAttention?: boolean | undefined;
295
294
  attentionReason?: "finished" | "error" | "permission" | null | undefined;
296
295
  attentionTimestamp?: string | null | undefined;
296
+ internal?: boolean | undefined;
297
297
  archivedAt?: string | null | undefined;
298
298
  }>;
299
299
  export type SerializableAgentConfig = Pick<AgentSessionConfig, "modeId" | "model" | "thinkingOptionId" | "featureValues" | "extra" | "systemPrompt" | "mcpServers">;
@@ -20,6 +20,7 @@ interface CreateAgentCommandDependencies {
20
20
  agentStorage: AgentStorage;
21
21
  logger: Logger;
22
22
  paseoHome?: string;
23
+ worktreesRoot?: string;
23
24
  workspaceGitService?: Pick<WorkspaceGitService, "getSnapshot" | "listWorktrees" | "resolveRepoRoot">;
24
25
  terminalManager?: TerminalManager | null;
25
26
  providerSnapshotManager: ProviderSnapshotManager;
@@ -63,6 +64,7 @@ export interface CreateAgentFromMcpInput {
63
64
  mode?: string;
64
65
  background: boolean;
65
66
  notifyOnFinish: boolean;
67
+ detached?: boolean;
66
68
  callerAgentId?: string;
67
69
  callerContext?: {
68
70
  lockedCwd?: string;
@@ -94,7 +94,12 @@ async function resolveMcpCreateAgent(dependencies, input) {
94
94
  featureValues: input.features,
95
95
  parent: parentAgent,
96
96
  });
97
- const labels = mergeLabels(input.callerAgentId, input.callerContext?.childAgentDefaultLabels, input.labels);
97
+ const labels = mergeLabels({
98
+ callerAgentId: input.callerAgentId,
99
+ detached: input.detached ?? false,
100
+ childAgentDefaultLabels: input.callerContext?.childAgentDefaultLabels,
101
+ labels: input.labels,
102
+ });
98
103
  const trimmedPrompt = input.initialPrompt.trim();
99
104
  return {
100
105
  config: {
@@ -220,6 +225,7 @@ async function resolveMcpCwd(params) {
220
225
  ...(params.initialPrompt ? { firstAgentContext: { prompt: params.initialPrompt } } : {}),
221
226
  runSetup: false,
222
227
  paseoHome: dependencies.paseoHome,
228
+ worktreesRoot: dependencies.worktreesRoot,
223
229
  },
224
230
  createPaseoWorktree: dependencies.createPaseoWorktree,
225
231
  resolveDefaultBranch: baseBranch ? async () => baseBranch : undefined,
@@ -260,12 +266,17 @@ async function createMcpWorktree(options) {
260
266
  throw toWorktreeRequestError(error);
261
267
  }
262
268
  }
263
- function mergeLabels(callerAgentId, childAgentDefaultLabels, labels) {
269
+ function mergeLabels(params) {
264
270
  const mergedLabels = {
265
- ...(callerAgentId ? { [PARENT_AGENT_ID_LABEL]: callerAgentId } : {}),
266
- ...childAgentDefaultLabels,
267
- ...labels,
271
+ ...(!params.detached && params.callerAgentId
272
+ ? { [PARENT_AGENT_ID_LABEL]: params.callerAgentId }
273
+ : {}),
274
+ ...params.childAgentDefaultLabels,
275
+ ...params.labels,
268
276
  };
277
+ if (params.detached) {
278
+ delete mergedLabels[PARENT_AGENT_ID_LABEL];
279
+ }
269
280
  return Object.keys(mergedLabels).length > 0 ? mergedLabels : undefined;
270
281
  }
271
282
  //# sourceMappingURL=create.js.map
@@ -7,6 +7,7 @@ import type { AgentManager } from "./agent-manager.js";
7
7
  import type { AgentStorage } from "./agent-storage.js";
8
8
  interface CreateAgentLifecycleDispatchDependencies {
9
9
  paseoHome: string;
10
+ worktreesRoot?: string;
10
11
  agentManager: AgentManager;
11
12
  agentStorage: AgentStorage;
12
13
  github: GitHubService;
@@ -46,6 +46,7 @@ export class CreateAgentLifecycleDispatch {
46
46
  firstAgentContext,
47
47
  runSetup: false,
48
48
  paseoHome: this.dependencies.paseoHome,
49
+ worktreesRoot: this.dependencies.worktreesRoot,
49
50
  };
50
51
  switch (target.mode) {
51
52
  case "branch-off":
@@ -108,12 +109,14 @@ export class CreateAgentLifecycleDispatch {
108
109
  async archiveAutoCreatedWorktree(options) {
109
110
  const ownership = await isPaseoOwnedWorktreeCwd(options.worktreePath, {
110
111
  paseoHome: this.dependencies.paseoHome,
112
+ worktreesRoot: this.dependencies.worktreesRoot,
111
113
  });
112
114
  if (!ownership.allowed) {
113
115
  throw new Error("Auto-created worktree is not a Paseo-owned worktree");
114
116
  }
115
117
  await archivePaseoWorktree({
116
118
  paseoHome: this.dependencies.paseoHome,
119
+ worktreesRoot: this.dependencies.worktreesRoot,
117
120
  github: this.dependencies.github,
118
121
  workspaceGitService: this.dependencies.workspaceGitService,
119
122
  agentManager: this.dependencies.agentManager,
@@ -129,6 +132,7 @@ export class CreateAgentLifecycleDispatch {
129
132
  targetPath: options.worktreePath,
130
133
  repoRoot: options.repoRoot ?? ownership.repoRoot ?? null,
131
134
  worktreesRoot: ownership.worktreeRoot,
135
+ worktreesBaseRoot: this.dependencies.worktreesRoot,
132
136
  requestId: randomUUID(),
133
137
  });
134
138
  if (options.agentId) {
@@ -25,6 +25,7 @@ export interface AgentMcpServerOptions {
25
25
  clearWorkspaceArchiving?: ArchivePaseoWorktreeDependencies["clearWorkspaceArchiving"];
26
26
  createPaseoWorktree?: CreatePaseoWorktreeWorkflowFn;
27
27
  paseoHome?: string;
28
+ worktreesRoot?: string;
28
29
  /**
29
30
  * ID of the agent that is connecting to this MCP server.
30
31
  * Used for cwd/mode inheritance when agents spawn child agents.
@@ -176,17 +176,28 @@ function normalizeScheduleCadenceArg(value) {
176
176
  }
177
177
  return trimmed;
178
178
  }
179
+ function normalizeScheduleTimeZoneArg(value) {
180
+ return normalizeScheduleCadenceArg(value);
181
+ }
179
182
  function resolveScheduleUpdateCadence(input) {
180
183
  const every = normalizeScheduleCadenceArg(input.every);
181
184
  const cron = normalizeScheduleCadenceArg(input.cron);
185
+ const timeZone = normalizeScheduleTimeZoneArg(input.timezone);
182
186
  if (every !== undefined && cron !== undefined) {
183
187
  throw new Error("Specify at most one of every or cron");
184
188
  }
189
+ if (timeZone !== undefined && cron === undefined) {
190
+ throw new Error("timezone can only be used with cron");
191
+ }
185
192
  if (every !== undefined) {
186
193
  return { type: "every", everyMs: parseDurationString(every) };
187
194
  }
188
195
  if (cron !== undefined) {
189
- return { type: "cron", expression: cron };
196
+ return {
197
+ type: "cron",
198
+ expression: cron,
199
+ ...(timeZone !== undefined ? { timezone: timeZone } : {}),
200
+ };
190
201
  }
191
202
  return undefined;
192
203
  }
@@ -289,6 +300,23 @@ export async function createAgentMcpServer(options) {
289
300
  });
290
301
  const registerRawTool = server.registerTool.bind(server);
291
302
  const registerTool = (name, config, handler) => registerRawTool(name, relaxMcpToolOutputSchema(config), (async (args, extra) => addModelVisibleStructuredContent(await handler(args, extra))));
303
+ const buildCronScheduleCadence = (input) => {
304
+ const expression = input.cron?.trim() ?? "";
305
+ if (!expression) {
306
+ throw new Error("cron is required");
307
+ }
308
+ const timezone = normalizeScheduleTimeZoneArg(input.timezone);
309
+ return {
310
+ type: "cron",
311
+ expression,
312
+ ...(timezone !== undefined ? { timezone } : {}),
313
+ };
314
+ };
315
+ const buildScheduleExpiry = (expiresIn) => {
316
+ return expiresIn === undefined
317
+ ? undefined
318
+ : new Date(Date.now() + parseDurationString(expiresIn)).toISOString();
319
+ };
292
320
  const resolveCallerAgent = () => {
293
321
  if (!callerAgentId) {
294
322
  return null;
@@ -314,7 +342,7 @@ export async function createAgentMcpServer(options) {
314
342
  if (opts?.required) {
315
343
  throw new Error("cwd is required");
316
344
  }
317
- throw new Error("cwd is required when no caller agent is available");
345
+ throw new Error("cwd is required outside an agent-scoped session");
318
346
  }
319
347
  return expandUserPath(trimmedCwd);
320
348
  };
@@ -458,7 +486,7 @@ export async function createAgentMcpServer(options) {
458
486
  cwd: z
459
487
  .string()
460
488
  .optional()
461
- .describe("Optional working directory. Defaults to the caller agent working directory."),
489
+ .describe("Optional working directory. Defaults to your current working directory."),
462
490
  title: z
463
491
  .string()
464
492
  .trim()
@@ -473,16 +501,16 @@ export async function createAgentMcpServer(options) {
473
501
  .trim()
474
502
  .min(1, "initialPrompt is required")
475
503
  .describe("Required first task to run immediately after creation."),
476
- background: z
504
+ detached: z
477
505
  .boolean()
478
506
  .optional()
479
507
  .default(false)
480
- .describe("Run agent in background. If false (default), waits for completion or permission request. If true, returns immediately."),
508
+ .describe("If true, the created agent stands on its own: it does not appear in your subagent track and is not archived with you."),
481
509
  notifyOnFinish: z
482
510
  .boolean()
483
511
  .optional()
484
- .default(false)
485
- .describe("Send a notification prompt to the caller agent when this agent finishes, errors, or needs permission. Requires a caller agent context."),
512
+ .default(true)
513
+ .describe("Get notified when the created agent finishes, errors, or needs permission. Set false only for truly fire-and-forget agents."),
486
514
  };
487
515
  const topLevelInputSchema = {
488
516
  cwd: z
@@ -530,7 +558,7 @@ export async function createAgentMcpServer(options) {
530
558
  .boolean()
531
559
  .optional()
532
560
  .default(false)
533
- .describe("Send a notification prompt to the caller agent when this agent finishes, errors, or needs permission. Requires a caller agent context."),
561
+ .describe("Agent-scoped only: get notified when the created agent finishes, errors, or needs permission."),
534
562
  };
535
563
  const createAgentInputSchema = callerAgentId ? agentToAgentInputSchema : topLevelInputSchema;
536
564
  const agentToAgentCreateAgentArgsSchema = z.object(agentToAgentInputSchema).strict();
@@ -563,7 +591,7 @@ export async function createAgentMcpServer(options) {
563
591
  }
564
592
  const handler = resolveSpeakHandler?.(callerAgentId) ?? null;
565
593
  if (!handler) {
566
- throw new Error(`No speak handler registered for caller agent '${callerAgentId}'`);
594
+ throw new Error(`No speak handler registered for your session '${callerAgentId}'`);
567
595
  }
568
596
  await handler({
569
597
  text: args.text,
@@ -592,14 +620,30 @@ export async function createAgentMcpServer(options) {
592
620
  availableModes: z.array(ProviderModeSchema),
593
621
  lastMessage: z.string().nullable().optional(),
594
622
  permission: AgentPermissionRequestPayloadSchema.nullable().optional(),
623
+ guidance: z.string().optional(),
595
624
  },
596
625
  }, async (args) => {
597
- const { parsedArgs, worktree } = resolveCreateAgentToolArgs(args);
598
- const { snapshot, background, initialPromptStarted } = await createAgentCommand({
626
+ const resolvedArgs = resolveCreateAgentToolArgs(args);
627
+ const { parsedArgs, worktree } = resolvedArgs;
628
+ let requestedBackground;
629
+ let notifyOnFinish;
630
+ let detached;
631
+ if (resolvedArgs.kind === "agent-scoped") {
632
+ requestedBackground = true;
633
+ notifyOnFinish = resolvedArgs.parsedArgs.notifyOnFinish;
634
+ detached = resolvedArgs.parsedArgs.detached;
635
+ }
636
+ else {
637
+ requestedBackground = resolvedArgs.parsedArgs.background;
638
+ notifyOnFinish = resolvedArgs.parsedArgs.notifyOnFinish ?? false;
639
+ detached = false;
640
+ }
641
+ const { snapshot, background: createdInBackground, initialPromptStarted, } = await createAgentCommand({
599
642
  agentManager,
600
643
  agentStorage,
601
644
  logger: childLogger,
602
645
  paseoHome: options.paseoHome,
646
+ worktreesRoot: options.worktreesRoot,
603
647
  workspaceGitService: options.workspaceGitService,
604
648
  terminalManager,
605
649
  providerSnapshotManager,
@@ -614,14 +658,15 @@ export async function createAgentMcpServer(options) {
614
658
  features: parsedArgs.settings?.features,
615
659
  labels: parsedArgs.labels,
616
660
  mode: parsedArgs.settings?.modeId,
617
- background: parsedArgs.background ?? false,
618
- notifyOnFinish: parsedArgs.notifyOnFinish ?? false,
661
+ background: requestedBackground,
662
+ notifyOnFinish,
663
+ detached,
619
664
  callerAgentId,
620
665
  callerContext,
621
666
  worktree,
622
667
  });
623
668
  try {
624
- if (!background && initialPromptStarted) {
669
+ if (!createdInBackground && initialPromptStarted) {
625
670
  const result = await waitForAgentWithTimeout(agentManager, snapshot.id, {
626
671
  waitForActive: true,
627
672
  });
@@ -648,8 +693,11 @@ export async function createAgentMcpServer(options) {
648
693
  childLogger.error({ err: error, agentId: snapshot.id }, "Failed to run initial prompt");
649
694
  throw error;
650
695
  }
651
- // Return immediately if background=true
696
+ // Return immediately for async creation.
652
697
  const currentSnapshot = agentManager.getAgent(snapshot.id) ?? snapshot;
698
+ const guidance = callerAgentId && notifyOnFinish && initialPromptStarted
699
+ ? "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."
700
+ : undefined;
653
701
  const response = {
654
702
  content: [],
655
703
  structuredContent: ensureValidJson({
@@ -661,6 +709,7 @@ export async function createAgentMcpServer(options) {
661
709
  availableModes: currentSnapshot.availableModes,
662
710
  lastMessage: null,
663
711
  permission: null,
712
+ ...(guidance ? { guidance } : {}),
664
713
  }),
665
714
  };
666
715
  return response;
@@ -668,12 +717,14 @@ export async function createAgentMcpServer(options) {
668
717
  function resolveCreateAgentToolArgs(args) {
669
718
  if (callerAgentId) {
670
719
  return {
720
+ kind: "agent-scoped",
671
721
  parsedArgs: agentToAgentCreateAgentArgsSchema.parse(args),
672
722
  worktree: undefined,
673
723
  };
674
724
  }
675
725
  const parsedArgs = topLevelCreateAgentArgsSchema.parse(args);
676
726
  return {
727
+ kind: "top-level",
677
728
  parsedArgs,
678
729
  worktree: resolveTopLevelCreateAgentWorktree(parsedArgs),
679
730
  };
@@ -773,7 +824,7 @@ export async function createAgentMcpServer(options) {
773
824
  .boolean()
774
825
  .optional()
775
826
  .default(false)
776
- .describe("Send a notification prompt to the caller agent when this agent finishes, errors, or needs permission."),
827
+ .describe("Agent-scoped only: get notified when this run finishes, errors, or needs permission."),
777
828
  },
778
829
  outputSchema: {
779
830
  success: z.boolean(),
@@ -1014,7 +1065,7 @@ export async function createAgentMcpServer(options) {
1014
1065
  cwd: z
1015
1066
  .string()
1016
1067
  .optional()
1017
- .describe("Optional working directory. Defaults to the caller agent cwd."),
1068
+ .describe("Optional working directory. Defaults to your current working directory."),
1018
1069
  all: z.boolean().optional().describe("List terminals across all working directories."),
1019
1070
  },
1020
1071
  outputSchema: {
@@ -1047,7 +1098,7 @@ export async function createAgentMcpServer(options) {
1047
1098
  cwd: z
1048
1099
  .string()
1049
1100
  .optional()
1050
- .describe("Optional working directory. Defaults to the caller agent cwd."),
1101
+ .describe("Optional working directory. Defaults to your current working directory."),
1051
1102
  name: z.string().optional().describe("Optional terminal name."),
1052
1103
  },
1053
1104
  outputSchema: TerminalSummarySchema.shape,
@@ -1157,65 +1208,80 @@ export async function createAgentMcpServer(options) {
1157
1208
  });
1158
1209
  registerTool("create_schedule", {
1159
1210
  title: "Create schedule",
1160
- description: "Create a recurring schedule that runs on an agent or a new agent.",
1211
+ description: "Create a recurring schedule that starts a new agent on a cron cadence.",
1161
1212
  inputSchema: {
1162
1213
  prompt: z.string().trim().min(1, "prompt is required"),
1163
- every: z.string().optional(),
1164
- cron: z.string().optional(),
1214
+ cron: z.string().trim().min(1, "cron is required"),
1215
+ timezone: z
1216
+ .string()
1217
+ .trim()
1218
+ .min(1)
1219
+ .optional()
1220
+ .describe("IANA time zone for the cron cadence. For example: America/New_York."),
1165
1221
  name: z.string().optional(),
1166
- target: z.enum(["self", "new-agent"]).optional(),
1167
- provider: AgentProviderEnum.optional().describe("Provider, or provider/model (for example: codex or codex/gpt-5.4)."),
1222
+ provider: AgentProviderEnum.describe("Provider, or provider/model (for example: codex or codex/gpt-5.4)."),
1168
1223
  cwd: z.string().optional(),
1169
1224
  maxRuns: z.number().int().positive().optional(),
1170
1225
  expiresIn: z.string().optional(),
1171
1226
  },
1172
1227
  outputSchema: ScheduleSummarySchema.shape,
1173
- }, async ({ prompt, every, cron, name, target, provider, cwd, maxRuns, expiresIn }) => {
1228
+ }, async ({ prompt, cron, timezone, name, provider, cwd, maxRuns, expiresIn }) => {
1174
1229
  if (!scheduleService) {
1175
1230
  throw new Error("Schedule service is not configured");
1176
1231
  }
1177
- const normalizedEvery = normalizeScheduleCadenceArg(every);
1178
- const normalizedCron = normalizeScheduleCadenceArg(cron);
1179
- const cadenceCount = Number(normalizedEvery !== undefined) + Number(normalizedCron !== undefined);
1180
- if (cadenceCount !== 1) {
1181
- throw new Error("Specify exactly one of every or cron");
1232
+ const expiresAt = buildScheduleExpiry(expiresIn);
1233
+ const schedule = await scheduleService.create({
1234
+ prompt: prompt.trim(),
1235
+ cadence: buildCronScheduleCadence({
1236
+ cron,
1237
+ ...(timezone !== undefined ? { timezone } : {}),
1238
+ }),
1239
+ target: resolveNewAgentScheduleTarget({ provider, cwd }),
1240
+ ...(name?.trim() ? { name: name.trim() } : {}),
1241
+ ...(maxRuns === undefined ? {} : { maxRuns }),
1242
+ ...(expiresAt === undefined ? {} : { expiresAt }),
1243
+ });
1244
+ return {
1245
+ content: [],
1246
+ structuredContent: ensureValidJson(toScheduleSummary(schedule)),
1247
+ };
1248
+ });
1249
+ registerTool("create_heartbeat", {
1250
+ title: "Create heartbeat",
1251
+ description: "Create a recurring heartbeat that sends you a prompt on a cron cadence.",
1252
+ inputSchema: {
1253
+ prompt: z.string().trim().min(1, "prompt is required"),
1254
+ cron: z.string().trim().min(1, "cron is required"),
1255
+ timezone: z
1256
+ .string()
1257
+ .trim()
1258
+ .min(1)
1259
+ .optional()
1260
+ .describe("IANA time zone for the cron cadence. For example: America/New_York."),
1261
+ name: z.string().optional(),
1262
+ maxRuns: z.number().int().positive().optional(),
1263
+ expiresIn: z.string().optional(),
1264
+ },
1265
+ outputSchema: ScheduleSummarySchema.shape,
1266
+ }, async ({ prompt, cron, timezone, name, maxRuns, expiresIn }) => {
1267
+ if (!scheduleService) {
1268
+ throw new Error("Schedule service is not configured");
1182
1269
  }
1183
- const scheduleTarget = target === "self"
1184
- ? (() => {
1185
- const callerAgent = resolveCallerAgent();
1186
- if (!callerAgentId || !callerAgent) {
1187
- throw new Error("target=self requires a caller agent");
1188
- }
1189
- const trimmedCwd = cwd?.trim();
1190
- if (trimmedCwd && expandUserPath(trimmedCwd) !== callerAgent.cwd) {
1191
- throw new Error("cwd can only differ from the caller agent when target=new-agent");
1192
- }
1193
- if (provider !== undefined) {
1194
- const resolved = resolveScheduleProviderAndModel({
1195
- provider,
1196
- defaultProvider: callerAgent.provider,
1197
- });
1198
- if (resolved.provider !== callerAgent.provider ||
1199
- (resolved.model !== undefined && resolved.model !== callerAgent.config.model)) {
1200
- throw new Error("provider can only differ from the caller agent when target=new-agent");
1201
- }
1202
- }
1203
- return { type: "agent", agentId: callerAgentId };
1204
- })()
1205
- : (() => {
1206
- return resolveNewAgentScheduleTarget({ provider, cwd });
1207
- })();
1270
+ if (!callerAgentId) {
1271
+ throw new Error("create_heartbeat requires an agent-scoped session");
1272
+ }
1273
+ resolveCallerAgent();
1274
+ const expiresAt = buildScheduleExpiry(expiresIn);
1208
1275
  const schedule = await scheduleService.create({
1209
1276
  prompt: prompt.trim(),
1210
- cadence: normalizedEvery !== undefined
1211
- ? { type: "every", everyMs: parseDurationString(normalizedEvery) }
1212
- : { type: "cron", expression: normalizedCron },
1213
- target: scheduleTarget,
1277
+ cadence: buildCronScheduleCadence({
1278
+ cron,
1279
+ ...(timezone !== undefined ? { timezone } : {}),
1280
+ }),
1281
+ target: { type: "agent", agentId: callerAgentId },
1214
1282
  ...(name?.trim() ? { name: name.trim() } : {}),
1215
1283
  ...(maxRuns === undefined ? {} : { maxRuns }),
1216
- ...(expiresIn === undefined
1217
- ? {}
1218
- : { expiresAt: new Date(Date.now() + parseDurationString(expiresIn)).toISOString() }),
1284
+ ...(expiresAt === undefined ? {} : { expiresAt }),
1219
1285
  });
1220
1286
  return {
1221
1287
  content: [],
@@ -1320,6 +1386,12 @@ export async function createAgentMcpServer(options) {
1320
1386
  id: z.string(),
1321
1387
  every: z.string().optional().describe("New interval duration string (e.g. 5m, 1h)."),
1322
1388
  cron: z.string().optional().describe("New cron expression."),
1389
+ timezone: z
1390
+ .string()
1391
+ .trim()
1392
+ .min(1)
1393
+ .optional()
1394
+ .describe("IANA time zone for cron cadence; requires cron. For example: America/New_York."),
1323
1395
  name: z.string().nullable().optional().describe("New name (null to clear)."),
1324
1396
  prompt: z.string().trim().min(1).optional().describe("New prompt text."),
1325
1397
  maxRuns: z
@@ -1487,7 +1559,7 @@ export async function createAgentMcpServer(options) {
1487
1559
  cwd: z
1488
1560
  .string()
1489
1561
  .optional()
1490
- .describe("Optional repository cwd. Defaults to the caller agent cwd."),
1562
+ .describe("Optional repository cwd. Defaults to your current working directory."),
1491
1563
  },
1492
1564
  outputSchema: {
1493
1565
  worktrees: z.array(WorktreeSummarySchema),
@@ -1547,6 +1619,7 @@ export async function createAgentMcpServer(options) {
1547
1619
  const repoRoot = resolveScopedCwd(cwd, { required: true });
1548
1620
  const commandResult = await createPaseoWorktreeCommand({
1549
1621
  paseoHome: options.paseoHome,
1622
+ worktreesRoot: options.worktreesRoot,
1550
1623
  createPaseoWorktreeWorkflow: options.createPaseoWorktree,
1551
1624
  }, createMcpWorktreeCommandInput(repoRoot, target));
1552
1625
  if (!commandResult.ok) {
@@ -1572,7 +1645,7 @@ export async function createAgentMcpServer(options) {
1572
1645
  cwd: z
1573
1646
  .string()
1574
1647
  .optional()
1575
- .describe("Optional repository cwd. Defaults to the caller agent cwd."),
1648
+ .describe("Optional repository cwd. Defaults to your current working directory."),
1576
1649
  worktreePath: z.string().optional(),
1577
1650
  worktreeSlug: z.string().optional(),
1578
1651
  },
@@ -1747,6 +1820,7 @@ function archiveWorktreeDependencies(options, context) {
1747
1820
  }
1748
1821
  return {
1749
1822
  paseoHome: options.paseoHome,
1823
+ worktreesRoot: options.worktreesRoot,
1750
1824
  github: options.github,
1751
1825
  workspaceGitService: options.workspaceGitService,
1752
1826
  agentManager: context.agentManager,
@@ -282,6 +282,7 @@ export declare function toScheduleSummary(schedule: z.infer<typeof StoredSchedul
282
282
  } | {
283
283
  type: "cron";
284
284
  expression: string;
285
+ timezone?: string | undefined;
285
286
  };
286
287
  target: {
287
288
  agentId: string;
@@ -1148,6 +1148,19 @@ export class PiRpcAgentSession {
1148
1148
  }
1149
1149
  }
1150
1150
  handleMessageEnd(event, turnId) {
1151
+ if (event.message.role === "custom") {
1152
+ const text = getUserMessageText(event.message.content);
1153
+ if (text) {
1154
+ this.emit({
1155
+ type: "timeline",
1156
+ provider: PI_PROVIDER,
1157
+ turnId,
1158
+ item: { type: "assistant_message", text },
1159
+ });
1160
+ }
1161
+ this.completeTurn(turnId, []);
1162
+ return;
1163
+ }
1151
1164
  if (event.message.role !== "user") {
1152
1165
  return;
1153
1166
  }
@@ -22,6 +22,9 @@ export type PiAssistantContent = PiTextContent | PiThinkingContent | PiToolCallC
22
22
  export type PiAgentMessage = {
23
23
  role: "user";
24
24
  content: string | Array<PiTextContent | PiImageContent>;
25
+ } | {
26
+ role: "custom";
27
+ content: string | Array<PiTextContent | PiImageContent>;
25
28
  } | {
26
29
  role: "assistant";
27
30
  content: PiAssistantContent[];