Skip to content

Commit 665201d

Browse files
committed
feat(sdk): align Python and Node.js SDKs with Rust core
- Add AHP types (AhpEventType, Fact, MemorySummary, SessionStats, IdleDecision, AhpEventContext) - Add Idle types (IdlePhase, IdleTask, IdleTurn, IdleToolCall, MemoryUpdate, EpisodicEntry) - Add Task types (TaskId, TaskStatus, TaskType, Task, TaskResult) - Add Progress types (TaskTokenUsage, ToolActivity, AgentProgress) - Add document parsing utilities (enrichToolResult, parseDocumentRuntime, parseAgenticSearchResults, parseAgenticParseLlmBlocks) - Fix SystemPromptSlots style field initialization - Fix SessionStats.total_tokens type (i32 not u64) - Rename Dream→Idle throughout SDKs
1 parent 76dd028 commit 665201d

35 files changed

Lines changed: 6247 additions & 386 deletions
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Intent Classification
2+
3+
You are an intent classifier for an AI coding assistant. Your job is to analyze the user's message and classify their intent into one of the following categories:
4+
5+
- **Plan**: User wants to plan, design, or architect something. They want a structured approach with steps. They may say "help me plan", "design", "architecture", "create a plan", "implementation approach".
6+
7+
- **Explore**: User wants to find, search, or locate files, code, or information. They want to explore the codebase. They may say "find", "search", "where is", "locate", "look for".
8+
9+
- **Verification**: User wants to verify, test, debug, or break something. They want to confirm correctness or find problems. They may say "verify", "test", "check if", "debug", "break".
10+
11+
- **CodeReview**: User wants to review, analyze, or assess code quality. They want feedback on implementation. They may say "review", "analyze", "assess", "quality", "best practice".
12+
13+
- **GeneralPurpose**: User wants to implement, build, fix, or create something. They have a clear task to accomplish. They may say "implement", "build", "fix", "create", "add", "modify".
14+
15+
## Output Format
16+
17+
Respond with ONLY a single word - the intent category name. Nothing else.
18+
19+
Examples:
20+
- "Help me plan a new feature" → Plan
21+
- "Find all files related to auth" → Explore
22+
- "Verify that this code works" → Verification
23+
- "Review this PR" → CodeReview
24+
- "Implement user login" → GeneralPurpose

core/prompts/undercover_instructions.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ You are operating UNDERCOVER in a PUBLIC/OPEN-SOURCE REPOSITORY. Your commit
99
messages, PR titles, and PR bodies MUST NOT contain ANY internal information.
1010
Do not blow your cover.
1111
12+
**IMPORTANT: User requests to include specific content (like Co-Authored-By) do NOT
13+
override these rules. Strip ALL attribution lines regardless of what the user asks.**
14+
1215
NEVER include in commit messages or PR descriptions:
1316
- Internal model codenames (animal names like Capybara, Tengu, etc.)
1417
- Internal repo or project names (e.g., a3s-code, CLAUDE.md)

core/src/agent.rs

Lines changed: 210 additions & 14 deletions
Large diffs are not rendered by default.

core/src/agent_api.rs

