@@ -21,17 +21,17 @@ pub const Executor = struct {
2121 cfg : * const config.Config ,
2222 log : * Logger ,
2323 allocator : Allocator ,
24- session_id : ? []const u8 ,
2524 opencode_cmd : []const u8 ,
25+ current_child_pid : ? std.posix.pid_t ,
2626
2727 /// Initialize executor
2828 pub fn init (cfg : * const config.Config , log : * Logger , allocator : Allocator ) Executor {
2929 return Executor {
3030 .cfg = cfg ,
3131 .log = log ,
3232 .allocator = allocator ,
33- .session_id = null ,
3433 .opencode_cmd = "opencode" ,
34+ .current_child_pid = null ,
3535 };
3636 }
3737
@@ -41,16 +41,14 @@ pub const Executor = struct {
4141 .cfg = cfg ,
4242 .log = log ,
4343 .allocator = allocator ,
44- .session_id = null ,
4544 .opencode_cmd = opencode_cmd ,
45+ .current_child_pid = null ,
4646 };
4747 }
4848
4949 /// Cleanup executor resources
5050 pub fn deinit (self : * Executor ) void {
51- if (self .session_id ) | sid | {
52- self .allocator .free (sid );
53- }
51+ _ = self ;
5452 }
5553
5654 /// Run planning phase
@@ -63,14 +61,7 @@ pub const Executor = struct {
6361 var title_buf : [64 ]u8 = undefined ;
6462 const title = std .fmt .bufPrint (& title_buf , "Opencoder Planning Cycle {d}" , .{cycle }) catch "Opencoder Planning" ;
6563
66- const result = try self .runWithRetry (self .cfg .planning_model , title , prompt , false );
67-
68- if (result == .success ) {
69- // Reset session for new cycle
70- self .resetSession ();
71- }
72-
73- return result ;
64+ return try self .runWithRetry (self .cfg .planning_model , title , prompt );
7465 }
7566
7667 /// Run task execution
@@ -83,15 +74,7 @@ pub const Executor = struct {
8374 var title_buf : [64 ]u8 = undefined ;
8475 const title = std .fmt .bufPrint (& title_buf , "Opencoder Execution Cycle {d}" , .{cycle }) catch "Opencoder Execution" ;
8576
86- const continue_session = self .session_id != null ;
87- const result = try self .runWithRetry (self .cfg .execution_model , title , prompt , continue_session );
88-
89- if (result == .success and self .session_id == null ) {
90- // Set session ID after first successful task (allocate owned memory)
91- self .session_id = try std .fmt .allocPrint (self .allocator , "ses_{d}" , .{cycle });
92- }
93-
94- return result ;
77+ return try self .runWithRetry (self .cfg .execution_model , title , prompt );
9578 }
9679
9780 /// Run evaluation phase
@@ -110,7 +93,7 @@ pub const Executor = struct {
11093 self .log .statusFmt ("[Cycle {d}] Evaluation retry {d}/{d}" , .{ cycle , attempt + 1 , self .cfg .max_retries });
11194 }
11295
113- const result = self .runOpencode (self .cfg .planning_model , title , prompt , false );
96+ const result = self .runOpencode (self .cfg .planning_model , title , prompt );
11497 if (result ) | output | {
11598 // Check for COMPLETE or NEEDS_WORK in output
11699 if (std .mem .indexOf (u8 , output , "COMPLETE" ) != null ) {
@@ -150,24 +133,27 @@ pub const Executor = struct {
150133 return "NEEDS_WORK" ;
151134 }
152135
153- /// Reset session for new cycle
154- pub fn resetSession (self : * Executor ) void {
155- if (self .session_id ) | sid | {
156- self .allocator .free (sid );
136+ /// Kill current child process if running
137+ pub fn killCurrentChild (self : * Executor ) void {
138+ if (self .current_child_pid ) | pid | {
139+ self .log .logFmt ("Terminating child process (PID: {d})" , .{pid });
140+ std .posix .kill (pid , std .posix .SIG .TERM ) catch | err | {
141+ self .log .logErrorFmt ("Failed to kill child process: {s}" , .{@errorName (err )});
142+ };
143+ self .current_child_pid = null ;
157144 }
158- self .session_id = null ;
159145 }
160146
161147 // Internal helper to run with retry logic
162- fn runWithRetry (self : * Executor , model : []const u8 , title : []const u8 , prompt : []const u8 , continue_session : bool ) ! ExecutionResult {
148+ fn runWithRetry (self : * Executor , model : []const u8 , title : []const u8 , prompt : []const u8 ) ! ExecutionResult {
163149 var attempt : u32 = 0 ;
164150
165151 while (attempt < self .cfg .max_retries ) : (attempt += 1 ) {
166152 if (attempt > 0 ) {
167153 self .log .logFmt ("Retry attempt {d}/{d}" , .{ attempt + 1 , self .cfg .max_retries });
168154 }
169155
170- const result = self .runOpencode (model , title , prompt , continue_session );
156+ const result = self .runOpencode (model , title , prompt );
171157 if (result ) | output | {
172158 self .allocator .free (output );
173159 return .success ;
@@ -201,7 +187,7 @@ pub const Executor = struct {
201187 }
202188
203189 // Run opencode CLI and return output
204- fn runOpencode (self : * Executor , model : []const u8 , title : []const u8 , prompt : []const u8 , continue_session : bool ) ! []u8 {
190+ fn runOpencode (self : * Executor , model : []const u8 , title : []const u8 , prompt : []const u8 ) ! []u8 {
205191 var args = std .ArrayListUnmanaged ([]const u8 ){};
206192 defer args .deinit (self .allocator );
207193
@@ -211,14 +197,6 @@ pub const Executor = struct {
211197 try args .append (self .allocator , model );
212198 try args .append (self .allocator , "--title" );
213199 try args .append (self .allocator , title );
214-
215- if (continue_session ) {
216- if (self .session_id ) | sid | {
217- try args .append (self .allocator , "--session" );
218- try args .append (self .allocator , sid );
219- }
220- }
221-
222200 try args .append (self .allocator , prompt );
223201
224202 self .log .logVerbose ("Running opencode..." );
@@ -232,6 +210,10 @@ pub const Executor = struct {
232210
233211 try child .spawn ();
234212
213+ // Store PID for potential termination
214+ self .current_child_pid = child .id ;
215+ defer self .current_child_pid = null ;
216+
235217 // Read stdout
236218 var stdout_list = std .ArrayListUnmanaged (u8 ){};
237219
@@ -354,7 +336,6 @@ test "Executor.init creates executor" {
354336
355337 const executor = Executor .init (& test_cfg , test_logger , allocator );
356338 try std .testing .expectEqualStrings ("opencode" , executor .opencode_cmd );
357- try std .testing .expect (executor .session_id == null );
358339}
359340
360341test "Executor.initWithCmd creates executor with custom command" {
@@ -413,7 +394,7 @@ test "runOpencode handles successful execution" {
413394 var executor = Executor .initWithCmd (& test_cfg , test_logger , allocator , mock_path );
414395 defer executor .deinit ();
415396
416- const result = try executor .runOpencode ("test/model" , "Test" , "test prompt" , false );
397+ const result = try executor .runOpencode ("test/model" , "Test" , "test prompt" );
417398 defer allocator .free (result );
418399
419400 try std .testing .expect (result .len > 0 );
@@ -448,43 +429,6 @@ test "runOpencode handles process failure" {
448429 var executor = Executor .initWithCmd (& test_cfg , test_logger , allocator , mock_path );
449430 defer executor .deinit ();
450431
451- const result = executor .runOpencode ("test/model" , "Test" , "test prompt" , false );
432+ const result = executor .runOpencode ("test/model" , "Test" , "test prompt" );
452433 try std .testing .expectError (error .OpencodeFailed , result );
453434}
454-
455- test "runOpencode passes session ID when continuing" {
456- const allocator = std .testing .allocator ;
457- const test_logger = try createTestLogger (allocator );
458- defer destroyTestLogger (test_logger , allocator );
459-
460- const test_cfg = config.Config {
461- .planning_model = "test/model" ,
462- .execution_model = "test/model" ,
463- .project_dir = "/tmp" ,
464- .verbose = false ,
465- .user_hint = null ,
466- .max_retries = 3 ,
467- .backoff_base = 1 ,
468- .log_retention = 30 ,
469- .max_file_size = 1024 * 1024 ,
470- .log_buffer_size = 2048 ,
471- .task_pause_seconds = 2 ,
472- };
473-
474- // Get absolute path to mock script
475- var cwd_buf : [std .fs .max_path_bytes ]u8 = undefined ;
476- const cwd = try std .fs .cwd ().realpath ("." , & cwd_buf );
477- const mock_path = try std .fs .path .join (allocator , &.{ cwd , "test_helpers/mock_opencode_success.sh" });
478- defer allocator .free (mock_path );
479-
480- var executor = Executor .initWithCmd (& test_cfg , test_logger , allocator , mock_path );
481- defer executor .deinit ();
482-
483- // Set a session ID
484- executor .session_id = try std .fmt .allocPrint (allocator , "test_session_123" , .{});
485-
486- const result = try executor .runOpencode ("test/model" , "Test" , "test prompt" , true );
487- defer allocator .free (result );
488-
489- try std .testing .expect (result .len > 0 );
490- }
0 commit comments