chore(wip): checkpoint P8/C avant chantier Codex inter-agents
Sauvegarde de l'arbre de travail en cours (persistance P8, conversations C-series, write-portal frontend, médiation d'entrée) avant d'attaquer le support de la délégation inter-agents pour les profils Codex. Le round-trip inter-agent question/réponse est couvert sans tokens par les tests loopback existants (state::mcp_e2e_loopback_tests). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -27,10 +27,9 @@ import type {
|
||||
TerminalHandle,
|
||||
} from "@/ports";
|
||||
import {
|
||||
MediatedInput,
|
||||
ResumeConversationPopup,
|
||||
TerminalView,
|
||||
useAgentBusy,
|
||||
useWritePortal,
|
||||
} from "@/features/terminals";
|
||||
import { useGateways } from "@/app/di";
|
||||
import { leaves, normalizeWeights, resizeAdjacent } from "./layout";
|
||||
@ -203,10 +202,12 @@ function LeafView({ id, session, agent, conversationId, agentWasRunning, cwd, vm
|
||||
const siblingIndex = parentSplit ? (parentSplit.index === 0 ? 1 : 0) : 0;
|
||||
const { agent: agentGateway, system } = useGateways();
|
||||
|
||||
// Per-agent busy map (lot F1) feeds the mediated input strip: while the agent
|
||||
// is processing a turn, "Envoyer" is dimmed (but the enqueue stays open) and
|
||||
// "Interrompre" is active. Absent key ⇒ idle.
|
||||
const busyMap = useAgentBusy();
|
||||
// The single write-portal of this cell (ARCHITECTURE §20). It owns the human
|
||||
// line counter, the local delegation FIFO, the handshake (b→e) and the overlay
|
||||
// shown while a delegation is being injected. For a plain (agent-less) cell
|
||||
// `agent` is null: the portal stays inert (no subscription, no overlay) and is
|
||||
// simply not passed to the terminal.
|
||||
const { portal, overlay } = useWritePortal(projectId, agent ?? null);
|
||||
|
||||
// Load the project's agents for the dropdown.
|
||||
const [agents, setAgents] = useState<Agent[]>([]);
|
||||
@ -552,44 +553,66 @@ function LeafView({ id, session, agent, conversationId, agentWasRunning, cwd, vm
|
||||
)}
|
||||
</div>
|
||||
{/* Option 1 (Terminal + MCP): every cell — plain or agent — renders the
|
||||
raw xterm {@link TerminalView}. Agent cells are interactive PTYs; their
|
||||
cross-model delegation happens through MCP tools, not a chat view.
|
||||
Re-key on the agent so the right opener is captured at mount; the
|
||||
persisted session drives re-attach (never a re-spawn) when navigating.
|
||||
|
||||
Lot F2 (cadrage §4.2): in an **agent** cell the terminal is output-only
|
||||
for the human (`agentMode`) and the {@link MediatedInput} strip is
|
||||
mounted **under** it — keystrokes route through IdeA, not the PTY. A
|
||||
**plain** cell keeps the raw-shell behaviour (keystrokes → PTY) and has
|
||||
no input strip. We stack terminal + strip in a column so the strip sits
|
||||
beneath the live output. */}
|
||||
raw xterm {@link TerminalView}. Agent cells are **native terminals**
|
||||
(ARCHITECTURE §20): every human keystroke (Enter included) reaches the
|
||||
PTY. There is no mediated-input strip; cross-model delegation flows
|
||||
through the write-portal (which injects at a clean line boundary) and
|
||||
MCP tools. Re-key on the agent so the right opener is captured at mount;
|
||||
the persisted session drives re-attach (never a re-spawn) when
|
||||
navigating. The agent cell passes its {@link WritePortal} so keystroke
|
||||
counting + injection suspension are wired; a plain cell passes none. */}
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
position: "relative",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
minHeight: 0,
|
||||
minWidth: 0,
|
||||
}}
|
||||
>
|
||||
<div style={{ flex: "1 1 auto", minHeight: 0, minWidth: 0 }}>
|
||||
<TerminalView
|
||||
key={`${id}-${agentId ?? "plain"}`}
|
||||
cwd={cwd}
|
||||
open={terminalOpener}
|
||||
reattach={reattachOpener}
|
||||
sessionId={session}
|
||||
onSessionId={(sid) => void vm.setSession(id, sid)}
|
||||
agentMode={agentId != null}
|
||||
/>
|
||||
</div>
|
||||
{agentId != null && (
|
||||
<MediatedInput
|
||||
projectId={projectId}
|
||||
agentId={agentId}
|
||||
busy={busyMap[agentId] ?? false}
|
||||
/>
|
||||
<TerminalView
|
||||
key={`${id}-${agentId ?? "plain"}`}
|
||||
cwd={cwd}
|
||||
open={terminalOpener}
|
||||
reattach={reattachOpener}
|
||||
sessionId={session}
|
||||
onSessionId={(sid) => void vm.setSession(id, sid)}
|
||||
agentMode={agentId != null}
|
||||
portal={agentId != null ? portal : undefined}
|
||||
/>
|
||||
{/* Write-portal overlay (ARCHITECTURE §20.3 step b/e): while a delegation
|
||||
is being injected into the agent's PTY, a grey veil with a centred
|
||||
message sits above the terminal. Only ever shown for an agent cell. */}
|
||||
{agentId != null && overlay && (
|
||||
<div
|
||||
data-testid="write-portal-overlay"
|
||||
role="status"
|
||||
aria-live="polite"
|
||||
style={{
|
||||
position: "absolute",
|
||||
inset: 0,
|
||||
zIndex: 4,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
background: "rgba(0, 0, 0, 0.45)",
|
||||
color: "var(--color-content, #e0e0e0)",
|
||||
fontSize: 13,
|
||||
pointerEvents: "none",
|
||||
userSelect: "none",
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
background: "var(--color-surface, #1e1e1e)",
|
||||
border: "1px solid var(--color-border, #3a3a3a)",
|
||||
borderRadius: 4,
|
||||
padding: "6px 12px",
|
||||
}}
|
||||
>
|
||||
Un agent est en train de parler…
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{busyNotice && (
|
||||
|
||||
Reference in New Issue
Block a user