muonroi-cli 1.8.5 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/packages/agent-harness-core/src/event-filter.js +11 -0
- package/dist/packages/agent-harness-core/src/event-redact.js +7 -0
- package/dist/packages/agent-harness-core/src/event-tee.d.ts +20 -4
- package/dist/packages/agent-harness-core/src/event-tee.js +32 -5
- package/dist/packages/agent-harness-core/src/mcp-server.d.ts +14 -0
- package/dist/packages/agent-harness-core/src/mcp-server.js +86 -10
- package/dist/packages/agent-harness-core/src/predicate.d.ts +1 -1
- package/dist/packages/agent-harness-core/src/protocol.d.ts +24 -2
- package/dist/src/agent-harness/mock-model.d.ts +10 -0
- package/dist/src/agent-harness/mock-model.js +6 -2
- package/dist/src/chat/chat-keychain.d.ts +7 -12
- package/dist/src/chat/chat-keychain.js +19 -86
- package/dist/src/cli/keys.d.ts +8 -45
- package/dist/src/cli/keys.js +18 -324
- package/dist/src/council/clarifier.d.ts +4 -2
- package/dist/src/council/clarifier.js +158 -36
- package/dist/src/council/debate-planner.js +3 -2
- package/dist/src/council/debate.js +59 -5
- package/dist/src/council/index.d.ts +23 -0
- package/dist/src/council/index.js +37 -2
- package/dist/src/council/llm.d.ts +62 -0
- package/dist/src/council/llm.js +123 -23
- package/dist/src/council/panel-select.js +13 -3
- package/dist/src/council/planner.js +17 -0
- package/dist/src/council/preflight.d.ts +10 -0
- package/dist/src/council/preflight.js +36 -0
- package/dist/src/council/prompts.d.ts +9 -1
- package/dist/src/council/prompts.js +23 -6
- package/dist/src/council/types.d.ts +10 -0
- package/dist/src/ee/auth.d.ts +19 -0
- package/dist/src/ee/auth.js +39 -0
- package/dist/src/ee/client.js +28 -3
- package/dist/src/ee/ee-onboarding.js +6 -26
- package/dist/src/flow/compaction/compress.d.ts +2 -2
- package/dist/src/flow/compaction/compress.js +21 -8
- package/dist/src/flow/compaction/extract.d.ts +3 -3
- package/dist/src/flow/compaction/extract.js +6 -6
- package/dist/src/flow/compaction/index.d.ts +2 -1
- package/dist/src/flow/compaction/index.js +29 -3
- package/dist/src/flow/compaction/progress.d.ts +35 -0
- package/dist/src/flow/compaction/progress.js +35 -0
- package/dist/src/generated/version.d.ts +1 -1
- package/dist/src/generated/version.js +1 -1
- package/dist/src/gsd/flags.d.ts +11 -0
- package/dist/src/gsd/flags.js +19 -0
- package/dist/src/gsd/plan-council.js +104 -72
- package/dist/src/gsd/verdict-schema.d.ts +1 -1
- package/dist/src/headless/council-answers.js +4 -0
- package/dist/src/index.js +129 -260
- package/dist/src/lsp/builtins.js +3 -1
- package/dist/src/lsp/manager.d.ts +5 -1
- package/dist/src/lsp/manager.js +249 -3
- package/dist/src/lsp/npm-cache.d.ts +11 -1
- package/dist/src/lsp/npm-cache.js +17 -1
- package/dist/src/lsp/runtime.d.ts +6 -1
- package/dist/src/lsp/runtime.js +17 -1
- package/dist/src/lsp/types.d.ts +83 -1
- package/dist/src/lsp/types.js +10 -0
- package/dist/src/mcp/client-pool.js +43 -15
- package/dist/src/mcp/lsp-tools.d.ts +5 -1
- package/dist/src/mcp/lsp-tools.js +93 -2
- package/dist/src/mcp/mcp-keychain.d.ts +3 -5
- package/dist/src/mcp/mcp-keychain.js +9 -49
- package/dist/src/mcp/setup-guide-text.d.ts +1 -1
- package/dist/src/mcp/setup-guide-text.js +22 -2
- package/dist/src/mcp/tools-server.d.ts +10 -0
- package/dist/src/mcp/tools-server.js +10 -2
- package/dist/src/models/catalog.json +19 -19
- package/dist/src/orchestrator/ask-user.d.ts +61 -0
- package/dist/src/orchestrator/ask-user.js +65 -0
- package/dist/src/orchestrator/compaction.d.ts +2 -3
- package/dist/src/orchestrator/compaction.js +8 -8
- package/dist/src/orchestrator/council-manager.js +9 -8
- package/dist/src/orchestrator/council-request.d.ts +49 -0
- package/dist/src/orchestrator/council-request.js +62 -0
- package/dist/src/orchestrator/interactive-pause.d.ts +26 -0
- package/dist/src/orchestrator/interactive-pause.js +36 -0
- package/dist/src/orchestrator/message-processor.d.ts +4 -0
- package/dist/src/orchestrator/message-processor.js +26 -8
- package/dist/src/orchestrator/orchestrator.d.ts +25 -0
- package/dist/src/orchestrator/orchestrator.js +204 -50
- package/dist/src/orchestrator/preprocessor.js +2 -2
- package/dist/src/orchestrator/safety-askcard.d.ts +1 -1
- package/dist/src/orchestrator/safety-askcard.js +5 -2
- package/dist/src/orchestrator/safety-intercept.d.ts +5 -0
- package/dist/src/orchestrator/safety-intercept.js +7 -0
- package/dist/src/orchestrator/stall-watchdog.d.ts +8 -1
- package/dist/src/orchestrator/stall-watchdog.js +24 -3
- package/dist/src/orchestrator/stream-runner.d.ts +13 -3
- package/dist/src/orchestrator/stream-runner.js +54 -21
- package/dist/src/orchestrator/tool-engine.d.ts +19 -0
- package/dist/src/orchestrator/tool-engine.js +241 -25
- package/dist/src/orchestrator/turn-watchdog.d.ts +7 -0
- package/dist/src/orchestrator/turn-watchdog.js +38 -9
- package/dist/src/pil/agent-operating-contract.d.ts +1 -1
- package/dist/src/pil/agent-operating-contract.js +6 -4
- package/dist/src/pil/discovery.d.ts +1 -1
- package/dist/src/pil/discovery.js +2 -2
- package/dist/src/pil/layer1_5-complexity-size.d.ts +7 -0
- package/dist/src/pil/layer1_5-complexity-size.js +31 -5
- package/dist/src/pil/llm-classify.d.ts +78 -3
- package/dist/src/pil/llm-classify.js +351 -111
- package/dist/src/pil/native-capabilities-workbook.d.ts +1 -1
- package/dist/src/pil/native-capabilities-workbook.js +7 -0
- package/dist/src/pil/pipeline.js +2 -0
- package/dist/src/pil/repo-grounding-probe.d.ts +15 -0
- package/dist/src/pil/repo-grounding-probe.js +136 -0
- package/dist/src/pil/repo-structure-hints.d.ts +7 -0
- package/dist/src/pil/repo-structure-hints.js +45 -0
- package/dist/src/product-loop/artifact-io.js +4 -0
- package/dist/src/product-loop/criteria-seed.d.ts +51 -0
- package/dist/src/product-loop/criteria-seed.js +200 -0
- package/dist/src/product-loop/discovery-interview.d.ts +9 -0
- package/dist/src/product-loop/discovery-interview.js +37 -18
- package/dist/src/product-loop/discovery-recommender.js +2 -1
- package/dist/src/product-loop/discovery-schema.js +14 -1
- package/dist/src/product-loop/discovery-triage.d.ts +23 -0
- package/dist/src/product-loop/discovery-triage.js +109 -0
- package/dist/src/product-loop/gather.js +150 -2
- package/dist/src/product-loop/index.js +7 -0
- package/dist/src/product-loop/loop-driver.js +21 -8
- package/dist/src/product-loop/phase-plan.d.ts +16 -0
- package/dist/src/product-loop/phase-plan.js +42 -4
- package/dist/src/product-loop/phase-rituals.d.ts +3 -0
- package/dist/src/product-loop/phase-rituals.js +8 -3
- package/dist/src/product-loop/phase-runner.js +30 -11
- package/dist/src/product-loop/plan-adherence-review.d.ts +26 -0
- package/dist/src/product-loop/plan-adherence-review.js +144 -0
- package/dist/src/product-loop/sprint-runner.d.ts +62 -0
- package/dist/src/product-loop/sprint-runner.js +309 -8
- package/dist/src/product-loop/types.d.ts +25 -0
- package/dist/src/providers/anthropic.d.ts +9 -8
- package/dist/src/providers/anthropic.js +13 -47
- package/dist/src/providers/auth/grok-oauth.d.ts +1 -0
- package/dist/src/providers/auth/grok-oauth.js +30 -5
- package/dist/src/providers/auth/openai-oauth.d.ts +1 -0
- package/dist/src/providers/auth/openai-oauth.js +14 -0
- package/dist/src/providers/auth/token-store.d.ts +9 -9
- package/dist/src/providers/auth/token-store.js +8 -70
- package/dist/src/providers/auth/types.d.ts +8 -0
- package/dist/src/providers/env-store.d.ts +17 -0
- package/dist/src/providers/env-store.js +228 -0
- package/dist/src/providers/keychain.d.ts +21 -17
- package/dist/src/providers/keychain.js +124 -135
- package/dist/src/providers/runtime.d.ts +24 -9
- package/dist/src/providers/runtime.js +48 -37
- package/dist/src/providers/strategies/thinking-mode.js +9 -1
- package/dist/src/providers/strategies/xai.strategy.js +27 -0
- package/dist/src/providers/warm.d.ts +65 -0
- package/dist/src/providers/warm.js +145 -0
- package/dist/src/self-qa/agentic-loop.js +3 -2
- package/dist/src/storage/transcript.js +56 -2
- package/dist/src/tools/git-safety.d.ts +19 -0
- package/dist/src/tools/git-safety.js +168 -0
- package/dist/src/tools/native-tools.d.ts +1 -1
- package/dist/src/tools/native-tools.js +76 -1
- package/dist/src/tools/registry.d.ts +17 -0
- package/dist/src/tools/registry.js +116 -1
- package/dist/src/types/index.d.ts +29 -1
- package/dist/src/ui/app.js +67 -4
- package/dist/src/ui/components/agent-rail-activities.d.ts +26 -0
- package/dist/src/ui/components/agent-rail-activities.js +47 -0
- package/dist/src/ui/components/compact-progress-card.d.ts +24 -0
- package/dist/src/ui/components/compact-progress-card.js +42 -0
- package/dist/src/ui/components/council-phase-timeline.js +17 -2
- package/dist/src/ui/components/council-question-card.js +1 -0
- package/dist/src/ui/components/message-view.d.ts +15 -0
- package/dist/src/ui/components/message-view.js +50 -1
- package/dist/src/ui/components/tool-group.d.ts +15 -3
- package/dist/src/ui/components/tool-group.js +69 -11
- package/dist/src/ui/containers/modals-layer.d.ts +2 -1
- package/dist/src/ui/containers/modals-layer.js +2 -2
- package/dist/src/ui/council-harness-event.d.ts +57 -0
- package/dist/src/ui/council-harness-event.js +46 -0
- package/dist/src/ui/heartbeat-debug.d.ts +29 -0
- package/dist/src/ui/heartbeat-debug.js +45 -0
- package/dist/src/ui/modals/api-key-modal.js +1 -1
- package/dist/src/ui/modals/model-picker-modal.d.ts +8 -18
- package/dist/src/ui/modals/model-picker-modal.js +8 -10
- package/dist/src/ui/slash/ee.js +81 -0
- package/dist/src/ui/slash/menu-items.js +11 -2
- package/dist/src/ui/use-app-logic.js +354 -224
- package/dist/src/ui/utils/agent-activities.d.ts +39 -0
- package/dist/src/ui/utils/agent-activities.js +96 -0
- package/dist/src/ui/utils/group-tool-entries.d.ts +26 -0
- package/dist/src/ui/utils/group-tool-entries.js +111 -0
- package/dist/src/ui/utils/tool-summary.d.ts +21 -0
- package/dist/src/ui/utils/tool-summary.js +91 -0
- package/dist/src/utils/event-loop-monitor.d.ts +85 -0
- package/dist/src/utils/event-loop-monitor.js +107 -0
- package/dist/src/utils/llm-deadline.d.ts +14 -0
- package/dist/src/utils/llm-deadline.js +19 -0
- package/dist/src/utils/loop-profiler.d.ts +102 -0
- package/dist/src/utils/loop-profiler.js +202 -0
- package/dist/src/utils/settings.d.ts +27 -0
- package/dist/src/utils/settings.js +38 -2
- package/dist/src/utils/side-question.d.ts +1 -2
- package/dist/src/utils/side-question.js +2 -2
- package/dist/src/verify/entrypoint.js +51 -16
- package/dist/src/verify/orchestrator.d.ts +1 -1
- package/dist/src/verify/orchestrator.js +20 -3
- package/package.json +1 -2
- package/dist/src/cli/bw-vault.d.ts +0 -55
- package/dist/src/cli/bw-vault.js +0 -133
- package/dist/src/mcp/ee-tools.d.ts +0 -46
- package/dist/src/mcp/ee-tools.js +0 -194
package/dist/src/ui/app.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "@opentui/react/jsx-runtime";
|
|
2
2
|
import { Semantic, SemanticProvider, useAgentInputBridge } from "@muonroi/agent-harness-opentui";
|
|
3
3
|
import os from "os";
|
|
4
|
+
import { useEffect, useMemo, useState } from "react";
|
|
4
5
|
import { isContextRailEnabled, isRoundGroupsEnabled } from "../gsd/flags.js";
|
|
5
6
|
import { getCurrentModel, } from "../utils/settings.js";
|
|
6
7
|
import { formatSubagentName } from "../utils/subagent-display.js";
|
|
@@ -9,6 +10,8 @@ import { ProductStatusCard } from "./cards/product-status-card.js";
|
|
|
9
10
|
import { BtwOverlay } from "./components/btw-overlay.js";
|
|
10
11
|
import { makePairKey } from "./components/bubble-layout.js";
|
|
11
12
|
import { ContextRail } from "./components/context-rail.js";
|
|
13
|
+
import { AgentRailActivities } from "./components/agent-rail-activities.js";
|
|
14
|
+
import { CompactProgressCard } from "./components/compact-progress-card.js";
|
|
12
15
|
import { CopyFlashBanner } from "./components/copy-flash-banner.js";
|
|
13
16
|
import { CouncilDebatePill } from "./components/council-debate-pill.js";
|
|
14
17
|
import { CouncilInfoCardView } from "./components/council-info-card.js";
|
|
@@ -64,6 +67,7 @@ import "./slash/status.js";
|
|
|
64
67
|
import "./slash/ponytail.js";
|
|
65
68
|
import { CONNECT_CHANNELS, } from "./constants.js";
|
|
66
69
|
import { buildPreflightQuestion, } from "./utils/format.js";
|
|
70
|
+
import { collectAgentActivities } from "./utils/agent-activities.js";
|
|
67
71
|
import { toolArgs, toolLabel, tryParseArg } from "./utils/tools.js";
|
|
68
72
|
/**
|
|
69
73
|
* Render the EE "experience injected" chunk for the TUI: the count line PLUS a
|
|
@@ -373,7 +377,15 @@ ${prompt}`;
|
|
|
373
377
|
const SPLASH_PROVIDERS = ["deepseek", "zai", "opencode-go", "xai"];
|
|
374
378
|
import { useAppLogic } from "./use-app-logic.js";
|
|
375
379
|
export function App({ agent, startupConfig, initialMessage, onExit, onRelaunch }) {
|
|
376
|
-
const { activeHaltCard, activeSubagent, activeToast, activeToolCalls, agentRows, agentRuntime, agentsEditorDraft, agentsEditorError, agentsEditorField, agentsEditorModelIndex, agentsEditorSyncKey, agentsModalIndex, agentsSearchQuery, apiKeyError, apiKeyInputRef, apiKeyPrompt, blockPrompt, btwState,
|
|
380
|
+
const { activeHaltCard, activeSubagent, activeToast, activeToolCalls, agentRows, agentRuntime, agentsEditorDraft, agentsEditorError, agentsEditorField, agentsEditorModelIndex, agentsEditorSyncKey, agentsModalIndex, agentsSearchQuery, apiKeyError, apiKeyInputRef, apiKeyPrompt, blockPrompt, btwState, oauthLogin, oauthProviders, configuredProviders, connectModalIndex, contextStats, copyFlashId, councilCardState, councilInfoCards, councilMeta, councilRounds, selectedRound, setSelectedRound, councilMessages, councilTranscriptExpanded, councilPhases, councilPlaceholders, councilProgress, councilStatuses, councilTodoExpanded, compactRun, defaultProvider, disabledModels, disabledProviders, dismissToast, editingMcpId, editingSubagent, expandedMessages, filteredModels, filteredSlashItems, getPartnerLast, getSide, haltSelectedIndex, handlePaste, handleRootMouseDown, handleRootMouseUp, handleSubmit, hasMessages, height, initNewForm, inputRef, isProcessing, isUpdating, lastReasoningElapsedMs, liveTurnSourceLabel, mcpArgsRef, mcpCommandRef, mcpCwdRef, mcpEditorDraft, mcpEditorError, mcpEditorField, mcpEditorSyncKey, mcpEnvRef, mcpHeadersRef, mcpLabelRef, mcpModalIndex, mcpRows, mcpSearchQuery, mcpUrlRef, messages, modeInfo, model, modelInfo, modelPickerFocus, modelPickerIndex, modelSearchQuery, pasteBlocks, pendingCouncilPreflight, pendingCouncilQuestion, pendingPaymentApproval, planQuestions, pointToExistingForm, pqs, preflightCardState, productStatus, providerChipIndex, providersWithKey, queuedMessages, reasoningActive, reasoningEffortByModel, resolveStyle, sandboxMode, sandboxSettings, sandboxSettingsEditBuffer, sandboxSettingsEditing, sandboxSettingsFocusIndex, scheduleModalIndex, scheduleRows, scheduleSearchQuery, scrollRef, newSinceLock, scrollLockedAway, railVisible, sessionId, sessionPickerIndex, sessionPickerList, sessionTitle, sessionTree, showAgentsEditor, showAgentsModal, showApiKeyModal, showConnectModal, showCopyBanner, showMcpEditor, showMcpModal, showModelPicker, showPlanPanel, showSandboxPicker, showScheduleModal, showSessionPicker, showSlashMenu, showTelegramPairModal, showTelegramTokenModal, showUpdateModal, showWalletPicker, slashInputIsMatched, slashMenuIndex, slashSearchQuery, streamContent, streamReasoning, subagentInstructionRef, subagentNameRef, submitApiKey, submitMcpEditor, submitSubagentEditor, submitTelegramPair, submitTelegramToken, t, taskListSnapshot, telegramPairError, telegramPairInputRef, telegramTokenError, telegramTokenInputRef, typeahead, updateInfo, updateOutput, walletDisplayInfo, walletFocusIndex, walletSettings, width, } = useAppLogic({ agent, startupConfig, initialMessage, onExit, onRelaunch });
|
|
381
|
+
// True while a tool group is streaming its items. Gates the legacy inline
|
|
382
|
+
// activeToolCalls list so a running tool isn't printed twice (see below).
|
|
383
|
+
const hasActiveToolGroup = useMemo(() => messages.some((m) => m.type === "tool_group" && m.toolGroup?.state === "active"), [messages]);
|
|
384
|
+
// Roster of every sub-agent / background job spawned this session, for the
|
|
385
|
+
// rail. Derived from the transcript + in-flight calls, so it cannot drift from
|
|
386
|
+
// what actually ran.
|
|
387
|
+
const agentActivities = useMemo(() => collectAgentActivities(messages, activeToolCalls), [messages, activeToolCalls]);
|
|
388
|
+
const [selectedActivity, setSelectedActivity] = useState(null);
|
|
377
389
|
// Agent-mode input bridge: translate harness {op:"type"/"press"} commands into
|
|
378
390
|
// synthetic OpenTUI keypresses (via the shared useAppContext().keyHandler) so
|
|
379
391
|
// an external driver (MCP harness / vitest spec) can actually type into the
|
|
@@ -381,6 +393,40 @@ export function App({ agent, startupConfig, initialMessage, onExit, onRelaunch }
|
|
|
381
393
|
// had no consumer, so type/press silently no-opped (frames flowed out, input
|
|
382
394
|
// was dead). No-op when agentRuntime is undefined (normal interactive mode).
|
|
383
395
|
useAgentInputBridge(agentRuntime);
|
|
396
|
+
// Publish the transcript scrollbox's geometry so a spec can tell WHY a scroll
|
|
397
|
+
// assertion failed. Without it, "the lock did not engage" and "the transcript
|
|
398
|
+
// never overflowed, so there was nothing to scroll away from" are the same
|
|
399
|
+
// observation — which is what made scroll-lock.spec unfixable by inspection
|
|
400
|
+
// (measured: scrollHeight 27 == viewportH 27 at the instant `idle` fired).
|
|
401
|
+
//
|
|
402
|
+
// Opt-in (MUONROI_HARNESS_SCROLL_GEOM=1) on top of agent-mode, so a spec that
|
|
403
|
+
// does not ask for it is bit-for-bit unaffected: this adds a timer and extra
|
|
404
|
+
// frames, and the harness shares a process boundary with a native renderer
|
|
405
|
+
// that segfaults under load. Observability must not perturb the thing it
|
|
406
|
+
// observes.
|
|
407
|
+
//
|
|
408
|
+
// Sampled on an interval, never read during render: scrollHeight/viewport are
|
|
409
|
+
// layout getters, and sampling them outside the render pass keeps them from
|
|
410
|
+
// feeding back into it. setState only on a real change, so a settled
|
|
411
|
+
// transcript stops producing frames.
|
|
412
|
+
const [scrollGeom, setScrollGeom] = useState(null);
|
|
413
|
+
useEffect(() => {
|
|
414
|
+
if (!agentRuntime || process.env.MUONROI_HARNESS_SCROLL_GEOM !== "1")
|
|
415
|
+
return;
|
|
416
|
+
const read = () => {
|
|
417
|
+
const sb = scrollRef.current;
|
|
418
|
+
if (!sb)
|
|
419
|
+
return;
|
|
420
|
+
const next = {
|
|
421
|
+
vpH: sb.viewport?.height ?? -1,
|
|
422
|
+
scrollH: sb.scrollHeight ?? -1,
|
|
423
|
+
manual: Boolean(sb._hasManualScroll),
|
|
424
|
+
};
|
|
425
|
+
setScrollGeom((prev) => prev && prev.vpH === next.vpH && prev.scrollH === next.scrollH && prev.manual === next.manual ? prev : next);
|
|
426
|
+
};
|
|
427
|
+
const id = setInterval(read, 200);
|
|
428
|
+
return () => clearInterval(id);
|
|
429
|
+
}, [agentRuntime, scrollRef]);
|
|
384
430
|
// Context rail (MUONROI_CONTEXT_RAIL): only render when enabled, the user
|
|
385
431
|
// hasn't hidden it (Ctrl+B), and the terminal is wide enough that a fixed
|
|
386
432
|
// side panel doesn't starve the transcript. Below 100 cols it stays inline.
|
|
@@ -466,7 +512,22 @@ export function App({ agent, startupConfig, initialMessage, onExit, onRelaunch }
|
|
|
466
512
|
const renderCouncilMeta = (cols) => (_jsxs(_Fragment, { children: [councilPhases.length > 0 && (_jsx(Semantic, { id: "council-phases", role: "listbox", name: "Council Phases", children: _jsx(CouncilPhaseTimeline, { phases: councilPhases, theme: t, expanded: councilTranscriptExpanded }) })), productStatus && _jsx(ProductStatusCard, { data: productStatus, theme: t }), councilStatuses.length > 0 && (_jsx(Semantic, { id: "council-status", role: "listbox", name: "Council Status", children: _jsx(CouncilStatusList, { statuses: councilStatuses, theme: t, sessionId: sessionId ?? undefined }) })), councilInfoCards.map((card, idx) => (_jsx(Semantic, { id: `council-card-${idx}`, role: "listitem", name: card.title || `Council card ${idx}`, children: _jsx(CouncilInfoCardView, { card: card, terminalCols: cols, theme: t }, `info-card-${idx}-${card.title}`) }, `sem-info-${idx}-${card.title}`))), isRoundGroupsEnabled() && councilRounds.length > 0 && (_jsx(CouncilRailRounds, { rounds: councilRounds, selected: selectedRound, onSelect: setSelectedRound, width: cols, theme: t }))] }));
|
|
467
513
|
return (_jsx(SemanticProvider, { registry: agentRuntime
|
|
468
514
|
? agentRuntime.registry
|
|
469
|
-
: { register: () => () => { }, update: () => { }, snapshot: () => ({ nodes: [] }), clear: () => { } }, children: _jsxs("box", { width: width, height: height, backgroundColor: t.background, flexDirection: "column", onMouseUp: handleRootMouseUp, onMouseDown: handleRootMouseDown, children: [copyFlashId > 0 ? _jsx(CopyFlashBanner, { t: t, width: width }) : null, hasMessages ? (_jsxs("box", { flexGrow: 1, flexDirection: "column", children: [_jsx(SessionHeader, { t: t, modeInfo: modeInfo, sessionTitle: sessionTitle, sessionId: sessionId, onCopySessionId: showCopyBanner }), _jsxs("box", { flexGrow: 1, paddingBottom: 1, paddingTop: 1, paddingLeft: 2, paddingRight: 2, gap: 1, flexDirection: "row", children: [_jsxs("box", { flexDirection: "column", flexGrow: 1, gap: 1, children: [_jsx(Semantic, { id: "log", role: "log", props: {
|
|
515
|
+
: { register: () => () => { }, update: () => { }, snapshot: () => ({ nodes: [] }), clear: () => { } }, children: _jsxs("box", { width: width, height: height, backgroundColor: t.background, flexDirection: "column", onMouseUp: handleRootMouseUp, onMouseDown: handleRootMouseDown, children: [copyFlashId > 0 ? _jsx(CopyFlashBanner, { t: t, width: width }) : null, hasMessages ? (_jsxs("box", { flexGrow: 1, flexDirection: "column", children: [_jsx(SessionHeader, { t: t, modeInfo: modeInfo, sessionTitle: sessionTitle, sessionId: sessionId, onCopySessionId: showCopyBanner }), _jsxs("box", { flexGrow: 1, paddingBottom: 1, paddingTop: 1, paddingLeft: 2, paddingRight: 2, gap: 1, flexDirection: "row", children: [_jsxs("box", { flexDirection: "column", flexGrow: 1, gap: 1, children: [_jsx(Semantic, { id: "log", role: "log", props: {
|
|
516
|
+
scrollTop: scrollRef.current?.scrollTop ?? 0,
|
|
517
|
+
locked: scrollLockedAway,
|
|
518
|
+
newSinceLock,
|
|
519
|
+
// Present only in agent-mode (see the sampler above).
|
|
520
|
+
...(scrollGeom
|
|
521
|
+
? {
|
|
522
|
+
viewportH: scrollGeom.vpH,
|
|
523
|
+
scrollHeight: scrollGeom.scrollH,
|
|
524
|
+
manualScroll: scrollGeom.manual,
|
|
525
|
+
// The one derived answer every scroll spec actually
|
|
526
|
+
// wants: is there anything to scroll away from?
|
|
527
|
+
overflows: scrollGeom.scrollH > scrollGeom.vpH,
|
|
528
|
+
}
|
|
529
|
+
: {}),
|
|
530
|
+
}, children: _jsxs("scrollbox", { ref: scrollRef, flexGrow: 1, stickyScroll: true, stickyStart: "bottom", children: [(() => {
|
|
470
531
|
const mcpRuns = computeMcpRunInfo(messages);
|
|
471
532
|
// Phase 5 F7 — index of the last assistant message so
|
|
472
533
|
// MessageView can skip auto-collapse on it (final answer
|
|
@@ -482,7 +543,9 @@ export function App({ agent, startupConfig, initialMessage, onExit, onRelaunch }
|
|
|
482
543
|
return messages.map((msg, i) => (
|
|
483
544
|
// biome-ignore lint/suspicious/noArrayIndexKey: append-only message log; index is part of the stable semantic id
|
|
484
545
|
_jsx(Semantic, { id: `msg-${i}`, role: "listitem", name: `${msg.type ?? "msg"}:${String(msg.content ?? "").slice(0, 40)}`, children: _jsx(MessageView, { entry: msg, index: i, t: t, modeColor: modeInfo.color, expandedMessages: expandedMessages, mcpRun: mcpRuns[i], isFinalAssistant: i === _lastAssistantIdx }, `${msg.timestamp?.getTime?.() ?? i}-${msg.type}-${msg.remoteKey ?? ""}-${String(msg.content ?? "").slice(0, 24)}`) }, `sem-${msg.timestamp?.getTime?.() ?? i}-${i}`)));
|
|
485
|
-
})(), liveTurnSourceLabel && (activeToolCalls.length > 0 || streamContent || isProcessing) && (_jsx("box", { paddingLeft: 3, marginTop: 1, flexShrink: 0, children: _jsx("text", { fg: t.textMuted, children: liveTurnSourceLabel }) })), activeToolCalls
|
|
546
|
+
})(), liveTurnSourceLabel && (activeToolCalls.length > 0 || streamContent || isProcessing) && (_jsx("box", { paddingLeft: 3, marginTop: 1, flexShrink: 0, children: _jsx("text", { fg: t.textMuted, children: liveTurnSourceLabel }) })), activeToolCalls
|
|
547
|
+
.filter((tc) => !hasActiveToolGroup || tc.function.name === "task" || tc.function.name === "delegate")
|
|
548
|
+
.map((tc) => tc.function.name === "task" ? (_jsx(SubagentTaskLine, { t: t, agent: tryParseArg(tc, "agent") || "sub-agent", label: toolArgs(tc) || "Working", pending: true }, tc.id)) : tc.function.name === "delegate" ? (_jsx(DelegationTaskLine, { t: t, label: toolArgs(tc) || "Background research", pending: true, id: undefined }, tc.id)) : (_jsx(InlineTool, { t: t, pending: true, children: toolLabel(tc) }, tc.id))), activeSubagent && _jsx(SubagentActivity, { t: t, status: activeSubagent }), compactRun && (_jsx(CompactProgressCard, { t: t, progress: compactRun.progress, startedAt: compactRun.startedAt })), !railActive && renderCouncilMeta(width), (() => {
|
|
486
549
|
const turns = councilMessages.map((cm, idx) => ({ cm, idx }));
|
|
487
550
|
const debateTurns = turns.filter(({ cm }) => cm.kind !== "synthesis");
|
|
488
551
|
const synthesisTurns = turns.filter(({ cm }) => cm.kind === "synthesis");
|
|
@@ -556,7 +619,7 @@ export function App({ agent, startupConfig, initialMessage, onExit, onRelaunch }
|
|
|
556
619
|
!pendingCouncilQuestion &&
|
|
557
620
|
!pendingCouncilPreflight && _jsx(ShimmerText, { t: t, text: "Planning next moves" }), showPlanPanel && _jsx(PlanQuestionsPanel, { t: t, questions: planQuestions, state: pqs }), pendingPaymentApproval && _jsx(PaymentApprovalPanel, { t: t, payment: pendingPaymentApproval }), activeHaltCard && (_jsx(HaltRecoveryCard, { halt: activeHaltCard, selectedIndex: haltSelectedIndex, terminalCols: width, theme: t })), initNewForm && _jsx(InitNewFormCard, { state: initNewForm, terminalCols: width, theme: t }), pointToExistingForm && (_jsx(PointToExistingFormCard, { state: pointToExistingForm, terminalCols: width, theme: t })), councilProgress && (_jsx(Semantic, { id: "continue-as-council-progress", role: "log", name: "Council brainstorm", children: _jsx("box", { flexDirection: "column", borderStyle: "single", borderColor: councilProgress.status === "error" ? t.initFormError : t.text, padding: 1, marginTop: 1, children: _jsxs("text", { fg: t.text, children: [councilProgress.status === "running" && "Council brainstorming — writing spec.md...", councilProgress.status === "done" &&
|
|
558
621
|
`Council brainstorm complete: ${councilProgress.specPath}${councilProgress.hasContent ? "" : " (no content — production council wiring deferred)"}`, councilProgress.status === "error" &&
|
|
559
|
-
`Council brainstorm failed: ${councilProgress.error}`] }) }) })), pendingCouncilQuestion && councilCardState && (_jsx(CouncilQuestionCard, { question: pendingCouncilQuestion, theme: t, state: councilCardState })), pendingCouncilPreflight && preflightCardState && (_jsx(CouncilQuestionCard, { question: buildPreflightQuestion(pendingCouncilPreflight), theme: t, state: preflightCardState }))] }) }), scrollLockedAway && (_jsx("box", { flexShrink: 0, alignItems: "center", marginTop: 0, children: _jsx(JumpToLatestPill, { newSinceLock: newSinceLock }) })), btwState && _jsx(BtwOverlay, { state: btwState, theme: t }), taskListSnapshot && (_jsx("box", { flexShrink: 0, paddingLeft: 2, paddingRight: 2, marginBottom: 1, children: _jsx(TaskListPanel, { snapshot: taskListSnapshot, t: t, expanded: false, collapsed: councilStatuses.length > 0 && !councilTodoExpanded }) })), activeToast ? (_jsx("box", { flexShrink: 0, marginBottom: 1, children: _jsx(Toast, { level: activeToast.level, text: activeToast.text, theme: t, onDismiss: dismissToast }, activeToast.id) })) : null, _jsx("box", { flexShrink: 0, children: _jsx(PromptBox, { t: t, inputRef: inputRef, isProcessing: isProcessing, showModelPicker: showModelPicker, showSandboxPicker: showSandboxPicker, showWalletPicker: showWalletPicker, showSlashMenu: showSlashMenu, showPlanQuestions: showPlanPanel, showApiKeyModal: showApiKeyModal, blockPrompt: blockPrompt, onSubmit: handleSubmit, onPaste: handlePaste, pasteBlocks: pasteBlocks, modeInfo: modeInfo, model: model, modelInfo: modelInfo, contextStats: contextStats, queuedCount: queuedMessages.length, queuedMessages: queuedMessages, typeahead: typeahead, slashItems: filteredSlashItems, slashSelectedIndex: slashMenuIndex, slashInputIsMatched: slashInputIsMatched, composerValue: showSlashMenu ? `/${slashSearchQuery}` : undefined }) })] }), railActive && (_jsxs(ContextRail, { width: railWidth, rows: railRows, children: [_jsx(SessionTreeCard, { nodes: sessionTree }), renderCouncilMeta(railWidth)] }))] }), _jsx("box", { paddingLeft: 2, paddingRight: 2, flexShrink: 0, children: _jsx(StatusBar, {}) }), _jsxs("box", { paddingLeft: 2, paddingRight: 2, paddingBottom: 1, flexDirection: "row", flexShrink: 0, children: [_jsx("text", { fg: t.textDim, children: agent.getCwd().replace(os.homedir(), "~") }), sandboxMode === "shuru" ? _jsx("text", { fg: "#f97316", children: " · sandbox" }) : null, _jsx("box", { flexGrow: 1 })] })] })) : (_jsxs(_Fragment, { children: [_jsxs("box", { flexGrow: 1, alignItems: "center", paddingLeft: 2, paddingRight: 2, children: [_jsx("box", { flexGrow: 1, minHeight: 0 }), _jsx("box", { flexShrink: 0, alignItems: "center", children: _jsx(HeroLogo, { t: t }) }), _jsx("box", { height: 1, minHeight: 0, flexShrink: 1 }), _jsxs("box", { width: "100%", maxWidth: 75, flexShrink: 0, children: [activeToast ? (_jsx("box", { marginBottom: 1, flexShrink: 0, children: _jsx(Toast, { level: activeToast.level, text: activeToast.text, theme: t, onDismiss: dismissToast }, activeToast.id) })) : null, _jsx(PromptBox, { t: t, inputRef: inputRef, isProcessing: isProcessing, showModelPicker: showModelPicker, showSandboxPicker: showSandboxPicker, showWalletPicker: showWalletPicker, showSlashMenu: showSlashMenu, showPlanQuestions: showPlanPanel, showApiKeyModal: showApiKeyModal, blockPrompt: blockPrompt, onSubmit: handleSubmit, onPaste: handlePaste, pasteBlocks: pasteBlocks, modeInfo: modeInfo, model: model, modelInfo: modelInfo, contextStats: contextStats, placeholder: "What are we building?", typeahead: typeahead, slashItems: filteredSlashItems, slashSelectedIndex: slashMenuIndex, slashInputIsMatched: slashInputIsMatched, composerValue: showSlashMenu ? `/${slashSearchQuery}` : undefined })] }), _jsx("box", { height: 2, minHeight: 0, flexShrink: 1 }), _jsx("box", { flexGrow: 1, minHeight: 0 })] }), updateInfo?.hasUpdate && (_jsx("box", { paddingLeft: 2, paddingRight: 2, flexDirection: "row", flexShrink: 0, children: _jsxs("text", { fg: "#f59e0b", children: ["┃ Update available: v", startupConfig.version, " → v", updateInfo.latestVersion, " — run /update to install"] }) })), isUpdating && (_jsx("box", { paddingLeft: 2, paddingRight: 2, flexDirection: "row", flexShrink: 0, children: _jsx("text", { fg: "#f59e0b", children: "┃ Updating..." }) })), updateOutput && !isUpdating && (_jsx("box", { paddingLeft: 2, paddingRight: 2, flexDirection: "row", flexShrink: 0, children: _jsxs("text", { fg: updateOutput.startsWith("Update complete") ? "#22c55e" : "#ef4444", children: ["┃ ", updateOutput] }) })), _jsx("box", { paddingLeft: 2, paddingRight: 2, flexShrink: 0, children: _jsx(StatusBar, {}) }), _jsxs("box", { paddingLeft: 2, paddingRight: 2, paddingBottom: 1, flexDirection: "row", flexShrink: 0, children: [_jsx("text", { fg: t.textDim, children: agent.getCwd().replace(os.homedir(), "~") }), sandboxMode === "shuru" ? _jsx("text", { fg: "#f97316", children: " · sandbox" }) : null, _jsx("box", { flexGrow: 1 }), _jsx("text", { fg: t.textDim, children: `v${startupConfig.version}` })] })] })), showApiKeyModal && (_jsx(ApiKeyModal, { t: t, width: width, height: height, inputRef: apiKeyInputRef, error: apiKeyError, onSubmit: submitApiKey })), showUpdateModal && updateInfo && (_jsx(UpdateModal, { t: t, width: width, height: height, currentVersion: startupConfig.version, latestVersion: updateInfo.latestVersion })), showMcpModal && !showMcpEditor && (_jsx(McpBrowserModal, { t: t, width: width, height: height, selectedIndex: mcpModalIndex, searchQuery: mcpSearchQuery, rows: mcpRows })), showMcpEditor && (_jsx(McpEditorModal, { t: t, width: width, height: height, draft: mcpEditorDraft, focusedField: mcpEditorField, syncKey: mcpEditorSyncKey, error: mcpEditorError, title: editingMcpId ? "Edit MCP Server" : "Add MCP Server", labelRef: mcpLabelRef, urlRef: mcpUrlRef, headersRef: mcpHeadersRef, commandRef: mcpCommandRef, argsRef: mcpArgsRef, cwdRef: mcpCwdRef, envRef: mcpEnvRef, onSubmit: submitMcpEditor })), showScheduleModal && (_jsx(ScheduleBrowserModal, { t: t, width: width, height: height, selectedIndex: scheduleModalIndex, searchQuery: scheduleSearchQuery, rows: scheduleRows })), showAgentsModal && !showAgentsEditor && (_jsx(SubagentsBrowserModal, { t: t, width: width, height: height, selectedIndex: agentsModalIndex, searchQuery: agentsSearchQuery, rows: agentRows })), showAgentsEditor && (_jsx(SubagentEditorModal, { t: t, width: width, height: height, draft: agentsEditorDraft, focusedField: agentsEditorField, modelIndex: agentsEditorModelIndex, error: agentsEditorError, title: editingSubagent ? `Edit sub-agent: ${formatSubagentName(editingSubagent.name)}` : "Add sub-agent", nameRef: subagentNameRef, instructionRef: subagentInstructionRef, onSubmit: submitSubagentEditor, showRemoveHint: !!editingSubagent }, `subagent-editor-${agentsEditorSyncKey}`)), showModelPicker && (_jsx(ModelPickerModal, { t: t, currentModel: model, selectedIndex: modelPickerIndex, width: width, height: height, searchQuery: modelSearchQuery, filteredModels: filteredModels, reasoningEffortByModel: reasoningEffortByModel, configuredProviders: configuredProviders, disabledProviders: disabledProviders, disabledModels: disabledModels, defaultProvider: defaultProvider, focus: modelPickerFocus, providerChipIndex: providerChipIndex, providersWithKey: providersWithKey, apiKeyPrompt: apiKeyPrompt,
|
|
622
|
+
`Council brainstorm failed: ${councilProgress.error}`] }) }) })), pendingCouncilQuestion && councilCardState && (_jsx(CouncilQuestionCard, { question: pendingCouncilQuestion, theme: t, state: councilCardState })), pendingCouncilPreflight && preflightCardState && (_jsx(CouncilQuestionCard, { question: buildPreflightQuestion(pendingCouncilPreflight), theme: t, state: preflightCardState }))] }) }), scrollLockedAway && (_jsx("box", { flexShrink: 0, alignItems: "center", marginTop: 0, children: _jsx(JumpToLatestPill, { newSinceLock: newSinceLock }) })), btwState && _jsx(BtwOverlay, { state: btwState, theme: t }), taskListSnapshot && (_jsx("box", { flexShrink: 0, paddingLeft: 2, paddingRight: 2, marginBottom: 1, children: _jsx(TaskListPanel, { snapshot: taskListSnapshot, t: t, expanded: false, collapsed: councilStatuses.length > 0 && !councilTodoExpanded }) })), activeToast ? (_jsx("box", { flexShrink: 0, marginBottom: 1, children: _jsx(Toast, { level: activeToast.level, text: activeToast.text, theme: t, onDismiss: dismissToast }, activeToast.id) })) : null, _jsx("box", { flexShrink: 0, children: _jsx(PromptBox, { t: t, inputRef: inputRef, isProcessing: isProcessing, showModelPicker: showModelPicker, showSandboxPicker: showSandboxPicker, showWalletPicker: showWalletPicker, showSlashMenu: showSlashMenu, showPlanQuestions: showPlanPanel, showApiKeyModal: showApiKeyModal, blockPrompt: blockPrompt, onSubmit: handleSubmit, onPaste: handlePaste, pasteBlocks: pasteBlocks, modeInfo: modeInfo, model: model, modelInfo: modelInfo, contextStats: contextStats, queuedCount: queuedMessages.length, queuedMessages: queuedMessages, typeahead: typeahead, slashItems: filteredSlashItems, slashSelectedIndex: slashMenuIndex, slashInputIsMatched: slashInputIsMatched, composerValue: showSlashMenu ? `/${slashSearchQuery}` : undefined }) })] }), railActive && (_jsxs(ContextRail, { width: railWidth, rows: railRows, children: [_jsx(SessionTreeCard, { nodes: sessionTree }), _jsx(AgentRailActivities, { activities: agentActivities, selected: selectedActivity, onSelect: setSelectedActivity, width: railWidth, t: t }), renderCouncilMeta(railWidth)] }))] }), _jsx("box", { paddingLeft: 2, paddingRight: 2, flexShrink: 0, children: _jsx(StatusBar, {}) }), _jsxs("box", { paddingLeft: 2, paddingRight: 2, paddingBottom: 1, flexDirection: "row", flexShrink: 0, children: [_jsx("text", { fg: t.textDim, children: agent.getCwd().replace(os.homedir(), "~") }), sandboxMode === "shuru" ? _jsx("text", { fg: "#f97316", children: " · sandbox" }) : null, _jsx("box", { flexGrow: 1 })] })] })) : (_jsxs(_Fragment, { children: [_jsxs("box", { flexGrow: 1, alignItems: "center", paddingLeft: 2, paddingRight: 2, children: [_jsx("box", { flexGrow: 1, minHeight: 0 }), _jsx("box", { flexShrink: 0, alignItems: "center", children: _jsx(HeroLogo, { t: t }) }), _jsx("box", { height: 1, minHeight: 0, flexShrink: 1 }), _jsxs("box", { width: "100%", maxWidth: 75, flexShrink: 0, children: [activeToast ? (_jsx("box", { marginBottom: 1, flexShrink: 0, children: _jsx(Toast, { level: activeToast.level, text: activeToast.text, theme: t, onDismiss: dismissToast }, activeToast.id) })) : null, _jsx(PromptBox, { t: t, inputRef: inputRef, isProcessing: isProcessing, showModelPicker: showModelPicker, showSandboxPicker: showSandboxPicker, showWalletPicker: showWalletPicker, showSlashMenu: showSlashMenu, showPlanQuestions: showPlanPanel, showApiKeyModal: showApiKeyModal, blockPrompt: blockPrompt, onSubmit: handleSubmit, onPaste: handlePaste, pasteBlocks: pasteBlocks, modeInfo: modeInfo, model: model, modelInfo: modelInfo, contextStats: contextStats, placeholder: "What are we building?", typeahead: typeahead, slashItems: filteredSlashItems, slashSelectedIndex: slashMenuIndex, slashInputIsMatched: slashInputIsMatched, composerValue: showSlashMenu ? `/${slashSearchQuery}` : undefined })] }), _jsx("box", { height: 2, minHeight: 0, flexShrink: 1 }), _jsx("box", { flexGrow: 1, minHeight: 0 })] }), updateInfo?.hasUpdate && (_jsx("box", { paddingLeft: 2, paddingRight: 2, flexDirection: "row", flexShrink: 0, children: _jsxs("text", { fg: "#f59e0b", children: ["┃ Update available: v", startupConfig.version, " → v", updateInfo.latestVersion, " — run /update to install"] }) })), isUpdating && (_jsx("box", { paddingLeft: 2, paddingRight: 2, flexDirection: "row", flexShrink: 0, children: _jsx("text", { fg: "#f59e0b", children: "┃ Updating..." }) })), updateOutput && !isUpdating && (_jsx("box", { paddingLeft: 2, paddingRight: 2, flexDirection: "row", flexShrink: 0, children: _jsxs("text", { fg: updateOutput.startsWith("Update complete") ? "#22c55e" : "#ef4444", children: ["┃ ", updateOutput] }) })), _jsx("box", { paddingLeft: 2, paddingRight: 2, flexShrink: 0, children: _jsx(StatusBar, {}) }), _jsxs("box", { paddingLeft: 2, paddingRight: 2, paddingBottom: 1, flexDirection: "row", flexShrink: 0, children: [_jsx("text", { fg: t.textDim, children: agent.getCwd().replace(os.homedir(), "~") }), sandboxMode === "shuru" ? _jsx("text", { fg: "#f97316", children: " · sandbox" }) : null, _jsx("box", { flexGrow: 1 }), _jsx("text", { fg: t.textDim, children: `v${startupConfig.version}` })] })] })), showApiKeyModal && (_jsx(ApiKeyModal, { t: t, width: width, height: height, inputRef: apiKeyInputRef, error: apiKeyError, onSubmit: submitApiKey })), showUpdateModal && updateInfo && (_jsx(UpdateModal, { t: t, width: width, height: height, currentVersion: startupConfig.version, latestVersion: updateInfo.latestVersion })), showMcpModal && !showMcpEditor && (_jsx(McpBrowserModal, { t: t, width: width, height: height, selectedIndex: mcpModalIndex, searchQuery: mcpSearchQuery, rows: mcpRows })), showMcpEditor && (_jsx(McpEditorModal, { t: t, width: width, height: height, draft: mcpEditorDraft, focusedField: mcpEditorField, syncKey: mcpEditorSyncKey, error: mcpEditorError, title: editingMcpId ? "Edit MCP Server" : "Add MCP Server", labelRef: mcpLabelRef, urlRef: mcpUrlRef, headersRef: mcpHeadersRef, commandRef: mcpCommandRef, argsRef: mcpArgsRef, cwdRef: mcpCwdRef, envRef: mcpEnvRef, onSubmit: submitMcpEditor })), showScheduleModal && (_jsx(ScheduleBrowserModal, { t: t, width: width, height: height, selectedIndex: scheduleModalIndex, searchQuery: scheduleSearchQuery, rows: scheduleRows })), showAgentsModal && !showAgentsEditor && (_jsx(SubagentsBrowserModal, { t: t, width: width, height: height, selectedIndex: agentsModalIndex, searchQuery: agentsSearchQuery, rows: agentRows })), showAgentsEditor && (_jsx(SubagentEditorModal, { t: t, width: width, height: height, draft: agentsEditorDraft, focusedField: agentsEditorField, modelIndex: agentsEditorModelIndex, error: agentsEditorError, title: editingSubagent ? `Edit sub-agent: ${formatSubagentName(editingSubagent.name)}` : "Add sub-agent", nameRef: subagentNameRef, instructionRef: subagentInstructionRef, onSubmit: submitSubagentEditor, showRemoveHint: !!editingSubagent }, `subagent-editor-${agentsEditorSyncKey}`)), showModelPicker && (_jsx(ModelPickerModal, { t: t, currentModel: model, selectedIndex: modelPickerIndex, width: width, height: height, searchQuery: modelSearchQuery, filteredModels: filteredModels, reasoningEffortByModel: reasoningEffortByModel, configuredProviders: configuredProviders, disabledProviders: disabledProviders, disabledModels: disabledModels, defaultProvider: defaultProvider, focus: modelPickerFocus, providerChipIndex: providerChipIndex, providersWithKey: providersWithKey, apiKeyPrompt: apiKeyPrompt, oauthProviders: oauthProviders, oauthLogin: oauthLogin })), showSessionPicker && (_jsx(SessionPickerModal, { t: t, sessions: sessionPickerList, focusIndex: sessionPickerIndex, width: width, height: height })), showWalletPicker && (_jsx(WalletPickerModal, { t: t, settings: walletSettings, walletInfo: walletDisplayInfo, focusIndex: walletFocusIndex, width: width, height: height })), showSandboxPicker && (_jsx(SandboxPickerModal, { t: t, currentMode: sandboxMode, settings: sandboxSettings, focusIndex: sandboxSettingsFocusIndex, editing: sandboxSettingsEditing, editBuffer: sandboxSettingsEditBuffer, width: width, height: height })), showConnectModal && (_jsx(ConnectModal, { t: t, width: width, height: height, selectedIndex: connectModalIndex, channels: CONNECT_CHANNELS })), showTelegramTokenModal && (_jsx(TelegramTokenModal, { t: t, width: width, height: height, inputRef: telegramTokenInputRef, error: telegramTokenError, onSubmit: submitTelegramToken })), showTelegramPairModal && (_jsx(TelegramPairModal, { t: t, width: width, height: height, inputRef: telegramPairInputRef, error: telegramPairError, onSubmit: () => void submitTelegramPair() }))] }) }));
|
|
560
623
|
}
|
|
561
624
|
export { computeMcpRunInfo } from "./components/message-view.js";
|
|
562
625
|
function shouldOpenApiKeyModalForKey(key) {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/ui/components/agent-rail-activities.tsx
|
|
3
|
+
*
|
|
4
|
+
* Rail block listing every sub-agent / background job this session spawned.
|
|
5
|
+
*
|
|
6
|
+
* Before this, a spawned agent was visible only as a transient line in the
|
|
7
|
+
* transcript: it scrolled away, and once done there was nowhere to see WHAT it
|
|
8
|
+
* was asked or what it returned. The rail keeps the roster on screen, and a row
|
|
9
|
+
* opens to show the prompt and the result.
|
|
10
|
+
*
|
|
11
|
+
* Selection is by click (OpenTUI onMouseDown per row, same as
|
|
12
|
+
* council-rail-rounds.tsx) — clicking the open row closes it again.
|
|
13
|
+
*/
|
|
14
|
+
import type { AgentActivity } from "../utils/agent-activities.js";
|
|
15
|
+
import type { Theme } from "../theme.js";
|
|
16
|
+
/** Row prefix naming what was spawned: the agent type, or "shell" for a job. */
|
|
17
|
+
export declare function activityPrefix(a: AgentActivity): string;
|
|
18
|
+
export interface AgentRailActivitiesProps {
|
|
19
|
+
activities: AgentActivity[];
|
|
20
|
+
/** Tool-call id of the open row, or null when all are collapsed. */
|
|
21
|
+
selected: string | null;
|
|
22
|
+
onSelect: (id: string | null) => void;
|
|
23
|
+
width: number;
|
|
24
|
+
t: Theme;
|
|
25
|
+
}
|
|
26
|
+
export declare function AgentRailActivities({ activities, selected, onSelect, width, t }: AgentRailActivitiesProps): import("react").ReactNode;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "@opentui/react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* src/ui/components/agent-rail-activities.tsx
|
|
4
|
+
*
|
|
5
|
+
* Rail block listing every sub-agent / background job this session spawned.
|
|
6
|
+
*
|
|
7
|
+
* Before this, a spawned agent was visible only as a transient line in the
|
|
8
|
+
* transcript: it scrolled away, and once done there was nowhere to see WHAT it
|
|
9
|
+
* was asked or what it returned. The rail keeps the roster on screen, and a row
|
|
10
|
+
* opens to show the prompt and the result.
|
|
11
|
+
*
|
|
12
|
+
* Selection is by click (OpenTUI onMouseDown per row, same as
|
|
13
|
+
* council-rail-rounds.tsx) — clicking the open row closes it again.
|
|
14
|
+
*/
|
|
15
|
+
import { ListItem, Region } from "../primitives/semantic-primitives.js";
|
|
16
|
+
import { trunc } from "../utils/text.js";
|
|
17
|
+
/** Characters of detail shown when a row is open. Keeps the rail scrollable. */
|
|
18
|
+
const DETAIL_LIMIT = 600;
|
|
19
|
+
const GLYPH = { running: "▸", done: "✓", failed: "✗" };
|
|
20
|
+
function statusColor(status, t) {
|
|
21
|
+
if (status === "failed")
|
|
22
|
+
return t.diffRemovedFg;
|
|
23
|
+
if (status === "done")
|
|
24
|
+
return t.diffAddedFg;
|
|
25
|
+
return t.accent;
|
|
26
|
+
}
|
|
27
|
+
/** Row prefix naming what was spawned: the agent type, or "shell" for a job. */
|
|
28
|
+
export function activityPrefix(a) {
|
|
29
|
+
if (a.kind === "background")
|
|
30
|
+
return "shell";
|
|
31
|
+
return a.agent || (a.kind === "delegate" ? "delegate" : "sub-agent");
|
|
32
|
+
}
|
|
33
|
+
export function AgentRailActivities({ activities, selected, onSelect, width, t }) {
|
|
34
|
+
if (activities.length === 0)
|
|
35
|
+
return null;
|
|
36
|
+
const running = activities.filter((a) => a.status === "running").length;
|
|
37
|
+
return (_jsx(Region, { id: "agent-rail-activities", name: "Agents", value: `${activities.length}`, props: {
|
|
38
|
+
count: activities.length,
|
|
39
|
+
running,
|
|
40
|
+
selected: selected ?? "",
|
|
41
|
+
ids: activities.map((a) => a.id).join(","),
|
|
42
|
+
}, children: _jsxs("box", { flexDirection: "column", flexShrink: 0, children: [_jsx("text", { fg: t.textMuted, attributes: 1, children: running > 0 ? `Agents (${running} running)` : "Agents" }), activities.map((a) => {
|
|
43
|
+
const isOpen = selected === a.id;
|
|
44
|
+
return (_jsx(ListItem, { id: `rail-agent-${a.id}`, name: `${activityPrefix(a)} ${a.label}`, state: a.status, selected: isOpen || undefined, children: _jsxs("box", { flexDirection: "column", flexShrink: 0, children: [_jsx("box", { flexDirection: "row", onMouseDown: () => onSelect(isOpen ? null : a.id), children: _jsxs("text", { children: [_jsx("span", { style: { fg: statusColor(a.status, t) }, children: `${GLYPH[a.status]} ` }), _jsx("span", { style: { fg: t.textMuted }, children: `${activityPrefix(a)} ` }), _jsx("span", { style: { fg: t.text }, children: trunc(a.label, Math.max(8, width - 12)) })] }) }), isOpen && (_jsx("box", { paddingLeft: 2, flexDirection: "column", children: _jsx("text", { fg: t.textDim, children: a.detail.trim() ? trunc(a.detail, DETAIL_LIMIT) : "(no output yet)" }) }))] }) }, a.id));
|
|
45
|
+
})] }) }));
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=agent-rail-activities.js.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/ui/components/compact-progress-card.tsx
|
|
3
|
+
*
|
|
4
|
+
* Live progress for a /compact run. Before this, `/compact` awaited two LLM
|
|
5
|
+
* passes with zero UI — a minute of a frozen screen that reads as a hang.
|
|
6
|
+
*
|
|
7
|
+
* The percentage comes from the compaction pipeline itself
|
|
8
|
+
* (flow/compaction/progress.ts), not from a timer: it advances on real stage
|
|
9
|
+
* boundaries, and the dominant compress pass streams so it moves continuously.
|
|
10
|
+
* The elapsed clock is what proves liveness while a stage is mid-flight.
|
|
11
|
+
*/
|
|
12
|
+
import type { CompactProgress } from "../../flow/compaction/progress.js";
|
|
13
|
+
import type { Theme } from "../theme.js";
|
|
14
|
+
/** Elapsed as the status line shows it: "42s", "1m 19s". */
|
|
15
|
+
export declare function fmtCompactElapsed(ms: number): string;
|
|
16
|
+
/** Filled/unfilled bar of exactly `width` cells for a 0..100 percent. */
|
|
17
|
+
export declare function compactBar(percent: number, width?: number): string;
|
|
18
|
+
export interface CompactProgressCardProps {
|
|
19
|
+
progress: CompactProgress;
|
|
20
|
+
/** Epoch ms the run started. */
|
|
21
|
+
startedAt: number;
|
|
22
|
+
t: Theme;
|
|
23
|
+
}
|
|
24
|
+
export declare function CompactProgressCard({ progress, startedAt, t }: CompactProgressCardProps): import("react").ReactNode;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "@opentui/react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* src/ui/components/compact-progress-card.tsx
|
|
4
|
+
*
|
|
5
|
+
* Live progress for a /compact run. Before this, `/compact` awaited two LLM
|
|
6
|
+
* passes with zero UI — a minute of a frozen screen that reads as a hang.
|
|
7
|
+
*
|
|
8
|
+
* The percentage comes from the compaction pipeline itself
|
|
9
|
+
* (flow/compaction/progress.ts), not from a timer: it advances on real stage
|
|
10
|
+
* boundaries, and the dominant compress pass streams so it moves continuously.
|
|
11
|
+
* The elapsed clock is what proves liveness while a stage is mid-flight.
|
|
12
|
+
*/
|
|
13
|
+
import { useEffect, useState } from "react";
|
|
14
|
+
import { Region } from "../primitives/semantic-primitives.js";
|
|
15
|
+
const BAR_WIDTH = 34;
|
|
16
|
+
/** Elapsed as the status line shows it: "42s", "1m 19s". */
|
|
17
|
+
export function fmtCompactElapsed(ms) {
|
|
18
|
+
const totalSec = Math.max(0, Math.floor(ms / 1000));
|
|
19
|
+
if (totalSec < 60)
|
|
20
|
+
return `${totalSec}s`;
|
|
21
|
+
return `${Math.floor(totalSec / 60)}m ${totalSec % 60}s`;
|
|
22
|
+
}
|
|
23
|
+
/** Filled/unfilled bar of exactly `width` cells for a 0..100 percent. */
|
|
24
|
+
export function compactBar(percent, width = BAR_WIDTH) {
|
|
25
|
+
const clamped = Math.max(0, Math.min(100, Number.isFinite(percent) ? percent : 0));
|
|
26
|
+
const filled = Math.round((clamped / 100) * width);
|
|
27
|
+
return `${"█".repeat(filled)}${"░".repeat(Math.max(0, width - filled))}`;
|
|
28
|
+
}
|
|
29
|
+
export function CompactProgressCard({ progress, startedAt, t }) {
|
|
30
|
+
// The card owns its clock: a compaction can sit inside one LLM pass for a
|
|
31
|
+
// minute, and re-rendering the whole transcript once a second just to advance
|
|
32
|
+
// a timer is a cost the rest of the app should not pay.
|
|
33
|
+
const [now, setNow] = useState(() => Date.now());
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
const id = setInterval(() => setNow(Date.now()), 1000);
|
|
36
|
+
return () => clearInterval(id);
|
|
37
|
+
}, []);
|
|
38
|
+
const elapsed = fmtCompactElapsed(now - startedAt);
|
|
39
|
+
const pct = Math.max(0, Math.min(100, Math.round(progress.percent)));
|
|
40
|
+
return (_jsx(Region, { id: "compact-progress", name: progress.label, value: `${pct}%`, state: progress.stage, props: { percent: pct, elapsedMs: now - startedAt }, children: _jsxs("box", { paddingLeft: 3, marginTop: 1, flexShrink: 0, flexDirection: "column", children: [_jsxs("text", { children: [_jsx("span", { style: { fg: t.accent }, children: "✦ " }), _jsx("span", { style: { fg: t.text }, children: progress.label }), _jsx("span", { style: { fg: t.textMuted }, children: ` (${elapsed})` })] }), _jsx("box", { paddingLeft: 2, children: _jsxs("text", { children: [_jsx("span", { style: { fg: t.accent }, children: compactBar(pct) }), _jsx("span", { style: { fg: t.textMuted }, children: ` ${pct}%` })] }) })] }) }));
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=compact-progress-card.js.map
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "@opentui/react/jsx-runtime";
|
|
2
2
|
import { useEffect, useState } from "react";
|
|
3
|
+
import { heartbeatDebug } from "../heartbeat-debug.js";
|
|
3
4
|
const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
4
5
|
function Spinner() {
|
|
5
6
|
const [frame, setFrame] = useState(0);
|
|
6
7
|
useEffect(() => {
|
|
7
|
-
const id = setInterval(() =>
|
|
8
|
+
const id = setInterval(() => {
|
|
9
|
+
heartbeatDebug("phase-timeline-spinner", "timer");
|
|
10
|
+
setFrame((n) => (n + 1) % SPINNER_FRAMES.length);
|
|
11
|
+
}, 100);
|
|
8
12
|
return () => clearInterval(id);
|
|
9
13
|
}, []);
|
|
10
14
|
return _jsx(_Fragment, { children: SPINNER_FRAMES[frame] });
|
|
@@ -31,7 +35,10 @@ function formatElapsed(ms) {
|
|
|
31
35
|
function useHeartbeat(tickMs = 1000) {
|
|
32
36
|
const [, force] = useState(0);
|
|
33
37
|
useEffect(() => {
|
|
34
|
-
const id = setInterval(() =>
|
|
38
|
+
const id = setInterval(() => {
|
|
39
|
+
heartbeatDebug("phase-timeline-heartbeat", "timer");
|
|
40
|
+
force((n) => n + 1);
|
|
41
|
+
}, tickMs);
|
|
35
42
|
return () => clearInterval(id);
|
|
36
43
|
}, [tickMs]);
|
|
37
44
|
return Date.now();
|
|
@@ -74,6 +81,14 @@ export function CouncilPhaseTimeline({ phases, theme: t, expanded = false }) {
|
|
|
74
81
|
const markerColor = isDone ? t.planOptionCheck : t.accent;
|
|
75
82
|
const labelColor = isDone ? t.textMuted : t.text;
|
|
76
83
|
const liveElapsedMs = p.state === "active" && typeof p.startedAt === "number" ? Math.max(0, now - p.startedAt) : p.elapsedMs;
|
|
84
|
+
if (p.state === "active") {
|
|
85
|
+
heartbeatDebug("phase-timeline", "render", {
|
|
86
|
+
label: p.label,
|
|
87
|
+
liveElapsedMs,
|
|
88
|
+
startedAt: p.startedAt,
|
|
89
|
+
now,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
77
92
|
const elapsed = formatElapsed(liveElapsedMs);
|
|
78
93
|
const meta = elapsed ? ` (${elapsed})` : isDone ? "" : " …";
|
|
79
94
|
return (_jsxs("box", { flexDirection: "column", children: [_jsxs("box", { children: [_jsx("text", { fg: markerColor, children: marker }), _jsx("text", { fg: labelColor, children: ` ${p.label}` }), meta && _jsx("text", { fg: t.textDim, children: meta })] }), p.detail && (_jsx("box", { paddingLeft: 2, children: _jsx("text", { fg: t.textMuted, children: `└ ${truncate(p.detail, 100)}` }) }))] }, p.phaseId));
|
|
@@ -9,6 +9,7 @@ const PHASE_LABEL = {
|
|
|
9
9
|
"pil-acceptance": "Confirm the goal",
|
|
10
10
|
"tool-loop-cap": "Tool-loop checkpoint",
|
|
11
11
|
"safety-override": "Safety check",
|
|
12
|
+
"ask-user": "The agent is asking",
|
|
12
13
|
};
|
|
13
14
|
/**
|
|
14
15
|
* AskUserQuestion-style card. Controlled component — parent owns the state
|
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
import type { ChatEntry } from "../../types/index.js";
|
|
2
2
|
import type { Theme } from "../theme.js";
|
|
3
|
+
/**
|
|
4
|
+
* Index of the newest entry that renders a `ctrl+e` affordance, or -1.
|
|
5
|
+
*
|
|
6
|
+
* The TUI has ONE global ctrl+e, so it must resolve to a single target. Every
|
|
7
|
+
* component that prints "ctrl+e expand" must be represented here, otherwise the
|
|
8
|
+
* affordance lies: it tells the user a key works while nothing is listening.
|
|
9
|
+
* Mirrors the collapse conditions in AssistantMessageContent / UserMessageContent
|
|
10
|
+
* / ReasoningContent / ToolGroupView — keep the two in sync.
|
|
11
|
+
*/
|
|
12
|
+
export declare function findLastCollapsibleIndex(messages: ChatEntry[]): number;
|
|
13
|
+
export declare function ReasoningContent({ content, t, expanded }: {
|
|
14
|
+
content: string;
|
|
15
|
+
t: Theme;
|
|
16
|
+
expanded: boolean;
|
|
17
|
+
}): import("react").ReactNode;
|
|
3
18
|
export declare function AssistantMessageContent({ content, t, expanded, isFinal, }: {
|
|
4
19
|
content: string;
|
|
5
20
|
t: Theme;
|
|
@@ -28,6 +28,55 @@ const USER_MSG_COLLAPSED_LINES = 5;
|
|
|
28
28
|
// to this many lines to stop the chat scroll wall and cut markdown re-render
|
|
29
29
|
// cost. 8 fits comfortably on a short terminal and conveys the gist.
|
|
30
30
|
const ASSISTANT_MSG_COLLAPSED_LINES = 8;
|
|
31
|
+
// Chain-of-thought is never the answer — it is the model talking to itself.
|
|
32
|
+
// Unlike the final assistant message (which must stay fully visible), reasoning
|
|
33
|
+
// collapses even on the final entry: once the turn is answered, a wall of
|
|
34
|
+
// "[Thought]" above the reply is pure noise. 3 lines keeps a gist visible.
|
|
35
|
+
const REASONING_COLLAPSED_LINES = 3;
|
|
36
|
+
/**
|
|
37
|
+
* Index of the newest entry that renders a `ctrl+e` affordance, or -1.
|
|
38
|
+
*
|
|
39
|
+
* The TUI has ONE global ctrl+e, so it must resolve to a single target. Every
|
|
40
|
+
* component that prints "ctrl+e expand" must be represented here, otherwise the
|
|
41
|
+
* affordance lies: it tells the user a key works while nothing is listening.
|
|
42
|
+
* Mirrors the collapse conditions in AssistantMessageContent / UserMessageContent
|
|
43
|
+
* / ReasoningContent / ToolGroupView — keep the two in sync.
|
|
44
|
+
*/
|
|
45
|
+
export function findLastCollapsibleIndex(messages) {
|
|
46
|
+
let lastAssistantIdx = -1;
|
|
47
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
48
|
+
if (messages[i]?.type === "assistant") {
|
|
49
|
+
lastAssistantIdx = i;
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const lineCount = (s) => (s ? s.split("\n").length : 0);
|
|
54
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
55
|
+
const m = messages[i];
|
|
56
|
+
if (!m)
|
|
57
|
+
continue;
|
|
58
|
+
if (m.type === "user" && lineCount(m.content) > USER_MSG_COLLAPSED_LINES)
|
|
59
|
+
return i;
|
|
60
|
+
if (m.type === "assistant") {
|
|
61
|
+
if (lineCount(m.reasoning) > REASONING_COLLAPSED_LINES)
|
|
62
|
+
return i;
|
|
63
|
+
// The final assistant message never auto-collapses its body (F7), so it
|
|
64
|
+
// offers no body affordance to toggle.
|
|
65
|
+
if (i !== lastAssistantIdx && lineCount(m.content) > ASSISTANT_MSG_COLLAPSED_LINES)
|
|
66
|
+
return i;
|
|
67
|
+
}
|
|
68
|
+
if (m.type === "tool_group" && m.toolGroup?.state === "done" && (m.toolGroup?.items.length ?? 0) > 0)
|
|
69
|
+
return i;
|
|
70
|
+
}
|
|
71
|
+
return -1;
|
|
72
|
+
}
|
|
73
|
+
export function ReasoningContent({ content, t, expanded }) {
|
|
74
|
+
const lines = content.split("\n");
|
|
75
|
+
const isLong = lines.length > REASONING_COLLAPSED_LINES;
|
|
76
|
+
const body = !isLong || expanded ? content : lines.slice(0, REASONING_COLLAPSED_LINES).join("\n");
|
|
77
|
+
const hidden = lines.length - REASONING_COLLAPSED_LINES;
|
|
78
|
+
return (_jsxs("box", { paddingLeft: 0, marginBottom: 1, flexShrink: 0, flexDirection: "column", children: [_jsx("text", { fg: t.textMuted, children: "[Thought] Thought" }), _jsxs("box", { border: ["left"], borderColor: t.textMuted, paddingLeft: 2, marginTop: 1, flexDirection: "column", children: [_jsx(Markdown, { content: body, t: t }), isLong && (_jsxs("text", { fg: t.textDim, children: ["ctrl+e ", _jsx("span", { style: { fg: t.textMuted }, children: expanded ? "collapse" : `expand (${hidden} more lines)` })] }))] })] }));
|
|
79
|
+
}
|
|
31
80
|
export function AssistantMessageContent({ content, t, expanded, isFinal, }) {
|
|
32
81
|
// Strip stray model self-annotation macros (e.g. grok's trailing
|
|
33
82
|
// `\confidence{85}`) that leak into the answer — not instructed in the prompt.
|
|
@@ -142,7 +191,7 @@ function MessageViewImpl({ entry, index, t, modeColor, expandedMessages, mcpRun,
|
|
|
142
191
|
// ignore
|
|
143
192
|
}
|
|
144
193
|
}
|
|
145
|
-
return (_jsxs("box", { paddingLeft: 3, marginTop: 1, flexShrink: 0, flexDirection: "column", children: [entry.sourceLabel ? _jsx("text", { fg: t.textMuted, children: entry.sourceLabel }) : null, entry.reasoning ? (
|
|
194
|
+
return (_jsxs("box", { paddingLeft: 3, marginTop: 1, flexShrink: 0, flexDirection: "column", children: [entry.sourceLabel ? _jsx("text", { fg: t.textMuted, children: entry.sourceLabel }) : null, entry.reasoning ? (_jsx(ReasoningContent, { content: entry.reasoning, t: t, expanded: expandedMessages?.has(index) ?? false })) : null, fallbackSr ? (_jsx(StructuredResponseView, { t: t, sr: fallbackSr, modeColor: entry.modeColor || modeColor })) : (_jsx(AssistantMessageContent, { content: entry.content, t: t, expanded: expandedMessages?.has(index) ?? false, isFinal: isFinalAssistant === true }))] }));
|
|
146
195
|
}
|
|
147
196
|
case "tool_call":
|
|
148
197
|
return (_jsx("box", { paddingLeft: 3, marginTop: 1, children: _jsxs("text", { children: [_jsx("span", { style: { fg: entry.modeColor || modeColor }, children: "▣ " }), _jsx("span", { style: { fg: t.textMuted }, children: entry.content.replace("▣ ", "") })] }) }));
|
|
@@ -3,16 +3,28 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Claude-Code-style "tool streak" panel — replaces the previous one-line-per-
|
|
5
5
|
* tool render. Lifecycle:
|
|
6
|
-
* - active → header "●
|
|
7
|
-
* - done → collapsed "●
|
|
6
|
+
* - active → header "● Reading 2 files… (N tools · Ms)" + indented item list
|
|
7
|
+
* - done → collapsed "● Read 2 files, ran 1 shell command ctrl+e to expand"
|
|
8
8
|
* - failed → always-expanded header + items, error item flagged red
|
|
9
9
|
*
|
|
10
|
+
* The header names the work per CATEGORY (see utils/tool-summary.ts) rather
|
|
11
|
+
* than saying "Done": once the group collapses that line is all the user has,
|
|
12
|
+
* so it has to carry the meaning. Items add the detail a glance needs — the
|
|
13
|
+
* full shell command for bash, "Wrote N lines to <path>" before a write diff.
|
|
14
|
+
*
|
|
10
15
|
* The active group lives at the tail of the message stream as a regular
|
|
11
16
|
* ChatEntry of type "tool_group"; closing the streak (assistant text arrives
|
|
12
17
|
* or stream ends) flips its state to done/failed in place.
|
|
13
18
|
*/
|
|
14
|
-
import type { ChatEntry } from "../../types/index.js";
|
|
19
|
+
import type { ChatEntry, FileDiff, ToolCall } from "../../types/index.js";
|
|
15
20
|
import type { Theme } from "../theme.js";
|
|
21
|
+
/** The command a bash item ran, split for multi-line display. Null when absent. */
|
|
22
|
+
export declare function bashCommandLines(tc: ToolCall): string[] | null;
|
|
23
|
+
/**
|
|
24
|
+
* One-line prose outcome for a write/edit, e.g. "Wrote 161 lines to a.ts" or
|
|
25
|
+
* "Edited a.ts (+12 -3)". Null when the tool isn't a file mutation.
|
|
26
|
+
*/
|
|
27
|
+
export declare function writeOutcomeLine(toolName: string, diff: FileDiff): string | null;
|
|
16
28
|
export interface ToolGroupViewProps {
|
|
17
29
|
entry: ChatEntry;
|
|
18
30
|
t: Theme;
|