wave-agent-sdk 0.19.7 → 0.19.8

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 (37) hide show
  1. package/dist/agent.d.ts +7 -0
  2. package/dist/agent.js +9 -0
  3. package/dist/index.d.ts +1 -0
  4. package/dist/index.js +1 -0
  5. package/dist/managers/aiManager.d.ts +1 -0
  6. package/dist/managers/aiManager.js +3 -0
  7. package/dist/managers/subagentManager.js +6 -0
  8. package/dist/services/configurationService.d.ts +6 -0
  9. package/dist/services/configurationService.js +31 -0
  10. package/dist/services/remoteSettingsService.js +2 -0
  11. package/dist/tools/enterWorktreeTool.js +2 -1
  12. package/dist/types/configuration.d.ts +5 -0
  13. package/dist/types/permissions.d.ts +0 -2
  14. package/dist/types/processes.d.ts +27 -0
  15. package/dist/types/workflow.d.ts +1 -1
  16. package/dist/utils/containerSetup.js +0 -9
  17. package/dist/utils/worktreeSession.d.ts +1 -1
  18. package/dist/utils/worktreeSession.js +1 -1
  19. package/dist/utils/worktreeUtils.d.ts +7 -1
  20. package/dist/utils/worktreeUtils.js +10 -4
  21. package/dist/workflow/types.d.ts +5 -0
  22. package/package.json +1 -1
  23. package/src/agent.ts +10 -0
  24. package/src/index.ts +1 -0
  25. package/src/managers/aiManager.ts +4 -0
  26. package/src/managers/subagentManager.ts +6 -0
  27. package/src/services/configurationService.ts +37 -0
  28. package/src/services/remoteSettingsService.ts +1 -0
  29. package/src/tools/enterWorktreeTool.ts +2 -1
  30. package/src/types/configuration.ts +5 -0
  31. package/src/types/permissions.ts +0 -2
  32. package/src/types/processes.ts +29 -0
  33. package/src/types/workflow.ts +1 -0
  34. package/src/utils/containerSetup.ts +0 -11
  35. package/src/utils/worktreeSession.ts +1 -1
  36. package/src/utils/worktreeUtils.ts +14 -4
  37. package/src/workflow/types.ts +6 -0
package/dist/agent.d.ts CHANGED
@@ -390,4 +390,11 @@ export declare class Agent {
390
390
  * Check if there are any running background tasks or active subagents
391
391
  */
392
392
  get hasRunningBackgroundWork(): boolean;
393
+ /**
394
+ * Check if there are pending items (messages, bang commands, or background
395
+ * task notifications) in the message queue. Background task completion
396
+ * notifications are enqueued before the main agent's dispatch consumes them,
397
+ * so callers waiting for the agent to fully settle must also wait on this.
398
+ */
399
+ get hasPendingMessages(): boolean;
393
400
  }
package/dist/agent.js CHANGED
@@ -978,4 +978,13 @@ export class Agent {
978
978
  const activeSubagents = this.subagentManager.getActiveInstances().length > 0;
979
979
  return runningTasks || activeSubagents;
980
980
  }
981
+ /**
982
+ * Check if there are pending items (messages, bang commands, or background
983
+ * task notifications) in the message queue. Background task completion
984
+ * notifications are enqueued before the main agent's dispatch consumes them,
985
+ * so callers waiting for the agent to fully settle must also wait on this.
986
+ */
987
+ get hasPendingMessages() {
988
+ return this.messageQueue.hasPending();
989
+ }
981
990
  }
package/dist/index.d.ts CHANGED
@@ -22,6 +22,7 @@ export * from "./utils/tokenCalculation.js";
22
22
  export * from "./utils/gitUtils.js";
23
23
  export * from "./utils/nameGenerator.js";
24
24
  export * from "./utils/worktreeSession.js";
25
+ export { loadMergedWaveConfig } from "./services/configurationService.js";
25
26
  export * from "./types/index.js";
26
27
  export * from "./tools/buildTool.js";
27
28
  export type { ToolPlugin, ToolResult, ToolContext } from "./tools/types.js";
