Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ AgentSpawn accepts exactly these `agent_type` values:

Track every agent id and background task id. Use `AgentWait` to collect results and `AgentSendInput` to route concrete follow-up instructions.

Use `AgentList` to inspect the latest status of your direct child agents. Use `AgentDelete` only when one or more direct children and their entire descendant subtrees are no longer needed; deletion is permanent and removes their sessions and pending results.

Use `SwarmReviewer` at risk-based checkpoints, especially for shared contracts, persistence, concurrency, cancellation, permissions, security boundaries, cross-module integration, or failed, skipped, incomplete, or uncertain verification.

## Review handling
Expand All @@ -47,6 +49,7 @@ Use `SwarmReviewer` at risk-based checkpoints, especially for shared contracts,
- If a review reports `needs_changes`, route each actionable finding to the responsible Worker.
- Request another review only when the fixes materially change the reviewed contract or remaining risk warrants it.
- Interrupt an agent only when its work is obsolete, unsafe, or irrecoverably blocked; set cascade deliberately when descendants should also stop.
- Use interruption when work should stop but the agent and session should remain available; use deletion only for permanent subtree removal.

# Constraints

Expand Down
39 changes: 39 additions & 0 deletions src/crates/assembly/agent-content/prompts/agents/ultra_mode.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ AgentSpawn accepts exactly these `agent_type` values:

Track every returned agent id and background task id. Use `AgentWait` to collect results before declaring a package complete.

Use `AgentList` to inspect the latest status of your direct child agents. Use `AgentDelete` only when one or more direct children and their entire descendant subtrees are no longer needed; deletion is permanent and removes their sessions and pending results.

## Review checkpoints

Use `SwarmReviewer` at risk-based checkpoints. Review work affecting shared contracts, persistence, concurrency, cancellation, permissions, security boundaries, cross-module integration, or critical prerequisites. Also review work with failed, skipped, incomplete, or uncertain verification.
Expand All @@ -65,6 +67,7 @@ Give each Reviewer the exact change set, originating Worker assignments, accepta
- If a review reports `needs_changes`, route each concrete finding to the responsible Worker with `AgentSendInput`.
- Request another review only when the fixes materially change the reviewed contract or remaining risk warrants it.
- Interrupt an agent only when its work is obsolete, unsafe, or irrecoverably blocked; set cascade deliberately when descendants should also stop.
- Use interruption when work should stop but the agent and session should remain available; use deletion only for permanent subtree removal.

# Decisions

Expand All @@ -73,3 +76,39 @@ Ask the user a focused question through `AskUserQuestion` when a missing decisio
# Completion

Confirm that all required packages reached a terminal result. Reconcile Reviewer findings, identify unresolved risks, and answer the user directly with the completed outcome.

# Tone and style
- Avoid emojis unless the user explicitly requests them.
- Keep responses concise. Use Github-flavored markdown when it improves readability.
- Communicate with the user in normal response text; use tools to perform work, not to narrate.


# File References
IMPORTANT: Whenever you mention a file path in normal prose that the user might want to open, make it a clickable markdown link: [text](url).

**Link URL path**:
- For files inside the workspace, use the workspace-relative path: [filename.ts](src/filename.ts)
- For files outside the workspace, use the absolute path as the URL: [settings.json](/external/project/settings.json)

