Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
c46a88a
feat(core): add Agent Team foundation (experimental, flag-gated)
tanzhenxin Jun 8, 2026
8c8bf2e
feat(core): add send_message team routing (experimental)
tanzhenxin Jun 8, 2026
cd573b9
feat(core): let the Agent tool spawn named teammates (experimental)
tanzhenxin Jun 8, 2026
336f8c8
feat(cli): render team_result/task_list tool displays (experimental)
tanzhenxin Jun 8, 2026
9e11fcc
feat(core): treat teammate messages as top-level turns (experimental)
tanzhenxin Jun 8, 2026
ac76dbb
feat(cli): drive teammates from the headless run loop (experimental)
tanzhenxin Jun 8, 2026
0954dd7
feat(cli): drive teammates from the interactive turn loop (experimental)
tanzhenxin Jun 8, 2026
3d16cd1
fix(core): declare proper-lockfile dependency for the team subsystem
tanzhenxin Jun 8, 2026
50e95be
fix(team): address review findings on the agent-team subsystem (exper…
tanzhenxin Jun 8, 2026
47649ad
fix(core): close ownership TOCTOU and lock-ordering hazard in agent-t…
tanzhenxin Jun 9, 2026
0f8d7ea
fix(core): harden agent-team message handling and auto-claim
tanzhenxin Jun 9, 2026
c8e5c51
fix(core): require approval for agent-team task_create/task_update
tanzhenxin Jun 9, 2026
818a90e
docs(core): correct completeOnIdle JSDoc for team teammates
tanzhenxin Jun 9, 2026
ba363e8
fix(core): harden agent-team leader callback and task envelope
tanzhenxin Jun 9, 2026
3d9de93
fix(core): make deleteTask edge cleanup resilient to partial failure
tanzhenxin Jun 9, 2026
78f0013
fix(cli): mount useTeamInProcess so teammate tabs render
tanzhenxin Jun 9, 2026
9219502
test(terminal-capture): add agent-team feature demo + capture fixes
tanzhenxin Jun 9, 2026
50a868e
fix(core): serialize in-process mailbox writers to fix Windows lock f…
tanzhenxin Jun 9, 2026
8e3be23
feat(team): render teammate reports as a compact notification line
tanzhenxin Jun 9, 2026
61e8a94
fix(terminal-capture): widen agent-team-demo Phase C budget so the GI…
tanzhenxin Jun 9, 2026
5a84641
fix(core): separate task-content nonce; forward send_message summary
tanzhenxin Jun 9, 2026
2ddb5a2
fix(core,cli): harden agent-team messaging per review round 4
tanzhenxin Jun 10, 2026
5a0d5e9
Merge remote-tracking branch 'origin/main' into feat/agent-team-v2
tanzhenxin Jun 10, 2026
41f873d
fix(core): don't let a failed dependent unblock abort task completion
tanzhenxin Jun 10, 2026
a5d2532
Merge remote-tracking branch 'origin/main' into feat/agent-team-v2
tanzhenxin Jun 10, 2026
583baca
fix(core): quarantine corrupt teammate inboxes; skip task scan when n…
tanzhenxin Jun 10, 2026
74cc344
fix(core): re-check task ownership under the lock when unassigning a …
tanzhenxin Jun 10, 2026
b5d6018
fix(core): harden team task file layer against partial writes and tra…
tanzhenxin Jun 10, 2026
58929fb
fix(core): drain messages enqueued during the IDLE transition; settle…
tanzhenxin Jun 10, 2026
9337cae
test(core): align FakeAgent queue and abort semantics with AgentInter…
tanzhenxin Jun 10, 2026
25ec5af
fix(core): surface spawn failures, handle shutdown_rejected, envelope…
tanzhenxin Jun 10, 2026
aacb825
fix(core): exclude workflow tool from teammates
tanzhenxin Jun 10, 2026
afcca7e
fix(core): make task tools visible to permission review; reject depen…
tanzhenxin Jun 10, 2026
da713de
fix(core): reclaim stale teams on team_create instead of wedging the …
tanzhenxin Jun 10, 2026
a1da6c4
fix(cli): pass teammate envelopes straight to the model, skipping she…
tanzhenxin Jun 10, 2026
00f44c8
fix(core): exclude Teammate from UserPromptSubmit hooks and record it…
tanzhenxin Jun 10, 2026
f3d32a9
fix(cli): stop teammate-approval rejections from escaping as unhandle…
tanzhenxin Jun 10, 2026
2e96d18
Merge remote-tracking branch 'origin/main' into feat/agent-team-v2
tanzhenxin Jun 10, 2026
96410d1
test(core): add getSessionId to team-lifecycle mock config
tanzhenxin Jun 10, 2026
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
Prev Previous commit
Next Next commit
feat(cli): drive teammates from the interactive turn loop (experimental)
Final Stage 5 piece of the Agent Team re-port. Wires the interactive (TUI)
useGeminiStream hook to the active team: it subscribes to TeamManager,
queues teammate messages, and drains them into the conversation as
SendMessageType.Teammate turns when idle, guarded against racing the
notification drain. Treats teammate turns like user/cron for image-format
checks and new-prompt stats. Ported onto main's rewritten hook.
  • Loading branch information
tanzhenxin committed Jun 8, 2026
commit 0954dd72268a237179985063c346242481edbed6
2 changes: 2 additions & 0 deletions packages/cli/src/ui/hooks/useGeminiStream.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,8 @@ describe('useGeminiStream', () => {
getCheckpointingEnabled: vi.fn(() => false),
getGeminiClient: mockGetGeminiClient,
getApprovalMode: () => ApprovalMode.DEFAULT,
getTeamManager: vi.fn(() => null),
onTeamManagerChange: vi.fn(),
getUsageStatisticsEnabled: () => true,
getDebugMode: () => false,
addHistory: vi.fn(),
Expand Down
71 changes: 69 additions & 2 deletions packages/cli/src/ui/hooks/useGeminiStream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1766,7 +1766,8 @@ export const useGeminiStream = (
// Check image format support for non-continuations
if (
submitType === SendMessageType.UserQuery ||
submitType === SendMessageType.Cron
submitType === SendMessageType.Cron ||
submitType === SendMessageType.Teammate
) {
const formatCheck = checkImageFormatsSupport(queryToSend);
if (formatCheck.hasUnsupportedFormats) {
Expand All @@ -1786,7 +1787,8 @@ export const useGeminiStream = (

if (
submitType === SendMessageType.UserQuery ||
submitType === SendMessageType.Cron
submitType === SendMessageType.Cron ||
submitType === SendMessageType.Teammate
) {
// trigger new prompt event for session stats in CLI
startNewPrompt();
Expand Down Expand Up @@ -2605,9 +2607,13 @@ export const useGeminiStream = (
}, [config]);

// When idle, drain the unified queue one item at a time.
// Skip when another submission is in flight (e.g. the teammate
// drain effect won this render) — the queue stays intact and
// the effect will re-fire when streamingState returns to Idle.
useEffect(() => {
if (
streamingState === StreamingState.Idle &&
!isSubmittingQueryRef.current &&
notificationQueueRef.current.length > 0
) {
const item = notificationQueueRef.current.shift()!;
Expand All @@ -2621,6 +2627,67 @@ export const useGeminiStream = (
}
}, [streamingState, submitQuery, notificationTrigger, addItem]);

// ─── Teammate message integration ─────────────────────────
const teammateQueueRef = useRef<string[]>([]);
const [teammateTrigger, setTeammateTrigger] = useState(0);

// Subscribe to TeamManager's leader message callback.
// Track the bound manager so we can detach the callback
// before a new manager replaces it (and on unmount) —
// otherwise a stale TeamManager could keep pushing into
// the active queue ref after team recreation/remount.
useEffect(() => {
let boundManager: import('@qwen-code/qwen-code-core').TeamManager | null =
null;
const handleManagerChange = (
manager: import('@qwen-code/qwen-code-core').TeamManager | null,
) => {
if (boundManager && boundManager !== manager) {
boundManager.setLeaderMessageCallback(null);
}
boundManager = manager;
if (manager) {
manager.setLeaderMessageCallback((formatted: string) => {
teammateQueueRef.current.push(formatted);
setTeammateTrigger((n) => n + 1);
});
}
};

config.onTeamManagerChange(handleManagerChange);

// Catch manager that was set before this effect ran
const current = config.getTeamManager();
if (current) {
handleManagerChange(current);
}

return () => {
config.onTeamManagerChange(null, handleManagerChange);
if (boundManager) {
boundManager.setLeaderMessageCallback(null);
boundManager = null;
}
};
}, [config]);

// When idle, drain teammate messages one batch at a time.
// Skip when another submission is in flight (e.g. the
// notification effect won this render and called submitQuery
// synchronously, flipping isSubmittingQueryRef). Without this
// guard the splice would drain the queue and submitQuery
// would early-return, permanently losing those messages.
useEffect(() => {
if (
streamingState === StreamingState.Idle &&
!isSubmittingQueryRef.current &&
teammateQueueRef.current.length > 0
) {
const batch = teammateQueueRef.current.splice(0);
submitQuery(batch.join('\n\n'), SendMessageType.Teammate);
}
}, [streamingState, submitQuery, teammateTrigger]);

return {
streamingState,
submitQuery,
Expand Down
Loading