package/dist/index.js CHANGED
@@ -26,6 +26,7 @@ export * from "./utils/tokenCalculation.js";
26
26
  export * from "./utils/gitUtils.js";
27
27
  export * from "./utils/nameGenerator.js";
28
28
  export * from "./utils/worktreeSession.js";
29
+ export { loadMergedWaveConfig } from "./services/configurationService.js";
29
30
  export * from "./types/index.js";
30
31
  // Export tool building utilities
31
32
  export * from "./tools/buildTool.js";
@@ -58,6 +58,7 @@ export declare class AIManager {
58
58
  getMaxInputTokens(): number;
59
59
  getLanguage(): string | undefined;
60
60
  getAutoMemoryEnabled(): boolean;
61
+ getWorktreeBaseRef(): "fresh" | "head";
61
62
  getWorkdir(): string;
62
63
  getOriginalWorkdir(): string;
63
64
  /**
@@ -128,6 +128,9 @@ export class AIManager {
128
128
  getAutoMemoryEnabled() {
129
129
  return this.configurationService.resolveAutoMemoryEnabled();
130
130
  }
131
+ getWorktreeBaseRef() {
132
+ return this.configurationService.resolveWorktreeBaseRef();
133
+ }
131
134
  getWorkdir() {
132
135
  return this.container.get("Workdir") ?? process.cwd();
133
136
  }
@@ -8,6 +8,7 @@ import { ToolManager } from "./toolManager.js";
8
8
  import { AGENT_TOOL_NAME, TASK_CREATE_TOOL_NAME, TASK_GET_TOOL_NAME, TASK_LIST_TOOL_NAME, TASK_UPDATE_TOOL_NAME, } from "../constants/tools.js";
9
9
  import { addConsolidatedAbortListener, createAbortPromise, } from "../utils/abortUtils.js";
10
10
  import { BackgroundTaskManager } from "./backgroundTaskManager.js";
11
+ import { MessageQueue } from "./messageQueue.js";
11
12
  import { logger } from "../utils/globalLogger.js";
12
13
  export class SubagentManager {
13
14
  constructor(container, options) {
@@ -135,6 +136,11 @@ export class SubagentManager {
135
136
  const subagentId = randomUUID();
136
137
  // Create a child container for the subagent to isolate its managers
137
138
  const subagentContainer = this.container.createChild();
139
+ // Register an independent MessageQueue so the subagent's AIManager drains its
140
+ // own (empty) queue instead of falling back to the parent container's queue.
141
+ // Without this, concurrent background subagents steal sibling completion
142
+ // notifications from the parent queue, causing the main agent to exit early.
143
+ subagentContainer.register("MessageQueue", new MessageQueue());
138
144
  // Register a modified AgentOptions without onLoadingChange to prevent subagent loading
139
145
  // from affecting the parent agent's loading state
140
146
  const parentOptions = this.container.get("AgentOptions");
@@ -85,6 +85,12 @@ export declare class ConfigurationService {
85
85
  * @returns Resolved auto-memory enabled state
86
86
  */
87
87
  resolveAutoMemoryEnabled(): boolean;
88
+ /**
89
+ * Resolves worktree base ref with fallbacks
90
+ * Resolution priority: settings.json > default ("fresh")
91
+ * @returns Resolved worktree base ref
92
+ */
93
+ resolveWorktreeBaseRef(): "fresh" | "head";
88
94
  /**
89
95
  * Resolves auto-memory extraction frequency with fallbacks
90
96
  * Resolution priority: settings.json > WAVE_AUTO_MEMORY_FREQUENCY > default (1)
@@ -241,6 +241,19 @@ export class ConfigurationService {
241
241
  }
242
242
  }
243
243
  }
244
+ // Validate worktree if present
245
+ if (config.worktree !== undefined) {
246
+ if (typeof config.worktree !== "object" || config.worktree === null) {
247
+ result.isValid = false;
248
+ result.errors.push("worktree configuration must be an object");
249
+ }
250
+ else if (config.worktree.baseRef !== undefined &&
251
+ config.worktree.baseRef !== "fresh" &&
252
+ config.worktree.baseRef !== "head") {
253
+ result.isValid = false;
254
+ result.errors.push(`Invalid worktree.baseRef: "${config.worktree.baseRef}". Must be "fresh" or "head".`);
255
+ }
256
+ }
244
257
  return result;
245
258
  }
246
259
  /**
@@ -506,6 +519,18 @@ export class ConfigurationService {
506
519
  // 3. Default (true)
507
520
  return true;
508
521
  }
522
+ /**
523
+ * Resolves worktree base ref with fallbacks
524
+ * Resolution priority: settings.json > default ("fresh")
525
+ * @returns Resolved worktree base ref
526
+ */
527
+ resolveWorktreeBaseRef() {
528
+ const baseRef = this.currentConfiguration?.worktree?.baseRef;
529
+ if (baseRef === "head") {
530
+ return "head";
531
+ }
532
+ return "fresh";
533
+ }
509
534
  /**
510
535
  * Resolves auto-memory extraction frequency with fallbacks
511
536
  * Resolution priority: settings.json > WAVE_AUTO_MEMORY_FREQUENCY > default (1)
@@ -944,6 +969,7 @@ export function loadWaveConfigFromFile(filePath) {
944
969
  : undefined,
945
970
  models: config.models || undefined,
946
971
  marketplaces: config.marketplaces || undefined,
972
+ worktree: config.worktree || undefined,
947
973
  };
948
974
  }
949
975
  catch (error) {
@@ -1074,6 +1100,10 @@ export function loadMergedWaveConfig(workdir) {
1074
1100
  mergedConfig.marketplaces = {};
1075
1101
  Object.assign(mergedConfig.marketplaces, config.marketplaces);
1076
1102
  }
1103
+ // Merge worktree (last one wins)
1104
+ if (config.worktree !== undefined) {
1105
+ mergedConfig.worktree = config.worktree;
1106
+ }
1077
1107
  // Merge models
1078
1108
  if (config.models) {
1079
1109
  if (!mergedConfig.models)
@@ -1111,5 +1141,6 @@ export function loadMergedWaveConfig(workdir) {
1111
1141
  models: mergedConfig.models && Object.keys(mergedConfig.models).length > 0
1112
1142
  ? mergedConfig.models
1113
1143
  : undefined,
1144
+ worktree: mergedConfig.worktree,
1114
1145
  };
1115
1146
  }
@@ -286,6 +286,8 @@ export function mergeRemoteSettings(localMerged, remote) {
286
286
  result.autoMemoryEnabled = remote.autoMemoryEnabled;
287
287
  if (remote.autoMemoryFrequency !== undefined)
288
288
  result.autoMemoryFrequency = remote.autoMemoryFrequency;
289
+ if (remote.worktree !== undefined)
290
+ result.worktree = remote.worktree;
289
291
  if (remote.models !== undefined)
290
292
  result.models = remote.models;
291
293
  if (remote.marketplaces !== undefined)
@@ -83,7 +83,8 @@ export const enterWorktreeTool = {
83
83
  };
84
84
  }
85
85
  // Create the worktree (captures originalHeadCommit internally)
86
- const worktreeInfo = createWorktree(name, mainRepoRoot);
86
+ const baseRef = context.aiManager?.getWorktreeBaseRef?.();
87
+ const worktreeInfo = createWorktree(name, mainRepoRoot, { baseRef });
87
88
  // Build session state
88
89
  const session = {
89
90
  originalCwd: context.workdir,
@@ -50,6 +50,11 @@ export interface WaveConfiguration {
50
50
  monitoring?: {
51
51
  telemetry?: Partial<TelemetryConfig>;
52
52
  };
53
+ /** Worktree configuration */
54
+ worktree?: {
55
+ /** Base ref for new worktrees: "fresh" (origin/<default-branch>, default) | "head" (local HEAD) */
56
+ baseRef?: "fresh" | "head";
57
+ };
53
58
  }