**Line targets**:
- For a specific line, append `#L<line>` to URL: [filename.ts:42](src/filename.ts#L42)
- For a line range, append `#L<start>-L<end>`: [filename.ts:42-51](src/filename.ts#L42-L51)

**Link text and formatting**:
- Link text should be the bare filename, optionally with line numbers; do not include directory prefixes.
- Do not output bare paths as plain text in normal prose. Raw paths are appropriate inside commands, code/config snippets, or when the user explicitly asks for a copyable path.
- Do not wrap link text or the whole markdown link in backticks.

<good-examples>
- Source file: [filename.ts](src/filename.ts)
- Specific line: [filename.ts:42](src/filename.ts#L42)
- External file line: [settings.json:12](/external/project/settings.json#L12)
- Generated report: [report.md](deep-research/report.md)
</good-examples>
<bad-examples>
- Bare path: src/filename.ts
- Backticks in link text: [`filename.ts:42`](src/filename.ts#L42)
- Whole link wrapped in backticks: `[report.md](deep-research/report.md)`
- Full path in link text: [src/filename.ts](src/filename.ts)
- Absolute path as plain text: /external/project/deep-research/report.md
</bad-examples>
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ impl UltraMode {
"AgentSpawn",
"AgentSendInput",
"AgentInterrupt",
"AgentList",
"AgentDelete",
"AgentWait",
"Read",
"Grep",
Expand All @@ -27,6 +29,7 @@ impl UltraMode {
"ExecCommand",
"WriteStdin",
"ExecControl",
"ListModels",
]
.into_iter()
.map(str::to_string)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ impl Agent for SwarmPlannerAgent {
"AgentSpawn",
"AgentSendInput",
"AgentInterrupt",
"AgentList",
"AgentDelete",
"AgentWait",
"Read",
"Grep",
Expand Down
25 changes: 25 additions & 0 deletions src/crates/assembly/core/src/agentic/agents/registry/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,31 @@ fn every_builtin_mode_with_control_hub_can_also_schedule_with_cron() {
}
}

#[test]
fn agent_list_and_delete_are_exposed_only_to_swarm_planners() {
for spec in builtin_agent_specs() {
let agent = (spec.factory)();
let has_list = agent.default_tools().iter().any(|tool| tool == "AgentList");
let has_delete = agent
.default_tools()
.iter()
.any(|tool| tool == "AgentDelete");
let should_have_controls = matches!(agent.id(), "Ultra" | "SwarmPlanner");
assert_eq!(
has_list,
should_have_controls,
"unexpected AgentList exposure for {}",
agent.id()
);
assert_eq!(
has_delete,
should_have_controls,
"unexpected AgentDelete exposure for {}",
agent.id()
);
}
}

#[test]
fn non_deep_review_builtin_subagents_default_to_primary() {
for agent_type in [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::coordination_store::{
BackgroundTaskRecord, BackgroundTaskRegistration, BackgroundTaskStatus, CoordinationStore,
RegisteredBackgroundTask,
DirectChildAgentRecord, RegisteredBackgroundTask,
};
use super::coordinator::{SubagentResult, SubagentResultStatus};
use crate::agentic::session::SessionManager;
Expand Down Expand Up @@ -61,15 +61,6 @@ pub(crate) enum BackgroundSubagentWaitMode {
All,
}

impl BackgroundSubagentWaitMode {
pub(crate) fn as_str(self) -> &'static str {
match self {
Self::Any => "any",
Self::All => "all",
}
}
}

#[derive(Debug, Clone)]
pub(crate) struct BackgroundSubagentWaitResult {
pub status: BackgroundSubagentWaitStatus,
Expand Down Expand Up @@ -489,6 +480,27 @@ impl BackgroundSubagentOutcomeStore {
.await
}

pub(crate) async fn direct_child_agents(
&self,
parent_session_id: &str,
) -> BitFunResult<Vec<DirectChildAgentRecord>> {
self.reconcile_stale_running_tasks(parent_session_id)
.await?;
self.coordination_store
.direct_child_agents(parent_session_id)
.await
}

pub(crate) async fn resolve_direct_child_agent_id(
&self,
parent_session_id: &str,
agent_id: &str,
) -> BitFunResult<String> {
self.coordination_store
.resolve_direct_child_agent_id(parent_session_id, agent_id)
.await
}

pub(crate) async fn reserve_swarm_child(
&self,
parent_session_id: &str,
Expand Down Expand Up @@ -532,6 +544,15 @@ impl BackgroundSubagentOutcomeStore {
.await
}

pub(crate) async fn swarm_subtree_session_ids_postorder(
&self,
session_id: &str,
) -> BitFunResult<Vec<String>> {
self.coordination_store
.swarm_subtree_session_ids_postorder(session_id)
.await
}

pub(crate) async fn delete_session_references(&self, session_id: &str) -> BitFunResult<()> {
let deleted_task_pks = self
.coordination_store
Expand Down
Loading