+ {heading(request.kind)}
+ {request.detail && {request.detail}
}
+ {request.reason && request.reason !== request.detail
+ ? {request.reason}
+ : null}
+ {request.cwd && {request.cwd}
}
+ {request.url && Open requested page}
+ {request.kind === "input" &&
+ {request.questions.map((question) => )}
+
}
+
+ {request.kind === "input"
+ ?
+ : request.kind === "elicitation"
+ ? <>
+
+
+ >
+ : <>
+
+
+ >}
+
+ ;
+}
diff --git a/dashboard/src/components/MultiMonitor/codex-composer.css b/dashboard/src/components/MultiMonitor/codex-composer.css
new file mode 100644
index 00000000..bfa9dd73
--- /dev/null
+++ b/dashboard/src/components/MultiMonitor/codex-composer.css
@@ -0,0 +1,223 @@
+.codex-composer {
+ display: flex;
+ min-height: 112px;
+ flex: 1;
+ flex-direction: column;
+ margin: 0 18px 12px;
+ padding: 13px 14px 10px;
+ border: 1px solid #333;
+ border-radius: 22px;
+ background: #242424;
+ box-shadow: 0 -12px 30px rgba(17, 17, 17, .95);
+ color: #a5a5a5;
+}
+
+.codex-composer-textarea {
+ width: 100%;
+ min-height: 44px;
+ max-height: 116px;
+ padding: 1px 2px;
+ overflow-y: auto;
+ border: 0;
+ outline: 0;
+ resize: none;
+ background: transparent;
+ color: #f2f2f2;
+ flex: 1;
+ font: 15px/1.45 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
+}
+
+.codex-composer:focus-within {
+ border-color: #5a5a5a;
+}
+
+.codex-composer-textarea::placeholder {
+ color: #a5a5a5;
+}
+
+.codex-composer-footer {
+ display: flex;
+ min-width: 0;
+ align-items: center;
+ gap: 9px;
+ font-size: 13px;
+}
+
+.codex-composer-control,
+.codex-composer-submit {
+ display: grid;
+ width: 28px;
+ height: 28px;
+ flex: 0 0 auto;
+ place-items: center;
+ border-radius: 50%;
+}
+
+.codex-composer-control svg,
+.codex-composer-submit svg,
+.codex-composer-custom svg,
+.codex-composer-mic {
+ width: 15px;
+ height: 15px;
+}
+
+.codex-composer-custom {
+ display: flex;
+ align-items: center;
+ gap: 5px;
+}
+
+.codex-composer-spacer {
+ flex: 1;
+}
+
+.codex-composer-model {
+ max-width: 150px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.codex-composer-model[data-state="sent"] {
+ color: #68d6a0;
+}
+
+.codex-composer-model[data-state="error"] {
+ color: #fca5a5;
+}
+
+.codex-composer-model[data-state="sending"] {
+ color: #d4d4d4;
+}
+
+.codex-composer-mic {
+ flex: 0 0 auto;
+}
+
+.codex-composer-submit {
+ padding: 0;
+ border: 0;
+ background: #f2f2f2;
+ color: #202020;
+ cursor: pointer;
+}
+
+.codex-composer-submit:disabled {
+ background: #5a5a5a;
+ color: #292929;
+ cursor: default;
+}
+
+.codex-composer-submit:focus-visible,
+.codex-request-card button:focus-visible,
+.codex-request-card input:focus-visible,
+.codex-request-card select:focus-visible {
+ outline: 2px solid #f2f2f2;
+ outline-offset: 2px;
+}
+
+.codex-request-card {
+ display: grid;
+ max-height: 300px;
+ gap: 8px;
+ margin: -2px -2px 12px;
+ padding: 12px;
+ overflow-y: auto;
+ border: 1px solid #4a4a4a;
+ border-radius: 14px;
+ background: #1b1b1b;
+ color: #dedede;
+ font: 13px/1.4 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
+}
+
+.codex-request-card strong {
+ color: #f1f1f1;
+ font-size: 14px;
+}
+
+.codex-request-card p {
+ margin: 0;
+}
+
+.codex-request-detail {
+ overflow-wrap: anywhere;
+ color: #e5e5e5;
+ white-space: pre-wrap;
+}
+
+.codex-request-reason,
+.codex-request-context,
+.codex-request-card small {
+ color: #999;
+}
+
+.codex-request-context {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.codex-request-card a {
+ color: #8fc9ff;
+}
+
+.codex-request-questions,
+.codex-request-questions label {
+ display: grid;
+ gap: 6px;
+}
+
+.codex-request-questions {
+ gap: 10px;
+}
+
+.codex-request-card input,
+.codex-request-card select {
+ min-width: 0;
+ height: 34px;
+ padding: 0 9px;
+ border: 1px solid #444;
+ border-radius: 8px;
+ background: #242424;
+ color: #f1f1f1;
+ font: inherit;
+}
+
+.codex-request-actions {
+ display: flex;
+ justify-content: flex-end;
+ gap: 7px;
+}
+
+.codex-request-actions button {
+ min-height: 32px;
+ padding: 5px 10px;
+ border: 1px solid #444;
+ border-radius: 8px;
+ background: #2b2b2b;
+ color: #eee;
+ cursor: pointer;
+ font: inherit;
+}
+
+.codex-request-actions button:last-child {
+ background: #ededed;
+ color: #222;
+}
+
+.codex-request-actions button:disabled {
+ cursor: default;
+ opacity: .5;
+}
+
+.codex-request-error {
+ margin: 0 2px 8px;
+ color: #fca5a5;
+ font-size: 12px;
+}
+
+@media (max-width: 620px) {
+ .codex-composer {
+ margin-inline: 10px;
+ }
+}
diff --git a/dashboard/src/components/MultiMonitor/codex-thread.css b/dashboard/src/components/MultiMonitor/codex-thread.css
new file mode 100644
index 00000000..86647b70
--- /dev/null
+++ b/dashboard/src/components/MultiMonitor/codex-thread.css
@@ -0,0 +1,260 @@
+.codex-thread-event {
+ min-width: 0;
+ color: #ededed;
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
+}
+
+.codex-message {
+ display: flex;
+ margin: 30px 0;
+}
+
+.codex-message:first-child {
+ margin-top: 0;
+}
+
+.codex-message[data-role="user"] {
+ justify-content: flex-end;
+ margin-top: 44px;
+ margin-bottom: 48px;
+}
+
+.codex-message[data-role="user"] + .codex-message[data-role="user"] {
+ margin-top: -30px;
+}
+
+.codex-message-body {
+ min-width: 0;
+ max-width: 100%;
+ overflow-wrap: anywhere;
+ font-size: 15.5px;
+ line-height: 1.52;
+}
+
+.codex-message[data-role="user"] .codex-message-body {
+ width: fit-content;
+ max-width: 78%;
+ padding: 11px 15px 12px;
+ border-radius: 20px;
+ background: #242424;
+ color: #f4f4f4;
+}
+
+.codex-message-body > :first-child {
+ margin-top: 0;
+}
+
+.codex-message-body > :last-child {
+ margin-bottom: 0;
+}
+
+.codex-message-body p {
+ margin: 0 0 14px;
+}
+
+.codex-message-body h3,
+.codex-message-body h4,
+.codex-message-body h5 {
+ margin: 18px 0 8px;
+ color: #f4f4f4;
+ font-size: 15.5px;
+ font-weight: 650;
+ line-height: 1.45;
+}
+
+.codex-message-body ul,
+.codex-message-body ol {
+ margin: 8px 0 14px;
+ padding-left: 22px;
+}
+
+.codex-message-body li {
+ margin: 4px 0;
+ padding-left: 2px;
+}
+
+.codex-message-body a {
+ color: #f1f1f1;
+ text-decoration-color: #737373;
+ text-underline-offset: 3px;
+}
+
+.codex-message-body code {
+ padding: 1px 4px;
+ border-radius: 4px;
+ background: #292929;
+ color: #efefef;
+ font: .88em/1.45 ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
+}
+
+.codex-message-body pre {
+ margin: 14px 0;
+ padding: 13px 14px;
+ overflow: auto;
+ border: 1px solid #303030;
+ border-radius: 9px;
+ background: #0b0b0b;
+ color: #e5e5e5;
+ font: 12px/1.55 ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
+ white-space: pre;
+}
+
+.codex-message-body pre code {
+ padding: 0;
+ background: transparent;
+ font: inherit;
+}
+
+.codex-worked {
+ display: flex;
+ align-items: center;
+ gap: 5px;
+ margin: 48px 0 28px;
+ padding-bottom: 13px;
+ border-bottom: 1px solid #292929;
+ color: #8e8e8e;
+ font-size: 15px;
+ line-height: 1.35;
+}
+
+.codex-edit-card {
+ margin: 18px 0 44px;
+ overflow: hidden;
+ border: 1px solid #343434;
+ border-radius: 12px;
+ background: #171717;
+}
+
+.codex-edit-header {
+ display: grid;
+ min-height: 58px;
+ align-items: center;
+ grid-template-columns: 40px minmax(0, 1fr) auto;
+ gap: 10px;
+ padding: 7px 12px;
+ border-bottom: 1px solid #303030;
+ background: #1d1d1d;
+}
+
+.codex-edit-icon {
+ display: grid;
+ width: 40px;
+ height: 40px;
+ place-items: center;
+ border-radius: 8px;
+ background: #111;
+ color: #b7b7b7;
+}
+
+.codex-edit-icon svg {
+ width: 18px;
+ height: 18px;
+}
+
+.codex-edit-heading {
+ display: grid;
+ min-width: 0;
+ gap: 1px;
+}
+
+.codex-edit-heading strong {
+ overflow: hidden;
+ color: #e5e5e5;
+ font-size: 15.5px;
+ font-weight: 500;
+ line-height: 1.25;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.codex-edit-stats {
+ display: inline-flex;
+ gap: 3px;
+ font: 14px/1.3 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
+}
+
+.codex-edit-stats [data-tone="add"] {
+ color: #00c865;
+}
+
+.codex-edit-stats [data-tone="remove"] {
+ color: #ff4a4a;
+}
+
+.codex-edit-actions {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ color: #e1e1e1;
+ font-size: 15px;
+}
+
+.codex-edit-actions > span:first-child {
+ display: inline-flex;
+ align-items: center;
+ gap: 4px;
+}
+
+.codex-edit-actions svg {
+ width: 14px;
+ height: 14px;
+}
+
+.codex-review-button {
+ padding: 5px 10px;
+ border: 1px solid #404040;
+ border-radius: 8px;
+ background: #232323;
+}
+
+.codex-edit-files {
+ color: #bdbdbd;
+ font-size: 15px;
+}
+
+.codex-edit-file {
+ display: flex;
+ min-height: 37px;
+ align-items: center;
+ justify-content: space-between;
+ gap: 12px;
+ padding: 7px 12px;
+ border-bottom: 1px solid #242424;
+}
+
+.codex-edit-file > span:first-child {
+ min-width: 0;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.codex-edit-files > .codex-edit-file:last-child {
+ border-bottom: 0;
+}
+
+.codex-edit-files details summary {
+ display: flex;
+ min-height: 38px;
+ align-items: center;
+ gap: 5px;
+ padding: 8px 12px;
+ background: #202020;
+ color: #e1e1e1;
+ cursor: pointer;
+ list-style: none;
+}
+
+.codex-edit-files details summary::-webkit-details-marker {
+ display: none;
+}
+
+.codex-edit-files details summary svg {
+ width: 15px;
+ height: 15px;
+ transition: transform 120ms ease;
+}
+
+.codex-edit-files details[open] summary svg {
+ transform: rotate(180deg);
+}
diff --git a/dashboard/src/components/MultiMonitor/index.ts b/dashboard/src/components/MultiMonitor/index.ts
new file mode 100644
index 00000000..f1719281
--- /dev/null
+++ b/dashboard/src/components/MultiMonitor/index.ts
@@ -0,0 +1 @@
+export { MultiMonitor } from "./MultiMonitor";
diff --git a/dashboard/src/components/MultiMonitor/multi-monitor.css b/dashboard/src/components/MultiMonitor/multi-monitor.css
new file mode 100644
index 00000000..029c4712
--- /dev/null
+++ b/dashboard/src/components/MultiMonitor/multi-monitor.css
@@ -0,0 +1,313 @@
+.multi-monitor-shell {
+ position: relative;
+ display: grid;
+ height: 100dvh;
+ min-height: 420px;
+ grid-template-rows: 58px minmax(0, 1fr);
+ overflow: hidden;
+ background: #111;
+ color: #f4f4f4;
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
+}
+
+.multi-monitor-toolbar {
+ z-index: 5;
+ display: flex;
+ min-width: 0;
+ align-items: center;
+ justify-content: space-between;
+ gap: 20px;
+ padding: 10px 14px 10px 18px;
+ border-bottom: 1px solid #303030;
+ background: #171717;
+}
+
+.multi-monitor-workspace-title {
+ display: flex;
+ min-width: 0;
+ align-items: baseline;
+ gap: 10px;
+}
+
+.multi-monitor-workspace-title h1 {
+ margin: 0;
+ overflow: hidden;
+ color: #ededed;
+ font-size: 15px;
+ font-weight: 600;
+ letter-spacing: -.01em;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.multi-monitor-count {
+ flex: 0 0 auto;
+ color: #8d8d8d;
+ font-size: 13px;
+}
+
+.multi-monitor-add {
+ display: flex;
+ min-width: 0;
+ align-items: center;
+ gap: 8px;
+}
+
+.multi-monitor-add .ui-select {
+ width: min(420px, 42vw);
+ height: 34px;
+ border-color: #333;
+ background: #1b1b1b;
+ color: #dedede;
+ font: 13px/1.2 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
+}
+
+.multi-monitor-add .ui-button {
+ height: 34px;
+ border-color: #373737;
+ background: #202020;
+ color: #f4f4f4;
+ font-family: inherit;
+ white-space: nowrap;
+}
+
+.multi-monitor-add .ui-button svg {
+ width: 15px;
+ height: 15px;
+}
+
+.multi-monitor-track {
+ display: grid;
+ height: 100%;
+ min-width: 0;
+ min-height: 0;
+ grid-auto-columns: minmax(min(520px, 100vw), 1fr);
+ grid-auto-flow: column;
+ grid-template-rows: minmax(0, 1fr);
+ overflow-x: auto;
+ overscroll-behavior-inline: contain;
+ scrollbar-color: #3c3c3c transparent;
+}
+
+.multi-monitor-column {
+ position: relative;
+ display: grid;
+ min-width: 0;
+ min-height: 0;
+ grid-template-rows: 56px minmax(0, 1fr) auto;
+ border-right: 1px solid #303030;
+ background: #111;
+}
+
+.multi-monitor-column:last-child {
+ border-right: 0;
+}
+
+.multi-monitor-thread-header {
+ display: flex;
+ min-width: 0;
+ align-items: center;
+ justify-content: space-between;
+ gap: 10px;
+ padding: 8px 10px 8px 16px;
+ border-bottom: 1px solid #2c2c2c;
+ background: #141414;
+}
+
+.multi-monitor-thread-identity {
+ display: flex;
+ min-width: 0;
+ align-items: center;
+ gap: 10px;
+}
+
+.multi-monitor-thread-copy {
+ min-width: 0;
+}
+
+.multi-monitor-thread-copy h2 {
+ margin: 0;
+ overflow: hidden;
+ color: #e7e7e7;
+ font-size: 13px;
+ font-weight: 550;
+ line-height: 1.35;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.multi-monitor-thread-copy p {
+ display: flex;
+ margin: 2px 0 0;
+ color: #777;
+ font-size: 11px;
+ line-height: 1.2;
+ gap: 9px;
+}
+
+.multi-monitor-thread-actions {
+ display: flex;
+ flex: 0 0 auto;
+ align-items: center;
+ gap: 2px;
+}
+
+.multi-monitor-status-dot {
+ flex: 0 0 auto;
+ width: 7px;
+ height: 7px;
+ border-radius: 50%;
+ background: #777;
+}
+
+.multi-monitor-column[data-state="working"] .multi-monitor-status-dot,
+.multi-monitor-column[data-state="live"] .multi-monitor-status-dot {
+ background: #5bd497;
+ box-shadow: 0 0 0 3px rgba(91, 212, 151, .1);
+}
+
+.multi-monitor-column[data-state="stale"] .multi-monitor-status-dot,
+.multi-monitor-column[data-state="refreshing"] .multi-monitor-status-dot {
+ background: #d3a758;
+}
+
+.multi-monitor-column-action {
+ display: grid;
+ width: 28px;
+ height: 28px;
+ place-items: center;
+ padding: 0;
+ border: 0;
+ border-radius: 8px;
+ background: transparent;
+ color: #858585;
+ cursor: pointer;
+}
+
+.multi-monitor-column-action:hover {
+ background: #292929;
+ color: #f2f2f2;
+}
+
+.multi-monitor-column-action:focus-visible {
+ outline: 2px solid #d8d8d8;
+ outline-offset: 1px;
+}
+
+.multi-monitor-column-action svg {
+ width: 15px;
+ height: 15px;
+}
+
+.multi-monitor-column-body {
+ min-width: 0;
+ min-height: 0;
+ overflow-y: auto;
+ overscroll-behavior: contain;
+ scrollbar-color: #3c3c3c transparent;
+}
+
+.multi-monitor-conversation {
+ min-height: 100%;
+ padding: 30px 30px 46px;
+}
+
+.multi-monitor-notice,
+.multi-monitor-service-error {
+ margin: 16px 30px;
+ padding: 10px 12px;
+ border: 1px solid #343434;
+ border-radius: 9px;
+ background: #1b1b1b;
+ color: #999;
+ font-size: 13px;
+}
+
+.multi-monitor-notice.is-error,
+.multi-monitor-service-error {
+ border-color: rgba(248, 113, 113, .25);
+ color: #fca5a5;
+}
+
+.multi-monitor-retention {
+ margin: 18px 30px 0;
+ color: #777;
+ font-size: 12px;
+ text-align: center;
+}
+
+.multi-monitor-composer-slot {
+ display: flex;
+ min-height: 124px;
+ align-items: stretch;
+}
+
+.codex-composer-placeholder {
+ min-height: 112px;
+ flex: 1;
+ margin: 0 18px 12px;
+}
+
+.multi-monitor-service-error {
+ position: absolute;
+ z-index: 4;
+ top: 66px;
+ right: 12px;
+ max-width: 420px;
+}
+
+.multi-monitor-empty {
+ display: grid;
+ min-height: 280px;
+ place-content: center;
+ gap: 6px;
+ padding: 24px;
+ color: #888;
+ text-align: center;
+}
+
+.multi-monitor-empty h1 {
+ margin: 0;
+ color: #d4d4d4;
+ font-size: 18px;
+ font-weight: 500;
+}
+
+.multi-monitor-empty p {
+ margin: 0;
+ font-size: 13px;
+}
+
+@media (max-width: 620px) {
+ .multi-monitor-shell {
+ grid-template-rows: auto minmax(0, 1fr);
+ }
+
+ .multi-monitor-toolbar {
+ align-items: stretch;
+ flex-direction: column;
+ gap: 8px;
+ padding: 10px 12px;
+ }
+
+ .multi-monitor-workspace-title {
+ justify-content: space-between;
+ }
+
+ .multi-monitor-add {
+ width: 100%;
+ }
+
+ .multi-monitor-add .ui-select {
+ width: 100%;
+ flex: 1;
+ }
+
+ .multi-monitor-track {
+ grid-auto-columns: 100vw;
+ }
+
+ .multi-monitor-conversation {
+ padding-inline: 18px;
+ }
+}
diff --git a/dashboard/src/components/index.ts b/dashboard/src/components/index.ts
index 5ddf147e..2374ab9e 100644
--- a/dashboard/src/components/index.ts
+++ b/dashboard/src/components/index.ts
@@ -14,4 +14,5 @@ export { OvenDefinition } from "./OvenDefinition";
export { OvenExplainer } from "./OvenExplainer";
export { BurnlistTable, ProjectGroup } from "./ProjectGroup";
export { StreamingDiff } from "./StreamingDiff";
+export { MultiMonitor } from "./MultiMonitor";
export { VisualParityPage } from "./VisualParity";
diff --git a/dashboard/src/hooks/useAgentMonitor.ts b/dashboard/src/hooks/useAgentMonitor.ts
index 72047bbe..71079f42 100644
--- a/dashboard/src/hooks/useAgentMonitor.ts
+++ b/dashboard/src/hooks/useAgentMonitor.ts
@@ -4,29 +4,52 @@ import type { AgentMonitorFeed, AgentMonitorPayload } from "@lib";
import { useOvenLiveData } from "@oven";
type FeedState = { feeds: AgentMonitorFeed[]; error: string; loading: boolean };
-type ActivationState = { error: string; loading: boolean };
+type ActivationState = {
+ canSend: boolean;
+ canSteerExternal: boolean;
+ error: string;
+ loading: boolean;
+ writeToken: string;
+};
type Selection = { repoKey: string; worktreeKey: string; session: string } | null;
type Repository = { repoKey: string; label: string };
+const AGENT_MONITOR_DISCOVERY_REFRESH_MS = 2_000;
export function useAgentMonitorActivation(repositories: Repository[]): ActivationState {
const keys = repositories.map((repository) => repository.repoKey).join(",");
- const [state, setState] = useState