Land staged session/MCP/chrome work + sidebar clear-J fix
This batches the in-flight [Unreleased] block from CHANGELOG.md into a single commit. Highlights: - Real MCP protocol layer (initialize / tools/list / tools/call) so vendor MCP clients can complete the handshake against the per-PID socket. Legacy direct-dispatch preserved for the harness. - New mcp_injection kinds — cli_override for codex, config_env for opencode — joining the existing env-var and config_file paths so patterm can slot into more agents without touching their real config or auth. - Ctrl+A/D and Ctrl+W/S focus navigation across tabs and intra-tab process lists, recognised in legacy / kitty CSI u / xterm modifyOtherKeys encodings. - Palette macros (sw / k / sp ) and reordering so open sessions surface above spawn-new entries. - Two-row tab bar, sidebar/tabbar/status chrome cache, viewport-wipe on agent spawn, CR-terminated orchestrator injections, and split- Enter PTY writes so paste-detecting TUIs see Enter as a key event. Also fixes the bug logged in TODO: claude's Ctrl+O tool-call expansion emits CSI 0 J, which the viewport renderer was forwarding verbatim — wiping the sidebar to the right of the cursor and leaving the chrome cache convinced nothing had changed. CSI 0 J and CSI 1 J are now translated into per-row ECH sequences clamped to the viewport, same as CSI 2 J and CSI K already were. Agent guides (CLAUDE.md / AGENTS.md) now spell out the TODO->CHANGELOG workflow so completed items land in the changelog rather than as ticked entries left behind in TODO.
This commit is contained in:
@@ -523,7 +523,12 @@ func encodeInput(args mcp.SendInputArgs) ([]byte, error) {
|
||||
}
|
||||
out := []byte(args.Text)
|
||||
if submit {
|
||||
out = append(out, '\n')
|
||||
// CR (`\r`) is what every terminal emits for Enter in raw
|
||||
// mode, and what TUI agents (claude/codex/…) bind to
|
||||
// "submit". Sending `\n` here used to land as a literal
|
||||
// newline inside their textareas, leaving the message
|
||||
// composed but not sent.
|
||||
out = append(out, '\r')
|
||||
}
|
||||
return out, nil
|
||||
case "paste":
|
||||
@@ -635,13 +640,13 @@ func classifySendMessage(caller, target *Child, callerID, message string) (strin
|
||||
return "", mcp.Errorf("not_related", "send_message: cannot send to self")
|
||||
}
|
||||
if caller != nil && target.ParentID == caller.ID {
|
||||
return "[orchestrator] " + message + "\n", nil
|
||||
return "[orchestrator] " + message + "\r", nil
|
||||
}
|
||||
if caller != nil && caller.ParentID == target.ID {
|
||||
return fmt.Sprintf("[sub-agent:%s] %s\n", caller.DisplayName(), message), nil
|
||||
return fmt.Sprintf("[sub-agent:%s] %s\r", caller.DisplayName(), message), nil
|
||||
}
|
||||
if caller == nil && target.ParentID == "" {
|
||||
return "[orchestrator] " + message + "\n", nil
|
||||
return "[orchestrator] " + message + "\r", nil
|
||||
}
|
||||
return "", mcp.Errorf("not_related", "send_message: %q is neither parent nor child of caller (siblings must route through the parent in v1)", target.ID)
|
||||
}
|
||||
@@ -670,7 +675,7 @@ func (h *toolHost) TimerWait(callerID string, seconds float64, label string) (st
|
||||
if !caller.IsLive() {
|
||||
return
|
||||
}
|
||||
line := fmt.Sprintf("[system] Your timer [%s] has completed.\n", label)
|
||||
line := fmt.Sprintf("[system] Your timer [%s] has completed.\r", label)
|
||||
_ = caller.InjectAsOrchestrator([]byte(line))
|
||||
}()
|
||||
return id, nil
|
||||
|
||||
Reference in New Issue
Block a user