54
59
  /**
55
60
  * Legacy alias for backward compatibility - will be deprecated
@@ -15,8 +15,6 @@ export interface PermissionDecision {
15
15
  newPermissionMode?: PermissionMode;
16
16
  /** Signal to persist a new allowed rule */
17
17
  newPermissionRule?: string;
18
- /** Signal to clear the conversation context and proceed with the plan */
19
- clearContext?: boolean;
20
18
  }
21
19
  /** Callback function for custom permission logic */
22
20
  export type PermissionCallback = (context: ToolPermissionContext) => Promise<PermissionDecision>;
@@ -44,6 +44,33 @@ export interface BackgroundWorkflow extends BackgroundTaskBase {
44
44
  runId: string;
45
45
  }
46
46
  export type BackgroundTask = BackgroundShell | BackgroundSubagent | BackgroundWorkflow;
47
+ /**
48
+ * Serializable summary of a BackgroundTask, used for notifications where the
49
+ * full stdout/stderr and non-serializable process/onStop fields must be
50
+ * stripped to control payload size. Output is fetched on demand via
51
+ * getBackgroundTaskOutput.
52
+ */
53
+ export interface BackgroundTaskSummary {
54
+ id: string;
55
+ type: BackgroundTaskType;
56
+ status: BackgroundTaskStatus;
57
+ startTime: number;
58
+ endTime?: number;
59
+ command?: string;
60
+ description?: string;
61
+ exitCode?: number;
62
+ runtime?: number;
63
+ outputPath?: string;
64
+ }
65
+ /** Output snapshot returned by getBackgroundTaskOutput. */
66
+ export interface BackgroundTaskOutput {
67
+ stdout: string;
68
+ stderr: string;
69
+ status: BackgroundTaskStatus;
70
+ outputPath?: string;
71
+ type: BackgroundTaskType;
72
+ exitCode?: number;
73
+ }
47
74
  export interface ForegroundTask {
48
75
  id: string;
49
76
  backgroundHandler: () => Promise<void>;
@@ -1 +1 @@
1
- export type { WorkflowRun, WorkflowMeta, WorkflowPhaseState, } from "../workflow/types.js";
1
+ export type { WorkflowRun, WorkflowMeta, WorkflowPhaseState, SerializableWorkflowRun, } from "../workflow/types.js";
@@ -179,21 +179,12 @@ export function setupAgentContainer(setupOptions) {
179
179
  });
