Skip to content

Commit d34eb52

Browse files
committed
feat(auggie): add session resuming support and improve pause/skip handling
implement session resuming capability in Auggie engine by adding resumeSessionId and resumePrompt options enhance step runner to properly distinguish between pause and skip abort scenarios add session ID tracking and callback in Auggie execution
1 parent 5a7f051 commit d34eb52

File tree

4 files changed

+54
-6
lines changed

4 files changed

+54
-6
lines changed

config/main.agents.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ module.exports = [
7878
description: 'BMAD product owner controller for autonomous mode',
7979
role: 'controller',
8080
promptPath: path.join(promptsDir, 'bmad', 'controller', 'PO.md'),
81-
engine: 'codex'
81+
engine: 'opencode'
8282
},
8383

8484
// BMAD agents

src/infra/engines/providers/auggie/execution/commands.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ export interface AuggieCommandOptions {
33
* Model identifier (if supported by Auggie)
44
*/
55
model?: string;
6+
/**
7+
* Session ID to resume
8+
*/
9+
resumeSessionId?: string;
610
}
711

812
export interface AuggieCommand {
@@ -13,6 +17,11 @@ export interface AuggieCommand {
1317
export function buildAuggieRunCommand(options: AuggieCommandOptions = {}): AuggieCommand {
1418
const args: string[] = ['--print', '--quiet', '--output-format', 'json'];
1519

20+
// Add resume flag if resuming a session
21+
if (options.resumeSessionId?.trim()) {
22+
args.push('--resume', options.resumeSessionId.trim());
23+
}
24+
1625
// Add model if specified (check Auggie docs for exact flag)
1726
if (options.model?.trim()) {
1827
args.push('--model', options.model.trim());

src/infra/engines/providers/auggie/execution/runner.ts

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,14 @@ import type { ParsedTelemetry } from '../../../core/types.js';
1010
export interface RunAuggieOptions {
1111
prompt: string;
1212
workingDir: string;
13+
resumeSessionId?: string;
14+
resumePrompt?: string;
1315
model?: string;
1416
env?: NodeJS.ProcessEnv;
1517
onData?: (chunk: string) => void;
1618
onErrorData?: (chunk: string) => void;
1719
onTelemetry?: (telemetry: ParsedTelemetry) => void;
20+
onSessionId?: (sessionId: string) => void;
1821
abortSignal?: AbortSignal;
1922
timeout?: number; // Timeout in milliseconds (default: 1800000ms = 30 minutes)
2023
}
@@ -48,6 +51,20 @@ function cleanAnsi(text: string, plainLogs: boolean): string {
4851
return text.replace(ANSI_ESCAPE_SEQUENCE, '');
4952
}
5053

54+
/**
55+
* Build the final resume prompt combining steering instruction with user message
56+
*/
57+
function buildResumePrompt(userPrompt?: string): string {
58+
const defaultPrompt = 'Continue from where you left off.';
59+
60+
if (!userPrompt) {
61+
return defaultPrompt;
62+
}
63+
64+
// Combine steering instruction with user's message
65+
return `[USER STEERING] The user paused this session to give you new direction. Continue from where you left off, but prioritize the user's request: "${userPrompt}"`;
66+
}
67+
5168
// Note: Auggie returns a single result object, not streaming events like OpenCode
5269
// Therefore, we don't need complex formatting helpers like formatToolUse or formatStepEvent
5370

@@ -71,11 +88,14 @@ export async function runAuggie(options: RunAuggieOptions): Promise<RunAuggieRes
7188
const {
7289
prompt,
7390
workingDir,
91+
resumeSessionId,
92+
resumePrompt,
7493
model,
7594
env,
7695
onData,
7796
onErrorData,
7897
onTelemetry,
98+
onSessionId,
7999
abortSignal,
80100
timeout = 1800000,
81101
} = options;
@@ -91,13 +111,16 @@ export async function runAuggie(options: RunAuggieOptions): Promise<RunAuggieRes
91111
const runnerEnv = resolveRunnerEnv(env);
92112
const plainLogs =
93113
(env?.CODEMACHINE_PLAIN_LOGS ?? process.env.CODEMACHINE_PLAIN_LOGS ?? '').toString() === '1';
94-
const { command, args } = buildAuggieRunCommand({ model });
114+
const { command, args } = buildAuggieRunCommand({ model, resumeSessionId });
95115

96116
// Add working directory to args (Auggie uses --workspace-root, not --cwd)
97117
args.push('--workspace-root', workingDir);
98118

119+
// When resuming, use the resume prompt instead of the original prompt
120+
const effectivePrompt = resumeSessionId ? buildResumePrompt(resumePrompt) : prompt;
121+
99122
// Add prompt as positional argument (Auggie accepts it as the last argument)
100-
args.push(prompt);
123+
args.push(effectivePrompt);
101124

102125
logger.debug(
103126
`Auggie runner - prompt length: ${prompt.length}, lines: ${prompt.split('\n').length}, model: ${
@@ -108,6 +131,7 @@ export async function runAuggie(options: RunAuggieOptions): Promise<RunAuggieRes
108131
const telemetryCapture = createTelemetryCapture('auggie', model, prompt, workingDir);
109132
let jsonBuffer = '';
110133
let isFirstStep = true;
134+
let sessionIdCaptured = false;
111135

112136
const processLine = (line: string): void => {
113137
if (!line.trim()) {
@@ -131,6 +155,12 @@ export async function runAuggie(options: RunAuggieOptions): Promise<RunAuggieRes
131155
// Auggie returns a single JSON object with type "result"
132156
// Format: {"type":"result","result":"...","is_error":false,"subtype":"success","session_id":"...","num_turns":0}
133157
if (parsedObj.type === 'result') {
158+
// Capture session ID from result event
159+
if (!sessionIdCaptured && parsedObj.session_id && onSessionId) {
160+
sessionIdCaptured = true;
161+
onSessionId(parsedObj.session_id);
162+
}
163+
134164
const resultText = typeof parsedObj.result === 'string' ? parsedObj.result : '';
135165
const isError = parsedObj.is_error === true;
136166

src/workflows/step/run.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -202,9 +202,18 @@ export async function runStepFresh(ctx: RunnerContext): Promise<RunStepResult |
202202
return { output: stepOutput };
203203
} catch (error) {
204204
if (error instanceof Error && error.name === 'AbortError') {
205-
debug('[step/run] Step aborted, skipping to next');
206-
ctx.emitter.updateAgentStatus(uniqueAgentId, 'skipped');
207-
ctx.machine.send({ type: 'SKIP' });
205+
// PAUSE sends PAUSE event (transitions running→awaiting) BEFORE calling abort()
206+
// SKIP only calls abort() without sending any event first
207+
// So if machine is still 'running', this was a skip - send SKIP to advance
208+
// If machine is 'awaiting', this was a pause - preserve that state
209+
if (ctx.machine.state === 'running') {
210+
debug('[step/run] Step aborted via skip, advancing to next step');
211+
ctx.emitter.updateAgentStatus(uniqueAgentId, 'skipped');
212+
ctx.machine.send({ type: 'SKIP' });
213+
} else {
214+
debug('[step/run] Step aborted via pause, entering awaiting state');
215+
ctx.emitter.updateAgentStatus(uniqueAgentId, 'awaiting');
216+
}
208217
return null;
209218
}
210219

0 commit comments

Comments
 (0)