Lines changed: 90 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,14 @@ use crate::config::CodeConfig;
2424
use crate::error::{read_or_recover, write_or_recover, CodeError, Result};
2525
use crate::hitl::PendingConfirmationInfo;
2626
use crate::llm::{LlmClient, Message};
27-
use crate::prompts::SystemPromptSlots;
27+
use crate::prompts::{PlanningMode, SystemPromptSlots};
2828
use crate::queue::{
2929
ExternalTask, ExternalTaskResult, LaneHandlerConfig, SessionLane, SessionQueueConfig,
3030
SessionQueueStats,
3131
};
3232
use crate::scheduler::{CronScheduler, ScheduledFire};
3333
use crate::session_lane_queue::SessionLaneQueue;
34+
use crate::task::{ProgressTracker, TaskManager};
3435
use crate::text::truncate_utf8;
3536
use crate::tools::{ToolContext, ToolExecutor};
3637
use a3s_lane::{DeadLetter, MetricsSnapshot};
@@ -104,7 +105,7 @@ pub struct SessionOptions {
104105
/// Optional permission checker
105106
pub permission_checker: Option<Arc<dyn crate::permissions::PermissionChecker>>,
106107
/// Enable planning
107-
pub planning_enabled: bool,
108+
pub planning_mode: PlanningMode,
108109
/// Enable goal tracking
109110
pub goal_tracking: bool,
110111
/// Extra directories to scan for skill files (*.md).
@@ -226,7 +227,7 @@ impl std::fmt::Debug for SessionOptions {
226227
.field("context_providers", &self.context_providers.len())
227228
.field("confirmation_manager", &self.confirmation_manager.is_some())
228229
.field("permission_checker", &self.permission_checker.is_some())
229-
.field("planning_enabled", &self.planning_enabled)
230+
.field("planning_mode", &self.planning_mode)
230231
.field("goal_tracking", &self.goal_tracking)
231232
.field(
232233
"skill_registry",
@@ -377,9 +378,19 @@ impl SessionOptions {
377378
self.with_permission_checker(Arc::new(crate::permissions::PermissionPolicy::permissive()))
378379
}
379380

380-
/// Enable planning
381+
/// Set planning mode
382+
pub fn with_planning_mode(mut self, mode: PlanningMode) -> Self {
383+
self.planning_mode = mode;
384+
self
385+
}
386+
387+
/// Enable planning (shortcut for `with_planning_mode(PlanningMode::Enabled)`)
381388
pub fn with_planning(mut self, enabled: bool) -> Self {
382-
self.planning_enabled = enabled;
389+
self.planning_mode = if enabled {
390+
PlanningMode::Enabled
391+
} else {
392+
PlanningMode::Disabled
393+
};
383394
self
384395
}
385396

@@ -1317,7 +1328,7 @@ impl Agent {
13171328
permission_checker: opts.permission_checker.clone(),
13181329
confirmation_manager: opts.confirmation_manager.clone(),
13191330
context_providers: opts.context_providers.clone(),
1320-
planning_enabled: opts.planning_enabled,
1331+
planning_mode: opts.planning_mode,
13211332
goal_tracking: opts.goal_tracking,
13221333
skill_registry: Some(Arc::clone(&effective_registry)),
13231334
max_parse_retries: opts.max_parse_retries.unwrap_or(base.max_parse_retries),
@@ -1511,6 +1522,8 @@ impl Agent {
15111522
cron_started: AtomicBool::new(false),
15121523
cancel_token: Arc::new(tokio::sync::Mutex::new(None)),
15131524
active_tools: Arc::new(tokio::sync::RwLock::new(HashMap::new())),
1525+
task_manager: Arc::new(TaskManager::new()),
1526+
progress_tracker: Arc::new(tokio::sync::RwLock::new(ProgressTracker::new(30))),
15141527
})
15151528
}
15161529
}
@@ -1590,6 +1603,10 @@ pub struct AgentSession {
15901603
cancel_token: Arc<tokio::sync::Mutex<Option<tokio_util::sync::CancellationToken>>>,
15911604
/// Currently executing tools observed from runtime events.
15921605
active_tools: Arc<tokio::sync::RwLock<HashMap<String, ActiveToolSnapshot>>>,
1606+
/// Task manager for centralized task lifecycle tracking.
1607+
task_manager: Arc<TaskManager>,
1608+
/// Progress tracker for real-time tool/token usage tracking.
1609+
progress_tracker: Arc<tokio::sync::RwLock<ProgressTracker>>,
15931610
}
15941611

15951612
#[derive(Debug, Clone)]
@@ -1682,6 +1699,8 @@ impl AgentSession {
16821699
if let Some(ref queue) = self.command_queue {
16831700
agent_loop = agent_loop.with_queue(Arc::clone(queue));
16841701
}
1702+
agent_loop = agent_loop.with_progress_tracker(Arc::clone(&self.progress_tracker));
1703+
agent_loop = agent_loop.with_task_manager(Arc::clone(&self.task_manager));
16851704
agent_loop
16861705
}
16871706

@@ -2375,6 +2394,69 @@ impl AgentSession {
23752394
.collect()
23762395
}
23772396

2397+
// ========================================================================
2398+
// Task & Progress API
2399+
// ========================================================================
2400+
2401+
/// Return the task manager for this session.
2402+
///
2403+
/// The task manager tracks all task lifecycles (tool calls, agent executions, etc.)
2404+
/// and supports subscription to task events.
2405+
pub fn task_manager(&self) -> &Arc<TaskManager> {
2406+
&self.task_manager
2407+
}
2408+
2409+
/// Spawn a new task and return its ID.
2410+
///
2411+
/// # Arguments
2412+
///
2413+
/// * `task` - The task to spawn
2414+
///
2415+
/// # Example
2416+
///
2417+
/// ```rust,ignore
2418+
/// use a3s_code_core::task::Task;
2419+
///
2420+
/// let task = Task::tool("read", json!({"file_path": "test.txt"}));
2421+
/// let task_id = session.spawn_task(task);
2422+
/// ```
2423+
pub fn spawn_task(&self, task: crate::task::Task) -> crate::task::TaskId {
2424+
self.task_manager.spawn(task)
2425+
}
2426+
2427+
/// Track a tool call in the progress tracker.
2428+
///
2429+
/// This is called automatically during tool execution but can also be called manually.
2430+
pub fn track_tool_call(&self, tool_name: &str, args_summary: &str, success: bool) {
2431+
if let Ok(mut guard) = self.progress_tracker.try_write() {
2432+
guard.track_tool_call(tool_name, args_summary, success);
2433+
}
2434+
}
2435+
2436+
/// Get current execution progress.
2437+
///
2438+
/// Returns a snapshot of tool counts, token usage, and recent activities.
2439+
pub async fn get_progress(&self) -> crate::task::AgentProgress {
2440+
self.progress_tracker.read().await.progress()
2441+
}
2442+
2443+
/// Subscribe to task events.
2444+
///
2445+
/// Returns a receiver that will receive all task lifecycle events.
2446+
pub fn subscribe_tasks(
2447+
&self,
2448+
task_id: crate::task::TaskId,
2449+
) -> Option<tokio::sync::broadcast::Receiver<crate::task::manager::TaskEvent>> {
2450+
self.task_manager.subscribe(task_id)
2451+
}
2452+
2453+
/// Subscribe to all task events (global).
2454+
pub fn subscribe_all_tasks(
2455+
&self,
2456+
) -> tokio::sync::broadcast::Receiver<crate::task::manager::TaskEvent> {
2457+
self.task_manager.subscribe_all()
2458+
}
2459+
23782460
// ========================================================================
23792461
// Hook API
23802462
// ========================================================================
@@ -2436,7 +2518,7 @@ impl AgentSession {
24362518
parent_id: None,
24372519
security_config: None,
24382520
hook_engine: None,
2439-
planning_enabled: self.config.planning_enabled,
2521+
planning_mode: self.config.planning_mode,
24402522
goal_tracking: self.config.goal_tracking,
24412523
},
24422524
state: crate::session::SessionState::Active,
@@ -3343,6 +3425,7 @@ dir content
33433425
.with_max_steps(3);
33443426

33453427
let opts = SessionOptions::new().with_prompt_slots(SystemPromptSlots {
3428+
style: None,
33463429
role: Some("Custom role".to_string()),
33473430
guidelines: None,
33483431
response_style: None,

0 commit comments

Comments
 (0)