180
180
  }
181
181
  const decision = await options.canUseTool(context);
182
- const planFilePath = permissionManager.getPlanFilePath();
183
182
  if (decision.newPermissionMode) {
184
183
  setPermissionMode(decision.newPermissionMode);
185
184
  }
186
185
  if (decision.newPermissionRule) {
187
186
  await addPermissionRule(decision.newPermissionRule);
188
187
  }
189
- if (decision.clearContext) {
190
- messageManager.clearMessages();
191
- if (planFilePath) {
192
- messageManager.addUserMessage({
193
- content: `Implement the plan at ${planFilePath}`,
194
- });
195
- }
196
- }
197
188
  return decision;
198
189
  }
199
190
  : undefined;
@@ -5,7 +5,7 @@
5
5
  * "WorktreeSession" container slot registered in containerSetup.ts and accessed via
6
6
  * AIManager.getWorktreeSession()/setWorktreeSession()). This keeps worktree state
7
7
  * isolated per session in stdio multi-agent mode — a process-level singleton would
8
- * leak state across concurrent sessions (see specs/047-worktree.md FR-042).
8
+ * leak state across concurrent sessions (see docs/specs/multi-agent/worktree.md FR-042).
9
9
  */
10
10
  export interface WorktreeSession {
11
11
  /** The working directory the session was in before EnterWorktree */
@@ -5,6 +5,6 @@
5
5
  * "WorktreeSession" container slot registered in containerSetup.ts and accessed via
6
6
  * AIManager.getWorktreeSession()/setWorktreeSession()). This keeps worktree state
7
7
  * isolated per session in stdio multi-agent mode — a process-level singleton would
8
- * leak state across concurrent sessions (see specs/047-worktree.md FR-042).
8
+ * leak state across concurrent sessions (see docs/specs/multi-agent/worktree.md FR-042).
9
9
  */
10
10
  export {};
@@ -25,8 +25,14 @@ export declare function generateWorktreeName(): string;
25
25
  export declare function getHeadCommit(cwd: string): string;
