Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
a688a16
feat: add agent tree feed view
austinywang May 12, 2026
0e51436
Merge remote-tracking branch 'origin/main' into issue-3932-subagent-t…
austinywang May 12, 2026
79f2728
Fix feed empty state view return
austinywang May 12, 2026
3ce3f8a
Fix feed tree keyboard snapshot scope
austinywang May 12, 2026
99ccd86
Address agent tree review feedback
austinywang May 12, 2026
e5a2392
Fix agent tree spawn reconciliation
austinywang May 12, 2026
c9d75a5
Merge remote-tracking branch 'origin/main' into issue-3932-subagent-t…
austinywang May 12, 2026
1b889d7
fix: defer feed agent graph rebuilds
austinywang May 12, 2026
7dc1492
fix: honor parent source in agent graph
austinywang May 12, 2026
6a70b79
Merge remote-tracking branch 'origin/main' into issue-3932-subagent-t…
austinywang May 12, 2026
09c9115
test: cover ambiguous agent spawn pruning
austinywang May 12, 2026
dab7ff3
fix: keep ambiguous agent spawns
austinywang May 12, 2026
b3c5d9d
fix: coalesce agent graph rebuilds
austinywang May 12, 2026
bca988f
test: cover parent child spawn pruning
austinywang May 12, 2026
f33a6f7
fix: centralize agent child linking
austinywang May 12, 2026
ee38cba
fix: address agent graph review feedback
austinywang May 12, 2026
c830c16
fix: address agent tree review findings
austinywang May 12, 2026
a0390a7
fix: simplify agent tree view type checking
austinywang May 12, 2026
a52e309
fix: share agent spawn tool detection
austinywang May 12, 2026
b2ca36e
fix: avoid invalidating inactive agent tree builds
austinywang May 12, 2026
812784f
fix: address agent tree follow-up reviews
austinywang May 12, 2026
4d61c04
fix: polish agent tree pending state
austinywang May 12, 2026
c95c6f3
fix: reduce agent tree rebuild work
austinywang May 12, 2026
36076bc
fix: preserve agent tree session metadata
austinywang May 13, 2026
5ac8709
fix: tighten agent tree ownership
austinywang May 13, 2026
d5c1f71
fix: use app debug log for feed focus bridge
austinywang May 13, 2026
adcfb97
merge: sync main into agent tree branch
austinywang May 13, 2026
5dedfe8
fix: let agent tree page older feed history
austinywang May 13, 2026
e20963b
fix: remove duplicate agent tree empty state
austinywang May 13, 2026
e0d2bb3
fix: avoid duplicate agent metadata text
austinywang May 13, 2026
644f63e
Merge remote-tracking branch 'origin/main' into issue-3932-subagent-t…
austinywang May 14, 2026
a86f82e
fix: isolate feed store observation snapshot
austinywang May 15, 2026
0e21156
fix: align agent spawn resolution scoring
austinywang May 17, 2026
f4ec1f1
fix: address agent tree review feedback
austinywang May 17, 2026
bb23aad
fix: make feed focus registration idempotent
austinywang May 17, 2026
d08f1cf
Merge remote-tracking branch 'origin/main' into issue-3932-subagent-t…
austinywang May 17, 2026
a2cd526
fix: reuse feed focus registration path
austinywang May 17, 2026
6baf833
test: cover sanitized agent prompt metadata
austinywang May 18, 2026
0184e89
fix: preserve agent prompt metadata
austinywang May 18, 2026
200adc5
test: cover subagent model priority
austinywang May 18, 2026
e5ad63d
fix: prefer spawn input model metadata
austinywang May 18, 2026
38da68b
fix: keep spawn metadata off parent agent nodes
austinywang May 18, 2026
1f9c00e
Merge remote-tracking branch 'origin/main' into issue-3932-subagent-t…
austinywang May 18, 2026
b73d471
Merge remote-tracking branch 'origin/main' into issue-3932-subagent-t…
austinywang May 18, 2026
50a2d5e
fix: keep parent models out of child nodes
austinywang May 18, 2026
5f9e48d
fix: scope child spawn metadata
austinywang May 18, 2026
65b125b
Merge branch 'main' of https://github.com/manaflow-ai/cmux into issue…
austinywang May 19, 2026
e6042cf
merge: keep feature submodule pointers
austinywang May 19, 2026
57e8a30
fix: align bonsplit pointer with merged workspace API
austinywang May 19, 2026
e7e0678
fix: clear stale agent tree selection
austinywang May 19, 2026
63bdc56
merge: resolve conflicts with main
austinywang May 19, 2026
2c0630b
merge: resolve conflicts with main
austinywang May 22, 2026
6eb5d9f
Merge remote-tracking branch 'origin/main' into issue-3932-subagent-t…
austinywang May 22, 2026
ab8f6a6
fix: retry feed focus registration when coordinator is unavailable
austinywang May 22, 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
78 changes: 75 additions & 3 deletions CLI/cmux.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Foundation
import CMUXAgentLaunch
import CMUXWorkstream
import CoreFoundation
import CryptoKit
import Darwin
Expand Down Expand Up @@ -19336,7 +19337,15 @@ struct CMUXCLI {
"last_assistant_message", "lastAssistantMessage", "assistantPreamble", "assistant_preamble",
"event", "event_name", "hook_event_name", "hookEventName", "type", "kind", "notification_type", "matcher", "reason", "source", "terminationReason",
"title", "summary", "message", "body", "text", "prompt", "error", "codex_error_info", "codexErrorInfo",
"additional_details", "additionalDetails", "description",
"additional_details", "additionalDetails", "description", "model", "subagent_model", "subagentModel",
"parent_workstream_id", "parentWorkstreamId", "parent_workstream", "parentWorkstream",
"parent_session_id", "parentSessionId", "parentSessionID",
"child_workstream_id", "childWorkstreamId", "child_session_id", "childSessionId", "childSessionID",
"subagent_workstream_id", "subagentWorkstreamId", "subagent_session_id", "subagentSessionId",
"parent_source", "parentSource", "parent_agent_source", "parentAgentSource",
"child_source", "childSource", "subagent_source", "subagentSource",
"subagent_type", "subagentType", "agent_type", "agentType",
"task_description", "taskDescription", "name",
] {
if let value = compactClaudeHookValue(object[key], key: key) {
compact[key] = value
Expand All @@ -19345,7 +19354,18 @@ struct CMUXCLI {

if let toolInput = object["tool_input"] as? [String: Any] {
var compactToolInput: [String: Any] = [:]
for key in ["file_path", "command", "pattern", "description", "query", "plan", "planFilePath"] {
for key in [
"file_path", "command", "pattern", "description", "query", "plan", "planFilePath",
"prompt", "message", "model", "subagent_model", "subagentModel",
"parent_workstream_id", "parentWorkstreamId", "parent_workstream", "parentWorkstream",
"parent_session_id", "parentSessionId", "parentSessionID",
"child_workstream_id", "childWorkstreamId", "child_session_id", "childSessionId", "childSessionID",
"subagent_workstream_id", "subagentWorkstreamId", "subagent_session_id", "subagentSessionId",
"parent_source", "parentSource", "parent_agent_source", "parentAgentSource",
"child_source", "childSource", "subagent_source", "subagentSource",
"subagent_type", "subagentType", "agent_type", "agentType",
"task_description", "taskDescription", "title", "name",
] {
if let value = compactClaudeHookToolInputValue(toolInput[key], key: key) {
compactToolInput[key] = value
}
Expand Down Expand Up @@ -19412,7 +19432,7 @@ struct CMUXCLI {

private func claudeHookCompactFieldLimit(for key: String) -> Int {
switch key {
case "tool_name", "toolName", "turn_id", "turnId", "conversation_id", "conversationId", "event", "event_name", "hook_event_name", "hookEventName", "type", "kind", "notification_type", "matcher", "reason", "source":
case "tool_name", "toolName", "turn_id", "turnId", "conversation_id", "conversationId", "event", "event_name", "hook_event_name", "hookEventName", "type", "kind", "notification_type", "matcher", "reason", "source", "model", "subagent_model", "subagentModel", "subagent_type", "subagentType", "agent_type", "agentType":
return 80
case "transcript_path", "transcriptPath":
return 240
Expand Down Expand Up @@ -25211,6 +25231,7 @@ export default function cmuxPiSessionExtension(pi: ExtensionAPI) {
) {
event["context"] = context
}
copyAgentGraphMetadata(to: &event, rawObject: parsedInput.object, toolName: toolName)
enrichUserPromptSubmitFeedEvent(
&event,
hookEventName: hookEventName,
Expand Down Expand Up @@ -25302,6 +25323,56 @@ export default function cmuxPiSessionExtension(pi: ExtensionAPI) {
}
}

private func copyAgentGraphMetadata(
to event: inout [String: Any],
rawObject: [String: Any]?,
toolName: String?
) {
guard let rawObject else { return }
let graphKeys = [
"parent_workstream_id", "parentWorkstreamId", "parent_workstream", "parentWorkstream",
"parent_session_id", "parentSessionId", "parentSessionID",
"child_workstream_id", "childWorkstreamId", "subagent_workstream_id", "subagentWorkstreamId",
"child_session_id", "childSessionId", "childSessionID", "subagent_session_id", "subagentSessionId",
"parent_source", "parentSource", "parent_agent_source", "parentAgentSource",
"child_source", "childSource", "subagent_source", "subagentSource",
"subagent_type", "subagentType", "agent_type", "agentType",
"model", "subagent_model", "subagentModel",
"task_description", "taskDescription",
]
let genericKeys = ["description", "title", "name"]

func copyValue(_ raw: Any?, key: String) {
guard event[key] == nil else { return }
if let value = raw as? String {
let trimmed = value.trimmingCharacters(in: .whitespacesAndNewlines)
if !trimmed.isEmpty {
event[key] = trimmed
}
} else if let value = raw as? NSNumber {
event[key] = value.stringValue
}
}

for key in graphKeys {
copyValue(rawObject[key], key: key)
}

let isSpawnTool = toolName.map(WorkstreamAgentSpawnTool.isSpawnToolName) ?? false
if isSpawnTool {
for key in genericKeys {
copyValue(rawObject[key], key: key)
}
}

guard isSpawnTool,
let toolInput = feedToolInputDictionary(rawObject["tool_input"])
else { return }
for key in graphKeys + genericKeys + ["prompt", "message"] {
copyValue(toolInput[key], key: key)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CLI copies child model to parent extra fields

Medium Severity

When a spawn tool's raw hook payload lacks a top-level model but the tool input contains one, copyAgentGraphMetadata copies the child's model from tool input into the event dict's top-level model key (since event["model"] is still nil after the first rawObject loop). On the receiver, this becomes part of extraFieldsJSON. The graph builder's sessionModel for spawn tools reads extra["model"], so it incorrectly assigns the child's model to the parent session. The tool input model is already available separately via WorkstreamPayload.toolUse's toolInputJSON, making this copy both redundant and harmful.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit ab8f6a6. Configure here.

}

private func feedPromptText(from object: [String: Any]?) -> String? {
guard let object else { return nil }
if let direct = firstString(in: object, keys: ["prompt", "text", "message", "body"]) {
Expand Down Expand Up @@ -27159,6 +27230,7 @@ export default function cmuxPiSessionExtension(pi: ExtensionAPI) {
) {
eventDict["context"] = context
}
copyAgentGraphMetadata(to: &eventDict, rawObject: stdinObj, toolName: toolName)
enrichUserPromptSubmitFeedEvent(
&eventDict,
hookEventName: hookEventName,
Expand Down
Loading
Loading