3737- ** 8 Lifecycle Hooks** — Pre/post events for tool calls, sessions, messages, and errors
3838- ** Security** — 5 layers: sanitizer, taint tracking, interceptor, injection detection, audit logging
3939- ** Memory** — 4 types: episodic, semantic, procedural, working memory
40- - ** JSON-Structured Planning** — Execution plans and goal tracking via LlmPlanner
41- - ** Parallel Plan Execution** — Independent plan steps execute concurrently via wave-based dependency graph scheduling
40+ - ** JSON-Structured Planning** — Execution plans with dependency graphs and goal tracking via LlmPlanner
41+ - ** Parallel Plan Execution** — Independent plan steps execute concurrently via wave-based dependency graph scheduling ( ` tokio::JoinSet ` )
4242- ** Lane Queue** — Priority-based tool routing with parallel reads (Query lane) and external task offloading
4343- ** Context Compaction** — Auto-summarize long conversations (80% threshold)
4444- ** Context Store** — Persistent context storage (feature-gated: ` context-store ` )
@@ -276,6 +276,145 @@ providers {
276276
277277> ** Note:** ` skill_dirs ` and ` agent_dirs ` can be set in both ` CodeConfig ` (agent-level defaults) and ` SessionOptions ` (per-session overrides, merged with agent-level). ` queue_config ` is session-level only.
278278
279+ ## Parallel Plan Execution
280+
281+ When planning is enabled, A3S Code decomposes complex tasks into steps with a dependency graph and executes independent steps ** in parallel** . No configuration beyond ` planning_enabled = true ` is needed — the scheduler automatically groups independent steps into waves and spawns them concurrently via ` tokio::JoinSet ` .
282+
283+ ### How It Works
284+
285+ ```
286+ ExecutionPlan:
287+ step 1: Analyze auth module (no deps)
288+ step 2: Analyze database schema (no deps)
289+ step 3: Implement JWT integration (depends on 1, 2)
290+ step 4: Write tests (depends on 3)
291+
292+ Execution:
293+ Wave 1: [step 1, step 2] ← parallel (independent)
294+ Wave 2: [step 3] ← waits for wave 1
295+ Wave 3: [step 4] ← waits for wave 2
296+ ```
297+
298+ Each wave:
299+ 1 . ` get_ready_steps() ` finds steps whose dependencies are all ` Completed `
300+ 2 . ** Single step** → executes sequentially, preserving the full history chain
301+ 3 . ** Multiple steps** → spawns all into a ` JoinSet ` , each with a clone of the base history
302+ 4 . After the wave completes, results are merged into shared history for subsequent steps
303+ 5 . Failed steps are detected — dependent steps become unreachable (deadlock detection breaks the loop)
304+
305+ ### Rust
306+
307+ ``` rust
308+ use a3s_code_core :: {Agent , AgentConfig , AgentEvent , SessionOptions };
309+
310+ let agent = Agent :: new (" agent.hcl" ). await ? ;
311+ let session = agent . session (" /my-project" , Some (
312+ SessionOptions :: new ()
313+ . with_planning (true ) // enable plan decomposition
314+ . with_goal_tracking (true ) // track progress against success criteria
315+ ))? ;
316+
317+ // Stream with parallel step execution events
318+ let (mut rx , _handle ) = session . stream (" Refactor auth to use JWT and update all tests" ). await ? ;
319+ while let Some (event ) = rx . recv (). await {
320+ match event {
321+ AgentEvent :: StepStart { step_id , description , step_number , total_steps } => {
322+ println! (" [{step_number}/{total_steps}] Starting: {description}" );
323+ }
324+ AgentEvent :: StepEnd { step_id , status , step_number , total_steps } => {
325+ println! (" [{step_number}/{total_steps}] {status}" );
326+ }
327+ AgentEvent :: GoalProgress { goal , progress , completed_steps , total_steps } => {
328+ println! (" Progress: {:.0}% ({completed_steps}/{total_steps})" , progress * 100.0 );
329+ }
330+ AgentEvent :: TextDelta { text } => print! (" {text}" ),
331+ AgentEvent :: End { .. } => break ,
332+ _ => {}
333+ }
334+ }
335+ ```
336+
337+ ### TypeScript
338+
339+ ``` typescript
340+ const { Agent } = require (' @a3s-lab/code' );
341+
342+ const agent = await Agent .create (' agent.hcl' );
343+ const session = agent .session (' /my-project' , {
344+ planning: true ,
345+ goalTracking: true ,
346+ });
347+
348+ const events = await session .stream (' Refactor auth to use JWT and update all tests' );
349+ for (const event of events ) {
350+ switch (event .type ) {
351+ case ' step_start' :
352+ console .log (` [${event .stepNumber }/${event .totalSteps }] Starting: ${event .description } ` );
353+ break ;
354+ case ' step_end' :
355+ console .log (` [${event .stepNumber }/${event .totalSteps }] ${event .status } ` );
356+ break ;
357+ case ' goal_progress' :
358+ console .log (` Progress: ${(event .progress * 100 ).toFixed (0 )}% ` );
359+ break ;
360+ case ' text_delta' :
361+ process .stdout .write (event .text );
362+ break ;
363+ }
364+ }
365+ ```
366+
367+ ### Python
368+
369+ ``` python
370+ from a3s_code import Agent
371+
372+ agent = Agent.create(" agent.hcl" )
373+ session = agent.session(" /my-project" , planning = True , goal_tracking = True )
374+
375+ for event in session.stream(" Refactor auth to use JWT and update all tests" ):
376+ if event.event_type == " step_start" :
377+ print (f " [ { event.step_number} / { event.total_steps} ] Starting: { event.description} " )
378+ elif event.event_type == " step_end" :
379+ print (f " [ { event.step_number} / { event.total_steps} ] { event.status} " )
380+ elif event.event_type == " goal_progress" :
381+ print (f " Progress: { event.progress:.0% } ( { event.completed_steps} / { event.total_steps} ) " )
382+ elif event.event_type == " text_delta" :
383+ print (event.text, end = " " , flush = True )
384+ ```
385+
386+ ### Dependency Graph API
387+
388+ ``` rust
389+ use a3s_code_core :: planning :: {ExecutionPlan , Task , TaskStatus , Complexity };
390+
391+ let mut plan = ExecutionPlan :: new (" Refactor auth" , Complexity :: Complex );
392+
393+ // Independent steps — will run in parallel (Wave 1)
394+ plan . add_step (Task :: new (" s1" , " Analyze auth module" ));
395+ plan . add_step (Task :: new (" s2" , " Analyze database schema" ));
396+
397+ // Dependent step — waits for Wave 1 (Wave 2)
398+ plan . add_step (
399+ Task :: new (" s3" , " Implement JWT" )
400+ . with_dependencies (vec! [" s1" . to_string (), " s2" . to_string ()])
401+ );
402+
403+ // Wave 1: s1, s2 are ready
404+ assert_eq! (plan . get_ready_steps (). len (), 2 );
405+
406+ // After completing wave 1
407+ plan . mark_status (" s1" , TaskStatus :: Completed );
408+ plan . mark_status (" s2" , TaskStatus :: Completed );
409+
410+ // Wave 2: s3 is now ready
411+ assert_eq! (plan . get_ready_steps (). len (), 1 );
412+ assert_eq! (plan . get_ready_steps ()[0 ]. id, " s3" );
413+
414+ // Deadlock detection
415+ assert! (! plan . has_deadlock ());
416+ ```
417+
279418## Architecture
280419
281420```
@@ -300,8 +439,8 @@ providers {
300439│ │ │ Llm │ Security │ Memory │ File │ │ │
301440│ │ │ Planner │ │ │ History │ │ │
302441│ │ ├─────────┼──────────┼──────────┼─────────┤ │ │
303- │ │ │ Context │ Cost │ Cron │ Session │ │ │
304- │ │ │Compactor│ Tracking │Scheduler │ Store │ │ │
442+ │ │ │ Wave │ Context │ Cost │ Cron │ │ │
443+ │ │ │Scheduler│ Compactor │ Tracking │Scheduler│ │ │
305444│ │ └─────────┴──────────┴──────────┴─────────┘ │ │
306445│ └──────────────────────────────────────────────┘ │
307446└──────────────────────────────────────────────────────┘
@@ -377,7 +516,7 @@ code/
377516│ ├── hooks/ # HookEngine (8 lifecycle events)
378517│ ├── security/ # Sanitizer, taint tracking, injection detection, audit
379518│ ├── memory.rs # Episodic, semantic, procedural, working memory
380- │ ├── planning/ # LlmPlanner, execution plans, goal tracking
519+ │ ├── planning/ # LlmPlanner, execution plans, wave scheduler, goal tracking
381520│ ├── context.rs # Context compaction
382521│ ├── context_store/ # Persistent context store (feature-gated)
383522│ ├── mcp/ # Model Context Protocol integration
0 commit comments