26
26
  /**
27
27
  * Create a git worktree for use during a session.
28
+ * @param name Worktree name
29
+ * @param cwd Current working directory (will be resolved to main repo root)
30
+ * @param options Optional creation options
31
+ * @param options.baseRef "fresh" (default, origin/<default-branch>) | "head" (local HEAD)
28
32
  */
29
- export declare function createWorktree(name: string, cwd: string): WorktreeInfo;
33
+ export declare function createWorktree(name: string, cwd: string, options?: {
34
+ baseRef?: "fresh" | "head";
35
+ }): WorktreeInfo;
30
36
  /**
31
37
  * Remove a git worktree and its branch.
32
38
  */
@@ -68,8 +68,12 @@ export function getHeadCommit(cwd) {
68
68
  }
69
69
  /**
70
70
  * Create a git worktree for use during a session.
71
+ * @param name Worktree name
72
+ * @param cwd Current working directory (will be resolved to main repo root)
73
+ * @param options Optional creation options
74
+ * @param options.baseRef "fresh" (default, origin/<default-branch>) | "head" (local HEAD)
71
75
  */
72
- export function createWorktree(name, cwd) {
76
+ export function createWorktree(name, cwd, options) {
73
77
  const repoRoot = getGitMainRepoRoot(cwd);
74
78
  if (!repoRoot) {
75
79
  throw new Error("Cannot create a worktree: not in a git repository. Configure WorktreeCreate and WorktreeRemove hooks in settings.json to use worktree isolation with other VCS systems.");
@@ -78,7 +82,8 @@ export function createWorktree(name, cwd) {
78
82
  const originalHeadCommit = getHeadCommit(cwd);
79
83
  const worktreePath = path.join(repoRoot, ".wave", "worktrees", name);
80
84
  const branchName = `worktree-${name}`;
81
- const baseBranch = getDefaultRemoteBranch(cwd);
85
+ const useHead = options?.baseRef === "head";
86
+ const baseBranch = useHead ? "HEAD" : getDefaultRemoteBranch(cwd);
82
87
  // Ensure Wave runtime files are git-excluded in this repo
83
88
  ensureWaveRuntimeFilesExcluded(cwd);
84
89
  // Ensure parent directory exists
@@ -144,8 +149,9 @@ export function createWorktree(name, cwd) {
144
149
  throw new Error(`Failed to add worktree: ${innerError.message}`);
145
150
  }
146
151
  }
147
- if (stderr.includes("not a valid object name") ||
148
- stderr.includes("unknown revision")) {
152
+ if (!useHead &&
153
+ (stderr.includes("not a valid object name") ||
154
+ stderr.includes("unknown revision"))) {
149
155
  // Base branch not fetched yet — try fetching then retrying
150
156
  const branchNameOnly = baseBranch.split("/").pop();
151
157
  try {
@@ -38,6 +38,11 @@ export interface WorkflowRun {
38
38
  /** Error message from the failed agent */
39
39
  failedAgentError?: string;
40
40
  }
41
+ /**
42
+ * Serializable workflow run for stdio transport, with the non-serializable
43
+ * `completionPromise` stripped. Returned by the `getWorkflowRuns` RPC.
44
+ */
45
+ export type SerializableWorkflowRun = Omit<WorkflowRun, "completionPromise">;
41
46
  export interface JournalEntry {
42
47
  agentIndex: number;
43
48
  prompt: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wave-agent-sdk",
3
- "version": "0.19.7",
3
+ "version": "0.19.8",
4
4
  "description": "SDK for building AI-powered development tools and agents",
5
5
  "keywords": [
6
6
  "ai",
package/src/agent.ts CHANGED
@@ -1266,4 +1266,14 @@ export class Agent {
1266
1266
  this.subagentManager.getActiveInstances().length > 0;
1267
1267
  return runningTasks || activeSubagents;
1268
1268
  }
1269
+
1270
+ /**
1271
+ * Check if there are pending items (messages, bang commands, or background
1272
+ * task notifications) in the message queue. Background task completion
1273
+ * notifications are enqueued before the main agent's dispatch consumes them,
1274
+ * so callers waiting for the agent to fully settle must also wait on this.
1275
+ */
1276
+ public get hasPendingMessages(): boolean {
1277
+ return this.messageQueue.hasPending();
1278
+ }
1269
1279
  }
package/src/index.ts CHANGED
@@ -29,6 +29,7 @@ export * from "./utils/tokenCalculation.js";
29
29
  export * from "./utils/gitUtils.js";
30
30
  export * from "./utils/nameGenerator.js";
31
31
  export * from "./utils/worktreeSession.js";
32
+ export { loadMergedWaveConfig } from "./services/configurationService.js";
32
33
  export * from "./types/index.js";
33
34
 
34
35
  // Export tool building utilities
@@ -252,6 +252,10 @@ export class AIManager {
252
252
  return this.configurationService.resolveAutoMemoryEnabled();
253
253
  }
254
254
 
255
+ public getWorktreeBaseRef(): "fresh" | "head" {
256
+ return this.configurationService.resolveWorktreeBaseRef();
257
+ }
258
+
255
259
  public getWorkdir(): string {
256
260
  return this.container.get<string>("Workdir") ?? process.cwd();
257
261
  }
@@ -290,6 +290,12 @@ export class SubagentManager {
290
290
  // Create a child container for the subagent to isolate its managers
291
291
  const subagentContainer = this.container.createChild();
292
292
 
293
+ // Register an independent MessageQueue so the subagent's AIManager drains its
294
+ // own (empty) queue instead of falling back to the parent container's queue.
295
+ // Without this, concurrent background subagents steal sibling completion
296
+ // notifications from the parent queue, causing the main agent to exit early.
297
+ subagentContainer.register("MessageQueue", new MessageQueue());
298
+
293
299
  // Register a modified AgentOptions without onLoadingChange to prevent subagent loading
294
300
  // from affecting the parent agent's loading state
295
301
  const parentOptions =
@@ -320,6 +320,23 @@ export class ConfigurationService {
320
320
  }
321
321
  }
322
322
 
323
+ // Validate worktree if present
324
+ if (config.worktree !== undefined) {
325
+ if (typeof config.worktree !== "object" || config.worktree === null) {
326
+ result.isValid = false;
327
+ result.errors.push("worktree configuration must be an object");
328
+ } else if (
329
+ config.worktree.baseRef !== undefined &&
330
+ config.worktree.baseRef !== "fresh" &&
331
+ config.worktree.baseRef !== "head"
332
+ ) {
333
+ result.isValid = false;
334
+ result.errors.push(
335
+ `Invalid worktree.baseRef: "${config.worktree.baseRef}". Must be "fresh" or "head".`,
336
+ );
337
+ }
338
+ }
339
+
323
340
  return result;
324
341
  }
325
342
 
@@ -640,6 +657,19 @@ export class ConfigurationService {
640
657
  return true;
641
658
  }
642
659
 
660
+ /**
661
+ * Resolves worktree base ref with fallbacks
662
+ * Resolution priority: settings.json > default ("fresh")
663
+ * @returns Resolved worktree base ref
664
+ */
665
+ resolveWorktreeBaseRef(): "fresh" | "head" {
666
+ const baseRef = this.currentConfiguration?.worktree?.baseRef;
667
+ if (baseRef === "head") {
668
+ return "head";
669
+ }
670
+ return "fresh";
671
+ }
672
+
643
673
  /**
644
674
  * Resolves auto-memory extraction frequency with fallbacks
645
675
  * Resolution priority: settings.json > WAVE_AUTO_MEMORY_FREQUENCY > default (1)
@@ -1172,6 +1202,7 @@ export function loadWaveConfigFromFile(
1172
1202
  : undefined,
1173
1203
  models: config.models || undefined,
1174
1204
  marketplaces: config.marketplaces || undefined,
1205
+ worktree: config.worktree || undefined,
1175
1206
  };
1176
1207
  } catch (error) {
1177
1208
  if (error instanceof SyntaxError) {
@@ -1320,6 +1351,11 @@ export function loadMergedWaveConfig(
1320
1351
  Object.assign(mergedConfig.marketplaces, config.marketplaces);
1321
1352
  }
1322
1353
 
1354
+ // Merge worktree (last one wins)
1355
+ if (config.worktree !== undefined) {
1356
+ mergedConfig.worktree = config.worktree;
1357
+ }
1358
+
1323
1359
  // Merge models
1324
1360
  if (config.models) {
1325
1361
  if (!mergedConfig.models) mergedConfig.models = {};
@@ -1363,5 +1399,6 @@ export function loadMergedWaveConfig(
1363
1399
  mergedConfig.models && Object.keys(mergedConfig.models).length > 0
1364
1400
  ? mergedConfig.models
1365
1401
  : undefined,
1402
+ worktree: mergedConfig.worktree,
1366
1403
  };
1367
1404
  }
@@ -326,6 +326,7 @@ export function mergeRemoteSettings(
326
326
  result.autoMemoryEnabled = remote.autoMemoryEnabled;
327
327
  if (remote.autoMemoryFrequency !== undefined)
328
328
  result.autoMemoryFrequency = remote.autoMemoryFrequency;
329
+ if (remote.worktree !== undefined) result.worktree = remote.worktree;
329
330
  if (remote.models !== undefined) result.models = remote.models;
330
331
  if (remote.marketplaces !== undefined)
331
332
  result.marketplaces = remote.marketplaces;
@@ -102,7 +102,8 @@ export const enterWorktreeTool: ToolPlugin = {
102
102
  }
103
103
 
104
104
  // Create the worktree (captures originalHeadCommit internally)
105
- const worktreeInfo = createWorktree(name, mainRepoRoot);
105
+ const baseRef = context.aiManager?.getWorktreeBaseRef?.();
106
+ const worktreeInfo = createWorktree(name, mainRepoRoot, { baseRef });
106
107
 
107
108
  // Build session state
108
109
  const session: WorktreeSession = {
@@ -54,6 +54,11 @@ export interface WaveConfiguration {
54
54
  monitoring?: {
55
55
  telemetry?: Partial<TelemetryConfig>;
56
56
  };
57
+ /** Worktree configuration */
58
+ worktree?: {
59
+ /** Base ref for new worktrees: "fresh" (origin/<default-branch>, default) | "head" (local HEAD) */
60
+ baseRef?: "fresh" | "head";
61
+ };
57
62
  }
58
63
 
59
64
  /**
@@ -35,8 +35,6 @@ export interface PermissionDecision {
35
35
  newPermissionMode?: PermissionMode;
36
36
  /** Signal to persist a new allowed rule */
37
37
  newPermissionRule?: string;
38
- /** Signal to clear the conversation context and proceed with the plan */
39
- clearContext?: boolean;
40
38
  }
41
39
 
42
40
  /** Callback function for custom permission logic */
@@ -59,6 +59,35 @@ export type BackgroundTask =
59
59
  | BackgroundSubagent
60
60
  | BackgroundWorkflow;
61
61
 
62
+ /**
63
+ * Serializable summary of a BackgroundTask, used for notifications where the
64
+ * full stdout/stderr and non-serializable process/onStop fields must be
65
+ * stripped to control payload size. Output is fetched on demand via
66
+ * getBackgroundTaskOutput.
67
+ */
68
+ export interface BackgroundTaskSummary {
69
+ id: string;
70
+ type: BackgroundTaskType;
71
+ status: BackgroundTaskStatus;
72
+ startTime: number;
73
+ endTime?: number;
74
+ command?: string;
75
+ description?: string;
76
+ exitCode?: number;
77
+ runtime?: number;
78
+ outputPath?: string;
79
+ }
80
+
81
+ /** Output snapshot returned by getBackgroundTaskOutput. */
82
+ export interface BackgroundTaskOutput {
83
+ stdout: string;
84
+ stderr: string;
85
+ status: BackgroundTaskStatus;
86
+ outputPath?: string;
87
+ type: BackgroundTaskType;
88
+ exitCode?: number;
89
+ }
90
+
62
91
  export interface ForegroundTask {
63
92
  id: string;
64
93
  backgroundHandler: () => Promise<void>;
@@ -2,4 +2,5 @@ export type {
2
2
  WorkflowRun,
3
3
  WorkflowMeta,
4
4
  WorkflowPhaseState,
5
+ SerializableWorkflowRun,
5
6
  } from "../workflow/types.js";
@@ -256,8 +256,6 @@ export function setupAgentContainer(
256
256
 
257
257
  const decision = await options.canUseTool!(context);
258
258
 
259
- const planFilePath = permissionManager.getPlanFilePath();
260
-
261
259
  if (decision.newPermissionMode) {
262
260
  setPermissionMode(decision.newPermissionMode);
263
261
  }
@@ -266,15 +264,6 @@ export function setupAgentContainer(
266
264
  await addPermissionRule(decision.newPermissionRule);
267
265
  }
268
266
 
269
- if (decision.clearContext) {
270
- messageManager.clearMessages();
271
- if (planFilePath) {
272
- messageManager.addUserMessage({
273
- content: `Implement the plan at ${planFilePath}`,
274
- });
275
- }
276
- }
277
-
278
267
  return decision;
279
268
  }
280
269
  : undefined;
@@ -5,7 +5,7 @@
5
5
  * "WorktreeSession" container slot registered in containerSetup.ts and accessed via
6
6
  * AIManager.getWorktreeSession()/setWorktreeSession()). This keeps worktree state
7
7
  * isolated per session in stdio multi-agent mode — a process-level singleton would
8
- * leak state across concurrent sessions (see specs/047-worktree.md FR-042).
8
+ * leak state across concurrent sessions (see docs/specs/multi-agent/worktree.md FR-042).
9
9
  */
10
10
 
11
11
  export interface WorktreeSession {
@@ -93,8 +93,16 @@ export function getHeadCommit(cwd: string): string {
93
93
 
94
94
  /**
95
95
  * Create a git worktree for use during a session.
96
+ * @param name Worktree name
97
+ * @param cwd Current working directory (will be resolved to main repo root)
98
+ * @param options Optional creation options
99
+ * @param options.baseRef "fresh" (default, origin/<default-branch>) | "head" (local HEAD)
96
100
  */
97
- export function createWorktree(name: string, cwd: string): WorktreeInfo {
101
+ export function createWorktree(
102
+ name: string,
103
+ cwd: string,
104
+ options?: { baseRef?: "fresh" | "head" },
105
+ ): WorktreeInfo {
98
106
  const repoRoot = getGitMainRepoRoot(cwd);
99
107
  if (!repoRoot) {
100
108
  throw new Error(
@@ -107,7 +115,8 @@ export function createWorktree(name: string, cwd: string): WorktreeInfo {
107
115
 
108
116
  const worktreePath = path.join(repoRoot, ".wave", "worktrees", name);
109
117
  const branchName = `worktree-${name}`;
110
- const baseBranch = getDefaultRemoteBranch(cwd);
118
+ const useHead = options?.baseRef === "head";
119
+ const baseBranch = useHead ? "HEAD" : getDefaultRemoteBranch(cwd);
111
120
 
112
121
  // Ensure Wave runtime files are git-excluded in this repo
113
122
  ensureWaveRuntimeFilesExcluded(cwd);
@@ -183,8 +192,9 @@ export function createWorktree(name: string, cwd: string): WorktreeInfo {
183
192
  }
184
193
  }
185
194
  if (
186
- stderr.includes("not a valid object name") ||
187
- stderr.includes("unknown revision")
195
+ !useHead &&
196
+ (stderr.includes("not a valid object name") ||
197
+ stderr.includes("unknown revision"))
188
198
  ) {
189
199
  // Base branch not fetched yet — try fetching then retrying
190
200
  const branchNameOnly = baseBranch.split("/").pop()!;
@@ -42,6 +42,12 @@ export interface WorkflowRun {
42
42
  failedAgentError?: string;
43
43
  }
44
44
 
45
+ /**
46
+ * Serializable workflow run for stdio transport, with the non-serializable
47
+ * `completionPromise` stripped. Returned by the `getWorkflowRuns` RPC.
48
+ */
49
+ export type SerializableWorkflowRun = Omit<WorkflowRun, "completionPromise">;
50
+
45
51
  export interface JournalEntry {
46
52
  agentIndex: number;
47
53
  prompt: string;